merging in shooter code with less bugs
diff --git a/aos/aos.swig b/aos/aos.swig
deleted file mode 100644
index fee0700..0000000
--- a/aos/aos.swig
+++ /dev/null
@@ -1,45 +0,0 @@
-%module aos
-
-%include "std_string.i";
-%include "stdint.i";
-
-%{
-#include <time.h>
-#include "aos/common/time.h"
-#include "aos/common/queue.h"
-static_assert(sizeof(int) == sizeof(clockid_t),
- "Sizeof clockid_t has changed.");
-%}
-
-%apply int { clockid_t };
-
-// Makes the constructors from c++ types public so that this code can be called
-// from other places.
-%typemap(javabody) SWIGTYPE %{
- private long swigCPtr;
- protected boolean swigCMemOwn;
-
- public $javaclassname(long cPtr, boolean cMemoryOwn) {
- swigCMemOwn = cMemoryOwn;
- swigCPtr = cPtr;
- }
-
- public static long getCPtr($javaclassname obj) {
- return (obj == null) ? 0 : obj.swigCPtr;
- }
-%}
-
-%rename(add) operator+;
-%rename(subtract) operator-;
-%rename(multiply) operator*;
-%rename(divide) operator/;
-%rename(modulo) operator%;
-%rename(equalTo) operator==;
-%rename(notEqual) operator!=;
-%rename(greaterThan) operator>;
-%rename(lessThan) operator<;
-%rename(leq) operator<=;
-%rename(geq) operator>=;
-
-%include "aos/common/time.h"
-%include "aos/common/queue.h"
diff --git a/aos/atom_code/ipc_lib/unique_message_ptr.h b/aos/atom_code/ipc_lib/unique_message_ptr.h
new file mode 100644
index 0000000..e30a4c0
--- /dev/null
+++ b/aos/atom_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/build/aos.gyp b/aos/build/aos.gyp
index d9a1bf6..12ac2a1 100644
--- a/aos/build/aos.gyp
+++ b/aos/build/aos.gyp
@@ -2,9 +2,31 @@
# For the cRIO, shared_library means to build a .out file, NOT a shared library.
# This means that depending on shared libraries doesn't work very well.
# Shared libraries don't seem to be supported by the powerpc-wrs-vxworks
-# tools and gyp doesn't like a static_library that depends on static_librarys.
+# tools and gyp doesn't like a static_library that depends only on
+# other static_librarys.
{
'targets': [
+ # A target for things used by the logging implementation (except die) to
+ # depend on that allows linking successfully with logging calls but has no
+ # way to get initialized and so is basically useless unless something else
+ # links in the rest of the logging stuff.
+ {
+ '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)/common/common.gyp:die',
+ ],
+ },
{
'target_name': 'logging',
'type': 'static_library',
@@ -21,31 +43,15 @@
],
'export_dependent_settings': [
'<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:queue',
- ]
+ ],
}],
],
'dependencies': [
'<(AOS)/common/common.gyp:time',
'<(AOS)/common/common.gyp:once',
- '<(AOS)/common/common.gyp:mutex',
- '<(AOS)/common/common.gyp:die',
+ 'logging_interface',
+ '<(AOS)/common/common.gyp:queue_types',
],
},
- {
-# Private to make Brian happy. Don't use elsewhere in so targets or risk things
-# breaking.
- 'target_name': 'aos_swig',
- 'type': 'static_library',
- 'sources': [
- '<(AOS)/aos.swig',
- ],
- 'variables': {
- 'package': 'aos',
- },
- 'dependencies': [
- '<(AOS)/common/common.gyp:queues',
- ],
- 'includes': ['../build/swig.gypi'],
- },
],
}
diff --git a/aos/build/aos.gypi b/aos/build/aos.gypi
index 144bffb..c2c0813 100644
--- a/aos/build/aos.gypi
+++ b/aos/build/aos.gypi
@@ -79,6 +79,15 @@
'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 isn't a test that should get run by
+ # `build.sh tests`.
+ 'is_special_test%': 0,
+ },
'conditions': [
['DEBUG=="yes"', {
'cflags': [
@@ -163,9 +172,6 @@
'NOMINMAX',
],
}, {
- 'variables': {
- 'no_rsync%': 0,
- },
'target_conditions': [
# default to putting outputs into rsync_dir
['no_rsync==0 and _type!="static_library"', {
@@ -176,14 +182,6 @@
'product_dir': '<(so_dir)',
}
],
- ['_type=="loadable_module" or _type=="shared_library"', {
- 'ldflags': [
-# Support loading other shared objects that are in the same directory but not
-# the shared object load path. Required for using the swig-generated libs.
- '-Wl,-rpath=\\$$ORIGIN',
- ],
- }
- ],
],
'ldflags': [
'-pthread',
diff --git a/aos/build/aos_all.gyp b/aos/build/aos_all.gyp
index 426f84e..2584ee2 100644
--- a/aos/build/aos_all.gyp
+++ b/aos/build/aos_all.gyp
@@ -12,13 +12,17 @@
'dependencies': [
#'../linux_code/camera/camera.gyp:CameraHTTPStreamer',
#'../linux_code/camera/camera.gyp:CameraReader',
- '../linux_code/core/core.gyp:*',
+ '../linux_code/linux_code.gyp:core',
+ '../linux_code/logging/logging.gyp:binary_log_writer',
+ '../linux_code/logging/logging.gyp:log_streamer',
+ '../linux_code/logging/logging.gyp:log_displayer',
'../linux_code/ipc_lib/ipc_lib.gyp:raw_queue_test',
'../linux_code/ipc_lib/ipc_lib.gyp:ipc_stress_test',
'../linux_code/starter/starter.gyp:starter_exe',
'../linux_code/starter/starter.gyp:netconsole',
'../common/common.gyp:queue_test',
'../common/common.gyp:die_test',
+ '../common/common.gyp:queue_types_test',
'../common/util/util.gyp:trapezoid_profile_test',
'../common/util/util.gyp:wrapping_counter_test',
'<(DEPTH)/bbb_cape/src/bbb/bbb.gyp:cows_test',
diff --git a/aos/build/build.sh b/aos/build/build.sh
index 9073492..cf79fc3 100755
--- a/aos/build/build.sh
+++ b/aos/build/build.sh
@@ -36,6 +36,7 @@
if [[ "${ACTION}" != "clean" && ( ! -d ${OUTDIR} || -n \
"`find ${AOS}/.. -newer ${BUILD_NINJA} \( -name '*.gyp' -or -name '*.gypi' \)`" ) ]]; then
+ echo 'Running gyp...' 1>&2
# This is a gyp "file" that we pipe into gyp so that it will put the output
# in a directory named what we want where we want it.
GYP_INCLUDE=$(cat <<END
@@ -62,6 +63,7 @@
if [ ${PLATFORM} == crio ]; then
sed -i 's/nm -gD/nm/g' ${BUILD_NINJA}
fi
+ echo 'Done running gyp.' 1>&2
fi
if [ "${ACTION}" == "clean" ]; then
@@ -84,6 +86,6 @@
${OUTDIR}/lib/FRC_UserProgram.out
fi
if [[ ${ACTION} == tests ]]; then
- find ${OUTDIR}/tests -executable -exec {} \;
+ find ${OUTDIR}/tests -executable -exec ${AOS}/build/run_test.sh {} \;
fi
fi
diff --git a/aos/build/create_jar b/aos/build/create_jar
deleted file mode 100755
index 2ef496e..0000000
--- a/aos/build/create_jar
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-source `dirname $0`/jdk_tools_common
-
-# This is a helper script that compiles java files into a jar.
-
-SOURCEFILEPARENTDIRS=`echo $4 | tr -d '"'`
-SOURCEFILEDIRS=`echo $1 | tr -d '"'`
-[ -n "${SOURCEFILEPARENTDIRS}" ] && SOURCEFILEDIRS+=" `find ${SOURCEFILEPARENTDIRS} -type d -maxdepth 1`"
-SOURCEFILES=`find ${SOURCEFILEDIRS} -name *.java`
-MANIFEST_FILE=$5
-OUTPUT_JAR=$6
-HEADER_DIR=$7
-GEN_HEADERS=$8
-
-CLASSFILES_DIR=${TMPDIR}/classfiles
-
-[ -a ${CLASSFILES_DIR} ] && rm -r ${CLASSFILES_DIR}
-mkdir ${CLASSFILES_DIR}
-[ -a ${HEADER_DIR} ] && rm -r ${HEADER_DIR}
-mkdir -p ${HEADER_DIR}
-
-javac -d ${CLASSFILES_DIR} -classpath "${EXTRA_CLASSPATH}" ${SOURCEFILES}
-
-jar cfm ${OUTPUT_JAR} ${MANIFEST_FILE} \
- `find ${CLASSFILES_DIR} -name *.class | \
- sed "s:${CLASSFILES_DIR}/\(.*\):-C ${CLASSFILES_DIR} \1:g"`
-
-[ -z ${GEN_HEADERS} ] || javah -d ${HEADER_DIR} \
- -classpath "${EXTRA_CLASSPATH}:${OUTPUT_JAR}" ${GEN_HEADERS}
diff --git a/aos/build/download_externals.sh b/aos/build/download_externals.sh
index 36ce094..9af583b 100755
--- a/aos/build/download_externals.sh
+++ b/aos/build/download_externals.sh
@@ -49,13 +49,6 @@
[ -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 the javacv binaries
-JAVACV_VERSION=0.2
-JAVACV_DIR=${EXTERNALS}/javacv-bin
-JAVACV_ZIP=${EXTERNALS}/javacv-${JAVACV_VERSION}-bin.zip
-[ -f ${JAVACV_ZIP} ] || wget http://javacv.googlecode.com/files/javacv-${JAVACV_VERSION}-bin.zip -O ${JAVACV_ZIP}
-[ -d ${JAVACV_DIR} ] || ( cd ${EXTERNALS} && unzip ${JAVACV_ZIP} )
-
# get the simple one-jar template jar
ONEJAR_VERSION=0.97
ONEJAR_JAR=${EXTERNALS}/one-jar-boot-${ONEJAR_VERSION}.jar
@@ -77,7 +70,7 @@
# get gtest
GTEST_VERSION=1.6.0
-GTEST_DIR=${EXTERNALS}/gtest-${GTEST_VERSION}
+GTEST_DIR=${EXTERNALS}/gtest-${GTEST_VERSION}-p1
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 )
diff --git a/aos/build/externals.gyp b/aos/build/externals.gyp
index ee10566..4165574 100644
--- a/aos/build/externals.gyp
+++ b/aos/build/externals.gyp
@@ -14,7 +14,7 @@
# These versions have to be kept in sync with the ones in download_externals.sh.
'eigen_version': '3.1.3',
- 'gtest_version': '1.6.0',
+ 'gtest_version': '1.6.0-p1',
'onejar_version': '0.97',
'ctemplate_version': '129',
'gflags_version': '2.0',
@@ -75,26 +75,6 @@
},
},
{
- 'target_name': 'javacv',
- 'type': 'none',
- 'variables': {
- 'javacv_dir': '<(externals_abs)/javacv-bin',
- },
- 'direct_dependent_settings': {
- 'include_dirs': [
- '/usr/lib/jvm/default-java/include',
- '/usr/lib/jvm/default-java/include/linux',
- ],
- 'variables': {
- 'classpath': [
- '<(javacv_dir)/javacv.jar',
- '<(javacv_dir)/javacpp.jar',
- '<(javacv_dir)/javacv-linux-x86.jar',
- ],
- },
- },
- },
- {
'target_name': 'opencv',
'type': 'none',
'link_settings': {
@@ -180,9 +160,12 @@
'direct_dependent_settings': {
'include_dirs': ['<(externals)/gtest-<(gtest_version)/include'],
'target_conditions': [
- ['_type=="executable"', {
+ ['_type=="executable" and is_special_test==0', {
'product_dir': '<(test_dir)',
},
+ ], ['_type=="executable" and is_special_test==1', {
+ 'product_dir': '<(test_dir)-special',
+ },
],
],
},
diff --git a/aos/build/java.gypi b/aos/build/java.gypi
deleted file mode 100644
index d832b0a..0000000
--- a/aos/build/java.gypi
+++ /dev/null
@@ -1,74 +0,0 @@
-# Include this file in any target that is going to build java files.
-#
-# To use, create a target of the following form:
-# {
-# 'target_name': 'whatever',
-# 'variables': {
-# 'srcdirs': ['.', 'java'],
-# },
-# 'includes': ['path/to/java.gypi'],
-# }
-# See below for more variables.
-# To make any output jars include some loadable modules, set the 'jni_libs'
-# variable in 'direct_dependent_settings'. Making this easier causes lots of
-# recursion issues in gyp.
-# The dependency on these targets also has to be added manually.
-{
- 'type': 'none',
- 'variables': {
-# The manifest file for creating the jar.
- 'manifest%': '/dev/null',
-# Additional jars/directories to add to the classpath when compiling.
-# This target will automatically add itself to this list for any dependents.
- 'classpath': [],
-# Classes to generate JNI headers for.
-# They will be able to be #included as "jni/package_ClassName.h" by targets
-# that depend on this one.
- 'gen_headers': [],
-# Like 'srcdirs', except not required to exist at gyp time. However, nothing
-# here will depend on any files in these directories.
- 'gen_srcdirs': ['/dev/null'],
-# Like 'gen_srcdirs', except all folders that are children of this folder will
-# be used instead.
- 'gen_srcdir_parents%': [],
- 'srcdirs': ['/dev/null'],
- 'jar_dir': '<(PRODUCT_DIR)/jars',
- 'java_files': '<!(find <(srcdirs) -name *.java)',
- 'create_jar': '<(AOS)/build/create_jar',
- 'out_jar': '<(jar_dir)/<(_target_name).jar',
- 'header_dir': '<(SHARED_INTERMEDIATE_DIR)/jni_headers_<!(pwd | sed s:/:_:g)_<(_target_name)',
- 'no_rsync': 1,
- },
- 'direct_dependent_settings': {
- 'variables': {
- 'classpath': ['<(out_jar)'],
- },
- 'include_dirs': [
- '<(header_dir)',
- ],
- },
- 'actions': [
- {
- 'action_name': 'run javac',
- 'message': 'Compiling java code',
- 'inputs': [
- '<(create_jar)',
- '<@(java_files)',
- '>@(classpath)',
- '>@(gen_srcdirs)',
- '>(manifest)',
- ],
- 'outputs': [
- '<(out_jar)',
- ],
- 'action': [
- '<(create_jar)',
- '<(srcdirs) <(gen_srcdirs)',
- '<(INTERMEDIATE_DIR)', '>(classpath)',
- '>(gen_srcdir_parents)',
- '>(manifest)', '<(out_jar)',
- '<(header_dir)/jni', '>(gen_headers)',
- ],
- },
- ],
-}
diff --git a/aos/build/mkdirswig b/aos/build/mkdirswig
deleted file mode 100755
index 83403e4..0000000
--- a/aos/build/mkdirswig
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-
-# Creates the directory specified by the first argument and runs swig with the
-# rest of the arguments.
-mkdir -p $1
-shift
-swig $@
diff --git a/aos/build/onejar.gypi b/aos/build/onejar.gypi
deleted file mode 100644
index 6185f02..0000000
--- a/aos/build/onejar.gypi
+++ /dev/null
@@ -1,53 +0,0 @@
-# Include this file in any target that should get packaged with OneJAR.
-#
-# To use, create a target of the following form:
-# {
-# 'target_name': 'whatever',
-# 'variables': {
-# 'main_jar': 'something',
-# },
-# 'includes': ['path/to/onejar.gypi'],
-# },
-# See below for more variables.
-{
- 'type': 'none',
- 'variables': {
-# The names of loadable_module targets to add to the jar.
- 'jni_libs': [],
-# Additional jars to add to the output.
-# Named this so that targets from java.gypi will add themselves automatically.
- 'classpath': [],
- 'jar_dir': '<(PRODUCT_DIR)/jars',
- 'create_onejar': '<(AOS)/build/create_onejar',
- 'out_onejar': '<(rsync_dir)/<(_target_name).jar',
- 'main_jar_file': '<(jar_dir)/<(main_jar).jar',
- 'shared_objects': ">!(echo '>(jni_libs)' | sed 's:[^ ]*:<(so_dir)/lib\\0.so:g')",
- 'no_rsync': 1,
- },
- 'dependencies': [
- '<(EXTERNALS):onejar',
- ],
- 'product_dir': '<(PRODUCT_DIR)',
- 'actions': [
- {
- 'action_name': 'create onejar',
- 'message': 'Creating OneJAR jar',
- 'inputs': [
- '<(create_onejar)',
- '>@(classpath)',
- '<(main_jar_file)',
- '>@(shared_objects)',
- ],
- 'outputs': [
- '<(out_onejar)',
- ],
- 'action': [
- '<(create_onejar)',
- '<(main_jar_file)',
- '<(INTERMEDIATE_DIR)', '>(classpath)',
- '<(out_onejar)', '>(onejar_jar)',
- '>(shared_objects)',
- ],
- },
- ],
-}
diff --git a/aos/build/queues.gypi b/aos/build/queues.gypi
index 0c5da50..843272d 100644
--- a/aos/build/queues.gypi
+++ b/aos/build/queues.gypi
@@ -28,9 +28,6 @@
'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',
- 'output_swg': '<(out_dir)/<(RULE_INPUT_ROOT).q.swig',
- 'output_java_wrap': '<(out_dir)/<(RULE_INPUT_ROOT)_java_wrap.cc',
- 'java_dir': '<(out_dir)/<(RULE_INPUT_ROOT).q_java',
'no_rsync': 1,
},
'rules': [
@@ -44,19 +41,6 @@
'<(output_h)',
'<(output_cc)',
],
- 'conditions': [
- ['OS=="crio"', {
- 'outputs': [
- # cRIO doesn't do swig for a good reason.
- ]
- },{
- 'outputs': [
- '<(output_swg)',
- '<(output_java_wrap)',
- '<(java_dir)',
- ]
- }]
- ],
'inputs': [
'<(script)',
'<!@(find <(AOS)/build/queues/ -name *.rb)',
@@ -64,8 +48,6 @@
'<(AOS)/common/time.h',
],
'action': ['ruby', '<(script)',
- '--swig',
- '--swigccout', '<(output_java_wrap)',
'-I', '<(DEPTH)',
'<(RULE_INPUT_PATH)',
'-cpp_out',
@@ -98,14 +80,7 @@
'process_outputs_as_sources': 1,
},
],
- 'cflags': [
-# For the swig-generated C++ code.
- '-fno-strict-aliasing',
- '-Wno-cast-qual',
- ],
'include_dirs': [
- '/usr/lib/jvm/default-java/include',
- '/usr/lib/jvm/default-java/include/linux',
'<(prefix_dir)/<(_target_name)',
],
'direct_dependent_settings': {
@@ -116,5 +91,13 @@
'gen_srcdir_parents': ['<(out_dir)'],
},
},
+ '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
index ffee5d3..899ba85 100644
--- a/aos/build/queues/compiler.rb
+++ b/aos/build/queues/compiler.rb
@@ -1,22 +1,7 @@
-$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","swig.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 "fileutils"
-require "pathname"
+require File.dirname(__FILE__) + '/load.rb'
def parse_args(globals,args)
i = 0
- $swig = false
- $swigccout_path = ""
while(i < args.length)
if(args[i] == "-I")
args.delete_at(i)
@@ -28,9 +13,6 @@
end
path = args.delete_at(i)
globals.add_path(path)
- elsif(args[i] == "--swigccout")
- args.delete_at(i)
- $swigccout_path = args.delete_at(i)
elsif(args[i] == "-cpp_out")
args.delete_at(i)
path = args.delete_at(i)
@@ -47,9 +29,6 @@
exit!(-1)
end
$cpp_out = path.split(/\\|\//)
- elsif(args[i] == "--swig")
- $swig = true
- args.delete_at(i)
elsif(args[i] == "-cpp_base")
args.delete_at(i)
path = args.delete_at(i)
@@ -79,6 +58,14 @@
exit!(-1)
end
end
+def format_pipeline(output)
+ read_in, write_in = IO.pipe()
+ child = Process.spawn('clang-format-3.4 --style=google',
+ {:in=>read_in, write_in=>:close,
+ :out=>output})
+ read_in.close
+ [child, write_in]
+end
def build(filename,globals_template)
globals = Globals.new()
globals_template.paths.each do |path|
@@ -96,41 +83,30 @@
h_file_path = $cpp_base + "/" + rel_path + ".h"
cc_file_path = $cpp_base + "/" + rel_path + ".cc"
- swig_file_path = $cpp_base + "/" + rel_path + ".swig"
- java_directory = $cpp_base + "/" + rel_path + "_java/"
cpp_tree.add_cc_include((rel_path + ".h").inspect)
cpp_tree.add_cc_include("aos/common/byteorder.h".inspect)
cpp_tree.add_cc_include("aos/common/inttypes.h".inspect)
+ 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_using("::aos::to_network")
cpp_tree.add_cc_using("::aos::to_host")
- cpp_tree.add_swig_header_include("aos/common/queue.h".inspect)
- cpp_tree.add_swig_body_include("aos/linux_code/queue-tmpl.h".inspect)
- cpp_tree.add_swig_header_include("aos/common/time.h".inspect)
- cpp_tree.add_swig_include((rel_path + ".h").inspect)
header_file = File.open(h_file_path,"w+")
cc_file = File.open(cc_file_path,"w+")
- cpp_tree.write_header_file($cpp_base,header_file)
- cpp_tree.write_cc_file($cpp_base,cc_file)
- cc_file.close()
- header_file.close()
- if ($swig)
- swig_file = File.open(swig_file_path,"w+")
- cpp_tree.write_swig_file($cpp_base,swig_file,q_filename)
- swig_file.close()
- namespace = q_file.namespace.get_name()[1..-1]
- FileUtils.mkdir_p(java_directory)
- includes = globals.paths.collect { |a| "-I#{a}" }
-
- if (!system('/usr/bin/swig', *(includes + ['-I' + $cpp_base + '/',
- '-package', namespace,
- '-outdir', java_directory,
- '-o', $swigccout_path,
- '-c++', '-Wall', '-Wextra', '-java', swig_file_path])))
- puts "Swig failed."
- exit -1
- end
- end
+ 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)
+ cc_output.close()
+ header_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
end
begin
args = ARGV.dup
diff --git a/aos/build/queues/cpp_pretty_print/auto_gen.rb b/aos/build/queues/cpp_pretty_print/auto_gen.rb
index 633c7c0..d3fb480 100644
--- a/aos/build/queues/cpp_pretty_print/auto_gen.rb
+++ b/aos/build/queues/cpp_pretty_print/auto_gen.rb
@@ -18,6 +18,34 @@
@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
@@ -75,10 +103,6 @@
state.pp(@suite)
state.v_pad(2)
end
- def pp_pre_swig_file(state)
- end
- def pp_post_swig_file(state)
- end
alias_method :pp_header_file, :pp_forward_dec
alias_method :pp_cc_file, :pp
diff --git a/aos/build/queues/cpp_pretty_print/dep_file_pair.rb b/aos/build/queues/cpp_pretty_print/dep_file_pair.rb
index 571d9e0..c13bb9e 100644
--- a/aos/build/queues/cpp_pretty_print/dep_file_pair.rb
+++ b/aos/build/queues/cpp_pretty_print/dep_file_pair.rb
@@ -54,36 +54,6 @@
end
end
end
-class MemberElementPreSWIG < GroupElement
- def initialize(elem)
- @elem = elem
- end
- def pp(state)
- if(@elem.respond_to?(:pp_pre_swig_file))
- @elem.pp_pre_swig_file(state)
- else
- state.pp(@elem)
- end
- end
- def self.check(plan,elem)
- plan.push(self.new(elem)) if(elem.respond_to?(:pp_pre_swig_file))
- end
-end
-class MemberElementPostSWIG < GroupElement
- def initialize(elem)
- @elem = elem
- end
- def pp(state)
- if(@elem.respond_to?(:pp_post_swig_file))
- @elem.pp_post_swig_file(state)
- else
- state.pp(@elem)
- end
- end
- def self.check(plan,elem)
- plan.push(self.new(elem)) if(elem.respond_to?(:pp_post_swig_file))
- end
-end
class MemberElementCC < GroupElement
attr_accessor :pp_override
def initialize(elem)
@@ -181,36 +151,6 @@
end
end
end
-class SWIGPre_Mask
- def initialize(elem)
- @elem = elem
- end
- def plan_cc(plan)
- end
- def plan_pre_swig(plan)
- elem = MemberElementPreSWIG.new(@elem)
- plan.push(elem)
- end
- def plan_post_swig(plan)
- end
- def plan_header(plan);
- end
-end
-class SWIGPost_Mask
- def initialize(elem)
- @elem = elem
- end
- def plan_cc(plan)
- end
- def plan_pre_swig(plan)
- end
- def plan_post_swig(plan)
- elem = MemberElementPostSWIG.new(@elem)
- plan.push(elem)
- end
- def plan_header(plan);
- end
-end
class CC_Mask
def initialize(elem)
@elem = elem
@@ -222,10 +162,6 @@
end
def plan_header(plan);
end
- def plan_pre_swig(plan);
- end
- def plan_post_swig(plan);
- end
end
module Types
class TypeDef
@@ -246,6 +182,9 @@
@protections = {}
@deps = []
end
+ def parent_class
+ @parent.split(' ')[1] if @parent
+ end
class ProtectionGroup
def initialize(name)
@name = name
@@ -280,30 +219,6 @@
@members.push(CC_Mask.new(CPP::Comment.new(arg)))
end
end
- def add_pre_swig(arg)
- @members.push(SWIGPre_Mask.new(arg))
- end
- def add_post_swig(arg)
- @members.push(SWIGPost_Mask.new(arg))
- end
- def plan_pre_swig(plan)
- @members.each do |member|
- if(member.respond_to?(:plan_pre_swig))
- member.plan_pre_swig(plan)
- elsif(member.respond_to?(:pp_pre_swig_file))
- plan.push(MemberElementPreSWIG.new(member))
- end
- end
- end
- def plan_post_swig(plan)
- @members.each do |member|
- if(member.respond_to?(:plan_post_swig))
- member.plan_post_swig(plan)
- elsif(member.respond_to?(:pp_post_swig_file))
- plan.push(MemberElementPostSWIG.new(member))
- end
- end
- end
def plan_cc(plan)
@members.each do |member|
if(member.respond_to?(:plan_cc))
@@ -391,6 +306,7 @@
end
end
class Struct < Type
+ attr_accessor :members
def add_member(member)
@members.push(member)
return member
@@ -456,12 +372,6 @@
def add_cc(arg)
@members.push(CC_Mask.new(arg))
end
- def add_pre_swig(arg)
- @members.push(SWIGPre_Mask.new(arg))
- end
- def add_post_swig(arg)
- @members.push(SWIGPost_Mask.new(arg))
- end
def chop_method_prefix()
""
end
@@ -474,25 +384,6 @@
def add_var_dec(arg)
add DepMask.new(arg)
end
- def plan_pre_swig(plan)
- plan.implicit = ImplicitName.new(@name,plan.implicit)
- @members.each do |member|
- if(member.respond_to?(:plan_pre_swig))
- member.plan_pre_swig(plan)
- else
- MemberElementPreSWIG.check(plan,member)
- end
- end
- end
- def plan_post_swig(plan)
- @members.each do |member|
- if(member.respond_to?(:plan_post_swig))
- member.plan_post_swig(plan)
- else
- MemberElementPostSWIG.check(plan,member)
- end
- end
- end
def plan_cc(plan)
plan.implicit = ImplicitName.new(@name,plan.implicit)
@members.each do |member|
@@ -523,23 +414,11 @@
def initialize(rel_path)
@rel_path = rel_path
@cc_includes = []
- @swig_includes_h = []
- @swig_includes_swig = []
@header_includes = []
@spaces = []
@cc_usings = []
@cache = {}
end
- def add_swig_body_include(inc_path)
- @swig_includes_swig << CPP::SwigInclude.new(inc_path)
- end
- def add_swig_header_include(inc_path)
- @swig_includes_h << CPP::Include.new(inc_path)
- end
- def add_swig_include(inc_path)
- @swig_includes_h << CPP::Include.new(inc_path)
- @swig_includes_swig << CPP::SwigInclude.new(inc_path)
- end
def add_cc_include(inc_path)
@cc_includes << CPP::Include.new(inc_path)
end
@@ -588,30 +467,4 @@
include_guard.name = @rel_path.upcase.gsub(/[\.\/\\]/,"_") + "_H_"
CPP.pretty_print(include_guard,header_file)
end
- def write_swig_file(cpp_base,swig_file,q_filename)
- plan_pre_swig = GroupPlan.new(nil, elems_cc = [])
- plan_post_swig = GroupPlan.new(nil, elems_cc = [])
- q_filename_underscore = q_filename.gsub(".","_")
- @spaces.each do |space|
- space.plan_pre_swig(plan_pre_swig)
- space.plan_post_swig(plan_post_swig)
- end
- header_includes = CPP::SWIGBraces.new(CPP::Suite.new(@swig_includes_h))
- # TODO(aschuh): I should probably %import any other headers from this queue's dependencies.
- suite = CPP::Suite.new(["%module \"#{q_filename_underscore}\"",
- "%typemap(javaimports) SWIGTYPE, SWIGTYPE * \"import aos.QueueGroup; import aos.Message; import aos.Time;\"",
- "%pragma(java) jniclassimports=\"import aos.QueueGroup; import aos.Message; import aos.Time;\"",
- "%pragma(java) moduleimports=\"import aos.QueueGroup; import aos.Message; import aos.Time;\"",
- "%include \"std_string.i\"",
- "%include \"stdint.i\""] +
- [header_includes] +
- #["%import \"aos/common/time.h\"",
- #"%import \"aos/common/queue.h\""] +
- ["%import \"aos/aos.swig\""] +
- [plan_pre_swig] +
- @swig_includes_swig +
- [plan_post_swig]
- )
- CPP.pretty_print(suite, swig_file)
- end
end
diff --git a/aos/build/queues/cpp_pretty_print/swig.rb b/aos/build/queues/cpp_pretty_print/swig.rb
deleted file mode 100644
index ac6d062..0000000
--- a/aos/build/queues/cpp_pretty_print/swig.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-class CPP::SwigPragma
- attr_accessor :suite
- def initialize(language, pragmatype, suite = CPP::Suite.new())
- @suite = suite
- @language = language
- @pragmatype = pragmatype
- end
- def pp(state)
- state.needs_semi = false
- state.suppress_indent()
- state.print("%pragma(#{@language}) #{@pragmatype}=%{")
- 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("%}")
- state.endline()
- state.endline()
- end
-end
-class CPP::SWIGBraces
- attr_accessor :suite
- def initialize(suite = CPP::Suite.new())
- @suite = suite
- end
- def pp(state)
- state.needs_semi = false
- state.suppress_indent()
- state.print("%{")
- 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("%}")
- state.endline()
- state.endline()
- end
-end
-class CPP::SwigInclude
- 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
diff --git a/aos/build/queues/load.rb b/aos/build/queues/load.rb
new file mode 100644
index 0000000..c299d80
--- /dev/null
+++ b/aos/build/queues/load.rb
@@ -0,0 +1,14 @@
+$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 "fileutils"
+require "pathname"
diff --git a/aos/build/queues/objects/q_file.rb b/aos/build/queues/objects/q_file.rb
index beaf8f0..f73b638 100644
--- a/aos/build/queues/objects/q_file.rb
+++ b/aos/build/queues/objects/q_file.rb
@@ -138,7 +138,7 @@
suite << StructStmt.parse(tokens)
else
tokens.qError(<<ERROR_MSG)
-expected a "package","import","queue","queue_group", or "message" statement rather
+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.
diff --git a/aos/build/queues/output/message_dec.rb b/aos/build/queues/output/message_dec.rb
index 579ce0c..fdc03ee 100644
--- a/aos/build/queues/output/message_dec.rb
+++ b/aos/build/queues/output/message_dec.rb
@@ -4,7 +4,121 @@
require "digest/sha1"
end
-class Target::MessageDec < Target::Node
+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}, #{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)
+ 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)
+ else
+ type_name = member.type
+ end
+
+ cons.args << "#{type_name} #{member.name}_in"
+ 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 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
@@ -20,9 +134,6 @@
return "#{@name}"
end
end
- def add_member(member)
- @members << member
- end
def create_Print(type_class,cpp_tree)
member_func = CPP::MemberFunc.new(type_class,"size_t","Print")
type_class.add_member(member_func)
@@ -42,54 +153,12 @@
member_func.suite << "length -= super_size"
member_func.suite << "return super_size + snprintf(buffer, length, " + ([format] + args).join(", ") + ")";
end
- def create_Serialize(type_class,cpp_tree)
- member_func = CPP::MemberFunc.new(type_class,"size_t","Serialize")
+ def create_GetType(type_class, cpp_tree)
+ member_func = CPP::MemberFunc.new(type_class,"const ::aos::MessageType*","GetType")
type_class.add_member(member_func)
- #cpp_tree.cc_file.add_funct(member_func)
- member_func.args << "char *buffer"
- member_func.suite << "::aos::Message::Serialize(buffer)"
- member_func.const = true
- offset = 0
- @members.each do |elem|
- elem.toNetwork(offset,member_func.suite)
- offset += elem.size;
- end
- member_func.suite << "return 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)
- #cpp_tree.cc_file.add_funct(member_func)
- member_func.args << "const char *buffer"
- member_func.suite << "::aos::Message::Deserialize(buffer)"
- offset = 0
- @members.each do |elem|
- elem.toHost(offset,member_func.suite)
- offset += elem.size;
- end
- member_func.suite << "return Size()"
- end
- def create_Zero(type_class,cpp_tree)
- member_func = CPP::MemberFunc.new(type_class,"void","Zero")
- type_class.add_member(member_func)
- #cpp_tree.cc_file.add_funct(member_func)
- @members.each do |elem|
- elem.zeroCall(member_func.suite)
- end
- member_func.suite << "::aos::Message::Zero()"
- 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)
- #cpp_tree.cc_file.add_funct(member_func)
- size = 0
- @members.each do |elem|
- size += elem.size
- end
- member_func.suite << CPP::Return.new(CPP::Add.new(size,
- "::aos::Message::Size()"))
+ 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)
@@ -116,9 +185,7 @@
end
cpp_tree.set(self,type_class)
type_class.set_parent("public ::aos::Message")
- ts = (@members.collect { |elem|
- elem.type + " " + elem.name
- }).join(";")
+ ts = self.simpleStr()
self.msg_hash = "0x#{Digest::SHA1.hexdigest(ts)[-8..-1]}"
type_class.add_member("enum {kQueueLength = 1234, kHash = #{self.msg_hash}}")
@members.each do |elem|
@@ -130,6 +197,10 @@
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)
b_namespace = cpp_tree.get(b_loc = self.class.builder_loc(@loc))
@@ -146,7 +217,6 @@
msg_bld_t = "MessageBuilder< #{t}>"
safetemplate.add_member(:private,"#{safemsg_ptr_t} msg_ptr_")
template.add_member(:private,"#{msg_ptr_t} msg_ptr_")
- namespace.add_pre_swig("%feature(\"valuewrapper\") #{safemsg_bld_t}")
template.add_member(:private,"#{msg_bld_t}(const #{msg_bld_t}&)")
template.add_member(:private,"void operator=(const #{msg_bld_t}&)")
safetemplate.add_member(:private,"friend class ::aos::Queue< #{t}>")
@@ -170,13 +240,6 @@
DefineMembers(cpp_tree, safetemplate, safemsg_bld_t)
DefineMembers(cpp_tree, template, msg_bld_t)
- java_type_name = java_type_name(cpp_tree)
- namespace.add_post_swig("%template(#{java_type_name}) ::aos::Queue< #{t}>")
- namespace.add_post_swig("%template(#{java_ptr_name(cpp_tree)}) ::aos::SafeScopedMessagePtr< #{t}>")
- namespace.add_post_swig("%template(#{java_builder_name(cpp_tree)}) ::aos::SafeMessageBuilder< #{t}>")
- # TODO(aschuh): Figure out why this doesn't work and fix it.
- #namespace.add_post_swig("%typemap(javabase) #{@name} \"aos.Message\"")
-
end
def DefineMembers(cpp_tree, template, msg_bld_t)
send = template.def_func("bool","Send")
@@ -196,15 +259,6 @@
end
end
- def java_ptr_name(cpp_tree)
- return "#{@name}MessagePtr"
- end
- def java_builder_name(cpp_tree)
- return "#{@name}MessageBuilder"
- end
- def java_type_name(cpp_tree)
- return "#{@name}Queue"
- end
end
class Target::MessageElement < Target::Node
attr_accessor :name,:loc,:size,:zero,:type,:printformat
@@ -218,19 +272,25 @@
"#{@type} #{@name}"
end
def toNetwork(offset,suite, parent = "")
- offset = (offset == 0) ? "" : "#{offset} + "
suite << f_call = CPP::FuncCall.build("to_network",
"&#{parent}#{@name}",
- "&buffer[#{offset}::aos::Message::Size()]")
+ "&buffer[#{offset}]")
f_call.args.dont_wrap = true
end
def toHost(offset,suite, parent = "")
- offset = (offset == 0) ? "" : "#{offset} + "
suite << f_call = CPP::FuncCall.build("to_host",
- "&buffer[#{offset}::aos::Message::Size()]",
+ "&buffer[#{offset}]",
"&#{parent}#{@name}")
f_call.args.dont_wrap = true
end
+ def getTypeID()
+ Digest::SHA1.hexdigest(@type)[0..7].to_i(16) |
+ 0x2000 | # marks it as primitive
+ size
+ end
+ def simpleStr()
+ "#{@type} #{@name}"
+ end
def set_message_builder(suite)
suite << "msg_ptr_->#{@name} = #{@name}"
end
diff --git a/aos/build/queues/output/q_file.rb b/aos/build/queues/output/q_file.rb
index 1ef47a4..a39799d7 100644
--- a/aos/build/queues/output/q_file.rb
+++ b/aos/build/queues/output/q_file.rb
@@ -98,7 +98,6 @@
type_class.set_parent("public ::aos::QueueGroup")
@queues.each do |queue|
type_class.add_member(:public,queue.create_usage(cpp_tree))
- namespace.add_pre_swig("%immutable #{@name}::#{queue.name}")
end
create_Constructor(type_class,cpp_tree)
namespace.add(type_class)
@@ -155,7 +154,6 @@
cons = CPP::Constructor.new(init_class)
init_class.add_member(:public,cons)
cons.suite << if_stmt = CPP::If.new("0 == #{counter}++")
- if_stmt.suite << "printf(#{"making a #{@name}!\n".inspect})"
cons_call = CPP::FuncCall.new("new #{type_name}")
cons_call.args.push(@loc.queue_name(@name).inspect)
@@ -165,12 +163,10 @@
cons_call.args.push(@loc.queue_name(@name + "." + queue.name).inspect)
end
if_stmt.suite << CPP::Assign.new("#{@name}_ptr",cons_call)
- if_stmt.else_suite << CPP::FuncCall.build("printf","already made a #{@name}\n".inspect)
destruct = CPP::Destructor.new(init_class)
destruct.suite << if_stmt = CPP::If.new("0 == --#{counter}")
- if_stmt.suite << "printf(#{"deleting a #{@name}!! :) !!\n".inspect})"
if_stmt.suite << "delete #{@name}_ptr"
if_stmt.suite << CPP::Assign.new("#{@name}_ptr","NULL")
@@ -182,15 +178,11 @@
Create a reference to the new object in the pointer. Since we have already
created the initializer
COMMENT_END
- comments = str.split(/\n/).map{|str_sec| CPP::Comment.new(str_sec)}
- comments << "static UNUSED_VARIABLE #{type_name} &#{@name} = " +
- "#{@name}_initializer.get()"
- namespace.add_post_swig("%immutable #{@name}_initializer")
- namespace.add_post_swig(CPP::SwigPragma.new("java", "modulecode", CPP::Suite.new(["public static final #{@name} = get#{@name.capitalize}_initializer().get()"])))
- ifdef_statement = CPP::IfnDef.new(CPP::Suite.new(comments))
- ifdef_statement.name = "SWIG"
- namespace.add(ifdef_statement)
-
+ 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 = "&"
diff --git a/aos/build/queues/output/q_struct.rb b/aos/build/queues/output/q_struct.rb
index 6ce1794..05c2932 100644
--- a/aos/build/queues/output/q_struct.rb
+++ b/aos/build/queues/output/q_struct.rb
@@ -1,4 +1,4 @@
-class Target::StructDec < Target::Node
+class Target::StructDec < Target::StructBase
attr_accessor :name,:loc,:parent, :extern
def initialize(name)
@name = name
@@ -7,6 +7,13 @@
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)
@@ -20,16 +27,16 @@
@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)
return type_class
end
- def size()
- return @size if(@size)
- @size = 0
- @members.each do |elem|
- @size += elem.size
- end
- return @size
- end
def getPrintFormat()
return "{" + @members.collect { |elem| elem.toPrintFormat() }.join(", ") + "}"
end
@@ -41,13 +48,13 @@
def toHost(offset, suite, parent)
@members.each do |elem|
elem.toHost(offset, suite, parent)
- offset += elem.size()
+ offset += " + #{elem.size()}"
end
end
def toNetwork(offset, suite, parent)
@members.each do |elem|
elem.toNetwork(offset, suite, parent)
- offset += elem.size()
+ offset += " + #{elem.size()}"
end
end
def zeroCall(suite, parent)
@@ -81,6 +88,11 @@
def create_usage(cpp_tree)
return "#{type_name(cpp_tree)} #{@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 = "")
@type.fetchPrintArgs(args, parent + "#{@name}.")
end
@@ -97,5 +109,10 @@
def zeroCall(suite, parent = "")
@type.zeroCall(suite, parent + "#{@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
index 8c3bd51..db4e1af 100644
--- a/aos/build/queues/output/queue_dec.rb
+++ b/aos/build/queues/output/queue_dec.rb
@@ -6,10 +6,6 @@
def msg_hash()
return @type.msg_hash
end
- def java_type_name(cpp_tree)
- type = cpp_tree.get(@type)
- return "#{type.name}Queue"
- end
def full_message_name(cpp_tree)
type = cpp_tree.get(@type)
return @type.loc.to_cpp_id(type.name)
@@ -62,18 +58,15 @@
cons = CPP::Constructor.new(init_class)
init_class.add_member(:public,cons)
cons.suite << if_stmt = CPP::If.new("0 == #{counter}++")
- if_stmt.suite << "printf(#{"making a #{@name} queue!\n".inspect})"
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)
- if_stmt.else_suite << CPP::FuncCall.build("printf","already made a #{@name}\n".inspect)
destruct = CPP::Destructor.new(init_class)
init_class.add_member(:public,destruct)
destruct.suite << if_stmt = CPP::If.new("0 == --#{counter}")
- if_stmt.suite << "printf(#{"deleting a #{@name}!! :) !!\n".inspect})"
if_stmt.suite << "delete #{@name}_ptr"
if_stmt.suite << CPP::Assign.new("#{@name}_ptr","NULL")
@@ -84,16 +77,11 @@
Create a reference to the new object in the pointer. Since we have already
created the initializer
COMMENT_END
- comments = str.split(/\n/).map{|str_sec| CPP::Comment.new(str_sec)}
- comments << "static UNUSED_VARIABLE #{type_name} &#{@name} = " +
- "#{@name}_initializer.get()"
- namespace.add_post_swig("%immutable #{@name}_initializer")
- java_type_name = java_type_name(cpp_tree)
- namespace.add_post_swig(CPP::SwigPragma.new("java", "modulecode", CPP::Suite.new(["public static final #{java_type_name} #{@name} = get#{@name.capitalize}_initializer().get()"])))
-
- ifdef_statement = CPP::IfnDef.new(CPP::Suite.new(comments))
- ifdef_statement.name = "SWIG"
- namespace.add(ifdef_statement)
+ 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 = "&"
diff --git a/aos/build/queues/print_field.rb b/aos/build/queues/print_field.rb
new file mode 100644
index 0000000..7a9fbb4
--- /dev/null
+++ b/aos/build/queues/print_field.rb
@@ -0,0 +1,54 @@
+require File.dirname(__FILE__) + '/load.rb'
+
+TypeNames = [8, 16, 32, 64].collect do |size|
+ ["uint#{size}_t", "int#{size}_t"]
+end.flatten + ['bool', 'float', 'char', 'double']
+
+File.open(ARGV[0], 'w') 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"
+
+namespace aos {
+
+bool PrintField(char *output, size_t *output_bytes, const void *input,
+ size_t *input_bytes, uint32_t type) {
+ switch (type) {
+#{TypeNames.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 + 1;
+ return true;
+ }
+END2
+end.join('')}
+ default:
+ return false;
+ }
+}
+
+} // namespace aos
+END
+end
diff --git a/aos/build/run_test.sh b/aos/build/run_test.sh
new file mode 100755
index 0000000..9da830b
--- /dev/null
+++ b/aos/build/run_test.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+# This gets called by build.sh to run a test.
+
+EXECUTABLE=$1
+
+echo "Running $(basename ${EXECUTABLE})."
+${EXECUTABLE}
+exit $?
diff --git a/aos/build/swig.gypi b/aos/build/swig.gypi
deleted file mode 100644
index 2e99a95..0000000
--- a/aos/build/swig.gypi
+++ /dev/null
@@ -1,73 +0,0 @@
-# Include this file in any target that needs to use swig wrappers.
-#
-# To use, create a target of the following form:
-# {
-# 'target_name': 'my_target_javawrap',
-# 'type': 'static_library', # or any other type that can handle .cc files
-# 'sources': [
-# 'aos/example/target.swig',
-# ],
-# 'variables': {
-# 'package': 'aos.test',
-# },
-# 'includes': ['path/to/swig.gypi'],
-# },
-# Code that depends on this target will be able to use the swig wrapped
-# java classes.
-#
-# using <http://src.chromium.org/svn/trunk/src/build/protoc.gypi> as an
-# example of how this should work
-{
- 'variables': {
- 'prefix_dir': '<(SHARED_INTERMEDIATE_DIR)/',
- 'out_dir': '<(prefix_dir)/<(_target_name)/',
- 'output_java_wrap': '<(out_dir)/<(RULE_INPUT_ROOT)_wrap.cc',
- 'java_dir': '<(out_dir)/<(RULE_INPUT_ROOT)_java',
- 'no_rsync': 1,
- },
- 'rules': [
- {
- 'rule_name': 'genswig',
- 'extension': 'swig',
- 'outputs': [
- '<(output_java_wrap)',
- '<(java_dir)',
- ],
- 'action': [
- '<(DEPTH)/aos/build/mkdirswig',
- '<(java_dir)',
- '-I<(DEPTH)',
- '-outdir', ' <(java_dir)',
- '-package', '<(package)',
- '-o', '<(output_java_wrap)',
- '-c++',
- '-Wall',
- '-Wextra',
- '-java',
- '<(RULE_INPUT_PATH)'],
- 'message': 'Generating C++ code from <(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).swig',
- 'process_outputs_as_sources': 1,
- },
- ],
- 'cflags': [
-# For the swig-generated C++ code.
- '-fno-strict-aliasing',
- '-Wno-cast-qual',
- ],
- 'include_dirs': [
- '<(prefix_dir)/<(_target_name)',
- '/usr/lib/jvm/default-java/include',
- '/usr/lib/jvm/default-java/include/linux',
- ],
- 'direct_dependent_settings': {
- 'include_dirs': [
- '<(prefix_dir)/<(_target_name)',
- '/usr/lib/jvm/default-java/include',
- '/usr/lib/jvm/default-java/include/linux',
- ],
- 'variables': {
- 'gen_srcdir_parents': ['<(out_dir)'],
- },
- },
- 'hard_dependency': 1,
-}
diff --git a/aos/common/common.gyp b/aos/common/common.gyp
index 4c2b684..906b5b2 100644
--- a/aos/common/common.gyp
+++ b/aos/common/common.gyp
@@ -1,7 +1,7 @@
{
'targets': [
{
- 'target_name': 'queue_test_queue',
+ 'target_name': 'test_queue',
'type': 'static_library',
'sources': [
'<(AOS)/common/test_queue.q',
@@ -37,13 +37,57 @@
'time.cc'
],
'dependencies': [
- # TODO(aschuh): Fix this dependency loop by
- # providing a logging interface.
- # '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/build/aos.gyp:logging_interface',
'mutex',
],
},
{
+ 'target_name': 'queue_types',
+ 'type': 'static_library',
+ 'variables': {
+ 'print_field_cc': '<(SHARED_INTERMEDIATE_DIR)/print_field.cc',
+ },
+ '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',
+ ],
+ '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',
+ },
+ ],
+ },
+ {
+ 'target_name': 'queue_types_test',
+ 'type': 'executable',
+ 'sources': [
+ 'queue_types_test.cc',
+ ],
+ 'dependencies': [
+ 'queue_types',
+ '<(EXTERNALS):gtest',
+ 'test_queue',
+ ],
+ },
+ {
'target_name': 'queues',
'type': 'static_library',
'sources': [
@@ -138,6 +182,8 @@
'timing',
'time',
'control_loop_queues',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/util/util.gyp:log_interval',
],
'export_dependent_settings': [
'<(AOS)/common/messages/messages.gyp:aos_queues',
@@ -145,6 +191,8 @@
'timing',
'time',
'control_loop_queues',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/util/util.gyp:log_interval',
],
},
{
@@ -156,8 +204,9 @@
'dependencies': [
'<(EXTERNALS):gtest',
'queue_testutils',
- 'queue_test_queue',
+ 'test_queue',
'<(AOS)/common/util/util.gyp:thread',
+ 'die',
],
},
{
@@ -229,9 +278,7 @@
'dependencies': [
'mutex',
'<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:aos_sync',
- # TODO(aschuh): Fix this dependency loop by
- # providing a logging interface.
- # '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/build/aos.gyp:logging_interface',
],
'export_dependent_settings': [
'mutex',
@@ -259,9 +306,7 @@
}],
],
'dependencies': [
- # TODO(aschuh): Fix this dependency loop by
- # providing a logging interface.
- # '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/build/aos.gyp:logging_interface',
],
},
{
@@ -273,7 +318,7 @@
'dependencies': [
'<(EXTERNALS):gtest',
'mutex',
- '<(AOS)/build/aos.gyp:logging',
+ 'die',
],
},
{
@@ -291,6 +336,7 @@
'<(AOS)/build/aos.gyp:logging',
'queue_testutils',
'<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:core_lib',
+ 'die',
],
},
{
diff --git a/aos/common/condition_test.cc b/aos/common/condition_test.cc
index a10275b..65a1028 100644
--- a/aos/common/condition_test.cc
+++ b/aos/common/condition_test.cc
@@ -13,6 +13,7 @@
#include "aos/linux_code/ipc_lib/core_lib.h"
#include "aos/common/logging/logging.h"
#include "aos/common/macros.h"
+#include "aos/common/die.h"
using ::aos::time::Time;
using ::aos::common::testing::GlobalCoreInstance;
@@ -43,6 +44,11 @@
time::SleepFor(::Time::InSeconds(0.008));
}
+ protected:
+ void SetUp() override {
+ SetDieTestMode(true);
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(ConditionTest);
};
diff --git a/aos/common/control_loop/ControlLoop-tmpl.h b/aos/common/control_loop/ControlLoop-tmpl.h
index 69c3121..b5714d5 100644
--- a/aos/common/control_loop/ControlLoop-tmpl.h
+++ b/aos/common/control_loop/ControlLoop-tmpl.h
@@ -3,6 +3,7 @@
#include "aos/common/logging/logging.h"
#include "aos/common/control_loop/Timing.h"
#include "aos/common/messages/RobotState.q.h"
+#include "aos/common/logging/queue_logging.h"
namespace aos {
namespace control_loops {
@@ -10,6 +11,10 @@
// TODO(aschuh): Tests.
template <class T, bool has_position, bool fail_no_position>
+constexpr ::aos::time::Time
+ ControlLoop<T, has_position, fail_no_position>::kStaleLogInterval;
+
+template <class T, bool has_position, bool fail_no_position>
void ControlLoop<T, has_position, fail_no_position>::ZeroOutputs() {
aos::ScopedMessagePtr<OutputType> output =
control_loop_->output.MakeMessage();
@@ -19,9 +24,6 @@
template <class T, bool has_position, bool fail_no_position>
void ControlLoop<T, has_position, fail_no_position>::Iterate() {
- // Temporary storage for printing out inputs and outputs.
- char state[1024];
-
// Fetch the latest control loop goal and position. If there is no new
// goal, we will just reuse the old one.
// If there is no goal, we haven't started up fully. It isn't worth
@@ -31,12 +33,11 @@
// goals.
const GoalType *goal = control_loop_->goal.get();
if (goal == NULL) {
- LOG(ERROR, "No prior control loop goal.\n");
+ LOG_INTERVAL(no_prior_goal_);
ZeroOutputs();
return;
}
- goal->Print(state, sizeof(state));
- LOG(DEBUG, "goal={%s}\n", state);
+ LOG_STRUCT(DEBUG, "goal", *goal);
// Only pass in a position if we got one this cycle.
const PositionType *position = NULL;
@@ -51,15 +52,15 @@
if (control_loop_->position.get()) {
int msec_age = control_loop_->position.Age().ToMSec();
if (!control_loop_->position.IsNewerThanMS(kPositionTimeoutMs)) {
- LOG(ERROR, "Stale position. %d ms > %d ms. Outputs disabled.\n",
- msec_age, kPositionTimeoutMs);
+ LOG_INTERVAL(very_stale_position_);
ZeroOutputs();
return;
} else {
- LOG(ERROR, "Stale position. %d ms\n", msec_age);
+ LOG(ERROR, "Stale position. %d ms (< %d ms)\n", msec_age,
+ kPositionTimeoutMs);
}
} else {
- LOG(ERROR, "Never had a position.\n");
+ LOG_INTERVAL(no_prior_position_);
if (fail_no_position) {
ZeroOutputs();
return;
@@ -67,8 +68,7 @@
}
}
if (position) {
- position->Print(state, sizeof(state));
- LOG(DEBUG, "position={%s}\n", state);
+ LOG_STRUCT(DEBUG, "position", *position);
}
}
@@ -81,10 +81,9 @@
outputs_enabled = true;
} else {
if (aos::robot_state.get()) {
- int msec_age = aos::robot_state.Age().ToMSec();
- LOG(ERROR, "Driver Station packet is too old (%d ms).\n", msec_age);
+ LOG_INTERVAL(driver_station_old_);
} else {
- LOG(ERROR, "No Driver Station packet.\n");
+ LOG_INTERVAL(no_driver_station_);
}
}
@@ -100,8 +99,7 @@
control_loop_->output.MakeMessage();
RunIteration(goal, position, output.get(), status.get());
- output->Print(state, sizeof(state));
- LOG(DEBUG, "output={%s}\n", state);
+ LOG_STRUCT(DEBUG, "output", *output);
output.Send();
} else {
// The outputs are disabled, so pass NULL in for the output.
@@ -109,8 +107,7 @@
ZeroOutputs();
}
- status->Print(state, sizeof(state));
- LOG(DEBUG, "status={%s}\n", state);
+ LOG_STRUCT(DEBUG, "status", *status);
status.Send();
}
diff --git a/aos/common/control_loop/ControlLoop.h b/aos/common/control_loop/ControlLoop.h
index 6af7235..e48d259 100644
--- a/aos/common/control_loop/ControlLoop.h
+++ b/aos/common/control_loop/ControlLoop.h
@@ -7,6 +7,7 @@
#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 control_loops {
@@ -129,6 +130,25 @@
private:
// Pointer to the queue group
T *control_loop_;
+
+ typedef ::aos::util::SimpleLogInterval SimpleLogInterval;
+ static constexpr ::aos::time::Time kStaleLogInterval =
+ ::aos::time::Time::InSeconds(0.1);
+ SimpleLogInterval very_stale_position_ =
+ SimpleLogInterval(kStaleLogInterval, ERROR,
+ "outputs disabled because position is very stale");
+ SimpleLogInterval no_prior_goal_ =
+ SimpleLogInterval(kStaleLogInterval, ERROR,
+ "no prior goal");
+ SimpleLogInterval no_prior_position_ =
+ SimpleLogInterval(kStaleLogInterval, ERROR,
+ "no prior position");
+ SimpleLogInterval no_driver_station_ =
+ SimpleLogInterval(kStaleLogInterval, ERROR,
+ "no driver station packet");
+ SimpleLogInterval driver_station_old_ =
+ SimpleLogInterval(kStaleLogInterval, ERROR,
+ "driver station packet is too old");
};
} // namespace control_loops
diff --git a/aos/common/die.cc b/aos/common/die.cc
index 467b81d..0ea7007 100644
--- a/aos/common/die.cc
+++ b/aos/common/die.cc
@@ -6,13 +6,7 @@
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
-#ifdef __VXWORKS__
-#include <taskLib.h>
-// Have to re-declare it with __attribute__((noreturn)).
-extern "C" void abort() __attribute__((noreturn));
-#include <usrLib.h>
-#include <dbgLib.h>
-#endif
+#include <signal.h>
#include <string>
@@ -28,6 +22,7 @@
}
namespace {
+
// Calculates the filename to dump the message into.
const std::string GetFilename() {
#ifdef __VXWORKS__
@@ -50,6 +45,9 @@
}
#endif
}
+
+bool test_mode = false;
+
} // namespace
void VDie(const char *format, va_list args_in) {
@@ -60,31 +58,28 @@
fputs("aos fatal: ERROR!! details following\n", stderr);
va_copy(args1, args_in);
vfprintf(stderr, format, args1);
- fputs("aos fatal: ERROR!! see stderr for details\n", stdout);
+ if (!test_mode) {
+ 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 (%s)\n",
- filename.c_str(), errno, strerror(errno));
+ 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 (%s)\n",
+ filename.c_str(), errno, strerror(errno));
+ }
}
}
-#ifdef __VXWORKS__
- printf("I am 0x%x suspending for debugging purposes.\n", taskIdSelf());
- printf("\t`tt 0x%x` will give you a stack trace.\n", taskIdSelf());
- fputs("\t`lkAddr` will reverse lookup a symbol for you.\n", stdout);
- fputs("\t`dbgHelp` and `help` have some useful commands in them.\n", stdout);
- taskSuspend(0);
- printf("You weren't supposed to resume 0x%x!!. Going to really die now.\n",
- taskIdSelf());
-#endif
abort();
}
+void SetDieTestMode(bool new_test_mode) {
+ test_mode = new_test_mode;
+}
+
} // namespace aos
diff --git a/aos/common/die.h b/aos/common/die.h
index 28519a8..d973b42 100644
--- a/aos/common/die.h
+++ b/aos/common/die.h
@@ -15,6 +15,11 @@
__attribute__((noreturn))
__attribute__((format(gnu_printf, 1, 0)));
+// 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/logging/logging.gyp b/aos/common/logging/logging.gyp
index b6b339a..e230e47 100644
--- a/aos/common/logging/logging.gyp
+++ b/aos/common/logging/logging.gyp
@@ -11,5 +11,20 @@
'<(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',
+ ],
+ },
],
}
diff --git a/aos/common/logging/logging.h b/aos/common/logging/logging.h
index 6de118b..673cc9a 100644
--- a/aos/common/logging/logging.h
+++ b/aos/common/logging/logging.h
@@ -8,11 +8,6 @@
#include <stdint.h>
#include <stdlib.h>
-#ifdef __VXWORKS__
-// Because the vxworks system headers miss the noreturn...
-extern "C" void abort(void) __attribute__((noreturn));
-#endif
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -30,14 +25,13 @@
DECL_LEVEL(FATAL, 4); \
DECL_LEVEL(LOG_UNKNOWN, 5); /* unknown logging level */
#define DECL_LEVEL(name, value) static const log_level name = value;
-#undef ERROR
DECL_LEVELS;
#undef DECL_LEVEL
#define STRINGIFY(x) TO_STRING(x)
#define TO_STRING(x) #x
-//not static const size_t for c code
+// Not static const size_t for C code.
#define LOG_MESSAGE_LEN 400
#ifdef __VXWORKS__
@@ -77,41 +71,45 @@
#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)
+#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)
// 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)
+#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)
+#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)
+#define LOG_UNCORK(level, format, args...) \
+ do { \
+ log_uncork(__LINE__, LOG_CURRENT_FUNCTION, level, LOG_SOURCENAME, format, \
+ ##args); \
+ } while (0)
#ifdef __cplusplus
}
@@ -160,8 +158,8 @@
// 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)) { \
+#define CHECK(condition) \
+ if (__builtin_expect(!(condition), 0)) { \
LOG(FATAL, "CHECK(%s) failed\n", #condition); \
}
@@ -169,16 +167,16 @@
// 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(FATAL, "CHECK(%s) failed\n", exprtext); \
- } \
- } \
- inline void LogImpl##name(int v1, int v2, const char* exprtext) { \
- ::aos::LogImpl##name<int, int>(v1, v2, exprtext); \
+#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(FATAL, "CHECK(%s) failed\n", exprtext); \
+ } \
+ } \
+ 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
@@ -192,7 +190,7 @@
DEFINE_CHECK_OP_IMPL(Check_GE, >=)
DEFINE_CHECK_OP_IMPL(Check_GT, > )
-#define CHECK_OP(name, op, val1, val2) \
+#define CHECK_OP(name, op, val1, val2) \
::aos::LogImplCheck##name(val1, val2, \
STRINGIFY(val1) STRINGIFY(op) STRINGIFY(val2))
@@ -214,8 +212,7 @@
// Check that the input is non NULL. This very useful in constructor
// initializer lists.
-#define CHECK_NOTNULL(val) \
- ::aos::CheckNotNull(STRINGIFY(val), val)
+#define CHECK_NOTNULL(val) ::aos::CheckNotNull(STRINGIFY(val), val)
} // namespace aos
diff --git a/aos/common/logging/logging_impl.cc b/aos/common/logging/logging_impl.cc
index 980f65a..5f4a9b2 100644
--- a/aos/common/logging/logging_impl.cc
+++ b/aos/common/logging/logging_impl.cc
@@ -1,26 +1,19 @@
#include "aos/common/logging/logging_impl.h"
#include <assert.h>
+#include <stdarg.h>
-#include "aos/common/die.h"
#include "aos/common/time.h"
#include "aos/common/inttypes.h"
#include "aos/common/once.h"
+#include "aos/common/queue_types.h"
namespace aos {
namespace logging {
namespace {
using internal::Context;
-
-LogImplementation *global_top_implementation(NULL);
-// Just going to use a mutex instead of getting fancy because speed doesn't
-// really matter when accessing global_top_implementation.
-Mutex global_top_implementation_mutex;
-LogImplementation *get_global_top_implementation() {
- MutexLocker locker(&global_top_implementation_mutex);
- return global_top_implementation;
-}
+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
@@ -43,27 +36,7 @@
Context *context = Context::Get();
context->implementation = implementation;
- {
- MutexLocker locker(&global_top_implementation_mutex);
- global_top_implementation = implementation;
- }
-}
-
-// Prints format (with ap) into output and correctly deals with the result
-// being too long etc.
-void ExecuteFormat(char *output, size_t output_size,
- const char *format, va_list ap) {
- static const char *continued = "...\n";
- const size_t size = output_size - strlen(continued);
- const int ret = vsnprintf(output, size, format, ap);
- if (ret < 0) {
- LOG(FATAL, "vsnprintf(%p, %zd, %s, args) failed with %d (%s)\n",
- output, size, format, errno, strerror(errno));
- } else if (static_cast<uintmax_t>(ret) >= static_cast<uintmax_t>(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);
- }
+ global_top_implementation = implementation;
}
void NewContext() {
@@ -73,35 +46,26 @@
void *DoInit() {
SetGlobalImplementation(new RootLogImplementation());
-#ifndef __VXWORKS__
if (pthread_atfork(NULL /*prepare*/, NULL /*parent*/,
NewContext /*child*/) != 0) {
LOG(FATAL, "pthread_atfork(NULL, NULL, %p) failed\n",
NewContext);
}
-#endif
return NULL;
}
} // namespace
namespace internal {
+namespace {
-Context::Context()
- : implementation(get_global_top_implementation()),
- sequence(0) {
- cork_data.Reset();
-}
-
-void FillInMessage(log_level level, const char *format, va_list ap,
- LogMessage *message) {
+void FillInMessageBase(log_level level, LogMessage *message) {
Context *context = Context::Get();
- ExecuteFormat(message->message, sizeof(message->message), format, ap);
-
message->level = level;
message->source = context->source;
- memcpy(message->name, context->name.c_str(), context->name.size() + 1);
+ memcpy(message->name, context->name.c_str(), context->name.size());
+ message->name_length = context->name.size();
time::Time now = time::Time::Now();
message->seconds = now.sec();
@@ -110,16 +74,101 @@
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 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) {
- fprintf(output, "%s(%" PRId32 ")(%05" PRIu16 "): %s at"
- " %010" PRId32 ".%09" PRId32 "s: %s",
- message.name, static_cast<int32_t>(message.source), message.sequence,
- log_str(message.level), message.seconds, message.nseconds,
- message.message);
+#define BASE_FORMAT \
+ "%.*s(%" PRId32 ")(%05" PRIu16 "): %s at %010" PRId32 ".%09" PRId32 "s: "
+#define BASE_ARGS \
+ static_cast<int>(message.name_length), message.name, \
+ static_cast<int32_t>(message.source), message.sequence, \
+ log_str(message.level), message.seconds, message.nseconds
+ switch (message.type) {
+ case LogMessage::Type::kString:
+ fprintf(output, BASE_FORMAT "%.*s", BASE_ARGS,
+ static_cast<int>(message.message_length), message.message);
+ break;
+ case LogMessage::Type::kStruct:
+ char buffer[LOG_MESSAGE_LEN];
+ 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, 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;
+ }
}
} // 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[LOG_MESSAGE_LEN * 5];
+ 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);
+}
+
StreamLogImplementation::StreamLogImplementation(FILE *stream)
: stream_(stream) {}
@@ -130,68 +179,6 @@
internal::PrintMessage(stream_, message);
}
-void LogImplementation::DoVLog(log_level level, const char *format, va_list ap,
- int levels) {
- Context *context = Context::Get();
-
- LogImplementation *top_implementation = context->implementation;
- LogImplementation *new_implementation = top_implementation;
- LogImplementation *implementation = NULL;
- assert(levels >= 1);
- 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;
- implementation->DoLog(level, format, ap);
- context->implementation = top_implementation;
-
- 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);
- }
- }
-
- 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();
-}
-
void LogNext(log_level level, const char *format, ...) {
va_list ap;
va_start(ap, format);
@@ -229,25 +216,3 @@
} // 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_impl.h b/aos/common/logging/logging_impl.h
index 18f35bc..3d5b387 100644
--- a/aos/common/logging/logging_impl.h
+++ b/aos/common/logging/logging_impl.h
@@ -7,17 +7,29 @@
#include <limits.h>
#include <string.h>
#include <stdio.h>
+#include <stdarg.h>
#include <string>
+#include <functional>
#include "aos/common/logging/logging.h"
#include "aos/common/type_traits.h"
#include "aos/common/mutex.h"
+namespace aos {
+
+class 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.
namespace aos {
namespace logging {
@@ -28,21 +40,30 @@
// using a gcc function attribute.
// The struct that the code uses for making logging calls.
-// Packed so that it ends up the same under both linux and vxworks.
-struct __attribute__((packed)) LogMessage {
-#ifdef __VXWORKS__
- static_assert(sizeof(pid_t) == sizeof(int),
- "we use task IDs (aka ints) and pid_t interchangeably");
-#endif
- // Actually the task ID (aka a pointer to the TCB) on the cRIO.
+struct LogMessage {
+ enum class Type : uint8_t {
+ kString, kStruct,
+ };
+
+ int32_t seconds, nseconds;
+ // message_length is the length of everything in message for all types.
+ 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;
- int32_t seconds, nseconds;
char name[100];
- char message[LOG_MESSAGE_LEN];
+ 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;
+ };
};
static_assert(shm_ok<LogMessage>::value, "it's going in a queue");
@@ -81,6 +102,10 @@
void LogNext(log_level level, const char *format, ...)
__attribute__((format(LOG_PRINTF_FORMAT_TYPE, 2, 3)));
+// Will take a structure and log it.
+template <class T>
+void DoLogStruct(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
@@ -103,13 +128,38 @@
// Actually logs the given message. Implementations should somehow create a
// LogMessage and then call internal::FillInMessage.
virtual void DoLog(log_level level, const char *format, va_list ap) = 0;
+ void DoLogVariadic(log_level level, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ DoLog(level, format, ap);
+ va_end(ap);
+ }
- // Function of this class so that it can access DoLog.
- // Levels is how many LogImplementations to not use off the stack.
+ // 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);
+
+ // 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);
- // Friends so that they can access DoVLog.
+ // 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);
+
+ // 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 &);
LogImplementation *next_;
};
@@ -151,6 +201,8 @@
// goes.
namespace internal {
+extern 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.
struct Context {
@@ -202,14 +254,32 @@
} cork_data;
};
-// Fills in *message according to the given inputs. Used for implementing
-// LogImplementation::DoLog.
+// 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 *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);
// 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);
+
+// Runs the given function with the current LogImplementation (handles switching
+// it out while running function etc).
+void RunWithCurrentImplementation(
+ int levels, ::std::function<void(LogImplementation *)> function);
+
} // namespace internal
} // 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..b4244fb
--- /dev/null
+++ b/aos/common/logging/logging_interface.cc
@@ -0,0 +1,140 @@
+#include "aos/common/logging/logging_impl.h"
+
+#include <assert.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 {
+
+LogImplementation *global_top_implementation(NULL);
+
+Context::Context()
+ : implementation(global_top_implementation),
+ 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) {
+ LOG(FATAL, "vsnprintf(%p, %zd, %s, args) failed with %d (%s)\n",
+ output, size, format, errno, strerror(errno));
+ } 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;
+ assert(levels >= 1);
+ 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) {
+ implementation->DoLog(level, format, ap);
+
+ 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/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..eefb4ac
--- /dev/null
+++ b/aos/common/logging/queue_logging.cc
@@ -0,0 +1,35 @@
+#include "aos/common/logging/logging_impl.h"
+
+#include "aos/common/die.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(serialize)) {
+ 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..191d4c7
--- /dev/null
+++ b/aos/common/logging/queue_logging.h
@@ -0,0 +1,36 @@
+#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"
+
+namespace aos {
+namespace logging {
+
+#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) { \
+ fprintf(stderr, "DoLogStruct(FATAL) fell through!!!!!\n"); \
+ printf("see stderr\n"); \
+ abort(); \
+ } \
+ } 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/mutex_test.cpp b/aos/common/mutex_test.cpp
index 29e1956..f946dfa 100644
--- a/aos/common/mutex_test.cpp
+++ b/aos/common/mutex_test.cpp
@@ -10,6 +10,7 @@
#include "gtest/gtest.h"
#include "aos/linux_code/ipc_lib/aos_sync.h"
+#include "aos/common/die.h"
namespace aos {
namespace testing {
@@ -17,6 +18,11 @@
class MutexTest : public ::testing::Test {
public:
Mutex test_mutex;
+
+ protected:
+ void SetUp() override {
+ SetDieTestMode(true);
+ }
};
typedef MutexTest MutexDeathTest;
diff --git a/aos/common/network/team_number.cc b/aos/common/network/team_number.cc
index 3e9aeb6..38b169a 100644
--- a/aos/common/network/team_number.cc
+++ b/aos/common/network/team_number.cc
@@ -11,7 +11,10 @@
namespace network {
namespace {
+uint16_t override_team = 0;
+
uint16_t *DoGetTeamNumber() {
+ if (override_team != 0) return &override_team;
const in_addr &address = configuration::GetOwnIPAddress();
static uint16_t r =
(((address.s_addr & 0xFF00) >> 8) * 100) +
@@ -27,5 +30,7 @@
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
index f250c85..08103ed 100644
--- a/aos/common/network/team_number.h
+++ b/aos/common/network/team_number.h
@@ -11,6 +11,12 @@
// 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.
+void OverrideTeamNumber(uint16_t team);
+
} // namespace network
} // namespace aos
diff --git a/aos/common/queue.h b/aos/common/queue.h
index d37fe59..65bfa95 100644
--- a/aos/common/queue.h
+++ b/aos/common/queue.h
@@ -16,10 +16,13 @@
#endif // USE_UNSAFE
#include "aos/common/time.h"
-
namespace aos {
+class 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;
@@ -30,7 +33,7 @@
// Zeros out the time.
void Zero();
- // Returns the size of the message in bytes.
+ // Returns the size of the common fields.
static size_t Size() { return sizeof(Time); }
// Deserializes the common fields from the buffer.
@@ -43,6 +46,8 @@
// Writes the contents of the message to the provided buffer.
size_t Print(char *buffer, int length) const;
+
+ static const MessageType *GetType();
};
template <class T> class Queue;
@@ -117,7 +122,7 @@
// 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 copy the pointer, then it will correctly
+ // 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)
@@ -197,7 +202,7 @@
}
// Initializes the queue. This may optionally be called to do any one time
- // work before sending information, and may be be called mulitple times.
+ // 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();
@@ -224,7 +229,7 @@
// Returns true if the latest value in the queue is newer than age mseconds.
bool IsNewerThanMS(int age) {
- // TODO(aschuh): Log very verbosely if something is _ever_ stale;
+ // TODO(aschuh): Log very verbosely if something is _ever_ stale.
if (get() != NULL) {
return Age() < time::Time::InMS(age);
} else {
diff --git a/aos/common/queue_test.cc b/aos/common/queue_test.cc
index 32d1d23..58c88dc 100644
--- a/aos/common/queue_test.cc
+++ b/aos/common/queue_test.cc
@@ -6,6 +6,7 @@
#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;
@@ -15,6 +16,10 @@
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
@@ -207,7 +212,7 @@
// Tests that the hash works.
TEST_F(GroupTest, RealHash) {
- EXPECT_EQ(static_cast<uint32_t>(0x5b25097f), test_queuegroup.hash());
+ EXPECT_EQ(static_cast<uint32_t>(0x93596b2f), test_queuegroup.hash());
}
// Tests that name works.
@@ -262,7 +267,7 @@
msg.test_int = 2056;
msg.sent_time = Time(971, 254);
- std::string golden("971.000000254s, t, 2056");
+ std::string golden("971.000000254s, T, 2056");
EXPECT_EQ(golden.size(), msg.Print(printdata, sizeof(printdata)));
EXPECT_EQ(golden, std::string(printdata));
@@ -272,7 +277,7 @@
// 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>(0xcf740cc1),
+ EXPECT_EQ(static_cast<uint32_t>(0xc33651ac),
static_cast<uint32_t>(TestingMessage::kHash));
}
diff --git a/aos/common/queue_types.cc b/aos/common/queue_types.cc
new file mode 100644
index 0000000..d329cf1
--- /dev/null
+++ b/aos/common/queue_types.cc
@@ -0,0 +1,283 @@
+#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;
+ size_t fields_size = 0;
+ for (int i = 0; i < number_fields; ++i) {
+ fields_size += sizeof(fields[i]->type);
+ fields_size += sizeof(size_t);
+ fields_size += fields[i]->name.size();
+ }
+ if (max_bytes < sizeof(id) + sizeof(super_size) + sizeof(size_t) +
+ sizeof(number_fields) + name.size() + fields_size) {
+ return -1;
+ }
+
+ size_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(), name.size());
+ buffer += name.size();
+
+ for (int i = 0; i < number_fields; ++i) {
+ to_network(&fields[i]->type, buffer);
+ buffer += sizeof(fields[i]->type);
+ length = fields[i]->name.size();
+ to_network(&length, buffer);
+ buffer += sizeof(length);
+ memcpy(buffer, fields[i]->name.data(), fields[i]->name.size());
+ buffer += length;
+ }
+
+ return buffer - buffer_start;
+}
+
+MessageType *MessageType::Deserialize(const char *buffer, size_t *bytes) {
+ size_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) {
+ size_t field_name_length;
+ if (*bytes < sizeof(fields[i]->type) + sizeof(field_name_length)) {
+ return nullptr;
+ }
+ *bytes -= sizeof(fields[i]->type) + sizeof(field_name_length);
+
+ to_host(buffer, &fields[i]->type);
+ buffer += sizeof(fields[i]->type);
+ 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 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;
+ if (MessageType::IsPrimitive(type.fields[i]->type)) {
+ if (!PrintField(output, output_bytes, input, input_bytes,
+ type.fields[i]->type)) {
+ return false;
+ }
+ } else {
+ if (!PrintMessage(output, output_bytes, input, input_bytes,
+ type_cache::Get(type.fields[i]->type))) {
+ return false;
+ }
+ }
+
+ // Update the input and output pointers, ignoring the trailing '\0' that the
+ // subcall put on.
+ output += output_bytes_before - *output_bytes - 1;
+ *output_bytes += 1;
+ 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;
+}
+
+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);
+ if (cache.count(type_id) > 0) {
+ return cache.at(type_id).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);
+ }
+
+ ::aos::MutexLocker locker(&cache_lock);
+ CacheEntry &cached = cache.at(type_id);
+ if (cached.in_shm) return;
+
+ 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[512];
+ 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 < cached.type.number_fields; ++i) {
+ if (!MessageType::IsPrimitive(cached.type.fields[i]->type)) {
+ AddShm(cached.type.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..55d26b4
--- /dev/null
+++ b/aos/common/queue_types.h
@@ -0,0 +1,124 @@
+#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"
+
+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;
+ ::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];
+ }
+ }
+
+ // 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.
+ // Returns a new instance allocated with new or nullptr for error.
+ static MessageType *Deserialize(const char *buffer, size_t *bytes);
+
+ static bool IsPrimitive(uint32_t type_id) {
+ return (type_id & 0x2000) != 0;
+ }
+
+ // How many (serialized) bytes the superclass takes up.
+ size_t super_size;
+ // The type ID for this.
+ uint32_t id;
+ ::std::string name;
+
+ int number_fields;
+ const Field **fields;
+
+ private:
+ // Internal constructor for Deserialize to use.
+ MessageType(size_t super_size, uint32_t id, ::std::string &&name,
+ int 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.
+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));
+
+// 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..72eae94
--- /dev/null
+++ b/aos/common/queue_types_test.cc
@@ -0,0 +1,178 @@
+#include "aos/common/queue_types.h"
+
+#include <memory>
+
+#include "gtest/gtest.h"
+
+#include "aos/common/test_queue.q.h"
+#include "aos/common/byteorder.h"
+
+using ::aos::common::testing::Structure;
+using ::aos::common::testing::MessageWithStructure;
+using ::aos::common::testing::OtherTestingMessage;
+
+namespace aos {
+namespace testing {
+
+typedef MessageType::Field Field;
+
+static const MessageType kTestType1(5, 0x1234, "TestType1",
+ {new Field{0, "field1"},
+ new Field{0, "field2"},
+ new Field{0, "field3"}});
+
+class QueueTypesTest : public ::testing::Test {
+ public:
+ ::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 PrintFieldTest : public ::testing::Test {
+ public:
+ char input[128], output[128];
+ size_t input_bytes, output_bytes;
+};
+typedef PrintFieldTest PrintMessageTest;
+
+TEST_F(PrintFieldTest, Basic) {
+ static const uint16_t kData = 971;
+ input_bytes = sizeof(kData);
+ to_network(&kData, input);
+ output_bytes = sizeof(output);
+ ASSERT_TRUE(PrintField(output, &output_bytes, input, &input_bytes,
+ Structure::GetType()->fields[1]->type));
+ EXPECT_EQ(0u, input_bytes);
+ EXPECT_EQ(sizeof(output) - 4, output_bytes);
+ EXPECT_EQ(::std::string("971\0", 4),
+ ::std::string(output, sizeof(output) - output_bytes));
+}
+
+// Tests PrintField with trailing input bytes and no extra output bytes.
+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;
+ assert(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(0u, 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 = 5;
+ 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}";
+
+TEST_F(PrintMessageTest, Basic) {
+ assert(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) {
+ assert(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) {
+ assert(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);
+}
+
+} // namespace testing
+} // namespace aos
diff --git a/aos/common/test_queue.q b/aos/common/test_queue.q
index a7b441f..d603802 100644
--- a/aos/common/test_queue.q
+++ b/aos/common/test_queue.q
@@ -1,5 +1,17 @@
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;
diff --git a/aos/common/util/log_interval.h b/aos/common/util/log_interval.h
new file mode 100644
index 0000000..188a16c
--- /dev/null
+++ b/aos/common/util/log_interval.h
@@ -0,0 +1,85 @@
+#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_;
+ 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".
+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.Hit(LOG_SOURCENAME ": " STRINGIFY(__LINE__))
+ void Hit(const char *context) {
+ interval_.WantToLog();
+ if (interval_.ShouldLog()) {
+ log_do(level_, "%s: %.*s %d times over %f sec\n", context,
+ static_cast<int>(message_.size()), message_.data(),
+ interval_.Count(), interval_.interval().ToSeconds());
+ }
+ }
+
+ private:
+ LogInterval interval_;
+ const log_level level_;
+ const ::std::string message_;
+};
+
+} // namespace util
+} // namespace aos
+
+#endif // AOS_COMMON_UTIL_LOG_INTERVAL_H_
diff --git a/aos/common/util/util.gyp b/aos/common/util/util.gyp
index 852f3d7..98f1a3d 100644
--- a/aos/common/util/util.gyp
+++ b/aos/common/util/util.gyp
@@ -1,6 +1,21 @@
{
'targets': [
{
+ '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': 'thread',
'type': 'static_library',
'sources': [
@@ -37,8 +52,6 @@
'dependencies': [
':trapezoid_profile',
'<(EXTERNALS):gtest',
- # TODO(brians): remove this when time no longer requires it
- '<(AOS)/build/aos.gyp:logging',
],
},
{
diff --git a/aos/config/configure-prime.txt b/aos/config/configure-prime.txt
index 87b2818..1adaccd 100644
--- a/aos/config/configure-prime.txt
+++ b/aos/config/configure-prime.txt
@@ -67,10 +67,7 @@
11. Set the correct date.
`date` to check if date is correct.
`date -s <date string>` to set it if it isn't.
-12. Make it export UART1 on boot.
- Add the following to /boot/uboot/uenv.txt:
- "optargs=capemgr.enable_partno=BB-UART1"
-13. Fix the locale setup for SSHing in.
+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.
diff --git a/aos/config/starter b/aos/config/starter
index f7a9c18..1fc05a0 100755
--- a/aos/config/starter
+++ b/aos/config/starter
@@ -69,6 +69,7 @@
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
+ killall starter_loop.sh
start-stop-daemon --stop --quiet --retry=INT/10/KILL/5 --pidfile $PIDFILE
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
@@ -80,7 +81,7 @@
# sleep for some time.
#start-stop-daemon --stop --quiet --oknodo --retry=INT/10/KILL/5
#[ "$?" = 2 ] && return 2
- sleep 10
+ sleep 5
# Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE
return "$RETVAL"
diff --git a/aos/externals/gtest.patch b/aos/externals/gtest.patch
index 6caba01..cbf7294 100644
--- a/aos/externals/gtest.patch
+++ b/aos/externals/gtest.patch
@@ -1,63 +1,50 @@
-diff -rupN gtest-1.6.0-p1/fused-src/gtest/gtest-all.cc gtest-1.6.0/fused-src/gtest/gtest-all.cc
---- gtest-1.6.0-p1/fused-src/gtest/gtest-all.cc 2011-04-15 12:54:57.000000000 -0700
-+++ gtest-1.6.0/fused-src/gtest/gtest-all.cc 2012-11-12 17:42:37.881573135 -0800
-@@ -379,7 +379,25 @@ class GTEST_API_ SingleFailureChecker {
+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
+@@ -98,6 +98,21 @@ size_t GetThreadCount() {
+ }
+ }
- // cpplint thinks that the header is already included, so we want to
- // silence it.
-+#ifdef __VXWORKS__
-+# include <time.h> // NOLINT
-+# include <sys/times.h> // NOLINT
-+static inline int gettimeofday(struct timeval *tv, void *) {
-+ struct timespec ts;
++#elif GTEST_OS_LINUX
+
-+ if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
-+ printf("Gettimeofday error\n");
-+ tv->tv_sec = 0;
-+ tv->tv_usec = 0;
-+ return -1;
++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);
+ }
-+ tv->tv_sec = ts.tv_sec;
-+ tv->tv_usec = ts.tv_nsec/1000;
-+ return 0;
++ return thread_count;
+}
-+#else
- # include <sys/time.h> // NOLINT
-+#endif
- # include <unistd.h> // NOLINT
-
- #endif // GTEST_OS_LINUX
-@@ -7751,6 +7769,8 @@ bool FilePath::CreateFolder() const {
- delete [] unicode;
- #elif GTEST_OS_WINDOWS
- int result = _mkdir(pathname_.c_str());
-+#elif defined(__VXWORKS__)
-+ int result = mkdir(pathname_.c_str());
- #else
- int result = mkdir(pathname_.c_str(), 0777);
- #endif // GTEST_OS_WINDOWS_MOBILE
-@@ -7870,7 +7890,7 @@ void FilePath::Normalize() {
- namespace testing {
- namespace internal {
-
--#if defined(_MSC_VER) || defined(__BORLANDC__)
-+#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__VXWORKS__)
- // MSVC and C++Builder do not provide a definition of STDERR_FILENO.
- const int kStdOutFileno = 1;
- const int kStdErrFileno = 2;
-diff -rupN gtest-1.6.0-p1/include/gtest/internal/gtest-port.h gtest-1.6.0/include/gtest/internal/gtest-port.h
---- gtest-1.6.0-p1/include/gtest/internal/gtest-port.h 2011-04-15 12:49:10.000000000 -0700
-+++ gtest-1.6.0/include/gtest/internal/gtest-port.h 2012-11-12 17:27:33.536801263 -0800
-@@ -197,6 +197,12 @@
- #include <sstream> // NOLINT
- #include <string> // NOLINT
-
-+#ifdef __VXWORKS__
-+int read(int fd, void *buf, size_t count);
-+int write(int fd, const void *buf, size_t count);
-+int close(int fd);
-+#endif
+
- #define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com"
- #define GTEST_FLAG_PREFIX_ "gtest_"
- #define GTEST_FLAG_PREFIX_DASH_ "gtest-"
+ #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_limit));
++
+ ExecDeathTestArgs* const args = static_cast<ExecDeathTestArgs*>(child_arg);
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(close(args->close_fd));
diff --git a/aos/linux_code/camera/Buffers.cpp b/aos/linux_code/camera/Buffers.cpp
index e1d22b6..19c1d45 100644
--- a/aos/linux_code/camera/Buffers.cpp
+++ b/aos/linux_code/camera/Buffers.cpp
@@ -61,24 +61,21 @@
}
void Buffers::Release() {
- if (message_ != NULL) {
- queue_->FreeMessage(message_);
- message_ = NULL;
- }
+ message_.reset();
}
-const void *Buffers::GetNext(bool block,
- uint32_t *bytesused, timeval *timestamp, uint32_t *sequence) {
+const void *Buffers::GetNext(bool block, uint32_t *bytesused,
+ timeval *timestamp, uint32_t *sequence) {
Release();
// TODO(brians) make sure the camera reader process hasn't died
do {
if (block) {
- message_ = static_cast<const Message *>(queue_->ReadMessage(
- RawQueue::kPeek | RawQueue::kBlock));
+ message_.reset(static_cast<const Message *>(
+ queue_->ReadMessage(RawQueue::kPeek | RawQueue::kBlock)));
} else {
static int index = 0;
- message_ = static_cast<const Message *>(queue_->ReadMessageIndex(
- RawQueue::kBlock, &index));
+ message_.reset(static_cast<const Message *>(
+ queue_->ReadMessageIndex(RawQueue::kBlock, &index)));
}
} while (block && message_ == NULL);
if (message_ != NULL) {
@@ -132,9 +129,12 @@
return myfds[0];
}
-Buffers::Buffers() : server_(CreateSocket(connect)), fd_(FetchFD()), message_(NULL) {
+Buffers::Buffers()
+ : server_(CreateSocket(connect)),
+ fd_(FetchFD()),
+ queue_(RawQueue::Fetch(kQueueName.c_str(), sizeof(Message), 971, 1)),
+ message_(queue_) {
MMap();
- queue_ = RawQueue::Fetch(kQueueName.c_str(), sizeof(Message), 971, 1);
}
Buffers::~Buffers() {
diff --git a/aos/linux_code/camera/Buffers.h b/aos/linux_code/camera/Buffers.h
index b447468..aedd79f 100644
--- a/aos/linux_code/camera/Buffers.h
+++ b/aos/linux_code/camera/Buffers.h
@@ -8,6 +8,7 @@
#include "aos/linux_code/ipc_lib/queue.h"
#include "aos/common/type_traits.h"
+#include "aos/atom_code/ipc_lib/unique_message_ptr.h"
namespace aos {
namespace camera {
@@ -17,6 +18,7 @@
// It has to do a lot of the same things as all the other ones, but it gets
// the information from different places (some of it gets sent out by it).
friend class Reader;
+
// Not an abstract name so that an existing one can just be unlinked without
// disturbing it if necessary (like with shm_link).
static const std::string kFDServerName;
@@ -50,14 +52,17 @@
uint32_t sequence;
};
static_assert(shm_ok<Message>::value, "it's going through queues");
- // The current one. Sometimes NULL.
- const Message *message_;
- static const std::string kQueueName;
+
// NULL for the Reader one.
- RawQueue *queue_;
+ RawQueue *const queue_;
+ // The current one. Sometimes NULL.
+ unique_message_ptr<const Message> message_;
+
+ static const std::string kQueueName;
// Make the actual mmap calls.
// Called by Buffers() automatically.
void MMap();
+
public:
Buffers();
// Will clean everything up.
@@ -89,4 +94,3 @@
} // namespace aos
#endif
-
diff --git a/aos/linux_code/camera/camera.gyp b/aos/linux_code/camera/camera.gyp
index e4f1e04..119d105 100644
--- a/aos/linux_code/camera/camera.gyp
+++ b/aos/linux_code/camera/camera.gyp
@@ -1,44 +1,6 @@
{
'targets': [
{
- 'target_name': 'aos_camera',
- 'type': 'loadable_module',
- 'sources': [
- 'jni.cpp',
- ],
- 'dependencies': [
- '<(AOS)/common/network/network.gyp:socket_so',
- '<(AOS)/common/common.gyp:timing_so',
- 'private_aos_camera_jar',
- '<(EXTERNALS):libjpeg',
- ],
- 'export_dependent_settings': [
- '<(AOS)/common/network/network.gyp:socket_so',
- '<(AOS)/common/common.gyp:timing_so',
- 'private_aos_camera_jar',
- ],
- },
- {
- 'target_name': 'private_aos_camera_jar',
- 'dependencies': [
- '<(EXTERNALS):javacv',
- ],
- 'variables': {
- 'srcdirs': ['java'],
- 'gen_headers': ['aos.Natives'],
- },
- 'export_dependent_settings': [
- '<(EXTERNALS):javacv',
- ],
- 'direct_dependent_settings': {
- 'variables': {
- 'jni_libs': ['aos_camera'],
- },
- },
- 'includes': ['../../build/java.gypi'],
- 'hard_dependency': 1,
- },
- {
'target_name': 'buffers',
'type': 'static_library',
'sources': [
@@ -47,9 +9,11 @@
'dependencies': [
'<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:queue',
'<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/atom_code/ipc_lib/ipc_lib.gyp:scoped_message_ptr',
],
'export_dependent_settings': [
'<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:queue',
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:scoped_message_ptr',
],
},
{
diff --git a/aos/linux_code/camera/java/aos/CameraProcessor.java b/aos/linux_code/camera/java/aos/CameraProcessor.java
deleted file mode 100644
index 4f6c68d..0000000
--- a/aos/linux_code/camera/java/aos/CameraProcessor.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package aos;
-
-import java.io.IOException;
-import java.net.Inet4Address;
-import java.net.InetSocketAddress;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.channels.SocketChannel;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.googlecode.javacv.cpp.opencv_core;
-
-/**
- * Makes implementing code that processes frames from a camera easy.
- */
-public abstract class CameraProcessor {
- private static final Logger LOG = Logger.getLogger(CameraProcessor.class.getName());
- protected final ImageGetter getter;
- protected final ServableImage start = new ServableImage(ImageGetter.width, ImageGetter.height, opencv_core.IPL_DEPTH_8U, 3);
-
- /**
- * Parses any arguments it recognizes out of {@code args} and initializes stuff appropriately.
- * This includes using {@link QueueLogHandler} for all exceptions and {@link Thread#setDefaultUncaughtExceptionHandler}ing.
- * @param args from {@code main}
- */
- protected CameraProcessor(String[] args) throws UnknownHostException, IOException {
- QueueLogHandler.UseForAll();
- ReadableByteChannel channel = null;
- for (int i = 0; i < args.length; ++i) {
- final String c = args[i];
- if (c.equals("--host")) {
- String host = args[++i];
- final SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(Inet4Address.getByName(host), 9714));
- socketChannel.write(ByteBuffer.wrap(new byte[] {'\r', '\n', '\r', '\n'})); // get it past the read headers stage
- channel = socketChannel;
- } else {
- System.err.println("CameraProcessor: warning: unrecognized argument '" + c + "'. ignoring");
- }
- }
-
- if (channel != null) {
- getter = new ChannelImageGetter(channel);
- } else {
- System.out.println("creating QueueImageGetter");
- getter = new QueueImageGetter();
- System.out.println("done");
- }
-
- LOG.log(Level.INFO, "CameraProcessor is up");
- System.err.println("CameraProcessor is up (on stderr)");
- }
-
- protected abstract void RunIteration();
-
- protected void Run() {
- while (true) {
- if (!getter.get(start.getImage())) {
- LOG.log(Level.WARNING, "getting image failed");
- continue;
- }
- RunIteration();
- start.releaseImage();
- }
- }
-}
diff --git a/aos/linux_code/camera/java/aos/ChannelImageGetter.java b/aos/linux_code/camera/java/aos/ChannelImageGetter.java
deleted file mode 100644
index 511b55b..0000000
--- a/aos/linux_code/camera/java/aos/ChannelImageGetter.java
+++ /dev/null
@@ -1,158 +0,0 @@
-package aos;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.channels.SelectableChannel;
-import java.nio.channels.SelectionKey;
-import java.nio.channels.Selector;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * Retrieves images from a {@link InputChannel}. Expects the images in mjpg form.
- * For now, only accepts streams formatted pretty closely to how aos::camera::HTTPStreamer does it.
- */
-public class ChannelImageGetter extends JPEGImageGetter {
- /**
- * What to multiply each length by when it needs to allocate a larger buffer to fit an image.
- */
- private static final double extraLength = 1.2;
-
- private static final Logger LOG = Logger.getLogger(ChannelImageGetter.class
- .getName());
- private final ReadableByteChannel channel;
- private final Selector selector = Selector.open();
- private String separator = "--boundarydonotcross\r\n";
- private ByteBuffer current;
- private final ByteBuffer headerBuffer = ByteBuffer.allocateDirect(30);
- private final Map<String, String> headers = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
-
- public ChannelImageGetter(ReadableByteChannel channel) throws IOException {
- this.channel = channel;
- if (channel instanceof SelectableChannel) {
- ((SelectableChannel)channel).configureBlocking(false);
- }
- }
-
- @Override
- public ByteBuffer getJPEG() {
- try {
- if (!parseHeaders()) {
- return null;
- }
- LOG.log(Level.FINE, "parsed headers " + headers.toString());
- try {
- final int length = Integer.parseInt(headers.get("Content-Length"));
- if (current == null || current.capacity() < length) {
- LOG.log(Level.INFO, "allocating a new direct buffer of length " + length * extraLength);
- current = ByteBuffer.allocateDirect((int) (length * extraLength));
- } else {
- current.rewind();
- current.limit(length);
- }
- } catch (NumberFormatException e) {
- LOG.log(Level.WARNING, "couldn't parse '" + headers.get("Content-Length") + "' as a number");
- return null;
- }
- current.put(headerBuffer); // copy out any of the image that got buffered with the headers
- while (current.hasRemaining()) {
- channel.read(current);
- }
- current.flip();
- } catch (IOException e) {
- LOG.log(Level.WARNING, "reading the headers and/or image failed", e);
- return null;
- }
- return current;
- }
- // returns success
- private boolean parseHeaders() throws IOException {
- // Reads chunks into headerBuffer and parses out headers.
- // Looks for separator first.
-
- headerBuffer.clear();
- headers.clear();
- final byte[] separatorBytes = separator.getBytes();
- int separatorIndex = 0; // how much of the separator has been matched
- while (headerBuffer.hasRemaining() || headerBuffer.limit() < headerBuffer.capacity()) {
- if (channel instanceof SelectableChannel) {
- ((SelectableChannel)channel).register(selector, SelectionKey.OP_READ);
- selector.select();
- }
- headerBuffer.limit(headerBuffer.capacity());
- channel.read(headerBuffer);
- headerBuffer.flip();
- if (separatorIndex < separatorBytes.length) {
- // make sure we don't get part of the way through
- while (headerBuffer.remaining() >= (separatorBytes.length - separatorIndex)) {
- final byte c = headerBuffer.get();
- if (separatorBytes[separatorIndex++] != c) {
- separatorIndex = 0;
- }
- if (separatorIndex == separatorBytes.length) {
- break;
- }
- }
- headerBuffer.compact();
- } else {
- int keyEnd = 0, valueStart = 0;
- boolean foundEndR = false; // found the end \r
- while (headerBuffer.hasRemaining()) {
- final byte c = headerBuffer.get();
- if (foundEndR) {
- if (c != '\n') {
- LOG.log(Level.WARNING, "found \r\n\r but no \n afterwards");
- } else {
- return true;
- }
- } else if (keyEnd == 0) {
- if (c == ':') {
- keyEnd = headerBuffer.position() - 1;
- } else if (c == '\r') {
- foundEndR = true;
- }
- } else if (valueStart == 0) {
- if (c != ' ') {
- valueStart = headerBuffer.position() - 1;
- }
- } else {
- if (c == '\r') {
- final int valueEnd = headerBuffer.position();
- final byte[] key = new byte[keyEnd];
- headerBuffer.position(0);
- headerBuffer.get(key);
- final byte[] value = new byte[valueEnd - valueStart - 1];
- headerBuffer.position(valueStart);
- headerBuffer.get(value);
- headers.put(new String(key), new String(value));
-
- headerBuffer.get(); // get the \r
- headerBuffer.get(); // get the \n
-
- headerBuffer.compact();
- headerBuffer.flip();
-
- keyEnd = valueStart = 0;
- }
- }
- }
- }
- }
- // if we got here, then it doesn't have space left and we haven't finished
- LOG.log(Level.WARNING, "got a header that was too long. headerBuffer should be made bigger");
- return false;
- }
-
- @Override
- public double getTimestamp() {
- if (headers.containsKey("X-Timestamp")) {
- return Double.parseDouble(headers.get("X-Timestamp"));
- } else {
- throw new UnsupportedOperationException("source stream doesn't have X-Timestamp headers");
- }
- }
-
-}
diff --git a/aos/linux_code/camera/java/aos/DebugServer.java b/aos/linux_code/camera/java/aos/DebugServer.java
deleted file mode 100644
index 398cb11..0000000
--- a/aos/linux_code/camera/java/aos/DebugServer.java
+++ /dev/null
@@ -1,357 +0,0 @@
-package aos;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.channels.ClosedChannelException;
-import java.nio.channels.SelectionKey;
-import java.nio.channels.Selector;
-import java.nio.channels.ServerSocketChannel;
-import java.nio.channels.SocketChannel;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import com.googlecode.javacv.cpp.opencv_core.IplImage;
-
-/**
- * A server that serves {@link ServableImage}s.
- */
-public class DebugServer {
- private static final String initialHeaderString = "HTTP/1.0 200 OK\r\n"
- + "Connection: close\r\n"
- + "Server: AOS/0.0 Vision Code Debug\r\n"
- + "Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0\r\n"
- + "Pragma: no-cache\r\n"
- + "Expires: Mon, 3 Jan 2000 12:34:56 GMT\r\n"
- + "Content-Type: multipart/x-mixed-replace; boundary=boundarydonotcross\r\n";
- private static final String intermediateHeaderFormat = "\r\n--boundarydonotcross\r\n"
- + "Content-Type: image/bmp\r\n"
- + "Content-Length: %d\r\n"
- + "X-Timestamp: %f\r\n"
- + "\r\n";
- private static final ByteBuffer initialHeader;
- private static final Pattern headerPattern;
- static {
- initialHeader = ByteBuffer.wrap(initialHeaderString.getBytes())
- .asReadOnlyBuffer();
- headerPattern = Pattern.compile("^GET ([^? ]+)(?:\\?i=(\\S*))? HTTP/.*\r\n.*$",
- Pattern.DOTALL);
- }
-
- private static final Logger LOG = Logger.getLogger(DebugServer.class
- .getName());
- private final ServerSocketChannel server;
- private final Collection<Client> clients = new ArrayList<Client>();
- private final Map<String, ServableImage> images = new HashMap<String, ServableImage>();
- private final Map<String, Palette> palettes = new HashMap<String, Palette>();
- private double timestamp;
-
- public static class Palette {
- private final ByteArrayOutputStream out = new ByteArrayOutputStream();
- private int entries = 0;
-
- /**
- * Adds a new color. All 4 arguments are unsigned bytes.
- * @param r red
- * @param g green
- * @param b blue
- * @param a alpha (doesn't seem to work)
- * @return this
- */
- public Palette add(int r, int g, int b, int a) {
- out.write(b);
- out.write(g);
- out.write(r);
- out.write(a);
- ++entries;
- return this;
- }
-
- private int entries() {
- return entries;
- }
- private int bytes() {
- return entries * 4;
- }
- private void writeTo(ByteBuffer buffer) {
- buffer.put(out.toByteArray());
- }
- }
-
- private class Client {
- private final int bmpHeaderSize;
- private int bmpType;
- private Palette palette;
-
- private final SocketChannel channel;
- private ServableImage image = null;
- private IplImage img;
- private int index;
- private final ByteBuffer[] buffers;
- private final ByteBuffer initial = initialHeader.duplicate();
- private final ByteBuffer read = ByteBuffer.allocate(1024);
-
- public Client(SocketChannel channel) throws IOException {
- this.channel = channel;
- channel.configureBlocking(false);
-
- if (bmpType == 3) {
- bmpHeaderSize = 122;
- } else if (bmpType == 0) {
- bmpHeaderSize = 54;
- } else {
- throw new AssertionError("unknown bmpType value " + bmpType);
- }
- // [0] gets filled in by createBmpHeader which writes the header into [1]
- // [2] gets set to the image buffer
- buffers = new ByteBuffer[] { null, ByteBuffer.allocate(2048), null };
- }
-
- public void register(Selector selector) throws ClosedChannelException {
- channel.register(
- selector,
- (image == null) ? SelectionKey.OP_READ
- : SelectionKey.OP_WRITE, this);
- }
-
- public void close() {
- if (image != null) {
- image.setDebugging(false);
- }
- if (channel != null) {
- try {
- channel.close();
- } catch (IOException e) {
- LOG.log(Level.WARNING,
- "encountered error when closing channel", e);
- }
- }
- }
-
- private void createBmpHeader(ByteBuffer buffer) {
- // <http://en.wikipedia.org/wiki/BMP_file_format#File_structure>
- // explains what these all are
- // signed/unsigned numbers don't matter because they'd better not be
- // that big
- final int paletteSize = (palette == null) ? 0 : palette.bytes();
- buffers[0] = ByteBuffer.wrap(String.format(intermediateHeaderFormat,
- bmpHeaderSize + paletteSize + image.imageSize(), timestamp).getBytes());
- buffer.order(ByteOrder.LITTLE_ENDIAN);
- buffer.put((byte) 'B').put((byte) 'M');
- buffer.putInt(bmpHeaderSize + paletteSize + image.imageSize());
- buffer.putInt(0); // skip ahead 4 bytes
- buffer.putInt(bmpHeaderSize + paletteSize); // offset to start of image data
- buffer.putInt(bmpHeaderSize - 14); // size of the rest of the header
- // BMPs expect image data bottom to top, so -height to fix that
- buffer.putInt(ImageGetter.width).putInt(-ImageGetter.height);
- buffer.putShort((short) 1).putShort(image.bitsPerPixel());
- buffer.putInt(bmpType);
- buffer.putInt(image.imageSize()); // number of bytes in the actual
- // image
- buffer.putInt(2835).putInt(2835); // physical resolution; don't
- // think it matters
- buffer.putInt((palette == null) ? 0 : palette.entries());
- buffer.putInt(0); // # of important colors (0 means all)
- if (palette != null) {
- palette.writeTo(buffer);
- }
- final int expected;
- if (bmpType == 0) { // BI_RGB
- expected = bmpHeaderSize + paletteSize;
- } else if (bmpType == 3) { // BI_BITFIELDS
- buffer.putInt(0x0000FF00).putInt(0x00FF0000).putInt(0xFF000000)
- .putInt(0); // RGBA bitmasks
- buffer.putInt(0x57696E20); // LCS_WINDOWS_COLOR_SPACE
- expected = bmpHeaderSize - 48;
- } else {
- throw new AssertionError("unknown bmpType value " + bmpType);
- }
- if (buffer.position() != expected) {
- throw new AssertionError(
- "header ended up the wrong size. expected "
- + expected + " but got "
- + buffer.position());
- }
- buffer.limit(bmpHeaderSize + paletteSize);
- buffer.rewind();
- }
-
- /**
- * Does anything that this one can right now.
- *
- * @return whether or not to {@link #close()} and remove this one
- */
- public boolean run() throws IOException {
- if (image == null) {
- final int bytesRead = channel.read(read);
- final String readString = new String(read.array(), 0,
- read.position());
- LOG.log(Level.INFO, "read " + bytesRead
- + " header bytes position=" + read.position()
- + " string='" + readString + "'");
-
- final Matcher matcher = headerPattern.matcher(readString);
- if (matcher.matches()) {
- final String url = matcher.group(1);
- image = images.get(url);
- if (image == null) {
- LOG.log(Level.INFO, "couldn't find an image for url '"
- + url + "'. dropping client");
- return true;
- } else {
- LOG.log(Level.INFO, "found an image for url '"
- + url + "'");
- }
- palette = palettes.get(url);
- bmpType = 0; // could change this in the future
- createBmpHeader(buffers[1]);
- image.setDebugging(true);
- final String indexString = matcher.group(2);
- if (indexString != null) {
- index = Integer.parseInt(indexString);
- } else {
- index = 0;
- }
- LOG.log(Level.INFO, "using index " + index);
- } else if (!read.hasRemaining()) {
- read.flip();
- LOG.log(Level.WARNING,
- "ran out of buffer space reading the header. currently have '"
- + readString + "'. dropping connection");
- return true;
- } else if (bytesRead == -1) {
- read.flip();
- LOG.log(Level.WARNING,
- "reached end of stream for headers without getting anything valid. currently have "
- + read.limit()
- + " bytes ='"
- + readString
- + "'. dropping connection");
- return true;
- }
- } else if (initial.hasRemaining()) {
- channel.write(initial);
- } else {
- if (buffers[2] == null) {
- img = image.getSnapshot(index);
- if (img == null) {
- return false;
- } else {
- buffers[2] = img.getByteBuffer();
- LOG.log(Level.FINE, "got " + buffers[2]
- + " from the image");
- }
- }
- channel.write(buffers);
- boolean remaining = false;
- for (ByteBuffer c : buffers) {
- if (c.hasRemaining()) {
- remaining = true;
- }
- }
- if (!remaining) {
- for (ByteBuffer c : buffers) {
- c.rewind();
- }
- buffers[2] = null;
- image.releaseSnapshot(index, img);
- }
- }
- return false;
- }
- }
-
- public DebugServer(int port) throws IOException {
- server = ServerSocketChannel.open();
- server.configureBlocking(false);
- server.socket().bind(new InetSocketAddress(port), 10);
- new Thread("DebugServer") {
- @Override
- public void run() {
- try {
- loop();
- } catch (Throwable e) {
- LOG.log(Level.SEVERE,
- "trouble running the server loop", e);
- System.exit(1);
- }
- }
- }.start();
- }
-
- public void addImage(String name, ServableImage image) {
- if (image.bitsPerPixel() != 24) {
- throw new IllegalArgumentException("only 24-bit images are supported");
- // could support 16 and 32 bpp images by using bmpType of 3
- }
- images.put(name, image);
- }
- public void addImage(String name, ServableImage image, Palette palette) {
- if (image.bitsPerPixel() != 8) {
- throw new IllegalArgumentException("only 8-bit images are supported");
- // everything should work for 1, 2, and 4 bpp ones too except for padding etc
- }
- if (palette.entries() > (1 << image.bitsPerPixel())) {
- throw new IllegalArgumentException("too many colors in the palette");
- }
- images.put(name, image);
- palettes.put(name, palette);
- }
- /**
- * This timestamp is written out in the debugging images.
- * @param timestamp the current timestamp
- */
- public void setTimestamp(double timestamp) {
- this.timestamp = timestamp;
- }
-
- private void loop() throws IOException {
- final Selector selector = Selector.open();
- server.register(selector, SelectionKey.OP_ACCEPT);
- while (true) {
- try {
- for (Client c : clients) {
- c.register(selector);
- }
- selector.select();
- for (final Iterator<SelectionKey> i = selector.selectedKeys()
- .iterator(); i.hasNext();) {
- final SelectionKey c = i.next();
- if (c.isAcceptable()) {
- // there's only 1 socket there that can accept
- final SocketChannel channel = server.accept();
- if (channel != null) {
- clients.add(new Client(channel));
- }
- } else {
- final Client client = (Client) c.attachment();
- try {
- if (client.run()) {
- client.close();
- clients.remove(client);
- }
- } catch (Exception e) {
- LOG.log(Level.INFO, "dropping client " + client
- + " because it's run() threw an exception",
- e);
- client.close();
- clients.remove(client);
- }
- }
- i.remove();
- }
- } catch (IOException e) {
- LOG.log(Level.WARNING, "trouble running the server loop", e);
- }
- }
- }
-}
diff --git a/aos/linux_code/camera/java/aos/ImageGetter.java b/aos/linux_code/camera/java/aos/ImageGetter.java
deleted file mode 100644
index f0f1063..0000000
--- a/aos/linux_code/camera/java/aos/ImageGetter.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package aos;
-
-import com.googlecode.javacv.cpp.opencv_core;
-import com.googlecode.javacv.cpp.opencv_core.IplImage;
-
-/**
- * An object that can retrieve images from somewhere.
- */
-public interface ImageGetter {
- public static int width = 640, height = 480;
-
- /**
- * Gets an image.
- * @param out Where to write the image to. Must be a 3-channel {@link opencv_core#IPL_DEPTH_8U} image.
- * @return whether it succeeded or not
- */
- public boolean get(IplImage out);
- /**
- * Only valid after a successful {@link #get()}.
- * @return The timestamp from the most recent frame. Will be in seconds with at least ms accuracy.
- */
- public double getTimestamp();
-}
-
diff --git a/aos/linux_code/camera/java/aos/JPEGDecoder.java b/aos/linux_code/camera/java/aos/JPEGDecoder.java
deleted file mode 100644
index 63d12fd..0000000
--- a/aos/linux_code/camera/java/aos/JPEGDecoder.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package aos;
-
-import java.nio.ByteBuffer;
-
-import com.googlecode.javacv.cpp.opencv_core;
-import com.googlecode.javacv.cpp.opencv_core.IplImage;
-
-/**
- * Efficiently decodes a JPEG image from a @{class ByteBuffer} into an @{class IplImage}.
- * Instances are not safe for use from multiple threads.
- * The first use of an instance allocates some largish buffers which are never freed.
- */
-public class JPEGDecoder {
- private final long[] state = new long[1];
-
- /**
- * @param in Must be direct. The {@link ByteBuffer#limit()} of it will be respected.
- * @param out Where to write the decoded image to. Must be a 3-channel {@link opencv_core#IPL_DEPTH_8U} image.
- * Will be written in the RGB color space.
- * @return Whether or not it succeeded. If not, {@code out} is undefined.
- */
- public boolean decode(ByteBuffer in, IplImage out) {
- if (out.nChannels() != 3 || out.depth() != opencv_core.IPL_DEPTH_8U) {
- throw new IllegalArgumentException("out is invalid");
- }
- return Natives.decodeJPEG(state, in, in.limit(), out.getByteBuffer());
- }
-}
-
diff --git a/aos/linux_code/camera/java/aos/JPEGImageGetter.java b/aos/linux_code/camera/java/aos/JPEGImageGetter.java
deleted file mode 100644
index 84296e7..0000000
--- a/aos/linux_code/camera/java/aos/JPEGImageGetter.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package aos;
-
-import java.nio.ByteBuffer;
-
-import com.googlecode.javacv.cpp.opencv_core.IplImage;
-
-/**
- * Helper class for {@link ImageGetter}s that return JPEG images.
- */
-public abstract class JPEGImageGetter implements ImageGetter {
-
- private final JPEGDecoder decoder = new JPEGDecoder();
-
- @Override
- public boolean get(IplImage out) {
- final ByteBuffer jpeg = getJPEG();
- if (jpeg == null) return false;
- final boolean r = decoder.decode(jpeg, out);
- release();
- return r;
- }
-
- protected abstract ByteBuffer getJPEG();
- protected void release() {}
-
-}
diff --git a/aos/linux_code/camera/java/aos/JavaCVImageGetter.java b/aos/linux_code/camera/java/aos/JavaCVImageGetter.java
deleted file mode 100644
index 67398f0..0000000
--- a/aos/linux_code/camera/java/aos/JavaCVImageGetter.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package aos;
-
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.googlecode.javacv.FrameGrabber;
-import com.googlecode.javacv.cpp.opencv_core.IplImage;
-
-
-/**
- * Adapts between the JavaCV {@link FrameGrabber} and {@link ImageGetter}.
- * There is (at least) 1 extra copy involved, so this shouldn't be used if you care about speed.
- */
-public class JavaCVImageGetter implements ImageGetter {
- private static final Logger LOG = Logger.getLogger(JavaCVImageGetter.class
- .getName());
- private final FrameGrabber grabber;
-
- public JavaCVImageGetter(FrameGrabber grabber) {
- this.grabber = grabber;
- if (grabber.getImageWidth() != width || grabber.getImageHeight() != height) {
- if (grabber.getImageWidth() == 0 && grabber.getImageHeight() == 0) {
- LOG.log(Level.WARNING, "grabber says it will give 0x0 images at the start. ignoring");
- } else {
- throw new IllegalArgumentException("grabber says it will give images that are the wrong size!!");
- }
- }
- }
-
- @Override
- public boolean get(IplImage out) {
- try {
- final IplImage frame = grabber.grab();
- if (grabber.getImageWidth() != width || grabber.getImageHeight() != height) {
- LOG.log(Level.SEVERE, "grabber says it will give the wrong size images");
- return false;
- }
- if (out.imageSize() != frame.imageSize()) {
- LOG.log(Level.SEVERE, "the grabber gave a " + frame.imageSize() + "-byte image" +
- "but a " + out.imageSize() + "-byte image was passed in");
- return false;
- }
- out.getByteBuffer().put(frame.getByteBuffer());
- return true;
- } catch (FrameGrabber.Exception e) {
- LOG.log(Level.WARNING, "grabber.grab() threw an exception", e);
- return false;
- }
- }
-
- @Override
- public double getTimestamp() {
- // grabber.getTimestamp seems to be in ms (all the implementations are at least)
- return grabber.getTimestamp() / 1000.0;
- }
-}
diff --git a/aos/linux_code/camera/java/aos/NativeBufferError.java b/aos/linux_code/camera/java/aos/NativeBufferError.java
deleted file mode 100644
index 41c794d..0000000
--- a/aos/linux_code/camera/java/aos/NativeBufferError.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package aos;
-
-public class NativeBufferError extends NativeError {
- private static final long serialVersionUID = -5298480149664213316L;
-
- public NativeBufferError() {
- super();
- }
-
- public NativeBufferError(String message, Throwable cause) {
- super(message, cause);
- }
-
- public NativeBufferError(String message) {
- super(message);
- }
-
- public NativeBufferError(Throwable cause) {
- super(cause);
- }
-
-}
diff --git a/aos/linux_code/camera/java/aos/NativeError.java b/aos/linux_code/camera/java/aos/NativeError.java
deleted file mode 100644
index d410234..0000000
--- a/aos/linux_code/camera/java/aos/NativeError.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package aos;
-
-/**
- * Represents an error from native code.
- */
-public class NativeError extends Error {
- private static final long serialVersionUID = 7394872852984037261L;
-
- public NativeError() {
- super();
- }
-
- public NativeError(String message, Throwable cause) {
- super(message, cause);
- }
-
- public NativeError(String message) {
- super(message);
- }
-
- public NativeError(Throwable cause) {
- super(cause);
- }
-
-}
diff --git a/aos/linux_code/camera/java/aos/NativeLoader.java b/aos/linux_code/camera/java/aos/NativeLoader.java
deleted file mode 100644
index d4fe7a8..0000000
--- a/aos/linux_code/camera/java/aos/NativeLoader.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package aos;
-
-/**
- * Provides support for dealing with loading native code.
- */
-public class NativeLoader {
- /**
- * Loads a native library.
- * @param name the name of the gyp shared_library or loadable_module target to load
- */
- public static void load(String name) {
- System.load(System.getProperty("one-jar.expand.dir") + "/so_libs/lib" + name + ".so");
- }
-}
diff --git a/aos/linux_code/camera/java/aos/Natives.java b/aos/linux_code/camera/java/aos/Natives.java
deleted file mode 100644
index 8ea4e01..0000000
--- a/aos/linux_code/camera/java/aos/Natives.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package aos;
-
-import com.googlecode.javacv.cpp.opencv_core;
-
-import java.nio.ByteBuffer;
-import java.util.logging.Level;
-
-/**
- * <p>Package-private class that has all of the native functions in it to make them easier to implement.</p>
- * <p>WARNING: The raw native functions are <b>NOT</b> thread-safe!!!!! Any java functions that need to be thread safe MUST be synchronized in JAVA!</p>
- */
-class Natives {
- static {
- NativeLoader.load("aos_camera");
- nativeInit(ImageGetter.width, ImageGetter.height);
- }
- private static native void nativeInit(int width, int height);
- /**
- * Empty function to make sure the class gets loaded (which means loading the native library).
- */
- public static void ensureLoaded() {}
-
- /**
- * Decodes a JPEG from in into out. Both buffers must be direct.
- * @param state a long[1] for storing thread-local state
- * @param in the JPEG to decode
- * @param inBytes how many bytes long the JPEG is
- * @param out the buffer to write the decoded image into
- * @return Whether or not it succeeded. If not, {@code out} is undefined.
- */
- public static native boolean decodeJPEG(long[] state, ByteBuffer in, int inBytes, ByteBuffer out);
-
- /**
- * Thresholds in into out. Both buffers must be direct.
- * All of the short arguments should be unsigned bytes. The min and max parameters specify what colors to accept.
- * @param in The image to threshold. Must be a 3-channel {@link opencv_core#IPL_DEPTH_8U} image buffer in the HSV color space.
- * @param out Where to write the thresholded image to. Must be a 1-channel {@link opencv_core#IPL_DEPTH_8U} image buffer.
- * @param hoffset An offset to be added to the hue value before comparing it to {@code hmin} and {@code hmax}.
- * The addition will be performed to a {@code uint8_t}, which will wrap around. This means that it must be positive.
- * Useful for finding red values.
- */
- public static native void threshold(ByteBuffer in, ByteBuffer out, short hoffset, char hmin, char hmax,
- char smin, char smax, char vmin, char vmax);
-
- /**
- * Converts the colors from in to the format required for dumping them into a BMP image.
- * @param in The image to convert. Must be a 3-channel {@link opencv_core#IPL_DEPTH_8U} image buffer in the regular (BGR I think...) color space.
- * @param out Where to write the converted image to. Must be a 3-channel {@link opencv_core#IPL_DEPTH_8U} image buffer.
- */
- public static native void convertBGR2BMP(ByteBuffer in, ByteBuffer out);
-
- /**
- * Retrieves a JPEG image from the queue system. Will block until a new one is ready.
- * @param id from {@link #queueInit()}
- * @return Will be direct. This buffer <b>must not EVER</b> be written to.
- */
- public static native ByteBuffer queueGetJPEG(long id);
- /**
- * Retrieves the latest frame timestamp from the queue system. Must only be called between {@link #queueGetJPEG} and {@link #queueReleaseJPEG}.
- * @param id from {@link #queueInit()}
- * @return a timestamp
- */
- public static native double queueGetTimestamp(long id);
- /**
- * Releases the last image retrieved from the queue system. The result of the last {@link #queueGetJPEG()} will now be invalid.
- * @param id from {@link #queueInit()}
- */
- public static native void queueReleaseJPEG(long id);
- /**
- * Prepares to start retrieving JPEGs from the queues.
- * @return the ID to pass to the other queue functions
- */
- public static native long queueInit();
-
- /**
- * Puts the given message into the logging framework.
- * @param message the complete log message
- * @param level the level (from {@link Level#intValue()}
- */
- public static native void LOG(String message, int level);
-}
-
diff --git a/aos/linux_code/camera/java/aos/QueueImageGetter.java b/aos/linux_code/camera/java/aos/QueueImageGetter.java
deleted file mode 100644
index 7ed2f65..0000000
--- a/aos/linux_code/camera/java/aos/QueueImageGetter.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package aos;
-
-import java.nio.ByteBuffer;
-
-/**
- * Retrieves images from the queue system.<br>
- * {@link #getTimestamp()} returns the value from the v4l2 driver,
- * which is a CLOCK_MONOTONIC time for most (all?) of them and definitely uvcvideo.
- */
-public class QueueImageGetter extends JPEGImageGetter {
- private final long nativeID;
- public QueueImageGetter() {
- nativeID = Natives.queueInit();
- }
-
- @Override
- public ByteBuffer getJPEG() {
- final ByteBuffer buf = Natives.queueGetJPEG(nativeID);
- if (buf == null) {
- return null;
- }
- return buf.asReadOnlyBuffer();
- }
-
- @Override
- public void release() {
- Natives.queueReleaseJPEG(nativeID);
- }
-
- @Override
- public double getTimestamp() {
- return Natives.queueGetTimestamp(nativeID);
- }
-}
diff --git a/aos/linux_code/camera/java/aos/QueueLogHandler.java b/aos/linux_code/camera/java/aos/QueueLogHandler.java
deleted file mode 100644
index 3eb8938..0000000
--- a/aos/linux_code/camera/java/aos/QueueLogHandler.java
+++ /dev/null
@@ -1,119 +0,0 @@
-package aos;
-
-import java.lang.Thread.UncaughtExceptionHandler;
-import java.util.logging.Formatter;
-import java.util.logging.Handler;
-import java.util.logging.Level;
-import java.util.logging.LogRecord;
-import java.util.logging.Logger;
-import java.util.logging.SimpleFormatter;
-
-/**
- * <p>Sends messages to the AOS queue-based logging system. Also sends anything that's at least a {@link Level#WARNING} to {@link System#err}.</p>
- * <p>Writes out each stack frame of exceptions as a separate line after the first one with a \t at the beginning.</p>
- */
-public class QueueLogHandler extends Handler {
- private Formatter defaultFormatter;
-
- /**
- * Sets up the logging framework to use an instance of this class for all logging and returns the newly created instance.
- */
- public static QueueLogHandler UseForAll() {
- Natives.ensureLoaded();
- final Logger top = Logger.getLogger("");
- for (Handler c : top.getHandlers()) {
- top.removeHandler(c);
- }
- QueueLogHandler instance = new QueueLogHandler();
- top.addHandler(instance);
- top.setLevel(Level.ALL);
-
- Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
- @Override
- public void uncaughtException(Thread thread, Throwable e) {
- top.log(Level.SEVERE, "uncaught exception in thread " + thread, e);
- System.exit(1);
- }
- });
-
- return instance;
- }
-
- @Override
- public void close() throws SecurityException {
- }
- @Override
- public void flush() {
- }
-
- private Formatter findFormatter() {
- final Formatter r = getFormatter();
- if (r != null) {
- return r;
- }
- if (defaultFormatter != null) {
- return defaultFormatter;
- }
- return defaultFormatter = new SimpleFormatter();
- }
- @Override
- public void publish(LogRecord record) {
- /*final StringBuilder thrownString = new StringBuilder(0);
- if (record.getThrown() != null) {
- thrownString.append(": ");
- thrownString.append(record.getThrown().toString());
- for (StackTraceElement c : record.getThrown().getStackTrace()) {
- thrownString.append(" > ");
- thrownString.append(c.getClassName());
- thrownString.append('.');
- thrownString.append(c.getMethodName());
- thrownString.append('(');
- thrownString.append(c.getFileName());
- thrownString.append(':');
- thrownString.append(c.getLineNumber());
- thrownString.append(')');
- }
- }
- Natives.LOG(record.getSourceClassName() + ": " + record.getSourceMethodName() + ": " +
- findFormatter().formatMessage(record) + thrownString, record.getLevel().intValue());*/
- if (record.getThrown() instanceof UnsatisfiedLinkError || record.getThrown().getCause() instanceof UnsatisfiedLinkError) {
- record.setThrown(new UnsatisfiedLinkError("are you running a JVM of the correct bitness?").initCause(record.getThrown()));
- }
- Natives.LOG(record.getSourceClassName() + ": " + record.getSourceMethodName() + ": " +
- findFormatter().formatMessage(record), record.getLevel().intValue());
- if (record.getThrown() != null) {
- logException(record.getThrown(), record.getLevel().intValue(), false);
- }
-
- if (record.getLevel().intValue() >= Level.WARNING.intValue()) {
- System.err.println(findFormatter().format(record));
- }
- }
- private void logException(Throwable e, int level, boolean caused_by) {
- final StringBuilder thrownString = new StringBuilder();
- if (caused_by) {
- thrownString.append("Caused by: ");
- }
- thrownString.append(e.getClass().getName());
- thrownString.append(": ");
- thrownString.append(e.getLocalizedMessage());
- Natives.LOG(thrownString.toString(), level);
- for (StackTraceElement c : e.getStackTrace()) {
- thrownString.setLength(0);
- thrownString.append("\t");
- thrownString.append(c.getClassName());
- thrownString.append('.');
- thrownString.append(c.getMethodName());
- thrownString.append('(');
- thrownString.append(c.getFileName());
- thrownString.append(':');
- thrownString.append(c.getLineNumber());
- thrownString.append(')');
- Natives.LOG(thrownString.toString(), level);
- }
- if (e.getCause() != null) {
- logException(e.getCause(), level, true);
- }
- }
-
-}
diff --git a/aos/linux_code/camera/java/aos/ServableImage.java b/aos/linux_code/camera/java/aos/ServableImage.java
deleted file mode 100644
index c5db252..0000000
--- a/aos/linux_code/camera/java/aos/ServableImage.java
+++ /dev/null
@@ -1,145 +0,0 @@
-package aos;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Queue;
-import java.util.logging.Logger;
-
-import com.googlecode.javacv.cpp.opencv_core.IplImage;
-
-/**
- * Provides {@link IplImage}s that can be used with a {@link DebugServer}.
- */
-public class ServableImage {
- @SuppressWarnings("unused")
- private static final Logger LOG = Logger.getLogger(ServableImage.class
- .getName());
- private final int width, height, depth, channels;
- private final ArrayList<Queue<IplImage>> queues = new ArrayList<Queue<IplImage>>();
- private final ArrayList<IplImage> snapshots = new ArrayList<IplImage>();
- private final IplImage current;
- private int debugging = 0;
-
- public ServableImage(int width, int height, int depth, int channels) {
- this.width = width;
- this.height = height;
- this.depth = depth;
- this.channels = channels;
-
- current = IplImage.create(width, height, depth, channels);
- }
-
- /**
- * @return the number of bytes in each image
- */
- public int imageSize() {
- return width * height * depth * channels / 8;
- }
-
- /**
- * @return the number of bits in each pixel
- */
- public short bitsPerPixel() {
- return (short) (depth * channels);
- }
-
- /**
- * Retrieves an image that should be used for debugging. It clears the value
- * when called. {@link #releastSnapshot} MUST be called with the result.
- *
- * @param i
- * Which snapshot to retrieve. 0 means the most recent final
- * image.
- * @return The most recent image at this index. {@code null} if there isn't
- * a new one since last time this function was called. Will be in
- * the correct color space to dump into a BMP image if this is a
- * 3-channel image.
- */
- public synchronized IplImage getSnapshot(int i) {
- if (snapshots.size() > i) {
- return snapshots.get(i);
- } else {
- return null;
- }
- }
-
- public synchronized void releaseSnapshot(int i, IplImage image) {
- queues.get(i).add(image);
- }
-
- /**
- * This function will return the same image if called repeatedly until
- * {@link #releaseImage} is called.
- *
- * @return the current image
- */
- public synchronized IplImage getImage() {
- return current;
- }
-
- /**
- * Releases the current image (to be potentially sent out for debugging and
- * then reused).
- */
- public synchronized void releaseImage() {
- recordSnapshot(0);
- }
-
- /**
- * Records a copy of the current image for debugging. It will be accessible
- * at <{@code <base path>?i=<the value>}>. Does nothing unless
- * {@link #isDebugging()}. This method <i>should</i> get called regardless
- * of {@link #isDebugging()} to avoid outputting old debugging images. Note:
- * 0 is not a valid snapshot number.
- *
- * @param i
- * which snapshot this is
- */
- public synchronized void recordSnapshot(int i) {
- while (queues.size() <= i) {
- queues.add(null);
- }
- if (queues.get(i) == null) {
- queues.set(i, new ArrayDeque<IplImage>());
- }
- while (snapshots.size() <= i) {
- snapshots.add(null);
- }
- if (snapshots.get(i) != null) {
- releaseSnapshot(i, snapshots.get(i));
- }
- if (isDebugging()) {
- IplImage snapshot = queues.get(i).poll();
- if (snapshot == null) {
- snapshot = IplImage.create(width, height, depth, channels);
- }
- if (channels == 3) {
- Natives.convertBGR2BMP(current.getByteBuffer(),
- snapshot.getByteBuffer());
- } else {
- snapshot.getByteBuffer().put(current.getByteBuffer());
- }
- snapshots.set(i, snapshot);
- } else {
- snapshots.set(i, null);
- }
- }
-
- /**
- * @return whether or not to do extra debug work with the current image
- */
- public synchronized boolean isDebugging() {
- return debugging > 0;
- }
-
- /**
- * Whoever turns this on should turn it off when they're done.
- */
- synchronized void setDebugging(boolean debugging) {
- if (debugging) {
- ++this.debugging;
- } else {
- --this.debugging;
- }
- }
-}
diff --git a/aos/linux_code/camera/java/aos/Thresholder.java b/aos/linux_code/camera/java/aos/Thresholder.java
deleted file mode 100644
index adf1b49..0000000
--- a/aos/linux_code/camera/java/aos/Thresholder.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package aos;
-
-import com.googlecode.javacv.cpp.opencv_core;
-import com.googlecode.javacv.cpp.opencv_core.IplImage;
-
-public class Thresholder {
- /**
- * Thresholds in into out.
- * All of the int arguments should be unsigned bytes except hoffset, which should be a signed byte.
- * The min and max parameters specify what colors to accept.
- * @param in The image to threshold. Must be a 3-channel {@link opencv_core#IPL_DEPTH_8U} image in the HSV color space.
- * @param out Where to write the thresholded image to. Must be a 1-channel {@link opencv_core#IPL_DEPTH_8U} image.
- * @param hoffset An offset to be added to the hue value before comparing it to {@code hmin} and {@code hmax}.
- * The addition will be performed to a {@code uint8_t}, which will wrap around. This means that it must be positive.
- * Useful for finding red values.
- */
- public static void threshold(IplImage in, IplImage out, int hoffset, int hmin, int hmax,
- int smin, int smax, int vmin, int vmax) {
- if (in.nChannels() != 3 || in.depth() != opencv_core.IPL_DEPTH_8U) {
- throw new IllegalArgumentException("in is invalid");
- }
- if (out.nChannels() != 1 || out.depth() != opencv_core.IPL_DEPTH_8U) {
- throw new IllegalArgumentException("out is invalid");
- }
- Natives.threshold(in.getByteBuffer(), out.getByteBuffer(), (short)hoffset,
- (char)hmin, (char)hmax, (char)smin, (char)smax, (char)vmin, (char)vmax);
- }
-}
diff --git a/aos/linux_code/camera/jni.cpp b/aos/linux_code/camera/jni.cpp
deleted file mode 100644
index c547a9c..0000000
--- a/aos/linux_code/camera/jni.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
-#include <setjmp.h>
-
-#include "jni/aos_Natives.h"
-#include "aos/linux_code/camera/Buffers.h"
-#include "aos/externals/libjpeg/include/jpeglib.h"
-#include "aos/common/logging/logging_impl.h"
-#include "aos/linux_code/init.h"
-
-using aos::camera::Buffers;
-
-namespace {
-
-jclass nativeError, bufferError, outOfMemoryError;
-bool findClass(JNIEnv *env, const char *name, jclass *out) {
- jclass local = env->FindClass(name);
- if (out == NULL) return true;
- *out = static_cast<jclass>(env->NewGlobalRef(local));
- if (out == NULL) return true;
- env->DeleteLocalRef(local);
- return false;
-}
-
-// Checks that the size is correct and retrieves the address.
-// An expected_size of 0 means don't check it.
-// If this function returns NULL, a java exception will already have been
-// thrown.
-void *getBufferAddress(JNIEnv *env, jobject obj, jlong expected_size) {
- if (obj == NULL) {
- env->ThrowNew(nativeError, "null buffer");
- return NULL;
- }
- if (expected_size != 0 &&
- expected_size != env->GetDirectBufferCapacity(obj)) {
- char *str;
- if (asprintf(&str, "wrong size. expected %lld but got %lld",
- expected_size, env->GetDirectBufferCapacity(obj)) < 0) {
- env->ThrowNew(bufferError, "creating message failed");
- return NULL;
- }
- env->ThrowNew(bufferError, str);
- free(str);
- return NULL;
- }
- void *const r = env->GetDirectBufferAddress(obj);
- if (r == NULL) {
- env->ThrowNew(bufferError, "couldn't get address");
- }
- return r;
-}
-
-const int kImagePixels = Buffers::kWidth * Buffers::kHeight;
-
-void jpeg_log_message(jpeg_common_struct *cinfo, log_level level) {
- char buf[LOG_MESSAGE_LEN];
- cinfo->err->format_message(cinfo, buf);
- log_do(level, "libjpeg: %s\n", buf);
-}
-void jpeg_error_exit(jpeg_common_struct *cinfo) __attribute__((noreturn));
-void jpeg_error_exit(jpeg_common_struct *cinfo) {
- jpeg_log_message(cinfo, ERROR);
- longjmp(*static_cast<jmp_buf *>(cinfo->client_data), 1);
-}
-void jpeg_emit_message(jpeg_common_struct *cinfo, int msg_level) {
- if (msg_level < 0) {
- jpeg_log_message(cinfo, WARNING);
- longjmp(*static_cast<jmp_buf *>(cinfo->client_data), 2);
- }
- // this spews a lot of messages out
- //jpeg_log_message(cinfo, DEBUG);
-}
-
-// The structure used to hold all of the state for the functions that deal with
-// a Buffers. A pointer to this structure is stored java-side.
-struct BuffersHolder {
- Buffers buffers;
- timeval timestamp;
- BuffersHolder() : buffers() {}
-};
-
-} // namespace
-
-void Java_aos_Natives_nativeInit(JNIEnv *env, jclass, jint width, jint height) {
- if (findClass(env, "aos/NativeError", &nativeError)) return;
- if (findClass(env, "aos/NativeBufferError", &bufferError)) return;
- if (findClass(env, "java/lang/OutOfMemoryError", &outOfMemoryError)) return;
-
- aos::InitNRT();
-
- if (width != Buffers::kWidth || height != Buffers::kHeight) {
- env->ThrowNew(nativeError, "dimensions mismatch");
- return;
- }
-
- LOG(INFO, "nativeInit finished\n");
-}
-
-static_assert(sizeof(jlong) >= sizeof(void *),
- "can't stick pointers into jlongs");
-
-jboolean Java_aos_Natives_decodeJPEG(JNIEnv *env, jclass, jlongArray stateArray,
- jobject inobj, jint inLength,
- jobject outobj) {
- unsigned char *const in = static_cast<unsigned char *>(
- getBufferAddress(env, inobj, 0));
- if (in == NULL) return false;
- if (env->GetDirectBufferCapacity(inobj) < inLength) {
- env->ThrowNew(bufferError, "in is too small");
- return false;
- }
- unsigned char *const out = static_cast<unsigned char *>(
- getBufferAddress(env, outobj, kImagePixels * 3));
- if (out == NULL) return false;
-
- jpeg_decompress_struct *volatile cinfo; // volatile because of the setjmp call
-
- jlong state;
- env->GetLongArrayRegion(stateArray, 0, 1, &state);
- if (env->ExceptionCheck()) return false;
- if (state == 0) {
- cinfo = static_cast<jpeg_decompress_struct *>(malloc(sizeof(*cinfo)));
- if (cinfo == NULL) {
- env->ThrowNew(outOfMemoryError, "malloc for jpeg_decompress_struct");
- return false;
- }
- cinfo->err = jpeg_std_error(static_cast<jpeg_error_mgr *>(
- malloc(sizeof(*cinfo->err))));
- cinfo->client_data = malloc(sizeof(jmp_buf));
- cinfo->err->error_exit = jpeg_error_exit;
- cinfo->err->emit_message = jpeg_emit_message;
- // if the error handler sees a failure, it needs to clean up
- // (jpeg_abort_decompress) and then return the failure
- // set cinfo->client_data to the jmp_buf
- jpeg_create_decompress(cinfo);
- state = reinterpret_cast<intptr_t>(cinfo);
- env->SetLongArrayRegion(stateArray, 0, 1, &state);
- if (env->ExceptionCheck()) return false;
- } else {
- cinfo = reinterpret_cast<jpeg_decompress_struct *>(state);
- }
-
- // set up the jump buffer
- // this has to happen each time
- if (setjmp(*static_cast<jmp_buf *>(cinfo->client_data))) {
- jpeg_abort_decompress(cinfo);
- return false;
- }
-
- jpeg_mem_src(cinfo, in, inLength);
- jpeg_read_header(cinfo, TRUE);
- if (cinfo->image_width != static_cast<unsigned int>(Buffers::kWidth) ||
- cinfo->image_height != static_cast<unsigned int>(Buffers::kHeight)) {
- LOG(WARNING, "got (%ux%u) image but expected (%dx%d)\n", cinfo->image_width,
- cinfo->image_height, Buffers::kWidth, Buffers::kHeight);
- jpeg_abort_decompress(cinfo);
- return false;
- }
- cinfo->out_color_space = JCS_RGB;
- jpeg_start_decompress(cinfo);
- if (cinfo->output_components != 3) {
- LOG(WARNING, "libjpeg wants to return %d color components instead of 3\n",
- cinfo->out_color_components);
- jpeg_abort_decompress(cinfo);
- return false;
- }
- if (cinfo->output_width != static_cast<unsigned int>(Buffers::kWidth) ||
- cinfo->output_height != static_cast<unsigned int>(Buffers::kHeight)) {
- LOG(WARNING, "libjpeg wants to return a (%ux%u) image but need (%dx%d)\n",
- cinfo->output_width, cinfo->output_height,
- Buffers::kWidth, Buffers::kHeight);
- jpeg_abort_decompress(cinfo);
- return false;
- }
-
- unsigned char *buffers[Buffers::kHeight];
- for (int i = 0; i < Buffers::kHeight; ++i) {
- buffers[i] = &out[i * Buffers::kWidth * 3];
- }
- while (cinfo->output_scanline < cinfo->output_height) {
- jpeg_read_scanlines(cinfo, &buffers[cinfo->output_scanline],
- Buffers::kHeight - cinfo->output_scanline);
- }
-
- jpeg_finish_decompress(cinfo);
- return true;
-}
-
-void Java_aos_Natives_threshold(JNIEnv *env, jclass, jobject inobj,
- jobject outobj, jshort hoffset, jchar hmin,
- jchar hmax, jchar smin, jchar smax, jchar vmin,
- jchar vmax) {
- const unsigned char *__restrict__ const in = static_cast<unsigned char *>(
- getBufferAddress(env, inobj, kImagePixels * 3));
- if (in == NULL) return;
- char *__restrict__ const out = static_cast<char *>(
- getBufferAddress(env, outobj, kImagePixels));
- if (out == NULL) return;
-
- for (int i = 0; i < kImagePixels; ++i) {
- const uint8_t h = in[i * 3] + static_cast<uint8_t>(hoffset);
- out[i] = h > hmin && h < hmax &&
- in[i * 3 + 1] > smin && in[i * 3 + 1] < smax &&
- in[i * 3 + 2] > vmin && in[i * 3 + 2] < vmax;
- }
-}
-void Java_aos_Natives_convertBGR2BMP(JNIEnv *env, jclass,
- jobject inobj, jobject outobj) {
- const char *__restrict__ const in = static_cast<char *>(
- getBufferAddress(env, inobj, kImagePixels * 3));
- if (in == NULL) return;
- char *__restrict__ const out = static_cast<char *>(
- getBufferAddress(env, outobj, kImagePixels * 3));
- if (out == NULL) return;
-
- for (int i = 0; i < kImagePixels; ++i) {
- out[i * 3 + 0] = in[i * 3 + 2];
- out[i * 3 + 1] = in[i * 3 + 1];
- out[i * 3 + 2] = in[i * 3 + 0];
- }
-}
-
-jlong Java_aos_Natives_queueInit(JNIEnv *, jclass) {
- return reinterpret_cast<intptr_t>(new BuffersHolder());
-}
-void Java_aos_Natives_queueReleaseJPEG(JNIEnv *, jclass, jlong ptr) {
- reinterpret_cast<BuffersHolder *>(ptr)->buffers.Release();
-}
-jobject Java_aos_Natives_queueGetJPEG(JNIEnv *env, jclass, jlong ptr) {
- uint32_t size;
- BuffersHolder *const holder = reinterpret_cast<BuffersHolder *>(ptr);
- const void *const r = holder->buffers.GetNext(true, &size,
- &holder->timestamp, NULL);
- if (r == NULL) return NULL;
- return env->NewDirectByteBuffer(const_cast<void *>(r), size);
-}
-jdouble Java_aos_Natives_queueGetTimestamp(JNIEnv *, jclass, jlong ptr) {
- const BuffersHolder *const holder = reinterpret_cast<BuffersHolder *>(ptr);
- return holder->timestamp.tv_sec + holder->timestamp.tv_usec / 1000000.0;
-}
-
-void Java_aos_Natives_LOG(JNIEnv *env, jclass, jstring message, jint jlevel) {
- log_level level;
- if (jlevel >= 1000) {
- // Don't want to use FATAL because the uncaught java exception that is
- // likely to come next will be useful.
- level = ERROR;
- } else if (jlevel >= 900) {
- level = WARNING;
- } else if (jlevel >= 800) {
- level = INFO;
- } else {
- level = DEBUG;
- }
- // Can't use Get/ReleaseStringCritical because log_do might block waiting to
- // put its message into the queue.
- const char *const message_chars = env->GetStringUTFChars(message, NULL);
- if (message_chars == NULL) return;
- log_do(level, "%s\n", message_chars);
- env->ReleaseStringUTFChars(message, message_chars);
-}
diff --git a/aos/linux_code/core/core.cc b/aos/linux_code/core.cc
similarity index 91%
rename from aos/linux_code/core/core.cc
rename to aos/linux_code/core.cc
index c8cec67..a9a58d1 100644
--- a/aos/linux_code/core/core.cc
+++ b/aos/linux_code/core.cc
@@ -10,6 +10,8 @@
// 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 create the file given as a first argument.
int main(int argc, char **argv) {
aos::InitCreate();
diff --git a/aos/linux_code/core/BinaryLogReader.cpp b/aos/linux_code/core/BinaryLogReader.cpp
deleted file mode 100644
index 74cc95c..0000000
--- a/aos/linux_code/core/BinaryLogReader.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-#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 <map>
-
-#include "aos/linux_code/logging/linux_logging.h"
-#include "aos/linux_code/core/LogFileCommon.h"
-#include "aos/linux_code/init.h"
-#include "aos/linux_code/configuration.h"
-
-namespace aos {
-namespace logging {
-namespace linux_code {
-namespace {
-
-int BinaryLogReaderMain() {
- InitNRT();
-
- const char *folder = configuration::GetLoggingDirectory();
- if (access(folder, R_OK | W_OK) == -1) {
- LOG(FATAL, "folder '%s' does not exist. please create it\n", folder);
- }
- LOG(INFO, "logging to folder '%s'\n", folder);
-
- const time_t t = time(NULL);
- char *tmp;
- if (asprintf(&tmp, "%s/aos_log-%jd", folder, static_cast<uintmax_t>(t)) ==
- -1) {
- fprintf(stderr,
- "BinaryLogReader: couldn't create final name because of %d (%s)."
- " exiting\n", errno, strerror(errno));
- return EXIT_FAILURE;
- }
- char *tmp2;
- if (asprintf(&tmp2, "%s/aos_log-current", folder) == -1) {
- fprintf(stderr,
- "BinaryLogReader: couldn't create symlink name because of %d (%s)."
- " not creating current symlink\n", errno, strerror(errno));
- } else {
- if (unlink(tmp2) == -1 && (errno != EROFS && errno != ENOENT)) {
- fprintf(stderr,
- "BinaryLogReader: warning: unlink('%s') failed"
- " because of %d (%s)\n",
- tmp2, errno, strerror(errno));
- }
- if (symlink(tmp, tmp2) == -1) {
- fprintf(stderr, "BinaryLogReader: warning: symlink('%s', '%s') failed"
- " because of %d (%s)\n", tmp, tmp2, errno, strerror(errno));
- }
- 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) {
- fprintf(stderr,
- "BinaryLogReader: couldn't open file '%s' because of %d (%s)."
- " exiting\n", tmp, errno, strerror(errno));
- return EXIT_FAILURE;
- }
- LogFileAccessor writer(fd, true);
-
- struct timespec timespec;
- time_t last_sec = 0;
- while (true) {
- clock_gettime(CLOCK_MONOTONIC, ×pec);
- if (last_sec != timespec.tv_sec) {
- LOG(INFO, "msyncing output\n");
- last_sec = timespec.tv_sec;
- writer.Sync();
- }
-
- const LogMessage *const msg = ReadNext();
- if (msg == NULL) continue;
-
- // add 1 for terminating '\0'
- size_t name_size = strlen(msg->name) + 1;
- size_t message_size = strlen(msg->message) + 1;
-
- LogFileMessageHeader *const output = writer.GetWritePosition(
- sizeof(LogFileMessageHeader) + name_size + message_size);
- char *output_strings = reinterpret_cast<char *>(output) + sizeof(*output);
- output->name_size = name_size;
- output->message_size = message_size;
- 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, name_size);
- memcpy(output_strings + name_size, msg->message, message_size);
- 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/core/LogDisplayer.cpp b/aos/linux_code/core/LogDisplayer.cpp
deleted file mode 100644
index 3a98dd4..0000000
--- a/aos/linux_code/core/LogDisplayer.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <getopt.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <errno.h>
-
-#include "aos/linux_code/core/LogFileCommon.h"
-#include "aos/common/logging/logging_impl.h"
-
-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"
- "\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 and changing FILE\n";
-
-void PrintHelpAndExit() {
- fprintf(stderr, "Usage: %s %s", program_invocation_name, kArgsHelp);
-
- exit(EXIT_SUCCESS);
-}
-
-} // namespace
-
-int main(int argc, char **argv) {
- const char *filter_name = NULL;
- log_level filter_level = INFO;
- bool follow = false, start_at_beginning = true;
- const char *filename = "aos_log-current";
-
- while (true) {
- static struct option long_options[] = {
- {"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:l:p:fts:m:o:h",
- long_options, &option_index);
- if (c == -1) { // if we're at the end
- break;
- }
- switch (c) {
- case 0:
- fprintf(stderr, "LogDisplayer: got a 0 option but didn't set up any\n");
- abort();
- case 'n':
- filter_name = optarg;
- 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':
- abort();
- break;
- case 'f':
- follow = true;
- start_at_beginning = false;
- break;
- case 't':
- follow = false;
- break;
- case 'b':
- start_at_beginning = true;
- break;
- case 'e':
- start_at_beginning = false;
- break;
- case 'm':
- abort();
- 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();
- }
- }
-
- fprintf(stderr, "displaying down to level %s from file '%s'\n",
- ::aos::logging::log_str(filter_level), filename);
- if (optind < argc) {
- fprintf(stderr, "non-option ARGV-elements: ");
- while (optind < argc) {
- fprintf(stderr, "%s\n", argv[optind++]);
- }
- }
-
- int fd = open(filename, O_RDONLY);
- if (fd == -1) {
- fprintf(stderr, "error: couldn't open file '%s' for reading because of %s\n",
- filename, strerror(errno));
- exit(EXIT_FAILURE);
- }
- ::aos::logging::LogFileAccessor accessor(fd, false);
- if (!start_at_beginning) {
- accessor.MoveToEnd();
- }
- const ::aos::logging::LogFileMessageHeader *msg;
- ::aos::logging::LogMessage log_message;
- do {
- msg = accessor.ReadNextMessage(follow);
- if (msg == NULL) continue;
- if (::aos::logging::log_gt_important(filter_level, msg->level)) continue;
- if (filter_name != NULL &&
- strcmp(filter_name,
- reinterpret_cast<const char *>(msg) + sizeof(*msg)) != 0) {
- continue;
- }
-
- log_message.source = msg->source;
- log_message.sequence = msg->sequence;
- log_message.level = msg->level;
- log_message.seconds = msg->time_sec;
- log_message.nseconds = msg->time_nsec;
- strncpy(log_message.name,
- reinterpret_cast<const char *>(msg) + sizeof(*msg),
- sizeof(log_message.name));
- strncpy(log_message.message,
- reinterpret_cast<const char *>(msg) + sizeof(*msg) +
- msg->name_size,
- sizeof(log_message.message));
- ::aos::logging::internal::PrintMessage(stdout, log_message);
- } while (msg != NULL);
-}
diff --git a/aos/linux_code/core/LogFileCommon.h b/aos/linux_code/core/LogFileCommon.h
deleted file mode 100644
index 23ddc62..0000000
--- a/aos/linux_code/core/LogFileCommon.h
+++ /dev/null
@@ -1,199 +0,0 @@
-#ifndef AOS_LINUX_CODE_CORE_LOG_FILE_COMMON_H_
-#define AOS_LINUX_CODE_CORE_LOG_FILE_COMMON_H_
-
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <algorithm>
-
-#include "aos/common/logging/logging_impl.h"
-
-namespace aos {
-namespace logging {
-
-// File format: {
-// LogFileMessageHeader header;
-// char *name; // of the process that wrote the message
-// char *message;
-// } not crossing kPageSize boundaries into the file.
-//
-// Field sizes designed to fit the various values from LogMessage even on
-// other machines (hopefully) because they're baked into the files.
-//
-// A lot of the fields don't have comments because they're the same as the
-// identically named fields in LogMessage.
-struct __attribute__((aligned)) LogFileMessageHeader {
- // 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 log on a "page" set to 2
- // (by the futex_set) to indicate that the next log is on the next page
- mutex marker;
- static_assert(sizeof(marker) == 4, "mutex changed size!");
- log_level level;
- static_assert(sizeof(level) == 1, "log_level changed size!");
-
- 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");
- uint16_t sequence;
- static_assert(sizeof(sequence) == sizeof(LogMessage::sequence),
- "something changed");
-
- // both including the terminating '\0'
- uint32_t name_size;
- uint32_t message_size;
-};
-static_assert(std::is_pod<LogFileMessageHeader>::value,
- "LogFileMessageHeader will to get dumped to a file");
-
-// Handles the mmapping and munmapping for reading and writing log files.
-class LogFileAccessor {
- private:
- // 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 = 32768;
- // What to align messages to. Necessary for futexes to work.
- static const size_t kAlignment = 64;
- static_assert(kAlignment >= __alignof__(mutex), "futexes will complain");
-
- const int fd_;
- const bool writable_;
-
- off_t offset_; // into the file. will be aligned to kPageSize
- char *current_;
- size_t position_;
-
- inline unsigned long SystemPageSize() {
- static unsigned long r = sysconf(_SC_PAGESIZE);
- return r;
- }
- void MapNextPage() {
- if (writable_) {
- if (ftruncate(fd_, offset_ + kPageSize) == -1) {
- fprintf(stderr, "ftruncate(%d, %zd) failed with %d: %s. aborting\n",
- fd_, kPageSize, errno, strerror(errno));
- printf("see stderr\n");
- abort();
- }
- }
- current_ = static_cast<char *>(mmap(NULL, kPageSize,
- PROT_READ | (writable_ ? PROT_WRITE : 0),
- MAP_SHARED, fd_, offset_));
- if (current_ == MAP_FAILED) {
- fprintf(stderr, "mmap(NULL, %zd, PROT_READ | PROT_WRITE, MAP_SHARED, %d, %jd)"
- " failed with %d: %s. aborting\n", kPageSize, fd_,
- static_cast<intmax_t>(offset_), errno, strerror(errno));
- printf("see stderr\n");
- abort();
- }
- offset_ += kPageSize;
- }
- void Unmap(void *location) {
- if (munmap(location, kPageSize) == -1) {
- fprintf(stderr, "munmap(%p, %zd) failed with %d: %s. aborting\n",
- location, kPageSize, errno, strerror(errno));
- printf("see stderr\n");
- abort();
- }
- }
- public:
- 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) {
- fprintf(stderr, "LogFileCommon: system page size (%lu)"
- " not compatible with kPageSize (%zd). aborting\n",
- SystemPageSize(), kPageSize);
- printf("see stderr\n");
- abort();
- }
-
- MapNextPage();
- }
- // message_size should be the total number of bytes needed for the message
- LogFileMessageHeader *GetWritePosition(size_t message_size) {
- if (position_ + message_size + (kAlignment - (message_size % kAlignment)) +
- sizeof(mutex) > kPageSize) {
- char *const temp = current_;
- MapNextPage();
- if (futex_set_value(static_cast<mutex *>(static_cast<void *>(
- &temp[position_])), 2) == -1) {
- fprintf(stderr,
- "LogFileCommon: futex_set_value(%p, 2) failed with %d: %s."
- " readers will hang\n",
- &temp[position_], errno, strerror(errno));
- }
- Unmap(temp);
- position_ = 0;
- }
- LogFileMessageHeader *const r = static_cast<LogFileMessageHeader *>(
- static_cast<void *>(¤t_[position_]));
- position_ += message_size;
- // keep it aligned for next time
- position_ += kAlignment - (position_ % kAlignment);
- return r;
- }
- // may only return NULL if wait is false
- const LogFileMessageHeader *ReadNextMessage(bool wait) {
- LogFileMessageHeader *r;
- do {
- r = static_cast<LogFileMessageHeader *>(
- static_cast<void *>(¤t_[position_]));
- if (wait) {
- if (futex_wait(&r->marker) != 0) continue;
- }
- if (r->marker == 2) {
- Unmap(current_);
- MapNextPage();
- position_ = 0;
- r = static_cast<LogFileMessageHeader *>(static_cast<void *>(current_));
- }
- } while (wait && r->marker == 0);
- if (r->marker == 0) {
- return NULL;
- }
- position_ += sizeof(LogFileMessageHeader) + r->name_size + r->message_size;
- // keep it aligned for next time
- position_ += kAlignment - (position_ % kAlignment);
- return r;
- }
-
- // asynchronously syncs all open mappings
- void Sync() {
- msync(current_, kPageSize, MS_ASYNC | MS_INVALIDATE);
- }
-
- void MoveToEnd() {
- Unmap(current_);
- struct stat info;
- if (fstat(fd_, &info) == -1) {
- fprintf(stderr, "LOgFileCommon: fstat(%d, %p) failed with %d: %s\n",
- fd_, &info, errno, strerror(errno));
- printf("see stderr\n");
- abort();
- }
- offset_ = info.st_size - kPageSize;
- MapNextPage();
- }
-};
-
-} // namespace logging
-} // namespace aos
-
-#endif
diff --git a/aos/linux_code/core/core.gyp b/aos/linux_code/core/core.gyp
deleted file mode 100644
index d68168b..0000000
--- a/aos/linux_code/core/core.gyp
+++ /dev/null
@@ -1,50 +0,0 @@
-{
- 'targets': [
- {
- 'target_name': 'core',
- 'type': 'executable',
- 'sources': [
- 'core.cc',
- ],
- 'dependencies': [
- '<(AOS)/linux_code/linux_code.gyp:init',
- ],
- },
- {
- 'target_name': 'BinaryLogReader',
- 'type': 'executable',
- 'sources': [
- 'BinaryLogReader.cpp',
- ],
- 'dependencies': [
- '<(AOS)/build/aos.gyp:logging',
- '<(AOS)/linux_code/linux_code.gyp:init',
- '<(AOS)/linux_code/linux_code.gyp:configuration',
- ],
- },
- {
- 'target_name': 'LogStreamer',
- 'type': 'executable',
- 'sources': [
- 'LogStreamer.cpp',
- ],
- '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': 'LogDisplayer',
- 'type': 'executable',
- 'sources': [
- 'LogDisplayer.cpp',
- ],
- 'dependencies': [
- '<(AOS)/build/aos.gyp:logging',
- '<(AOS)/linux_code/linux_code.gyp:init',
- ],
- },
- ],
-}
diff --git a/aos/linux_code/init.cc b/aos/linux_code/init.cc
index 3c0704e..240a9d3 100644
--- a/aos/linux_code/init.cc
+++ b/aos/linux_code/init.cc
@@ -84,8 +84,8 @@
void Init(int relative_priority) {
if (getenv(kNoRealtimeEnvironmentVariable) == NULL) { // if nobody set it
LockAllMemory();
- // Only let rt processes run for 1 second straight.
- SetSoftRLimit(RLIMIT_RTTIME, 1000000, true);
+ // 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 priority 40.
diff --git a/aos/linux_code/ipc_lib/aos_sync.c b/aos/linux_code/ipc_lib/aos_sync.c
index 52ebed1..a607001 100644
--- a/aos/linux_code/ipc_lib/aos_sync.c
+++ b/aos/linux_code/ipc_lib/aos_sync.c
@@ -18,13 +18,19 @@
return result;
}
-// this code is based on something that appears to be based on <http://www.akkadia.org/drepper/futex.pdf>, which also has a lot of useful information
-// should probably use <http://lxr.linux.no/linux+v2.6.34/Documentation/robust-futexes.txt> once it becomes available
+// this code is based on something that appears to be based on
+// <http://www.akkadia.org/drepper/futex.pdf>, which also has a lot of useful
+// information
+// should probably use
+// <http://lxr.linux.no/linux+v2.6.34/Documentation/robust-futexes.txt> once it
+// becomes available
// (sys_set_robust_list appears to be the function name)
// <http://locklessinc.com/articles/futex_cheat_sheet/> and
// <http://locklessinc.com/articles/mutex_cv_futex/> are useful
-// <http://lwn.net/Articles/360699/> has a nice overview of futexes in late 2009 (fairly recent compared to everything else...)
-// can't use PRIVATE futex operations because they use the pid (or something) as part of the hash
+// <http://lwn.net/Articles/360699/> has a nice overview of futexes in late 2009
+// (fairly recent compared to everything else...)
+// can't use PRIVATE futex operations because they use the pid (or something) as
+// part of the hash
//
// Remember that EAGAIN and EWOUDBLOCK are the same! (ie if you get EAGAIN from
// FUTEX_WAIT, the docs call it EWOULDBLOCK...)
@@ -60,7 +66,6 @@
if (c == 1) c = xchg(m, 2);
while (c) {
/* Wait in the kernel */
- //printf("sync here %d\n", __LINE__);
if (sys_futex(m, FUTEX_WAIT, 2, timeout, NULL, 0) == -1) {
if (signals_fail && errno == EINTR) {
return 1;
@@ -69,7 +74,6 @@
return 2;
}
}
- //printf("sync here %d\n", __LINE__);
c = xchg(m, 2);
}
return 0;
@@ -148,6 +152,8 @@
mutex_unlock(m);
while (1) {
+ // Wait in the kernel iff the value of it doesn't change (ie somebody else
+ // does a wake) from before we unlocked the mutex.
if (sys_futex(c, FUTEX_WAIT, wait_start, NULL, NULL, 0) == -1) {
// If it failed for some reason other than somebody else doing a wake
// before we actually made it to sleep.
@@ -161,8 +167,12 @@
abort();
}
}
+ // Relock the mutex now that we're done waiting.
// Simplified mutex_lock that always leaves it
// contended in case anybody else got requeued.
+ // If we got requeued above, this will just succeed the first time because
+ // the person waking us from the above wait (changed to be on the mutex
+ // instead of the condition) will have just set it to 0.
while (xchg(m, 2) != 0) {
if (sys_futex(m, FUTEX_WAIT, 2, NULL, NULL, 0) == -1) {
// Try again if it was because of a signal or somebody else unlocked it
@@ -180,7 +190,11 @@
}
void condition_signal(mutex *c) {
+ // This will cause anybody else who is in between unlocking the mutex and
+ // going to sleep in the kernel to not go to sleep and return immediately
+ // instead.
__sync_fetch_and_add(c, 1);
+ // Wake at most 1 person who is waiting in the kernel.
if (sys_futex(c, FUTEX_WAKE, 1, NULL, NULL, 0) == -1) {
fprintf(stderr, "sync: FUTEX_WAKE(%p, 1, NULL, NULL, 0)"
" failed with %d: %s\n",
@@ -192,7 +206,9 @@
void condition_broadcast(mutex *c, mutex *m) {
__sync_fetch_and_add(c, 1);
- // Wake 1 waiter and requeue the rest.
+ // Wake at most 1 waiter and requeue the rest.
+ // Everybody else is going to have to wait for the 1st person to take the
+ // mutex anyways.
if (sys_futex_requeue(c, FUTEX_REQUEUE, 1, INT_MAX, m) == -1) {
fprintf(stderr, "sync: FUTEX_REQUEUE(%p, 1, INT_MAX, %p, 0)"
" failed with %d: %s\n",
diff --git a/aos/linux_code/ipc_lib/aos_sync.h b/aos/linux_code/ipc_lib/aos_sync.h
index 7a81ca3..0d55246 100644
--- a/aos/linux_code/ipc_lib/aos_sync.h
+++ b/aos/linux_code/ipc_lib/aos_sync.h
@@ -48,6 +48,8 @@
// They are designed for signalling when something happens (possibly to
// multiple listeners). A mutex manipulated with them can only be set or unset.
//
+// Another name for this kind of synchronization mechanism is a "notification".
+//
// 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
diff --git a/aos/linux_code/ipc_lib/condition.cc b/aos/linux_code/ipc_lib/condition.cc
index b764026..0ba4145 100644
--- a/aos/linux_code/ipc_lib/condition.cc
+++ b/aos/linux_code/ipc_lib/condition.cc
@@ -6,8 +6,8 @@
namespace aos {
-static_assert(shm_ok<Condition>::value, "Condition should work"
- " in shared memory");
+static_assert(shm_ok<Condition>::value,
+ "Condition should work in shared memory");
Condition::Condition(Mutex *m) : impl_(), m_(m) {}
diff --git a/aos/linux_code/ipc_lib/core_lib.c b/aos/linux_code/ipc_lib/core_lib.c
index cc1ccbb..2bd6c25 100644
--- a/aos/linux_code/ipc_lib/core_lib.c
+++ b/aos/linux_code/ipc_lib/core_lib.c
@@ -5,7 +5,7 @@
#include "aos/linux_code/ipc_lib/shared_mem.h"
-static inline uint8_t aos_8max(uint8_t l, uint8_t r) {
+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) {
diff --git a/aos/linux_code/ipc_lib/ipc_lib.gyp b/aos/linux_code/ipc_lib/ipc_lib.gyp
index f947d5e..08e8df8 100644
--- a/aos/linux_code/ipc_lib/ipc_lib.gyp
+++ b/aos/linux_code/ipc_lib/ipc_lib.gyp
@@ -44,8 +44,7 @@
'<(AOS)/common/common.gyp:condition',
'<(AOS)/common/common.gyp:mutex',
'core_lib',
- # TODO(brians): fix this once there's a nice logging interface to use
- # '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/build/aos.gyp:logging_interface',
],
},
{
@@ -61,6 +60,7 @@
'core_lib',
'<(AOS)/common/common.gyp:queue_testutils',
'<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/common.gyp:die',
],
},
{
@@ -77,6 +77,22 @@
'core_lib',
'<(AOS)/common/common.gyp:die',
],
+ '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
index 1b55e82..3067a20 100644
--- a/aos/linux_code/ipc_lib/ipc_stress_test.cc
+++ b/aos/linux_code/ipc_lib/ipc_stress_test.cc
@@ -7,7 +7,6 @@
#include <libgen.h>
#include <assert.h>
-#include <vector>
#include <string>
#include "aos/common/time.h"
@@ -22,6 +21,10 @@
// 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
@@ -33,14 +36,16 @@
// 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 ::std::vector< ::std::vector< ::std::string>> kTests = {
+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 ::std::vector< ::std::string> kDefaultArgs = {
+static const char *kDefaultArgs[] = {
"--gtest_repeat=30",
"--gtest_shuffle",
};
@@ -75,7 +80,7 @@
// Gets called after each child forks to run a test.
void __attribute__((noreturn)) DoRunTest(
- Shared *shared, const ::std::vector< ::std::string> &test, int pipes[2]) {
+ Shared *shared, const char *(*test)[kTestMaxArgs], int pipes[2]) {
if (close(pipes[0]) == -1) {
Die("close(%d) of read end of pipe failed with %d: %s\n",
pipes[0], errno, strerror(errno));
@@ -93,13 +98,15 @@
pipes[1], STDERR_FILENO, errno, strerror(errno));
}
- size_t size = test.size();
- size_t default_size = kDefaultArgs.size();
+ 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 (const ::std::string &c : test) {
+ 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();
@@ -107,7 +114,7 @@
args[++i] = ci.c_str();
}
} else {
- args[i] = c.c_str();
+ args[i] = c;
}
++i;
}
@@ -120,7 +127,9 @@
void DoRun(Shared *shared) {
int iterations = 0;
// An iterator pointing to a random one of the tests.
- auto test = kTests.begin() + (getpid() % kTests.size());
+ // 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) {
@@ -128,7 +137,7 @@
}
switch (fork()) {
case 0: // in runner
- DoRunTest(shared, *test, pipes);
+ DoRunTest(shared, test, pipes);
case -1:
Die("fork() failed with %d: %s\n", errno, strerror(errno));
}
@@ -169,22 +178,21 @@
if (WEXITSTATUS(status) != 0) {
MutexLocker sync(&shared->output_mutex);
fprintf(stderr, "Test %s exited with status %d. output:\n",
- test->at(0).c_str(), WEXITSTATUS(status));
+ (*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->at(0).c_str(),
+ fprintf(stderr, "Test %s terminated by signal %d: %s.\n", (*test)[0],
WTERMSIG(status), strsignal(WTERMSIG(status)));
fputs(output.c_str(), stderr);
} else {
assert(WIFSTOPPED(status));
- Die("Test %s was stopped.\n", test->at(0).c_str());
+ Die("Test %s was stopped.\n", (*test)[0]);
}
++test;
- if (test == kTests.end()) test = kTests.begin();
+ if (test == kTests + 1) test = kTests;
++iterations;
}
{
@@ -212,7 +220,10 @@
new (shared) Shared(time::Time::Now() + kTestTime);
char *temp = strdup(argv[0]);
- shared->path = strdup(dirname(temp));
+ if (asprintf(const_cast<char **>(&shared->path),
+ "%s/../tests", dirname(temp)) == -1) {
+ Die("asprintf failed with %d: %s\n", errno, strerror(errno));
+ }
free(temp);
for (int i = 0; i < kTesters; ++i) {
diff --git a/aos/linux_code/ipc_lib/queue.cc b/aos/linux_code/ipc_lib/queue.cc
index e67f22c..8a86b7f 100644
--- a/aos/linux_code/ipc_lib/queue.cc
+++ b/aos/linux_code/ipc_lib/queue.cc
@@ -38,6 +38,7 @@
struct RawQueue::MessageHeader {
int ref_count;
int index; // in pool_
+ // 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),
@@ -50,8 +51,8 @@
memcpy(this, &temp, sizeof(*this));
}
};
-static_assert(shm_ok<RawQueue::MessageHeader>::value, "the whole point"
- " is to stick it in shared memory");
+static_assert(shm_ok<RawQueue::MessageHeader>::value,
+ "the whole point is to stick it in shared memory");
struct RawQueue::ReadData {
bool writable_start;
@@ -73,7 +74,7 @@
}
RawQueue::RawQueue(const char *name, size_t length, int hash, int queue_length)
- : readable_(&data_lock_), writable_(&data_lock_) {
+ : readable_(&data_lock_), writable_(&data_lock_) {
const size_t name_size = strlen(name) + 1;
char *temp = static_cast<char *>(shm_malloc(name_size));
memcpy(temp, name, name_size);
@@ -115,17 +116,17 @@
if (kFetchDebug) {
printf("fetching queue %s\n", name);
}
- if (mutex_lock(&global_core->mem_struct->queues.alloc_lock) != 0) {
+ if (mutex_lock(&global_core->mem_struct->queues.lock) != 0) {
return NULL;
}
RawQueue *current = static_cast<RawQueue *>(
- global_core->mem_struct->queues.queue_list);
+ 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.alloc_lock);
+ mutex_unlock(&global_core->mem_struct->queues.lock);
return current;
} else {
if (kFetchDebug) {
@@ -142,12 +143,12 @@
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.queue_list = r;
+ global_core->mem_struct->queues.pointer = r;
} else {
current->next_ = r;
}
- mutex_unlock(&global_core->mem_struct->queues.alloc_lock);
+ mutex_unlock(&global_core->mem_struct->queues.lock);
return r;
}
RawQueue *RawQueue::Fetch(const char *name, size_t length, int hash,
diff --git a/aos/linux_code/ipc_lib/queue_test.cc b/aos/linux_code/ipc_lib/queue_test.cc
index c87bd7b..0a982cc 100644
--- a/aos/linux_code/ipc_lib/queue_test.cc
+++ b/aos/linux_code/ipc_lib/queue_test.cc
@@ -15,6 +15,7 @@
#include "aos/common/queue_testutils.h"
#include "aos/common/time.h"
#include "aos/common/logging/logging.h"
+#include "aos/common/die.h"
using ::testing::AssertionResult;
using ::testing::AssertionSuccess;
@@ -136,6 +137,9 @@
void SetUp() override {
::testing::Test::SetUp();
+
+ SetDieTestMode(true);
+
fatal_failure = static_cast<char *>(shm_malloc(sizeof(fatal_failure)));
static bool registered = false;
if (!registered) {
diff --git a/aos/linux_code/ipc_lib/shared_mem.c b/aos/linux_code/ipc_lib/shared_mem.c
index 4e38117..069fe19 100644
--- a/aos/linux_code/ipc_lib/shared_mem.c
+++ b/aos/linux_code/ipc_lib/shared_mem.c
@@ -21,8 +21,10 @@
void init_shared_mem_core(aos_shm_core *shm_core) {
clock_gettime(CLOCK_REALTIME, &shm_core->identifier);
shm_core->msg_alloc_lock = 0;
- shm_core->queues.queue_list = NULL;
- shm_core->queues.alloc_lock = 0;
+ shm_core->queues.pointer = NULL;
+ shm_core->queues.lock = 0;
+ shm_core->queue_types.pointer = NULL;
+ shm_core->queue_types.lock = 0;
}
ptrdiff_t aos_core_get_mem_usage(void) {
@@ -128,3 +130,7 @@
}
return 0;
}
+
+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
index e5059c4..4d5e88e 100644
--- a/aos/linux_code/ipc_lib/shared_mem.h
+++ b/aos/linux_code/ipc_lib/shared_mem.h
@@ -18,10 +18,12 @@
// can have regular pointers to other stuff in shared memory.
#define SHM_START 0x20000000
-typedef struct aos_queue_global_t {
- mutex alloc_lock;
- void *queue_list; // an aos::Queue* declared in C code
-} aos_queue_global;
+// A structure that represents some kind of global pointer that everything
+// shares.
+typedef struct aos_global_pointer_t {
+ mutex lock;
+ void *pointer;
+} aos_global_pointer;
typedef struct aos_shm_core_t {
// clock_gettime(CLOCK_REALTIME, &identifier) gets called to identify
@@ -32,7 +34,12 @@
mutex creation_condition;
mutex msg_alloc_lock;
void *msg_alloc;
- aos_queue_global queues;
+ // 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;
enum aos_core_create {
@@ -62,6 +69,9 @@
int aos_core_create_shared_mem(enum aos_core_create to_create);
int 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
diff --git a/aos/linux_code/linux_code.gyp b/aos/linux_code/linux_code.gyp
index 49be0ce..7a83c59 100644
--- a/aos/linux_code/linux_code.gyp
+++ b/aos/linux_code/linux_code.gyp
@@ -23,5 +23,15 @@
'<(AOS)/build/aos.gyp:logging',
],
},
+ {
+ 'target_name': 'core',
+ 'type': 'executable',
+ 'sources': [
+ 'core.cc',
+ ],
+ 'dependencies': [
+ 'init',
+ ],
+ },
],
}
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..8c8c8b4
--- /dev/null
+++ b/aos/linux_code/logging/binary_log_file.cc
@@ -0,0 +1,131 @@
+#include "aos/linux_code/logging/binary_log_file.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.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();
+}
+
+LogFileMessageHeader *LogFileAccessor::GetWritePosition(size_t message_size) {
+ if (position_ + message_size + (kAlignment - (message_size % kAlignment)) +
+ sizeof(mutex) > kPageSize) {
+ char *const temp = current_;
+ MapNextPage();
+ if (futex_set_value(static_cast<mutex *>(static_cast<void *>(
+ &temp[position_])), 2) == -1) {
+ LOG(WARNING,
+ "futex_set_value(%p, 2) failed with %d: %s. readers will hang\n",
+ &temp[position_], errno, strerror(errno));
+ }
+ Unmap(temp);
+ position_ = 0;
+ }
+ LogFileMessageHeader *const r = static_cast<LogFileMessageHeader *>(
+ static_cast<void *>(¤t_[position_]));
+ position_ += message_size;
+ AlignPosition();
+ return r;
+}
+
+const LogFileMessageHeader *LogFileAccessor::ReadNextMessage(bool wait) {
+ LogFileMessageHeader *r;
+ do {
+ r = static_cast<LogFileMessageHeader *>(
+ static_cast<void *>(¤t_[position_]));
+ if (wait) {
+ if (futex_wait(&r->marker) != 0) continue;
+ }
+ if (r->marker == 2) {
+ Unmap(current_);
+ MapNextPage();
+ position_ = 0;
+ r = static_cast<LogFileMessageHeader *>(static_cast<void *>(current_));
+ }
+ } while (wait && r->marker == 0);
+ if (r->marker == 0) {
+ return NULL;
+ }
+ position_ += sizeof(LogFileMessageHeader) + r->name_size + r->message_size;
+ AlignPosition();
+ if (position_ >= kPageSize) {
+ LOG(FATAL, "corrupt log file running over page size\n");
+ }
+ return r;
+}
+
+void LogFileAccessor::Sync() const {
+ msync(current_, kPageSize, MS_ASYNC | MS_INVALIDATE);
+}
+
+bool LogFileAccessor::IsLastPage() {
+ if (is_last_page_ != 0) {
+ return is_last_page_ == 2;
+ }
+
+ struct stat info;
+ if (fstat(fd_, &info) == -1) {
+ LOG(FATAL, "fstat(%d, %p) failed with %d: %s\n", fd_, &info, errno,
+ strerror(errno));
+ }
+ bool r = offset_ == static_cast<off_t>(info.st_size - kPageSize);
+ is_last_page_ = r ? 2 : 1;
+ return r;
+}
+
+void LogFileAccessor::MapNextPage() {
+ if (writable_) {
+ if (ftruncate(fd_, offset_ + kPageSize) == -1) {
+ fprintf(stderr, "ftruncate(%d, %zd) failed with %d: %s. aborting\n",
+ fd_, kPageSize, errno, strerror(errno));
+ printf("see stderr\n");
+ abort();
+ }
+ }
+ current_ = static_cast<char *>(
+ mmap(NULL, kPageSize, PROT_READ | (writable_ ? PROT_WRITE : 0),
+ MAP_SHARED, fd_, offset_));
+ if (current_ == MAP_FAILED) {
+ LOG(FATAL,
+ "mmap(NULL, %zd, PROT_READ | PROT_WRITE, MAP_SHARED, %d, %jd)"
+ " failed with %d: %s. aborting\n",
+ kPageSize, fd_, static_cast<intmax_t>(offset_), errno,
+ strerror(errno));
+ }
+ offset_ += kPageSize;
+}
+
+void LogFileAccessor::Unmap(void *location) {
+ if (munmap(location, kPageSize) == -1) {
+ LOG(FATAL, "munmap(%p, %zd) failed with %d: %s. aborting\n", location,
+ kPageSize, errno, strerror(errno));
+ }
+ is_last_page_ = 0;
+}
+
+} // 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..0f8c3fb
--- /dev/null
+++ b/aos/linux_code/logging/binary_log_file.h
@@ -0,0 +1,132 @@
+#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,
+ };
+
+ // 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.
+ mutex marker;
+ static_assert(sizeof(marker) == 4, "mutex changed size!");
+ static_assert(MESSAGE_ALIGNMENT >= alignof(mutex),
+ "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);
+
+ // message_size should be the total number of bytes needed for the message.
+ LogFileMessageHeader *GetWritePosition(size_t message_size);
+ // May return NULL iff wait is false.
+ const LogFileMessageHeader *ReadNextMessage(bool wait);
+
+ // Asynchronously syncs all open mappings.
+ void Sync() const;
+
+ bool IsLastPage();
+
+ private:
+ // 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
+
+ const int fd_;
+ const bool writable_;
+
+ // Into the file. Always a multiple of kPageSize.
+ off_t offset_;
+ char *current_;
+ size_t position_;
+
+ // 0 = unknown, 1 = no, 2 = yes
+ int is_last_page_ = 0;
+
+ void MapNextPage();
+ void Unmap(void *location);
+
+ // Advances position to the next (aligned) location.
+ void AlignPosition() {
+ position_ += kAlignment - (position_ % kAlignment);
+ }
+};
+
+} // 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..67cb166
--- /dev/null
+++ b/aos/linux_code/logging/binary_log_writer.cc
@@ -0,0 +1,183 @@
+#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 <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"
+
+namespace aos {
+namespace logging {
+namespace linux_code {
+namespace {
+
+void CheckTypeWritten(uint32_t type_id, LogFileAccessor &writer) {
+ static ::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);
+ }
+
+ 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);
+}
+
+int BinaryLogReaderMain() {
+ InitNRT();
+
+ const char *folder = configuration::GetLoggingDirectory();
+ if (access(folder, R_OK | W_OK) == -1) {
+ LOG(FATAL, "folder '%s' does not exist. please create it\n", folder);
+ }
+ LOG(INFO, "logging to folder '%s'\n", folder);
+
+ const time_t t = time(NULL);
+ char *tmp;
+ if (asprintf(&tmp, "%s/aos_log-%jd", folder, static_cast<uintmax_t>(t)) ==
+ -1) {
+ fprintf(stderr,
+ "BinaryLogReader: couldn't create final name because of %d (%s)."
+ " exiting\n", errno, strerror(errno));
+ return EXIT_FAILURE;
+ }
+ char *tmp2;
+ if (asprintf(&tmp2, "%s/aos_log-current", folder) == -1) {
+ fprintf(stderr,
+ "BinaryLogReader: couldn't create symlink name because of %d (%s)."
+ " not creating current symlink\n", errno, strerror(errno));
+ } else {
+ if (unlink(tmp2) == -1 && (errno != EROFS && errno != ENOENT)) {
+ fprintf(stderr,
+ "BinaryLogReader: warning: unlink('%s') failed"
+ " because of %d (%s)\n",
+ tmp2, errno, strerror(errno));
+ }
+ if (symlink(tmp, tmp2) == -1) {
+ fprintf(stderr, "BinaryLogReader: warning: symlink('%s', '%s') failed"
+ " because of %d (%s)\n", tmp, tmp2, errno, strerror(errno));
+ }
+ 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) {
+ fprintf(stderr,
+ "BinaryLogReader: couldn't open file '%s' because of %d (%s)."
+ " exiting\n", tmp, errno, strerror(errno));
+ return EXIT_FAILURE;
+ }
+ LogFileAccessor writer(fd, true);
+
+ struct timespec timespec;
+ time_t last_sec = 0;
+ while (true) {
+ clock_gettime(CLOCK_MONOTONIC, ×pec);
+ if (last_sec != timespec.tv_sec) {
+ LOG(INFO, "msyncing output\n");
+ last_sec = timespec.tv_sec;
+ writer.Sync();
+ }
+
+ const LogMessage *const msg = ReadNext();
+ if (msg == NULL) continue;
+
+ size_t output_length =
+ sizeof(LogFileMessageHeader) + msg->name_length + msg->message_length;
+ if (msg->type == LogMessage::Type::kStruct) {
+ output_length += sizeof(msg->structure.type_id) + sizeof(uint32_t) +
+ msg->structure.string_length;
+ CheckTypeWritten(msg->structure.type_id, writer);
+ }
+ 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);
+
+ uint32_t length = msg->structure.string_length;
+ memcpy(position, &length, sizeof(length));
+ position += sizeof(length);
+ memcpy(position, msg->structure.serialized, length);
+ position += length;
+ output->message_size += sizeof(length) + length;
+
+ memcpy(position,
+ msg->structure.serialized + msg->structure.string_length,
+ msg->message_length);
+
+ output->type = LogFileMessageHeader::MessageType::kStruct;
+ break;
+ }
+
+ 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..d039c70
--- /dev/null
+++ b/aos/linux_code/logging/linux_interface.cc
@@ -0,0 +1,65 @@
+#include "aos/linux_code/logging/linux_logging.h"
+
+#include <sys/prctl.h>
+
+#include "aos/linux_code/thread_local.h"
+#include "aos/common/die.h"
+
+namespace aos {
+namespace logging {
+namespace internal {
+namespace {
+
+::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) {
+ Die("prctl(PR_GET_NAME, %p) failed with %d: %s\n",
+ thread_name_array, errno, strerror(errno));
+ }
+ 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_THREAD_LOCAL Context *my_context(NULL);
+
+} // namespace
+
+Context *Context::Get() {
+ if (my_context == NULL) {
+ my_context = new Context();
+ my_context->name = GetMyName();
+ if (my_context->name.size() + 1 > sizeof(LogMessage::name)) {
+ Die("logging: process/thread name '%s' is too long\n",
+ my_context->name.c_str());
+ }
+ my_context->source = getpid();
+ }
+ return my_context;
+}
+
+void Context::Delete() {
+ delete my_context;
+ my_context = NULL;
+}
+
+} // namespace internal
+} // namespace logging
+} // namespace aos
diff --git a/aos/linux_code/logging/linux_logging.cc b/aos/linux_code/logging/linux_logging.cc
index faeb04a..676e3b7 100644
--- a/aos/linux_code/logging/linux_logging.cc
+++ b/aos/linux_code/logging/linux_logging.cc
@@ -8,87 +8,46 @@
#include <errno.h>
#include <unistd.h>
#include <limits.h>
-#include <sys/prctl.h>
#include <algorithm>
#include "aos/common/die.h"
#include "aos/common/logging/logging_impl.h"
-#include "aos/linux_code/thread_local.h"
#include "aos/linux_code/ipc_lib/queue.h"
namespace aos {
namespace logging {
namespace {
-using internal::Context;
-
-AOS_THREAD_LOCAL Context *my_context(NULL);
-
-::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) {
- Die("prctl(PR_GET_NAME, %p) failed with %d: %s\n",
- thread_name_array, errno, strerror(errno));
- }
- 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;
-}
-
RawQueue *queue;
} // namespace
-namespace internal {
-
-Context *Context::Get() {
- if (my_context == NULL) {
- my_context = new Context();
- my_context->name = GetMyName();
- if (my_context->name.size() + 1 > sizeof(LogMessage::name)) {
- Die("logging: process/thread name '%s' is too long\n",
- my_context->name.c_str());
- }
- my_context->source = getpid();
- }
- return my_context;
-}
-
-void Context::Delete() {
- delete my_context;
- my_context = NULL;
-}
-
-} // namespace internal
namespace linux_code {
namespace {
-class linuxQueueLogImplementation : public LogImplementation {
- virtual void DoLog(log_level level, const char *format, va_list ap) {
+class LinuxQueueLogImplementation : public LogImplementation {
+ LogMessage *GetMessageOrDie() {
LogMessage *message = static_cast<LogMessage *>(queue->GetMessage());
if (message == NULL) {
- LOG(FATAL, "queue get message failed\n");
+ LOG(FATAL, "%p->GetMessage() failed\n", queue);
+ } else {
+ return message;
}
+ }
+ 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);
}
};
@@ -103,7 +62,7 @@
Die("logging: couldn't fetch queue\n");
}
- AddImplementation(new linuxQueueLogImplementation());
+ AddImplementation(new LinuxQueueLogImplementation());
}
const LogMessage *ReadNext(int flags, int *index) {
diff --git a/aos/linux_code/logging/log_displayer.cc b/aos/linux_code/logging/log_displayer.cc
new file mode 100644
index 0000000..98021b5
--- /dev/null
+++ b/aos/linux_code/logging/log_displayer.cc
@@ -0,0 +1,243 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include <algorithm>
+
+#include "aos/linux_code/logging/binary_log_file.h"
+#include "aos/common/logging/logging_impl.h"
+#include "aos/common/queue_types.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"
+ "\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 and changing FILE\n";
+
+void PrintHelpAndExit() {
+ fprintf(stderr, "Usage: %s %s", program_invocation_name, kArgsHelp);
+
+ exit(EXIT_SUCCESS);
+}
+
+} // namespace
+
+int main(int argc, char **argv) {
+ const char *filter_name = NULL;
+ 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";
+
+ ::aos::logging::AddImplementation(
+ new ::aos::logging::StreamLogImplementation(stdout));
+
+ while (true) {
+ static struct option long_options[] = {
+ {"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:l:p:fts:m:o:h",
+ long_options, &option_index);
+ if (c == -1) { // if we're at the end
+ break;
+ }
+ switch (c) {
+ case 0:
+ fprintf(stderr, "LogDisplayer: got a 0 option but didn't set up any\n");
+ abort();
+ case 'n':
+ filter_name = optarg;
+ 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':
+ abort();
+ 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':
+ abort();
+ 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();
+ }
+ }
+
+ fprintf(stderr, "displaying down to level %s from file '%s'\n",
+ ::aos::logging::log_str(filter_level), filename);
+ if (optind < argc) {
+ fprintf(stderr, "non-option ARGV-elements: ");
+ while (optind < argc) {
+ fprintf(stderr, "%s\n", argv[optind++]);
+ }
+ }
+
+ int fd = open(filename, O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "error: couldn't open file '%s' for reading because of %s\n",
+ filename, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ ::aos::logging::linux_code::LogFileAccessor accessor(fd, false);
+
+ if (skip_to_end) {
+ fputs("skipping old logs...\n", stderr);
+ }
+
+ const LogFileMessageHeader *msg;
+ ::aos::logging::LogMessage log_message;
+ do {
+ msg = accessor.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);
+ ::aos::type_cache::Add(*type);
+ continue;
+ }
+
+ if (skip_to_end) {
+ if (accessor.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;
+ if (filter_name != NULL) {
+ if (filter_length != msg->name_size) continue;
+ if (memcmp(filter_name,
+ reinterpret_cast<const char *>(msg) + sizeof(*msg),
+ filter_length) !=
+ 0) {
+ continue;
+ }
+ }
+
+ log_message.source = msg->source;
+ log_message.sequence = msg->sequence;
+ log_message.level = msg->level;
+ log_message.seconds = msg->time_sec;
+ log_message.nseconds = msg->time_nsec;
+ memcpy(log_message.name, reinterpret_cast<const char *>(msg) + sizeof(*msg),
+ ::std::min<size_t>(sizeof(log_message.name), msg->name_size));
+ log_message.message_length = msg->message_size;
+ log_message.name_length = msg->name_size;
+
+ switch (msg->type) {
+ case LogFileMessageHeader::MessageType::kStruct: {
+ const char *position =
+ reinterpret_cast<const char *>(msg + 1) + msg->name_size;
+ memcpy(&log_message.structure.type_id, position,
+ sizeof(log_message.structure.type_id));
+ position += sizeof(log_message.structure.type_id);
+
+ uint32_t length;
+ memcpy(&length, position, sizeof(length));
+ log_message.structure.string_length = length;
+ position += sizeof(length);
+ memcpy(log_message.structure.serialized, position, length);
+ position += length;
+
+ log_message.message_length -=
+ sizeof(log_message.structure.type_id) + sizeof(uint32_t) +
+ log_message.structure.string_length;
+ memcpy(log_message.structure.serialized +
+ log_message.structure.string_length,
+ position, log_message.message_length);
+
+ log_message.type = ::aos::logging::LogMessage::Type::kStruct;
+ break;
+ }
+ case LogFileMessageHeader::MessageType::kString:
+ memcpy(
+ log_message.message,
+ reinterpret_cast<const char *>(msg) + sizeof(*msg) + msg->name_size,
+ ::std::min<size_t>(sizeof(log_message.message), msg->message_size));
+ log_message.type = ::aos::logging::LogMessage::Type::kString;
+ break;
+ case LogFileMessageHeader::MessageType::kStructType:
+ LOG(FATAL, "shouldn't get here\n");
+ break;
+ };
+ ::aos::logging::internal::PrintMessage(stdout, log_message);
+ } while (msg != NULL);
+}
diff --git a/aos/linux_code/core/LogStreamer.cpp b/aos/linux_code/logging/log_streamer.cc
similarity index 95%
rename from aos/linux_code/core/LogStreamer.cpp
rename to aos/linux_code/logging/log_streamer.cc
index d570489..b6813e9 100644
--- a/aos/linux_code/core/LogStreamer.cpp
+++ b/aos/linux_code/logging/log_streamer.cc
@@ -11,7 +11,6 @@
#include <inttypes.h>
#include "aos/linux_code/logging/linux_logging.h"
-#include "aos/linux_code/core/LogFileCommon.h"
#include "aos/linux_code/init.h"
#include "aos/linux_code/ipc_lib/queue.h"
#include "aos/common/logging/logging_impl.h"
diff --git a/aos/linux_code/logging/logging.gyp b/aos/linux_code/logging/logging.gyp
index dfb189c..32df43d 100644
--- a/aos/linux_code/logging/logging.gyp
+++ b/aos/linux_code/logging/logging.gyp
@@ -1,4 +1,58 @@
{
'targets': [
+ # linux_* is dealt with by aos/build/aos.gyp:logging.
+ {
+ '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',
+ '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',
+ ],
+ },
+ {
+ '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/queue-tmpl.h b/aos/linux_code/queue-tmpl.h
index 15b8608..84eb02c 100644
--- a/aos/linux_code/queue-tmpl.h
+++ b/aos/linux_code/queue-tmpl.h
@@ -52,7 +52,6 @@
return msg;
}
-#ifndef SWIG
operator bool() {
return msg_ != NULL;
}
@@ -70,7 +69,6 @@
assert(msg != NULL);
return msg;
}
-#endif // SWIG
// Sends the message and removes our reference to it.
// If the queue is full, over-rides the oldest message in it with our new
@@ -114,7 +112,6 @@
reset();
}
-#ifndef SWIG
// Implements a move constructor to take the message pointer from the
// temporary object to save work.
SafeScopedMessagePtr(SafeScopedMessagePtr<T> &&ptr)
@@ -122,7 +119,6 @@
msg_(ptr.msg_) {
ptr.msg_ = NULL;
}
-#endif // SWIG
// Copy constructor actually copies the data.
SafeScopedMessagePtr(const SafeScopedMessagePtr<T> &ptr)
@@ -130,13 +126,11 @@
msg_(NULL) {
reset(new T(*ptr.get()));
}
-#ifndef SWIG
// Equal operator copies the data.
void operator=(const SafeScopedMessagePtr<T> &ptr) {
queue_ = ptr.queue_;
reset(new T(*ptr.get()));
}
-#endif // SWIG
private:
// Provide access to private constructor.
@@ -254,7 +248,7 @@
// This builder uses the safe message pointer so that it can be safely copied
-// and used by SWIG or in places where it could be leaked.
+// in places where it could be leaked.
template <class T>
class SafeMessageBuilder {
public:
diff --git a/aos/linux_code/starter/starter.sh b/aos/linux_code/starter/starter.sh
index 62338af..9d7cc13 100755
--- a/aos/linux_code/starter/starter.sh
+++ b/aos/linux_code/starter/starter.sh
@@ -1,5 +1,7 @@
#!/bin/bash
+echo $$ > /tmp/starter.pid
+
echo '/home/driver/tmp/robot_logs/%e-%s-%p-%t.coredump' > /proc/sys/kernel/core_pattern
#echo $$ > /var/run/`basename $0`.pid IT FORKS AFTER THIS!!!!
diff --git a/bbb_cape/schematic/cape.pcb b/bbb_cape/schematic/cape.pcb
index 16258ad..c9667a5 100644
--- a/bbb_cape/schematic/cape.pcb
+++ b/bbb_cape/schematic/cape.pcb
@@ -6,7 +6,7 @@
PCB["971 BBB Cape" 500000 400000]
Grid[500.0 441 0 0]
-Cursor[315941 174500 0.000000]
+Cursor[267441 75500 0.000000]
PolyArea[3100.006200]
Thermal[0.500000]
DRC[800 1000 800 10 1000 500]
@@ -6240,12 +6240,6 @@
)
Layer(7 "silk")
(
- Line[251441 71500 248941 69000 600 1200 "clearline"]
- Line[251441 71500 248941 74000 600 1200 "clearline"]
- Line[248941 74000 248941 69000 600 1200 "clearline"]
- Line[251441 71500 257441 71500 600 1200 "clearline"]
- Line[251441 68000 251441 75000 600 1200 "clearline"]
- Line[248941 71500 242441 71500 600 1200 "clearline"]
Line[406441 305000 403941 307500 600 1200 "clearline"]
Line[406441 305000 408941 307500 600 1200 "clearline"]
Line[403941 307500 408941 307500 600 1200 "clearline"]
@@ -6264,6 +6258,12 @@
Line[87941 193000 87941 199000 600 1200 "clearline"]
Line[84441 193000 91441 193000 600 1200 "clearline"]
Line[87941 190500 87941 187500 600 1200 "clearline"]
+ Line[247941 72000 250441 74500 600 1200 "clearline"]
+ Line[247941 72000 250441 69500 600 1200 "clearline"]
+ Line[250441 69500 250441 74500 600 1200 "clearline"]
+ Line[241941 72000 247941 72000 600 1200 "clearline"]
+ Line[247941 68500 247941 75500 600 1200 "clearline"]
+ Line[250441 72000 256941 72000 600 1200 "clearline"]
Text[384000 226503 1 105 "7 5 4 3 1 0" "clearline"]
Text[308438 170515 0 100 "- DIO CLK RST" "clearline"]
Text[308438 178515 0 100 "SWD connector" "clearline"]
diff --git a/bbb_cape/src/bbb/bbb.gyp b/bbb_cape/src/bbb/bbb.gyp
index e46c99f..8efe553 100644
--- a/bbb_cape/src/bbb/bbb.gyp
+++ b/bbb_cape/src/bbb/bbb.gyp
@@ -100,10 +100,12 @@
'<(DEPTH)/bbb_cape/src/cape/cape.gyp:cows',
'crc',
'byte_io',
+ '<(AOS)/common/util/util.gyp:log_interval',
],
'export_dependent_settings': [
'<(AOS)/build/aos.gyp:logging',
'<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/util/util.gyp:log_interval',
],
},
{
@@ -161,12 +163,14 @@
'crc',
'sensor_generation',
'<(AOS)/linux_code/linux_code.gyp:configuration',
+ '<(AOS)/common/util/util.gyp:log_interval',
],
'export_dependent_settings': [
'packet_finder',
'data_struct',
'cape_manager',
'<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/util/util.gyp:log_interval',
],
},
{
@@ -215,5 +219,16 @@
'uart_reader',
],
},
+ {
+ 'target_name': 'test_sensor_receiver',
+ 'type': 'executable',
+ 'sources': [
+ 'test_sensor_receiver.cc',
+ ],
+ 'dependencies': [
+ 'sensor_reader',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ ],
+ },
],
}
diff --git a/bbb_cape/src/bbb/cape_manager.cc b/bbb_cape/src/bbb/cape_manager.cc
index 923021b..b5f2851 100644
--- a/bbb_cape/src/bbb/cape_manager.cc
+++ b/bbb_cape/src/bbb/cape_manager.cc
@@ -11,7 +11,7 @@
namespace bbb {
CapeManager::CapeManager()
- : uart_(750000), reset_(2, 5, true), custom_bootloader_(2, 3, false) {}
+ : uart_(750000), reset_(2, 5, true), custom_bootloader_(2, 3, true) {}
void CapeManager::DownloadHex(const ::std::string &filename) {
HexByteReader file(filename);
@@ -24,9 +24,8 @@
void CapeManager::DoReset(bool bootloader) {
static constexpr ::aos::time::Time kWaitTime =
::aos::time::Time::InSeconds(0.1);
- reset_.Set(false);
- ::aos::time::SleepFor(kWaitTime);
custom_bootloader_.Set(!bootloader);
+ reset_.Set(false);
::aos::time::SleepFor(kWaitTime);
reset_.Set(true);
::aos::time::SleepFor(kWaitTime);
diff --git a/bbb_cape/src/bbb/crc.cc b/bbb_cape/src/bbb/crc.cc
index 7c14706..88c338d 100644
--- a/bbb_cape/src/bbb/crc.cc
+++ b/bbb_cape/src/bbb/crc.cc
@@ -33,7 +33,8 @@
} // namespace
-uint32_t CalculateChecksum(uint8_t *data, size_t length, uint32_t initial) {
+uint32_t CalculateChecksum(const uint8_t *data, size_t length,
+ uint32_t initial) {
assert((length % 4) == 0);
static ::aos::Once<const uint32_t> table_once(GenerateTable);
diff --git a/bbb_cape/src/bbb/crc.h b/bbb_cape/src/bbb/crc.h
index 4d1dea7..c26f5db 100644
--- a/bbb_cape/src/bbb/crc.h
+++ b/bbb_cape/src/bbb/crc.h
@@ -16,7 +16,7 @@
// length is the number of bytes of data to read. It must be a multiple of 4.
// initial can be a previous return value to continue the same checksum over
// more data.
-uint32_t CalculateChecksum(uint8_t *data, size_t length,
+uint32_t CalculateChecksum(const uint8_t *data, size_t length,
uint32_t initial = 0xFFFFFFFF);
// Reads all data out of reader and does a checksum over all of it in reasonably
// sized pieces. Does all of the reads with a timeout of 0. Stops on the first
diff --git a/bbb_cape/src/bbb/gpo.cc b/bbb_cape/src/bbb/gpo.cc
index d17abe7..8ad599e 100644
--- a/bbb_cape/src/bbb/gpo.cc
+++ b/bbb_cape/src/bbb/gpo.cc
@@ -13,13 +13,15 @@
: GpioPin(bank, pin, false, initial_value) {}
void Gpo::Set(bool high) {
- // TODO(brians): Figure out why this breaks it.
- //rewind(value_handle_);
+ rewind(value_handle_);
if (fputc(high ? '1' : '0', value_handle_) == EOF) {
LOG(FATAL, "fputc(%c, %p) for pin (%d,%d) failed with %d: %s\n",
high ? '1': '0', value_handle_, bank_, pin_, errno, strerror(errno));
}
- sync();
+ if (fflush(value_handle_) == EOF) {
+ LOG(FATAL, "fflush(%p) for pin (%d,%d) failed with %d: %s\n",
+ value_handle_, bank_, pin_, errno, strerror(errno));
+ }
}
} // namespace bbb
diff --git a/bbb_cape/src/bbb/packet_finder.cc b/bbb_cape/src/bbb/packet_finder.cc
index eace2aa..5decec8 100644
--- a/bbb_cape/src/bbb/packet_finder.cc
+++ b/bbb_cape/src/bbb/packet_finder.cc
@@ -17,6 +17,15 @@
using ::aos::time::Time;
namespace bbb {
+namespace {
+
+// Enabling all of the debugging logs during normal operation on the BBB causes
+// it to use most of the CPU when it runs into trouble.
+const bool kDebugLogs = false;
+
+} // namespace
+
+constexpr ::aos::time::Time PacketFinder::kDebugLogInterval;
PacketFinder::PacketFinder(ByteReaderInterface *reader, size_t packet_size)
: reader_(reader),
@@ -57,8 +66,8 @@
if (getuid() == 0) {
// TODO(brians): Do this cleanly.
int chrt_result =
- system("bash -c 'chrt -r -p 55"
- " $(top -n1 | fgrep irq/89 | cut -d\" \" -f2)'");
+ system("chrt -o 0 bash -c 'chrt -r -p 55"
+ " $(pgrep irq/89)'");
if (chrt_result == -1) {
LOG(FATAL, "system(chrt -r -p 55 the_irq) failed\n");
} else if (!WIFEXITED(chrt_result) || WEXITSTATUS(chrt_result) != 0) {
@@ -102,7 +111,8 @@
reinterpret_cast<uint32_t *>(buf_), packet_size_,
reinterpret_cast<uint32_t *>(unstuffed_data_), packet_size_ - 4);
if (unstuffed == 0) {
- LOG(INFO, "invalid packet\n");
+ if (kDebugLogs) LOG(INFO, "invalid\n");
+ LOG_INTERVAL(invalid_packet_);
return false;
} else if (unstuffed != (packet_size_ - 4) / 4) {
LOG(WARNING, "packet is %" PRIu32 " words instead of %zu\n",
@@ -116,8 +126,11 @@
uint32_t calculated_checksum = cape::CalculateChecksum(
reinterpret_cast<uint8_t *>(unstuffed_data_), packet_size_ - 8);
if (sent_checksum != calculated_checksum) {
- LOG(INFO, "sent checksum: %" PRIx32 " vs calculated: %" PRIx32"\n",
- sent_checksum, calculated_checksum);
+ if (kDebugLogs) {
+ LOG(INFO, "sent %" PRIx32 " not %" PRIx32 "\n", sent_checksum,
+ calculated_checksum);
+ }
+ LOG_INTERVAL(bad_checksum_);
return false;
}
@@ -134,7 +147,7 @@
if (buf_[i] == 0) {
++zeros;
if (zeros == 4) {
- LOG(INFO, "found another packet start at %zd\n", i);
+ if (kDebugLogs) LOG(INFO, "start at %zd\n", i);
packet_bytes_ = packet_size_ - (i + 1);
memmove(buf_, buf_ + i + 1, packet_bytes_);
return false;
diff --git a/bbb_cape/src/bbb/packet_finder.h b/bbb_cape/src/bbb/packet_finder.h
index 80cb8b8..962291f 100644
--- a/bbb_cape/src/bbb/packet_finder.h
+++ b/bbb_cape/src/bbb/packet_finder.h
@@ -7,6 +7,7 @@
#include "aos/common/logging/logging.h"
#include "aos/common/time.h"
#include "aos/common/macros.h"
+#include "aos/common/util/log_interval.h"
namespace bbb {
@@ -64,6 +65,14 @@
// Whether we've increased the priority of the IRQ yet.
bool irq_priority_increased_ = false;
+ typedef ::aos::util::SimpleLogInterval SimpleLogInterval;
+ static constexpr ::aos::time::Time kDebugLogInterval =
+ ::aos::time::Time::InSeconds(0.3);
+ SimpleLogInterval invalid_packet_ =
+ SimpleLogInterval(kDebugLogInterval, INFO, "invalid packet");
+ SimpleLogInterval bad_checksum_ =
+ SimpleLogInterval(kDebugLogInterval, INFO, "bad checksum");
+
DISALLOW_COPY_AND_ASSIGN(PacketFinder);
};
diff --git a/bbb_cape/src/bbb/sensor_reader.cc b/bbb_cape/src/bbb/sensor_reader.cc
index 2e40903..fd4b79d 100644
--- a/bbb_cape/src/bbb/sensor_reader.cc
+++ b/bbb_cape/src/bbb/sensor_reader.cc
@@ -16,7 +16,12 @@
uint32_t ReadChecksum(const ::std::string &filename) {
HexByteReader reader(filename);
- return ::cape::CalculateChecksum(&reader);
+ uint32_t r = ::cape::CalculateChecksum(&reader);
+ /*static const uint8_t fill[4] = {0xFF, 0xFF, 0xFF, 0xFF};
+ for (size_t i = reader.GetSize(); i < 0x100000 - 0x4000; i += 4) {
+ r = ::cape::CalculateChecksum(fill, 4, r);
+ }*/
+ return r;
}
} // namespace
@@ -34,7 +39,7 @@
const DataStruct *SensorReader::ReadPacket() {
static constexpr ::aos::time::Time kResetTimeout =
- ::aos::time::Time::InSeconds(2.5);
+ ::aos::time::Time::InSeconds(5);
while (true) {
::aos::time::Time next_timeout = last_received_time_ + kResetTimeout;
@@ -46,7 +51,7 @@
if (packet_finder_.ReadPacket(next_timeout)) {
last_received_time_ = ::aos::time::Time::Now();
const DataStruct *data = packet_finder_.get_packet<DataStruct>();
- if (data->flash_checksum != expected_checksum_) {
+ if (data->flash_checksum != expected_checksum_ && false) {
LOG(WARNING, "Cape code checksum is %" PRIu32 ". Expected %" PRIu32
". Reflashing.\n",
data->flash_checksum, expected_checksum_);
@@ -62,7 +67,7 @@
last_cape_timestamp_ = data->timestamp;
return data;
}
- LOG(WARNING, "receiving packet failed\n");
+ LOG_INTERVAL(receive_failed_);
}
}
diff --git a/bbb_cape/src/bbb/sensor_reader.h b/bbb_cape/src/bbb/sensor_reader.h
index 48c863a..ed2fbcf 100644
--- a/bbb_cape/src/bbb/sensor_reader.h
+++ b/bbb_cape/src/bbb/sensor_reader.h
@@ -7,6 +7,7 @@
#include "aos/common/time.h"
#include "aos/common/macros.h"
+#include "aos/common/util/log_interval.h"
#include "bbb/packet_finder.h"
#include "bbb/data_struct.h"
@@ -51,6 +52,10 @@
::aos::time::Time last_received_time_ = ::aos::time::Time::InSeconds(0);
uint64_t last_cape_timestamp_;
+ ::aos::util::SimpleLogInterval receive_failed_ =
+ ::aos::util::SimpleLogInterval(::aos::time::Time::InSeconds(0.2), WARNING,
+ "receive failed");
+
DISALLOW_COPY_AND_ASSIGN(SensorReader);
};
diff --git a/bbb_cape/src/bbb/test_sensor_receiver.cc b/bbb_cape/src/bbb/test_sensor_receiver.cc
new file mode 100644
index 0000000..584acd3
--- /dev/null
+++ b/bbb_cape/src/bbb/test_sensor_receiver.cc
@@ -0,0 +1,50 @@
+#include <inttypes.h>
+
+#include "aos/linux_code/init.h"
+
+#include "bbb/sensor_reader.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+namespace {
+
+double gyro_translate(int64_t in) {
+ return in / 16.0 / 1000.0 / (180.0 / M_PI);
+}
+
+void PacketReceived(const ::bbb::DataStruct *data,
+ const ::aos::time::Time &cape_timestamp) {
+ LOG(DEBUG, "cape timestamp %010" PRId32 ".%09" PRId32 "s\n",
+ cape_timestamp.sec(), cape_timestamp.nsec());
+ if (data->uninitialized_gyro) {
+ LOG(DEBUG, "uninitialized gyro\n");
+ } else if (data->zeroing_gyro) {
+ LOG(DEBUG, "zeroing gyro\n");
+ } else if (data->bad_gyro) {
+ LOG(ERROR, "bad gyro\n");
+ } else if (data->old_gyro_reading) {
+ LOG(WARNING, "old/bad gyro reading\n");
+ } else {
+ LOG(DEBUG, "gyro reading is %f\n", gyro_translate(data->gyro_angle));
+ }
+
+ LOG(DEBUG, "digitals=%x\n", data->test.digitals);
+ for (int i = 0; i < 8; ++i) {
+ LOG(DEBUG, "enc[%d]=%" PRId32 "\n", i, data->test.encoders[i]);
+ LOG(DEBUG, "adc[%d]=%f (%" PRIx16 ")\n", i,
+ 3.3 * data->test.analogs[i] / 0x3FF, data->test.analogs[i]);
+ }
+}
+
+} // namespace
+
+int main() {
+ ::aos::Init(::bbb::SensorReader::kRelativePriority);
+ ::bbb::SensorReader reader("test");
+ while (true) {
+ PacketReceived(reader.ReadPacket(), reader.GetCapeTimestamp());
+ }
+ ::aos::Cleanup();
+}
diff --git a/bbb_cape/src/bbb/uart_reader.cc b/bbb_cape/src/bbb/uart_reader.cc
index a82faa1..c45c8ea 100644
--- a/bbb_cape/src/bbb/uart_reader.cc
+++ b/bbb_cape/src/bbb/uart_reader.cc
@@ -8,7 +8,6 @@
#include "aos/common/logging/logging.h"
// This is the code for receiving data from the cape via UART.
-// NOTE: In order for this to work, you must have the BB-UART1 device tree
// fragment active.
// `su -c "echo BB-UART1 > /sys/devices/bone_capemgr.*/slots"` works, but
// you have to do it every time. It is also possible to set it up to do that
@@ -16,9 +15,49 @@
namespace bbb {
namespace {
+
// TODO(brians): Determine this in some way that allows easy switching for
// testing with /dev/ttyUSB0 for example.
const char *device = "/dev/ttyO1";
+
+bool easy_access(const char *path) {
+ if (access(path, R_OK | W_OK) == 0) return true;
+ if (errno == EACCES || errno == ENOENT) return false;
+ LOG(FATAL, "access(%s, F_OK) failed with %d: %s\n", path, errno,
+ strerror(errno));
+}
+
+int open_device() {
+ if (easy_access(device)) {
+ LOG(INFO, "unexporting BB-UART1\n");
+ if (system("bash -c 'echo -$(cat /sys/devices/bone_capemgr.*/slots"
+ " | fgrep BB-UART1"
+ " | cut -c 2) > /sys/devices/bone_capemgr.*/slots'") ==
+ -1) {
+ LOG(FATAL, "system([disable OMAP UART]) failed with %d: %s\n", errno,
+ strerror(errno));
+ }
+ while (easy_access(device)) {
+ ::aos::time::SleepFor(::aos::time::Time::InSeconds(0.1));
+ }
+ }
+
+ LOG(INFO, "exporting BB-UART1\n");
+ // 2 strings to work around a VIM bug where the indenter locks up when they're
+ // combined as 1...
+ if (system("bash -c 'echo BB-UART1 > /sys/devices/bone_capemgr.*"
+ "/slots'") ==
+ -1) {
+ LOG(FATAL, "system([enable OMAP UART]) failed with %d: %s\n", errno,
+ strerror(errno));
+ }
+ while (!easy_access(device)) {
+ ::aos::time::SleepFor(::aos::time::Time::InSeconds(0.1));
+ }
+
+ return open(device, O_RDWR | O_NOCTTY | O_NONBLOCK);
+}
+
} // namespace
extern "C" {
@@ -32,11 +71,10 @@
} // extern "C"
UartReader::UartReader(int32_t baud_rate)
- : fd_(open(device, O_RDWR | O_NOCTTY | O_NONBLOCK)) {
+ : fd_(open_device()) {
if (fd_ < 0) {
- LOG(FATAL, "open(%s, O_RDWR | O_NOCTTY | O_NOBLOCK) failed with %d: %s."
- " Did you read my note in bbb/uart_reader.cc?\n",
+ LOG(FATAL, "open(%s, O_RDWR | O_NOCTTY | O_NOBLOCK) failed with %d: %s\n",
device, errno, strerror(errno));
}
diff --git a/bbb_cape/src/cape/bootloader.c b/bbb_cape/src/cape/bootloader.c
index 39f3080..5194832 100644
--- a/bbb_cape/src/cape/bootloader.c
+++ b/bbb_cape/src/cape/bootloader.c
@@ -74,8 +74,8 @@
while (!(SYSCFG->CMPCR & SYSCFG_CMPCR_READY)) {} // wait for it to be ready
if (GPIOC->IDR & (1 << 2)) {
- bootloader_start();
- } else {
jump_to_main();
+ } else {
+ bootloader_start();
}
}
diff --git a/bbb_cape/src/cape/bootloader_impl.c b/bbb_cape/src/cape/bootloader_impl.c
index 15d28b1..2cd287d 100644
--- a/bbb_cape/src/cape/bootloader_impl.c
+++ b/bbb_cape/src/cape/bootloader_impl.c
@@ -6,6 +6,7 @@
#include "cape/uart_common.h"
#include "cape/crc.h"
#include "cape/util.h"
+#include "cape/led.h"
// The protocol is pretty simple. Basically, when the bootloader is started, it
// expects repeated "packets" of data to write. It starts at MAIN_FLASH_START,
@@ -37,6 +38,11 @@
}
__attribute__((noreturn)) void bootloader_start(void) {
+ led_set(LED_ERR, 1);
+ led_set(LED_DB, 1);
+ led_set(LED_Z, 1);
+ led_set(LED_HB, 0);
+
crc_init();
// Unlock the flash so we can program it.
@@ -80,6 +86,7 @@
error = 1;
}
} else { // successfully received a byte
+ led_set(LED_HB, bytes_received & 1);
if (error == 0) {
buffer[bytes_received++] = (uint8_t)received;
if (bytes_received == sizeof(buffer)) {
diff --git a/bbb_cape/src/cape/fill_packet.c b/bbb_cape/src/cape/fill_packet.c
index 012b7e3..29ea095 100644
--- a/bbb_cape/src/cape/fill_packet.c
+++ b/bbb_cape/src/cape/fill_packet.c
@@ -25,9 +25,6 @@
static uint8_t buffer2[DATA_STRUCT_SEND_SIZE] __attribute__((aligned(4)));
static uint32_t flash_checksum;
-// These aren't really integers; they're (4-byte) variables whose addresses mark
-// various locations.
-extern uint8_t __etext, __data_start__, __data_end__;
static inline void do_fill_packet(struct DataStruct *packet) {
static uint64_t timestamp = 0;
@@ -82,9 +79,9 @@
encoder_init();
digital_init();
- uint8_t *flash_end = &__etext + (&__data_start__ - &__data_end__) + 8;
- flash_checksum = crc_calculate((void *)MAIN_FLASH_START,
- (size_t)(flash_end - MAIN_FLASH_START) / 4);
+ flash_checksum =
+ crc_calculate((void *)MAIN_FLASH_START,
+ (size_t)(MAIN_FLASH_END - MAIN_FLASH_START) / 4);
led_set(LED_ERR, 0);
gyro_init();
diff --git a/bbb_cape/src/cape/robot_comp.c b/bbb_cape/src/cape/robot_comp.c
index bf417e9..455a1c0 100644
--- a/bbb_cape/src/cape/robot_comp.c
+++ b/bbb_cape/src/cape/robot_comp.c
@@ -17,7 +17,8 @@
}
void robot_init(void) {
- gpio_setup_alt(GPIOB, 11, 3);
+ // Digital input 6.
+ gpio_setup_alt(GPIOB, 9, 3);
RCC->APB2ENR |= RCC_APB2ENR_TIM11EN;
TIM11->CR1 = TIM_CR1_URS;
TIM11->SMCR = 5 << 4 /* TI1 */ |
diff --git a/bbb_cape/src/flasher/stm32_flasher.cc b/bbb_cape/src/flasher/stm32_flasher.cc
index 0f081f5..48887d2 100644
--- a/bbb_cape/src/flasher/stm32_flasher.cc
+++ b/bbb_cape/src/flasher/stm32_flasher.cc
@@ -20,23 +20,27 @@
#include "bbb/gpo.h"
+namespace {
+
+void Reset(bool into_bootloader) {
+ ::bbb::Gpo reset(2, 5, true);
+ ::bbb::Gpo bootloader(2, 2, into_bootloader);
+ static constexpr ::aos::time::Time kWaitTime =
+ ::aos::time::Time::InSeconds(0.1);
+ reset.Set(false);
+ ::aos::time::SleepFor(kWaitTime);
+ reset.Set(true);
+ ::aos::time::SleepFor(kWaitTime);
+}
+
+} // namespace
+
int main(int argc, char **argv) {
::aos::logging::Init();
::aos::logging::AddImplementation(
new ::aos::logging::StreamLogImplementation(stdout));
- {
- ::bbb::Gpo reset(2, 5, true);
- ::bbb::Gpo bootloader(2, 2, false);
- static constexpr ::aos::time::Time kWaitTime =
- ::aos::time::Time::InSeconds(0.1);
- reset.Set(false);
- ::aos::time::SleepFor(kWaitTime);
- bootloader.Set(false);
- ::aos::time::SleepFor(kWaitTime);
- reset.Set(true);
- ::aos::time::SleepFor(kWaitTime);
- }
+ Reset(true);
if (argc < 2) {
fputs("Need an argument saying which target to download.\n", stderr);
@@ -170,7 +174,7 @@
// Read all of the 0xFFs that the parser inserts to pad the data out.
{
uint8_t garbage[1024];
- uint32_t length = start_address;
+ size_t length = start_address;
while (length > 0) {
uint32_t read = ::std::min(sizeof(garbage), length);
if (parser->read(p_st, garbage, &read) != PARSER_ERR_OK) {
@@ -183,7 +187,7 @@
uint32_t kFlashStart = 0x08000000;
uint8_t buffer[256]; // 256 is the biggest size supported
- uint32_t completed = 0;
+ size_t completed = 0;
while (completed < size) {
uint32_t address = start_address + completed + kFlashStart;
uint32_t length = ::std::min(size - completed, sizeof(buffer));
@@ -215,7 +219,7 @@
address, address + length);
}
completed += length;
- printf("\rWrote and verified 0x%08x/0x%08x",
+ printf("\rWrote and verified 0x%08zx/0x%08x",
completed, size);
fflush(stdout);
}
@@ -223,6 +227,7 @@
if (init_bl_exit(stm, serial, NULL /* GPIO sequence */)) {
LOG(INFO, "all done\n");
+ Reset(false);
} else {
LOG(FATAL, "init_bl_exit failed\n");
}
diff --git a/frc971/constants.cc b/frc971/constants.cc
index ce664d0..08d0355 100755
--- a/frc971/constants.cc
+++ b/frc971/constants.cc
@@ -70,14 +70,17 @@
shooter_zeroing_off_speed,
shooter_zeroing_speed,
position,
- 0.5,
- 0.1,
- 0.0,
- 1.57,
- {0.0, 2.05, {-0.1, 0.05}, {1.0, 1.1}, {2.0, 2.1}},
- {0.0, 2.05, {-0.1, 0.05}, {1.0, 1.1}, {2.0, 2.1}},
- 0.02, // claw_unimportant_epsilon
- 50505.05, // start_fine_tune_pos
+ {0.5,
+ 0.1,
+ 0.1,
+ 0.0,
+ 1.57,
+ {0.0, 2.05, 0.02, 2.02, {-0.1, 0.05}, {1.0, 1.1}, {2.0, 2.1}},
+ {0.0, 2.05, 0.02, 2.02, {-0.1, 0.05}, {1.0, 1.1}, {2.0, 2.1}},
+ 0.01, // claw_unimportant_epsilon
+ 0.9, // start_fine_tune_pos
+ 4.0,
+ }
};
break;
case kPracticeTeamNumber:
@@ -100,14 +103,17 @@
shooter_zeroing_off_speed,
shooter_zeroing_speed,
position,
- 0.5,
- 0.1,
- 0.0,
- 1.57,
- {0.0, 2.05, {-0.1, 0.05}, {1.0, 1.1}, {2.0, 2.1}},
- {0.0, 2.05, {-0.1, 0.05}, {1.0, 1.1}, {2.0, 2.1}},
- 0.02, // claw_unimportant_epsilon
- 50505.05, //start_fine_tune_pos
+ {0.5,
+ 0.2,
+ 0.1,
+ 0.0,
+ 1.57,
+ {0.0, 2.05, 0.02, 2.02, {-0.1, 0.05}, {1.0, 1.1}, {2.0, 2.1}},
+ {0.0, 2.05, 0.02, 2.02, {-0.1, 0.05}, {1.0, 1.1}, {2.0, 2.1}},
+ 0.01, // claw_unimportant_epsilon
+ 0.9, // start_fine_tune_pos
+ 4.0,
+ }
};
break;
default:
diff --git a/frc971/constants.h b/frc971/constants.h
index 01648f5..a5b7fb1 100755
--- a/frc971/constants.h
+++ b/frc971/constants.h
@@ -45,7 +45,7 @@
::std::function<StateFeedbackLoop<2, 2, 2>()> make_v_drivetrain_loop;
::std::function<StateFeedbackLoop<4, 2, 2>()> make_drivetrain_loop;
-
+
struct ShooterLimits {
double lower_limit;
double upper_limit;
@@ -55,7 +55,7 @@
};
ShooterLimits shooter;
-
+
double shooter_voltage;
double shooter_total_length;
double shooter_hall_effect_start_position;
@@ -63,33 +63,39 @@
double shooter_zeroing_speed;
double position;
- double claw_zeroing_off_speed;
- double claw_zeroing_speed;
+ struct Claws {
+ double claw_zeroing_off_speed;
+ double claw_zeroing_speed;
+ double claw_zeroing_separation;
- // claw seperation that would be considered a collision
- double claw_min_seperation;
- double claw_max_seperation;
+ // claw seperation that would be considered a collision
+ double claw_min_seperation;
+ double claw_max_seperation;
- // Three hall effects are known as front, calib and back
- struct AnglePair {
- double lower_angle;
- double upper_angle;
+ // Three hall effects are known as front, calib and back
+ struct AnglePair {
+ double lower_angle;
+ double upper_angle;
+ };
+
+ 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;
};
-
- struct Claw {
- 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;
+ Claws claw;
};
// Creates (once) a Values instance and returns a reference to it.
diff --git a/frc971/control_loops/claw/bottom_claw_motor_plant.cc b/frc971/control_loops/claw/bottom_claw_motor_plant.cc
deleted file mode 100644
index a0eb131..0000000
--- a/frc971/control_loops/claw/bottom_claw_motor_plant.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-#include "frc971/control_loops/claw/bottom_claw_motor_plant.h"
-
-#include <vector>
-
-#include "frc971/control_loops/state_feedback_loop.h"
-
-namespace frc971 {
-namespace control_loops {
-
-StateFeedbackPlantCoefficients<3, 1, 1> MakeBottomClawPlantCoefficients() {
- Eigen::Matrix<double, 3, 3> A;
- A << 1.0, 0.00904786878843, 0.000326582411818, 0.0, 0.815818233346, 0.0631746179893, 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> MakeBottomClawController() {
- Eigen::Matrix<double, 3, 1> L;
- L << 1.81581823335, 78.6334534274, 142.868137351;
- Eigen::Matrix<double, 1, 3> K;
- K << 92.6004807973, 4.38063492858, 1.11581823335;
- return StateFeedbackController<3, 1, 1>(L, K, MakeBottomClawPlantCoefficients());
-}
-
-StateFeedbackPlant<3, 1, 1> MakeBottomClawPlant() {
- ::std::vector<StateFeedbackPlantCoefficients<3, 1, 1> *> plants(1);
- plants[0] = new StateFeedbackPlantCoefficients<3, 1, 1>(MakeBottomClawPlantCoefficients());
- return StateFeedbackPlant<3, 1, 1>(plants);
-}
-
-StateFeedbackLoop<3, 1, 1> MakeBottomClawLoop() {
- ::std::vector<StateFeedbackController<3, 1, 1> *> controllers(1);
- controllers[0] = new StateFeedbackController<3, 1, 1>(MakeBottomClawController());
- return StateFeedbackLoop<3, 1, 1>(controllers);
-}
-
-} // namespace control_loops
-} // namespace frc971
diff --git a/frc971/control_loops/claw/bottom_claw_motor_plant.h b/frc971/control_loops/claw/bottom_claw_motor_plant.h
deleted file mode 100644
index fc905ca..0000000
--- a/frc971/control_loops/claw/bottom_claw_motor_plant.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef FRC971_CONTROL_LOOPS_CLAW_BOTTOM_CLAW_MOTOR_PLANT_H_
-#define FRC971_CONTROL_LOOPS_CLAW_BOTTOM_CLAW_MOTOR_PLANT_H_
-
-#include "frc971/control_loops/state_feedback_loop.h"
-
-namespace frc971 {
-namespace control_loops {
-
-StateFeedbackPlantCoefficients<3, 1, 1> MakeBottomClawPlantCoefficients();
-
-StateFeedbackController<3, 1, 1> MakeBottomClawController();
-
-StateFeedbackPlant<3, 1, 1> MakeBottomClawPlant();
-
-StateFeedbackLoop<3, 1, 1> MakeBottomClawLoop();
-
-} // namespace control_loops
-} // namespace frc971
-
-#endif // FRC971_CONTROL_LOOPS_CLAW_BOTTOM_CLAW_MOTOR_PLANT_H_
diff --git a/frc971/control_loops/claw/claw.cc b/frc971/control_loops/claw/claw.cc
index e3e7e6d..abff616 100755
--- a/frc971/control_loops/claw/claw.cc
+++ b/frc971/control_loops/claw/claw.cc
@@ -8,8 +8,7 @@
#include "aos/common/logging/logging.h"
#include "frc971/constants.h"
-#include "frc971/control_loops/claw/top_claw_motor_plant.h"
-#include "frc971/control_loops/claw/bottom_claw_motor_plant.h"
+#include "frc971/control_loops/claw/claw_motor_plant.h"
// Zeroing plan.
// There are 2 types of zeros. Enabled and disabled ones.
@@ -46,112 +45,149 @@
namespace frc971 {
namespace control_loops {
-void ZeroedStateFeedbackLoop::CapU() {
- const double old_voltage = voltage_;
- voltage_ += U(0, 0);
-
- uncapped_voltage_ = voltage_;
-
- double limit = zeroing_state() != UNKNOWN_POSITION ? 12.0 : kZeroingMaxVoltage;
-
- // 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) {
- 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) {
- voltage_ += X_hat(2, 0) - (last_voltage_ - 2.0);
- LOG(DEBUG, "X_hat(2, 0) = %f\n", X_hat(2, 0));
+void ClawLimitedLoop::CapU() {
+ uncapped_average_voltage_ = U(0, 0) + U(1, 0) / 2.0;
+ if (is_zeroing_) {
+ const frc971::constants::Values &values = constants::GetValues();
+ if (uncapped_average_voltage_ > values.claw.max_zeroing_voltage) {
+ const double difference =
+ uncapped_average_voltage_ - values.claw.max_zeroing_voltage;
+ U(0, 0) -= difference;
+ U(1, 0) -= difference;
+ } else if (uncapped_average_voltage_ < -values.claw.max_zeroing_voltage) {
+ const double difference =
+ -uncapped_average_voltage_ - values.claw.max_zeroing_voltage;
+ U(0, 0) += difference;
+ U(1, 0) += difference;
+ }
}
- 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));
+ double max_value =
+ ::std::max(::std::abs(U(0, 0)), ::std::abs(U(1, 0) + U(0, 0)));
- last_voltage_ = voltage_;
+ if (max_value > 12.0) {
+ LOG(DEBUG, "Capping U because max is %f\n", max_value);
+ U = U * 12.0 / max_value;
+ LOG(DEBUG, "Capping U is now %f %f\n", U(0, 0), U(1, 0));
+ }
}
ClawMotor::ClawMotor(control_loops::ClawGroup *my_claw)
: aos::control_loops::ControlLoop<control_loops::ClawGroup>(my_claw),
has_top_claw_goal_(false),
top_claw_goal_(0.0),
- top_claw_(MakeTopClawLoop()),
+ top_claw_(this),
has_bottom_claw_goal_(false),
bottom_claw_goal_(0.0),
- bottom_claw_(MakeBottomClawLoop()),
+ bottom_claw_(this),
+ claw_(MakeClawLoop()),
was_enabled_(false),
- doing_calibration_fine_tune_(false) {}
+ doing_calibration_fine_tune_(false),
+ capped_goal_(false) {}
const int ZeroedStateFeedbackLoop::kZeroingMaxVoltage;
-bool ZeroedStateFeedbackLoop::GetPositionOfEdge(
- const constants::Values::Claw &claw_values, double *edge_encoder,
- double *edge_angle) {
+bool ZeroedStateFeedbackLoop::DoGetPositionOfEdge(
+ const constants::Values::Claws::AnglePair &angles, double *edge_encoder,
+ double *edge_angle, const HallEffectTracker &sensor,
+ const char *hall_effect_name) {
+ if (sensor.posedge_count_changed()) {
+ if (posedge_value_ < last_encoder()) {
+ *edge_angle = angles.upper_angle;
+ LOG(INFO, "%s Posedge upper of %s -> %f\n", name_,
+ hall_effect_name, *edge_angle);
+ } else {
+ *edge_angle = angles.lower_angle;
+ LOG(INFO, "%s Posedge lower of %s -> %f\n", name_,
+ hall_effect_name, *edge_angle);
+ }
+ *edge_encoder = posedge_value_;
+ return true;
+ }
+ if (sensor.negedge_count_changed()) {
+ if (negedge_value_ > last_encoder()) {
+ *edge_angle = angles.upper_angle;
+ LOG(INFO, "%s Negedge upper of %s -> %f\n", name_,
+ hall_effect_name, *edge_angle);
+ } else {
+ *edge_angle = angles.lower_angle;
+ LOG(INFO, "%s Negedge lower of %s -> %f\n", name_,
+ hall_effect_name, *edge_angle);
+ }
+ *edge_encoder = negedge_value_;
+ return true;
+ }
+ return false;
+}
+
+bool ZeroedStateFeedbackLoop::GetPositionOfEdge(
+ const constants::Values::Claws::Claw &claw_values, double *edge_encoder,
+ double *edge_angle) {
// TODO(austin): Validate that the hall effect edge makes sense.
// We must now be on the side of the edge that we expect to be, and the
// encoder must have been on either side of the edge before and after.
- if (front_hall_effect_posedge_count_changed()) {
- if (encoder() - last_encoder() < 0) {
- *edge_angle = claw_values.front.upper_angle;
- } else {
- *edge_angle = claw_values.front.lower_angle;
- }
- *edge_encoder = posedge_value_;
+ // TODO(austin): Compute the last off range min and max and compare the edge
+ // value to the middle of the range. This will be quite a bit more reliable.
+
+ if (DoGetPositionOfEdge(claw_values.front, edge_encoder, edge_angle,
+ front_, "front")) {
return true;
}
- if (front_hall_effect_negedge_count_changed()) {
- if (encoder() - last_encoder() > 0) {
- *edge_angle = claw_values.front.upper_angle;
- } else {
- *edge_angle = claw_values.front.lower_angle;
- }
- *edge_encoder = negedge_value_;
+ if (DoGetPositionOfEdge(claw_values.calibration, edge_encoder, edge_angle,
+ calibration_, "calibration")) {
return true;
}
- if (calibration_hall_effect_posedge_count_changed()) {
- if (encoder() - last_encoder() < 0) {
- *edge_angle = claw_values.calibration.upper_angle;
- } else {
- *edge_angle = claw_values.calibration.lower_angle;
- }
- *edge_encoder = posedge_value_;
- return true;
- }
- if (calibration_hall_effect_negedge_count_changed()) {
- if (encoder() - last_encoder() > 0) {
- *edge_angle = claw_values.calibration.upper_angle;
- } else {
- *edge_angle = claw_values.calibration.lower_angle;
- }
- *edge_encoder = negedge_value_;
- return true;
- }
- if (back_hall_effect_posedge_count_changed()) {
- if (encoder() - last_encoder() < 0) {
- *edge_angle = claw_values.back.upper_angle;
- } else {
- *edge_angle = claw_values.back.lower_angle;
- }
- *edge_encoder = posedge_value_;
- return true;
- }
- if (back_hall_effect_negedge_count_changed()) {
- if (encoder() - last_encoder() > 0) {
- *edge_angle = claw_values.back.upper_angle;
- } else {
- *edge_angle = claw_values.back.lower_angle;
- }
- *edge_encoder = negedge_value_;
+ if (DoGetPositionOfEdge(claw_values.back, edge_encoder, edge_angle,
+ back_, "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);
+}
+
+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);
+}
+
+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) {
+ Y_(1, 0) += doffset;
+ X_hat(1, 0) += doffset;
+ LOG(INFO, "Changing top offset by %f\n", doffset);
+}
+void ClawLimitedLoop::ChangeBottomOffset(double doffset) {
+ Y_(0, 0) += doffset;
+ X_hat(0, 0) += doffset;
+ X_hat(1, 0) -= doffset;
+ LOG(INFO, "Changing bottom offset by %f\n", doffset);
+}
+
// Positive angle is up, and positive power is up.
void ClawMotor::RunIteration(const control_loops::ClawGroup::Goal *goal,
const control_loops::ClawGroup::Position *position,
@@ -168,15 +204,10 @@
}
// TODO(austin): Handle the disabled state and the disabled -> enabled
- // transition in all of these states.
- // TODO(austin): Handle zeroing while disabled.
+ // transition in all of these states.
+ // TODO(austin): Handle zeroing while disabled correctly (only use a single
+ // edge and direction when zeroing.)
- // TODO(austin): Save all the counters so we know when something actually
- // happens.
- // TODO(austin): Helpers to find the position of the claw on an edge.
-
- // TODO(austin): This may not be necesary because of the ControlLoop class.
- ::aos::robot_state.FetchLatest();
if (::aos::robot_state.get() == nullptr) {
return;
}
@@ -184,22 +215,41 @@
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_ = position->top.position;
+ top_claw_goal_ = top_claw_.absolute_position();
+ initial_seperation_ =
+ top_claw_.absolute_position() - bottom_claw_.absolute_position();
}
if (!has_bottom_claw_goal_) {
has_bottom_claw_goal_ = true;
- bottom_claw_goal_ = position->bottom.position;
+ bottom_claw_goal_ = bottom_claw_.absolute_position();
+ initial_seperation_ =
+ top_claw_.absolute_position() - bottom_claw_.absolute_position();
}
+ LOG(DEBUG, "Claw position is (top: %f bottom: %f\n",
+ top_claw_.absolute_position(), bottom_claw_.absolute_position());
}
bool autonomous = ::aos::robot_state->autonomous;
bool enabled = ::aos::robot_state->enabled;
+ enum CalibrationMode {
+ READY,
+ FINE_TUNE,
+ UNKNOWN_LOCATION
+ };
+
+ CalibrationMode mode;
+
if ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
(autonomous &&
@@ -213,6 +263,11 @@
// Limit the goals here.
bottom_claw_goal_ = goal->bottom_angle;
top_claw_goal_ = goal->bottom_angle + goal->seperation_angle;
+ has_bottom_claw_goal_ = true;
+ has_top_claw_goal_ = true;
+ doing_calibration_fine_tune_ = false;
+
+ mode = READY;
} else if (top_claw_.zeroing_state() !=
ZeroedStateFeedbackLoop::UNKNOWN_POSITION &&
bottom_claw_.zeroing_state() !=
@@ -220,79 +275,105 @@
// Time to fine tune the zero.
// Limit the goals here.
if (bottom_claw_.zeroing_state() != ZeroedStateFeedbackLoop::CALIBRATED) {
- // always get the bottom claw to calibrated first
- if (!doing_calibration_fine_tune_) {
- if (position->bottom.position > values.start_fine_tune_pos -
- values.claw_unimportant_epsilon &&
- position->bottom.position < values.start_fine_tune_pos +
- values.claw_unimportant_epsilon) {
- doing_calibration_fine_tune_ = true;
- bottom_claw_goal_ += values.claw_zeroing_off_speed * dt;
- } else {
- // send bottom to zeroing start
- bottom_claw_goal_ = values.start_fine_tune_pos;
- }
- } else {
- if (position->bottom.front_hall_effect ||
- position->bottom.back_hall_effect ||
- position->top.front_hall_effect ||
- position->top.back_hall_effect) {
- // this should not happen, but now we know it won't
- doing_calibration_fine_tune_ = false;
- bottom_claw_goal_ = values.start_fine_tune_pos;
- }
- if (position->bottom.calibration_hall_effect) {
- // do calibration
- bottom_claw_.SetCalibration(
- position->bottom.posedge_value,
- values.lower_claw.calibration.lower_angle);
- bottom_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
- // calinrated so we are done fine tuning bottom
- doing_calibration_fine_tune_ = false;
- }
- }
- // now set the top claw to track
- top_claw_goal_ = bottom_claw_goal_ + goal->seperation_angle;
+ // 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;
+ LOG(DEBUG, "Ready to fine tune the bottom\n");
+ } 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");
+ }
+ } else {
+ bottom_claw_goal_ += values.claw.claw_zeroing_speed * dt;
+ 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;
+ LOG(DEBUG, "Found a limit, starting over.\n");
+ }
+
+ if (bottom_claw_.calibration().value()) {
+ if (bottom_claw_.calibration().posedge_count_changed() &&
+ position) {
+ // do calibration
+ bottom_claw_.SetCalibration(
+ position->bottom.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 {
+ doing_calibration_fine_tune_ = false;
+ bottom_claw_goal_ = values.claw.start_fine_tune_pos;
+ }
+ } 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 (position->top.position > values.start_fine_tune_pos -
- values.claw_unimportant_epsilon &&
- position->top.position < values.start_fine_tune_pos +
- values.claw_unimportant_epsilon) {
- doing_calibration_fine_tune_ = true;
- top_claw_goal_ += values.claw_zeroing_off_speed * dt;
- } else {
- // send top to zeroing start
- top_claw_goal_ = values.start_fine_tune_pos;
- }
- } else {
- if (position->top.front_hall_effect ||
- position->top.back_hall_effect ||
- position->top.front_hall_effect ||
- position->top.back_hall_effect) {
- // this should not happen, but now we know it won't
- doing_calibration_fine_tune_ = false;
- top_claw_goal_ = values.start_fine_tune_pos;
- }
- if (position->top.calibration_hall_effect) {
- // do calibration
- top_claw_.SetCalibration(
- position->top.posedge_value,
- values.upper_claw.calibration.lower_angle);
- top_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
- // calinrated so we are done fine tuning top
- doing_calibration_fine_tune_ = false;
- }
- }
- // now set the bottom claw to track
- bottom_claw_goal_ = top_claw_goal_ - goal->seperation_angle;
+ // 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;
+ LOG(DEBUG, "Ready to fine tune the top\n");
+ } 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");
+ }
+ } else {
+ top_claw_goal_ += values.claw.claw_zeroing_speed * dt;
+ 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;
+ LOG(DEBUG, "Found a limit, starting over.\n");
+ }
+ if (top_claw_.calibration().value()) {
+ if (top_claw_.calibration().posedge_count_changed() &&
+ position) {
+ // do calibration
+ top_claw_.SetCalibration(
+ position->top.posedge_value,
+ values.claw.upper_claw.calibration.lower_angle);
+ top_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
+ // calinrated so we are done fine tuning top
+ doing_calibration_fine_tune_ = false;
+ LOG(DEBUG, "Calibrated the top correctly!\n");
+ } else {
+ doing_calibration_fine_tune_ = false;
+ top_claw_goal_ = values.claw.start_fine_tune_pos;
+ }
+ }
+ }
+ // now set the bottom claw to track
+ bottom_claw_goal_ = top_claw_goal_ - values.claw.claw_zeroing_separation;
}
+ mode = FINE_TUNE;
} else {
+ doing_calibration_fine_tune_ = false;
if (!was_enabled_ && enabled) {
if (position) {
top_claw_goal_ = position->top.position;
bottom_claw_goal_ = position->bottom.position;
+ initial_seperation_ =
+ position->top.position - position->bottom.position;
} else {
has_top_claw_goal_ = false;
has_bottom_claw_goal_ = false;
@@ -300,74 +381,96 @@
}
// TODO(austin): Limit the goals here.
- // Need to prevent windup, limit voltage, deal with windup on only 1 claw,
- // ...
- if (top_claw_.zeroing_state() ==
- ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
- }
- if (bottom_claw_.zeroing_state() ==
- ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
- }
- if (bottom_claw_.zeroing_state() !=
- ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
+ 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_zeroing_off_speed * dt;
- bottom_claw_goal_ += values.claw_zeroing_off_speed * dt;
+ top_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
+ bottom_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
// TODO(austin): Goal velocity too!
+ 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_zeroing_off_speed * dt;
- bottom_claw_goal_ -= values.claw_zeroing_off_speed * dt;
+ top_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
+ bottom_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
// TODO(austin): Goal velocity too!
+ LOG(DEBUG, "Both are unknown.\n");
}
}
if (enabled) {
top_claw_.SetCalibrationOnEdge(
- values.upper_claw, ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
+ values.claw.upper_claw, ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
bottom_claw_.SetCalibrationOnEdge(
- values.lower_claw, ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
+ values.claw.lower_claw, ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
} else {
top_claw_.SetCalibrationOnEdge(
- values.upper_claw, ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
+ values.claw.upper_claw, ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
bottom_claw_.SetCalibrationOnEdge(
- values.lower_claw, ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
+ values.claw.lower_claw, ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
}
+ mode = UNKNOWN_LOCATION;
}
- // TODO(austin): Handle disabled.
+ // TODO(austin): Handle disabled properly everwhere... Restart and all that
+ // jazz.
- // TODO(austin): ...
if (has_top_claw_goal_ && has_bottom_claw_goal_) {
- top_claw_.R << top_claw_goal_, 0.0, 0.0;
- bottom_claw_.R << bottom_claw_goal_, 0.0, 0.0;
+ claw_.R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_, 0, 0;
+ double separation = -971;
+ if (position != nullptr) {
+ separation = position->top.position - position->bottom.position;
+ }
+ LOG(DEBUG, "Goal is %f (bottom) %f, separation is %f\n", claw_.R(0, 0),
+ claw_.R(1, 0), separation);
- top_claw_.Update(output == nullptr);
- bottom_claw_.Update(output == nullptr);
+ // Only cap power when one of the halves of the claw is unknown.
+ claw_.set_is_zeroing(mode == UNKNOWN_LOCATION);
+ claw_.Update(output == nullptr);
} else {
- top_claw_.ZeroPower();
- bottom_claw_.ZeroPower();
+ claw_.Update(true);
}
- if (position) {
- //LOG(DEBUG, "pos: %f hall: %s absolute: %f\n", position->top_position,
- //position->top_calibration_hall_effect ? "true" : "false",
- //zeroed_joint_.absolute_position());
+ capped_goal_ = false;
+ switch (mode) {
+ case READY:
+ break;
+ case FINE_TUNE:
+ break;
+ case UNKNOWN_LOCATION: {
+ if (claw_.uncapped_average_voltage() > values.claw.max_zeroing_voltage) {
+ double dx = (claw_.uncapped_average_voltage() -
+ values.claw.max_zeroing_voltage) /
+ claw_.K(0, 0);
+ bottom_claw_goal_ -= dx;
+ top_claw_goal_ -= dx;
+ capped_goal_ = true;
+ LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
+ } else if (claw_.uncapped_average_voltage() <
+ -values.claw.max_zeroing_voltage) {
+ double dx = (claw_.uncapped_average_voltage() +
+ values.claw.max_zeroing_voltage) /
+ claw_.K(0, 0);
+ bottom_claw_goal_ -= dx;
+ top_claw_goal_ -= dx;
+ capped_goal_ = true;
+ LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
+ }
+ } break;
}
if (output) {
- output->top_claw_voltage = top_claw_.voltage();
- output->bottom_claw_voltage = bottom_claw_.voltage();
+ output->top_claw_voltage = claw_.U(1, 0) + claw_.U(0, 0);
+ output->bottom_claw_voltage = claw_.U(0, 0);
}
status->done = false;
- //::std::abs(zeroed_joint_.absolute_position() - goal->bottom_angle -
- //goal->seperation_angle) < 0.004;
was_enabled_ = ::aos::robot_state->enabled;
}
diff --git a/frc971/control_loops/claw/claw.gyp b/frc971/control_loops/claw/claw.gyp
index c541dad..52712d5 100644
--- a/frc971/control_loops/claw/claw.gyp
+++ b/frc971/control_loops/claw/claw.gyp
@@ -9,11 +9,11 @@
},
'dependencies': [
'<(AOS)/common/common.gyp:control_loop_queues',
- '<(AOS)/common/common.gyp:queues',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:queues',
],
'export_dependent_settings': [
'<(AOS)/common/common.gyp:control_loop_queues',
- '<(AOS)/common/common.gyp:queues',
+ '<(DEPTH)/frc971/control_loops/control_loops.gyp:queues',
],
'includes': ['../../../aos/build/queues.gypi'],
},
@@ -22,10 +22,7 @@
'type': 'static_library',
'sources': [
'claw.cc',
- 'top_claw_motor_plant.cc',
- 'bottom_claw_motor_plant.cc',
- 'unaugmented_top_claw_motor_plant.cc',
- 'unaugmented_bottom_claw_motor_plant.cc',
+ 'claw_motor_plant.cc',
],
'dependencies': [
'claw_loops',
diff --git a/frc971/control_loops/claw/claw.h b/frc971/control_loops/claw/claw.h
index c045d6f..9ff406e 100755
--- a/frc971/control_loops/claw/claw.h
+++ b/frc971/control_loops/claw/claw.h
@@ -7,8 +7,8 @@
#include "frc971/constants.h"
#include "frc971/control_loops/state_feedback_loop.h"
#include "frc971/control_loops/claw/claw.q.h"
-#include "frc971/control_loops/claw/top_claw_motor_plant.h"
-#include "frc971/control_loops/claw/bottom_claw_motor_plant.h"
+#include "frc971/control_loops/claw/claw_motor_plant.h"
+#include "frc971/control_loops/hall_effect_tracker.h"
namespace frc971 {
namespace control_loops {
@@ -22,50 +22,46 @@
// 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)
+ : StateFeedbackLoop<4, 2, 2>(loop),
+ uncapped_average_voltage_(0.0),
+ is_zeroing_(true) {}
+ virtual void CapU();
+
+ void set_is_zeroing(bool is_zeroing) { is_zeroing_ = is_zeroing; }
+
+ 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_;
+};
+
+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 StateFeedbackLoop<3, 1, 1> {
+class ZeroedStateFeedbackLoop {
public:
- ZeroedStateFeedbackLoop(StateFeedbackLoop<3, 1, 1> loop)
- : StateFeedbackLoop<3, 1, 1>(loop),
- voltage_(0.0),
- last_voltage_(0.0),
- uncapped_voltage_(0.0),
- offset_(0.0),
- front_hall_effect_posedge_count_(0.0),
- previous_front_hall_effect_posedge_count_(0.0),
- front_hall_effect_negedge_count_(0.0),
- previous_front_hall_effect_negedge_count_(0.0),
- calibration_hall_effect_posedge_count_(0.0),
- previous_calibration_hall_effect_posedge_count_(0.0),
- calibration_hall_effect_negedge_count_(0.0),
- previous_calibration_hall_effect_negedge_count_(0.0),
- back_hall_effect_posedge_count_(0.0),
- previous_back_hall_effect_posedge_count_(0.0),
- back_hall_effect_negedge_count_(0.0),
- previous_back_hall_effect_negedge_count_(0.0),
+ ZeroedStateFeedbackLoop(const char *name, ClawMotor *motor)
+ : offset_(0.0),
+ name_(name),
+ motor_(motor),
zeroing_state_(UNKNOWN_POSITION),
posedge_value_(0.0),
negedge_value_(0.0),
encoder_(0.0),
- last_encoder_(0.0){}
+ last_encoder_(0.0) {}
const static int kZeroingMaxVoltage = 5;
- // 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; }
-
enum JointZeroingState {
// We don't know where the joint is at all.
UNKNOWN_POSITION,
@@ -83,13 +79,76 @@
}
JointZeroingState zeroing_state() const { return zeroing_state_; }
- // Sets the calibration offset given the absolute angle and the corrisponding
- // encoder value.
- void SetCalibration(double edge_encoder, double edge_angle) {
- offset_ = edge_angle - edge_encoder;
+ void SetPositionValues(const HalfClawPosition &claw) {
+ front_.Update(claw.front);
+ calibration_.Update(claw.calibration);
+ back_.Update(claw.back);
+
+ posedge_value_ = claw.posedge_value;
+ negedge_value_ = claw.negedge_value;
+ last_encoder_ = encoder_;
+ encoder_ = claw.position;
}
- bool SetCalibrationOnEdge(const constants::Values::Claw &claw_values,
+ 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();
+ }
+
+ 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);
+
+#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 posedge_value_;
+ double negedge_value_;
+ double encoder_;
+ double last_encoder_;
+
+ 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 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 corrisponding
+ // encoder value.
+ void SetCalibration(double edge_encoder, double edge_angle);
+
+ bool SetCalibrationOnEdge(const constants::Values::Claws::Claw &claw_values,
JointZeroingState zeroing_state) {
double edge_encoder;
double edge_angle;
@@ -101,91 +160,29 @@
}
return false;
}
-
- void SetPositionValues(const HalfClawPosition &claw) {
- set_front_hall_effect_posedge_count(claw.front_hall_effect_posedge_count);
- set_front_hall_effect_negedge_count(claw.front_hall_effect_negedge_count);
- set_calibration_hall_effect_posedge_count(
- claw.calibration_hall_effect_posedge_count);
- set_calibration_hall_effect_negedge_count(
- claw.calibration_hall_effect_negedge_count);
- set_back_hall_effect_posedge_count(claw.back_hall_effect_posedge_count);
- set_back_hall_effect_negedge_count(claw.back_hall_effect_negedge_count);
-
- posedge_value_ = claw.posedge_value;
- negedge_value_ = claw.negedge_value;
- Eigen::Matrix<double, 1, 1> Y;
- Y << claw.position;
- Correct(Y);
- }
-
-#define COUNT_SETTER_GETTER(variable) \
- void set_##variable(int32_t value) { \
- previous_##variable##_ = variable##_; \
- variable##_ = value; \
- } \
- int32_t variable() const { return variable##_; } \
- bool variable##_changed() const { \
- return previous_##variable##_ != variable##_; \
- }
-
- // TODO(austin): Pull all this out of the new sub structure.
- COUNT_SETTER_GETTER(front_hall_effect_posedge_count);
- COUNT_SETTER_GETTER(front_hall_effect_negedge_count);
- COUNT_SETTER_GETTER(calibration_hall_effect_posedge_count);
- COUNT_SETTER_GETTER(calibration_hall_effect_negedge_count);
- COUNT_SETTER_GETTER(back_hall_effect_posedge_count);
- COUNT_SETTER_GETTER(back_hall_effect_negedge_count);
-
- bool any_hall_effect_changed() const {
- return front_hall_effect_posedge_count_changed() ||
- front_hall_effect_negedge_count_changed() ||
- calibration_hall_effect_posedge_count_changed() ||
- calibration_hall_effect_negedge_count_changed() ||
- back_hall_effect_posedge_count_changed() ||
- back_hall_effect_negedge_count_changed();
- }
-
- double position() const { return X_hat(0, 0); }
- double encoder() const { return encoder_; }
- double last_encoder() const { return last_encoder_; }
-
- // 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::Claw &claw,
- double *edge_encoder, double *edge_angle);
-
-#undef COUNT_SETTER_GETTER
-
- private:
- // The accumulated voltage to apply to the motor.
- double voltage_;
- double last_voltage_;
- double uncapped_voltage_;
- double offset_;
-
- double previous_position_;
-
- int32_t front_hall_effect_posedge_count_;
- int32_t previous_front_hall_effect_posedge_count_;
- int32_t front_hall_effect_negedge_count_;
- int32_t previous_front_hall_effect_negedge_count_;
- int32_t calibration_hall_effect_posedge_count_;
- int32_t previous_calibration_hall_effect_posedge_count_;
- int32_t calibration_hall_effect_negedge_count_;
- int32_t previous_calibration_hall_effect_negedge_count_;
- int32_t back_hall_effect_posedge_count_;
- int32_t previous_back_hall_effect_posedge_count_;
- int32_t back_hall_effect_negedge_count_;
- int32_t previous_back_hall_effect_negedge_count_;
-
- JointZeroingState zeroing_state_;
- double posedge_value_;
- double negedge_value_;
- double encoder_;
- double last_encoder_;
};
+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 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;
+ }
+};
class ClawMotor
: public aos::control_loops::ControlLoop<control_loops::ClawGroup> {
@@ -194,16 +191,24 @@
&control_loops::claw_queue_group);
// True if the state machine is ready.
+ bool capped_goal() const { return capped_goal_; }
+
+ // True if the state machine is ready.
bool is_ready() const { return false; }
+ void ChangeTopOffset(double doffset);
+ void ChangeBottomOffset(double doffset);
+
protected:
virtual void RunIteration(const control_loops::ClawGroup::Goal *goal,
const control_loops::ClawGroup::Position *position,
control_loops::ClawGroup::Output *output,
::aos::control_loops::Status *status);
- double top_absolute_position() const { return top_claw_.position(); }
- double bottom_absolute_position() const { return bottom_claw_.position(); }
+ 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.
@@ -213,15 +218,24 @@
// The zeroed joint to use.
bool has_top_claw_goal_;
double top_claw_goal_;
- ZeroedStateFeedbackLoop top_claw_;
+ TopZeroedStateFeedbackLoop top_claw_;
bool has_bottom_claw_goal_;
double bottom_claw_goal_;
- ZeroedStateFeedbackLoop bottom_claw_;
+ BottomZeroedStateFeedbackLoop bottom_claw_;
+
+ // The claw loop.
+ ClawLimitedLoop claw_;
bool was_enabled_;
bool doing_calibration_fine_tune_;
+ // The initial seperation when disabled. Used as the safe seperation
+ // distance.
+ double initial_seperation_;
+
+ bool capped_goal_;
+
DISALLOW_COPY_AND_ASSIGN(ClawMotor);
};
diff --git a/frc971/control_loops/claw/claw.q b/frc971/control_loops/claw/claw.q
index 66e3036..f78dee6 100644
--- a/frc971/control_loops/claw/claw.q
+++ b/frc971/control_loops/claw/claw.q
@@ -1,28 +1,18 @@
package frc971.control_loops;
import "aos/common/control_loop/control_loops.q";
+import "frc971/control_loops/control_loops.q";
struct HalfClawPosition {
// The current position of this half of the claw.
double position;
- // The value of the front hall effect sensor.
- bool front_hall_effect;
- // The number of positive and negative edges that have been captured on the
- // front hall effect sensor.
- int32_t front_hall_effect_posedge_count;
- int32_t front_hall_effect_negedge_count;
- // The value of the calibration hall effect sensor.
- bool calibration_hall_effect;
- // The number of positive and negative edges that have been captured on the
- // calibration hall effect sensor.
- int32_t calibration_hall_effect_posedge_count;
- int32_t calibration_hall_effect_negedge_count;
- // The value of the back hall effect sensor.
- bool back_hall_effect;
- // The number of positive and negative edges that have been captured on the
- // back hall effect sensor.
- int32_t back_hall_effect_posedge_count;
- int32_t back_hall_effect_negedge_count;
+
+ // 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;
// The encoder value at the last posedge of any of the claw hall effect
// sensors (front, calibration, or back).
diff --git a/frc971/control_loops/claw/claw_lib_test.cc b/frc971/control_loops/claw/claw_lib_test.cc
index 30155f5..e903e79 100644
--- a/frc971/control_loops/claw/claw_lib_test.cc
+++ b/frc971/control_loops/claw/claw_lib_test.cc
@@ -2,14 +2,15 @@
#include <memory>
-#include "gtest/gtest.h"
+#include "aos/common/network/team_number.h"
#include "aos/common/queue.h"
#include "aos/common/queue_testutils.h"
#include "frc971/control_loops/claw/claw.q.h"
#include "frc971/control_loops/claw/claw.h"
#include "frc971/constants.h"
-#include "frc971/control_loops/claw/unaugmented_top_claw_motor_plant.h"
-#include "frc971/control_loops/claw/unaugmented_bottom_claw_motor_plant.h"
+#include "frc971/control_loops/claw/claw_motor_plant.h"
+
+#include "gtest/gtest.h"
using ::aos::time::Time;
@@ -25,6 +26,15 @@
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(971); }
+};
+
+::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 {
@@ -33,9 +43,7 @@
// wrist, which will be treated as 0 by the encoder.
ClawMotorSimulation(double initial_top_position,
double initial_bottom_position)
- : top_claw_plant_(new StateFeedbackPlant<2, 1, 1>(MakeRawTopClawPlant())),
- bottom_claw_plant_(
- new StateFeedbackPlant<2, 1, 1>(MakeRawBottomClawPlant())),
+ : claw_plant_(new StateFeedbackPlant<4, 2, 2>(MakeClawPlant())),
claw_queue_group(".frc971.control_loops.claw_queue_group", 0x9f1a99dd,
".frc971.control_loops.claw_queue_group.goal",
".frc971.control_loops.claw_queue_group.position",
@@ -48,6 +56,12 @@
double initial_bottom_position) {
LOG(INFO, "Reinitializing to {top: %f, bottom: %f}\n", initial_top_position,
initial_bottom_position);
+ claw_plant_->X(0, 0) = initial_bottom_position;
+ claw_plant_->X(1, 0) = initial_top_position - initial_bottom_position;
+ claw_plant_->X(2, 0) = 0.0;
+ claw_plant_->X(3, 0) = 0.0;
+ claw_plant_->Y = claw_plant_->C() * claw_plant_->X;
+
ReinitializePartial(TOP_CLAW, initial_top_position);
ReinitializePartial(BOTTOM_CLAW, initial_bottom_position);
last_position_.Zero();
@@ -57,9 +71,9 @@
// Returns the absolute angle of the wrist.
double GetAbsolutePosition(ClawType type) const {
if (type == TOP_CLAW) {
- return top_claw_plant_->Y(0, 0);
+ return claw_plant_->Y(1, 0);
} else {
- return bottom_claw_plant_->Y(0, 0);
+ return claw_plant_->Y(0, 0);
}
}
@@ -69,10 +83,24 @@
}
// Makes sure pos is inside range (inclusive)
- bool CheckRange(double pos, struct constants::Values::AnglePair pair) {
+ bool CheckRange(double pos, const constants::Values::Claws::AnglePair &pair) {
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::ClawGroup::Position *position) {
@@ -81,25 +109,76 @@
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]);
+ 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 the hall effect sensor has been triggered if it is within
+ // Signal that each hall effect sensor has been triggered if it is within
// the correct range.
- position->top.front_hall_effect =
- CheckRange(pos[TOP_CLAW], values.upper_claw.front);
- position->top.calibration_hall_effect =
- CheckRange(pos[TOP_CLAW], values.upper_claw.calibration);
- position->top.back_hall_effect =
- CheckRange(pos[TOP_CLAW], values.upper_claw.back);
+ SetClawHallEffects(pos[TOP_CLAW], values.claw.upper_claw, &position->top);
+ SetClawHallEffects(pos[BOTTOM_CLAW], values.claw.lower_claw,
+ &position->bottom);
+ }
- position->bottom.front_hall_effect =
- CheckRange(pos[BOTTOM_CLAW], values.lower_claw.front);
- position->bottom.calibration_hall_effect =
- CheckRange(pos[BOTTOM_CLAW], values.lower_claw.calibration);
- position->bottom.back_hall_effect =
- CheckRange(pos[BOTTOM_CLAW], values.lower_claw.back);
+ void UpdateHallEffect(double angle,
+ double last_angle,
+ double initial_position,
+ double *posedge_value,
+ double *negedge_value,
+ 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);
+ *posedge_value = pair.lower_angle - initial_position;
+ } else {
+ LOG(DEBUG, "%s: Positive upper edge on %s hall effect\n", claw_name,
+ hall_effect_name);
+ *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);
+ *negedge_value = pair.lower_angle - initial_position;
+ } else {
+ LOG(DEBUG, "%s: Negative upper edge on %s hall effect\n", claw_name,
+ hall_effect_name);
+ *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->posedge_value,
+ &position->negedge_value, &position->front,
+ last_position.front, claw.front, claw_name, "front");
+ UpdateHallEffect(position->position + initial_position,
+ last_position.position + initial_position,
+ initial_position, &position->posedge_value,
+ &position->negedge_value, &position->calibration,
+ last_position.calibration, claw.calibration, claw_name,
+ "calibration");
+ UpdateHallEffect(position->position + initial_position,
+ last_position.position + initial_position,
+ initial_position, &position->posedge_value,
+ &position->negedge_value, &position->back,
+ last_position.back, claw.back, claw_name, "back");
}
// Sends out the position queue messages.
@@ -114,162 +193,12 @@
const frc971::constants::Values& values = constants::GetValues();
- // Handle the front hall effect.
- if (position->top.front_hall_effect &&
- !last_position_.top.front_hall_effect) {
- ++position->top.front_hall_effect_posedge_count;
-
- if (last_position_.top.position < values.upper_claw.front.lower_angle) {
- position->top.posedge_value =
- values.upper_claw.front.lower_angle - initial_position_[TOP_CLAW];
- } else {
- position->top.posedge_value =
- values.upper_claw.front.upper_angle - initial_position_[TOP_CLAW];
- }
- }
- if (!position->top.front_hall_effect &&
- last_position_.top.front_hall_effect) {
- ++position->top.front_hall_effect_negedge_count;
-
- if (position->top.position < values.upper_claw.front.lower_angle) {
- position->top.negedge_value =
- values.upper_claw.front.lower_angle - initial_position_[TOP_CLAW];
- } else {
- position->top.negedge_value =
- values.upper_claw.front.upper_angle - initial_position_[TOP_CLAW];
- }
- }
-
- // Handle the calibration hall effect.
- if (position->top.calibration_hall_effect &&
- !last_position_.top.calibration_hall_effect) {
- ++position->top.calibration_hall_effect_posedge_count;
-
- if (last_position_.top.position < values.upper_claw.calibration.lower_angle) {
- position->top.posedge_value =
- values.upper_claw.calibration.lower_angle - initial_position_[TOP_CLAW];
- } else {
- position->top.posedge_value =
- values.upper_claw.calibration.upper_angle - initial_position_[TOP_CLAW];
- }
- }
- if (!position->top.calibration_hall_effect &&
- last_position_.top.calibration_hall_effect) {
- ++position->top.calibration_hall_effect_negedge_count;
-
- if (position->top.position < values.upper_claw.calibration.lower_angle) {
- position->top.negedge_value =
- values.upper_claw.calibration.lower_angle - initial_position_[TOP_CLAW];
- } else {
- position->top.negedge_value =
- values.upper_claw.calibration.upper_angle - initial_position_[TOP_CLAW];
- }
- }
-
- // Handle the back hall effect.
- if (position->top.back_hall_effect &&
- !last_position_.top.back_hall_effect) {
- ++position->top.back_hall_effect_posedge_count;
-
- if (last_position_.top.position < values.upper_claw.back.lower_angle) {
- position->top.posedge_value =
- values.upper_claw.back.lower_angle - initial_position_[TOP_CLAW];
- } else {
- position->top.posedge_value =
- values.upper_claw.back.upper_angle - initial_position_[TOP_CLAW];
- }
- }
- if (!position->top.back_hall_effect &&
- last_position_.top.back_hall_effect) {
- ++position->top.back_hall_effect_negedge_count;
-
- if (position->top.position < values.upper_claw.back.lower_angle) {
- position->top.negedge_value =
- values.upper_claw.back.lower_angle - initial_position_[TOP_CLAW];
- } else {
- position->top.negedge_value =
- values.upper_claw.back.upper_angle - initial_position_[TOP_CLAW];
- }
- }
-
- // Now deal with the bottom part of the claw.
- // Handle the front hall effect.
- if (position->bottom.front_hall_effect &&
- !last_position_.bottom.front_hall_effect) {
- ++position->bottom.front_hall_effect_posedge_count;
-
- if (last_position_.bottom.position < values.lower_claw.front.lower_angle) {
- position->bottom.posedge_value =
- values.lower_claw.front.lower_angle - initial_position_[TOP_CLAW];
- } else {
- position->bottom.posedge_value =
- values.lower_claw.front.upper_angle - initial_position_[TOP_CLAW];
- }
- }
- if (!position->bottom.front_hall_effect &&
- last_position_.bottom.front_hall_effect) {
- ++position->bottom.front_hall_effect_negedge_count;
-
- if (position->bottom.position < values.lower_claw.front.lower_angle) {
- position->bottom.negedge_value =
- values.lower_claw.front.lower_angle - initial_position_[TOP_CLAW];
- } else {
- position->bottom.negedge_value =
- values.lower_claw.front.upper_angle - initial_position_[TOP_CLAW];
- }
- }
-
- // Handle the calibration hall effect.
- if (position->bottom.calibration_hall_effect &&
- !last_position_.bottom.calibration_hall_effect) {
- ++position->bottom.calibration_hall_effect_posedge_count;
-
- if (last_position_.bottom.position < values.lower_claw.calibration.lower_angle) {
- position->bottom.posedge_value =
- values.lower_claw.calibration.lower_angle - initial_position_[TOP_CLAW];
- } else {
- position->bottom.posedge_value =
- values.lower_claw.calibration.upper_angle - initial_position_[TOP_CLAW];
- }
- }
- if (!position->bottom.calibration_hall_effect &&
- last_position_.bottom.calibration_hall_effect) {
- ++position->bottom.calibration_hall_effect_negedge_count;
-
- if (position->bottom.position < values.lower_claw.calibration.lower_angle) {
- position->bottom.negedge_value =
- values.lower_claw.calibration.lower_angle - initial_position_[TOP_CLAW];
- } else {
- position->bottom.negedge_value =
- values.lower_claw.calibration.upper_angle - initial_position_[TOP_CLAW];
- }
- }
-
- // Handle the back hall effect.
- if (position->bottom.back_hall_effect &&
- !last_position_.bottom.back_hall_effect) {
- ++position->bottom.back_hall_effect_posedge_count;
-
- if (last_position_.bottom.position < values.lower_claw.back.lower_angle) {
- position->bottom.posedge_value =
- values.lower_claw.back.lower_angle - initial_position_[TOP_CLAW];
- } else {
- position->bottom.posedge_value =
- values.lower_claw.back.upper_angle - initial_position_[TOP_CLAW];
- }
- }
- if (!position->bottom.back_hall_effect &&
- last_position_.bottom.back_hall_effect) {
- ++position->bottom.back_hall_effect_negedge_count;
-
- if (position->bottom.position < values.lower_claw.back.lower_angle) {
- position->bottom.negedge_value =
- values.lower_claw.back.lower_angle - initial_position_[TOP_CLAW];
- } else {
- position->bottom.negedge_value =
- values.lower_claw.back.upper_angle - initial_position_[TOP_CLAW];
- }
- }
+ 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.
@@ -281,48 +210,35 @@
void Simulate() {
const frc971::constants::Values& v = constants::GetValues();
EXPECT_TRUE(claw_queue_group.output.FetchLatest());
- Simulate(TOP_CLAW, top_claw_plant_.get(), v.upper_claw,
- claw_queue_group.output->top_claw_voltage);
- Simulate(BOTTOM_CLAW, bottom_claw_plant_.get(), v.lower_claw,
- claw_queue_group.output->bottom_claw_voltage);
+
+ claw_plant_->U << claw_queue_group.output->bottom_claw_voltage,
+ claw_queue_group.output->top_claw_voltage -
+ claw_queue_group.output->bottom_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_seperation);
+ EXPECT_GE(claw_plant_->Y(1, 0) - claw_plant_->Y(0, 0),
+ v.claw.claw_min_seperation);
}
- // Top of the claw, the one with rollers
- ::std::unique_ptr<StateFeedbackPlant<2, 1, 1>> top_claw_plant_;
- // Bottom of the claw, the one with tusks
- ::std::unique_ptr<StateFeedbackPlant<2, 1, 1>> bottom_claw_plant_;
+ // 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) {
- StateFeedbackPlant<2, 1, 1>* plant;
- if (type == TOP_CLAW) {
- plant = top_claw_plant_.get();
- } else {
- plant = bottom_claw_plant_.get();
- }
initial_position_[type] = initial_position;
- plant->X(0, 0) = initial_position_[type];
- plant->X(1, 0) = 0.0;
- plant->Y = plant->C() * plant->X;
- last_voltage_[type] = 0.0;
- }
-
- void Simulate(ClawType type, StateFeedbackPlant<2, 1, 1>* plant,
- const constants::Values::Claw &claw, double nl_voltage) {
- plant->U << last_voltage_[type];
- plant->Update();
-
- // check top claw inside limits
- EXPECT_GE(claw.upper_limit, plant->Y(0, 0));
- EXPECT_LE(claw.lower_limit, plant->Y(0, 0));
-
- // TODO(austin): Check that the claws aren't too close to eachother.
- last_voltage_[type] = nl_voltage;
}
ClawGroup claw_queue_group;
double initial_position_[CLAW_COUNT];
- double last_voltage_[CLAW_COUNT];
control_loops::ClawGroup::Position last_position_;
};
@@ -353,7 +269,7 @@
".frc971.control_loops.claw_queue_group.status"),
claw_motor_(&claw_queue_group),
claw_motor_plant_(0.4, 0.2),
- min_seperation_(constants::GetValues().claw_min_seperation) {
+ min_seperation_(constants::GetValues().claw.claw_min_seperation) {
// Flush the robot state queue so we can use clean shared memory for this
// test.
::aos::robot_state.Clear();
@@ -361,9 +277,11 @@
}
void SendDSPacket(bool enabled) {
- ::aos::robot_state.MakeWithBuilder().enabled(enabled)
- .autonomous(false)
- .team_id(971).Send();
+ ::aos::robot_state.MakeWithBuilder()
+ .enabled(enabled)
+ .autonomous(false)
+ .team_id(971)
+ .Send();
::aos::robot_state.FetchLatest();
}
@@ -399,8 +317,8 @@
}
// Tests that the wrist zeros correctly starting on the hall effect sensor.
-TEST_F(ClawTest, ZerosStartingOn) {
- claw_motor_plant_.Reinitialize(100 * M_PI / 180.0, 90 * M_PI / 180.0);
+TEST_F(ClawTest, ZerosInTheMiddle) {
+ claw_motor_plant_.Reinitialize(0.5, 0.4);
claw_queue_group.goal.MakeWithBuilder()
.bottom_angle(0.1)
@@ -416,14 +334,37 @@
VerifyNearGoal();
}
-/*
-// Tests that missing positions are correctly handled.
-TEST_F(ClawTest, HandleMissingPosition) {
+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_group.goal.MakeWithBuilder()
.bottom_angle(0.1)
.seperation_angle(0.2)
.Send();
- for (int i = 0; i < 400; ++i) {
+ for (int i = 0; i < 600; ++i) {
+ claw_motor_plant_.SendPositionMessage();
+ claw_motor_.Iterate();
+ claw_motor_plant_.Simulate();
+
+ SendDSPacket(true);
+ }
+ VerifyNearGoal();
+}
+
+// Tests that missing positions are correctly handled.
+TEST_P(ZeroingClawTest, HandleMissingPosition) {
+ claw_motor_plant_.Reinitialize(GetParam().first, GetParam().second);
+
+ claw_queue_group.goal.MakeWithBuilder()
+ .bottom_angle(0.1)
+ .seperation_angle(0.2)
+ .Send();
+ for (int i = 0; i < 600; ++i) {
if (i % 23) {
claw_motor_plant_.SendPositionMessage();
}
@@ -435,6 +376,31 @@
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.1, 1.0),
+ ::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_group.goal.MakeWithBuilder()
diff --git a/frc971/control_loops/claw/claw_motor_plant.cc b/frc971/control_loops/claw/claw_motor_plant.cc
new file mode 100644
index 0000000..5d6598e
--- /dev/null
+++ b/frc971/control_loops/claw/claw_motor_plant.cc
@@ -0,0 +1,47 @@
+#include "frc971/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.00904786878843, 0.0, 0.0, 1.0, 0.0, 0.00904786878843, 0.0, 0.0, 0.815818233346, 0.0, 0.0, 0.0, 0.0, 0.815818233346;
+ Eigen::Matrix<double, 4, 2> B;
+ B << 0.000326582411818, 0.0, 0.0, 0.000326582411818, 0.0631746179893, 0.0, 0.0, 0.0631746179893;
+ 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, 24.0;
+ Eigen::Matrix<double, 2, 1> U_min;
+ U_min << -12.0, -24.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.71581823335, 5.38760974287e-16, -1.71581823335, 1.71581823335, 64.8264890043, 2.03572300346e-14, -64.8264890043, 64.8264890043;
+ Eigen::Matrix<double, 2, 4> K;
+ K << 146.100132128, 0.0, 6.7282816813, 0.0, 0.0, 275.346049928, 0.0, 12.0408756114;
+ return StateFeedbackController<4, 2, 2>(L, K, MakeClawPlantCoefficients());
+}
+
+StateFeedbackPlant<4, 2, 2> MakeClawPlant() {
+ ::std::vector<StateFeedbackPlantCoefficients<4, 2, 2> *> plants(1);
+ plants[0] = new StateFeedbackPlantCoefficients<4, 2, 2>(MakeClawPlantCoefficients());
+ return StateFeedbackPlant<4, 2, 2>(plants);
+}
+
+StateFeedbackLoop<4, 2, 2> MakeClawLoop() {
+ ::std::vector<StateFeedbackController<4, 2, 2> *> controllers(1);
+ controllers[0] = new StateFeedbackController<4, 2, 2>(MakeClawController());
+ return StateFeedbackLoop<4, 2, 2>(controllers);
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/frc971/control_loops/claw/claw_motor_plant.h b/frc971/control_loops/claw/claw_motor_plant.h
new file mode 100644
index 0000000..988cc20
--- /dev/null
+++ b/frc971/control_loops/claw/claw_motor_plant.h
@@ -0,0 +1,20 @@
+#ifndef FRC971_CONTROL_LOOPS_CLAW_CLAW_MOTOR_PLANT_H_
+#define FRC971_CONTROL_LOOPS_CLAW_CLAW_MOTOR_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+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 // FRC971_CONTROL_LOOPS_CLAW_CLAW_MOTOR_PLANT_H_
diff --git a/frc971/control_loops/claw/top_claw_motor_plant.cc b/frc971/control_loops/claw/top_claw_motor_plant.cc
deleted file mode 100644
index 113ff77..0000000
--- a/frc971/control_loops/claw/top_claw_motor_plant.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-#include "frc971/control_loops/claw/top_claw_motor_plant.h"
-
-#include <vector>
-
-#include "frc971/control_loops/state_feedback_loop.h"
-
-namespace frc971 {
-namespace control_loops {
-
-StateFeedbackPlantCoefficients<3, 1, 1> MakeTopClawPlantCoefficients() {
- Eigen::Matrix<double, 3, 3> A;
- A << 1.0, 0.00904786878843, 0.000326582411818, 0.0, 0.815818233346, 0.0631746179893, 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> MakeTopClawController() {
- Eigen::Matrix<double, 3, 1> L;
- L << 1.81581823335, 78.6334534274, 142.868137351;
- Eigen::Matrix<double, 1, 3> K;
- K << 92.6004807973, 4.38063492858, 1.11581823335;
- return StateFeedbackController<3, 1, 1>(L, K, MakeTopClawPlantCoefficients());
-}
-
-StateFeedbackPlant<3, 1, 1> MakeTopClawPlant() {
- ::std::vector<StateFeedbackPlantCoefficients<3, 1, 1> *> plants(1);
- plants[0] = new StateFeedbackPlantCoefficients<3, 1, 1>(MakeTopClawPlantCoefficients());
- return StateFeedbackPlant<3, 1, 1>(plants);
-}
-
-StateFeedbackLoop<3, 1, 1> MakeTopClawLoop() {
- ::std::vector<StateFeedbackController<3, 1, 1> *> controllers(1);
- controllers[0] = new StateFeedbackController<3, 1, 1>(MakeTopClawController());
- return StateFeedbackLoop<3, 1, 1>(controllers);
-}
-
-} // namespace control_loops
-} // namespace frc971
diff --git a/frc971/control_loops/claw/top_claw_motor_plant.h b/frc971/control_loops/claw/top_claw_motor_plant.h
deleted file mode 100644
index c74c976..0000000
--- a/frc971/control_loops/claw/top_claw_motor_plant.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef FRC971_CONTROL_LOOPS_CLAW_TOP_CLAW_MOTOR_PLANT_H_
-#define FRC971_CONTROL_LOOPS_CLAW_TOP_CLAW_MOTOR_PLANT_H_
-
-#include "frc971/control_loops/state_feedback_loop.h"
-
-namespace frc971 {
-namespace control_loops {
-
-StateFeedbackPlantCoefficients<3, 1, 1> MakeTopClawPlantCoefficients();
-
-StateFeedbackController<3, 1, 1> MakeTopClawController();
-
-StateFeedbackPlant<3, 1, 1> MakeTopClawPlant();
-
-StateFeedbackLoop<3, 1, 1> MakeTopClawLoop();
-
-} // namespace control_loops
-} // namespace frc971
-
-#endif // FRC971_CONTROL_LOOPS_CLAW_TOP_CLAW_MOTOR_PLANT_H_
diff --git a/frc971/control_loops/claw/unaugmented_bottom_claw_motor_plant.cc b/frc971/control_loops/claw/unaugmented_bottom_claw_motor_plant.cc
deleted file mode 100644
index cda15c4..0000000
--- a/frc971/control_loops/claw/unaugmented_bottom_claw_motor_plant.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-#include "frc971/control_loops/claw/unaugmented_bottom_claw_motor_plant.h"
-
-#include <vector>
-
-#include "frc971/control_loops/state_feedback_loop.h"
-
-namespace frc971 {
-namespace control_loops {
-
-StateFeedbackPlantCoefficients<2, 1, 1> MakeRawBottomClawPlantCoefficients() {
- Eigen::Matrix<double, 2, 2> A;
- A << 1.0, 0.00904786878843, 0.0, 0.815818233346;
- Eigen::Matrix<double, 2, 1> B;
- B << 0.000326582411818, 0.0631746179893;
- 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> MakeRawBottomClawController() {
- Eigen::Matrix<double, 2, 1> L;
- L << 1.71581823335, 64.8264890043;
- Eigen::Matrix<double, 1, 2> K;
- K << 130.590421637, 7.48987035533;
- return StateFeedbackController<2, 1, 1>(L, K, MakeRawBottomClawPlantCoefficients());
-}
-
-StateFeedbackPlant<2, 1, 1> MakeRawBottomClawPlant() {
- ::std::vector<StateFeedbackPlantCoefficients<2, 1, 1> *> plants(1);
- plants[0] = new StateFeedbackPlantCoefficients<2, 1, 1>(MakeRawBottomClawPlantCoefficients());
- return StateFeedbackPlant<2, 1, 1>(plants);
-}
-
-StateFeedbackLoop<2, 1, 1> MakeRawBottomClawLoop() {
- ::std::vector<StateFeedbackController<2, 1, 1> *> controllers(1);
- controllers[0] = new StateFeedbackController<2, 1, 1>(MakeRawBottomClawController());
- return StateFeedbackLoop<2, 1, 1>(controllers);
-}
-
-} // namespace control_loops
-} // namespace frc971
diff --git a/frc971/control_loops/claw/unaugmented_bottom_claw_motor_plant.h b/frc971/control_loops/claw/unaugmented_bottom_claw_motor_plant.h
deleted file mode 100644
index 8f59925..0000000
--- a/frc971/control_loops/claw/unaugmented_bottom_claw_motor_plant.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef FRC971_CONTROL_LOOPS_CLAW_UNAUGMENTED_BOTTOM_CLAW_MOTOR_PLANT_H_
-#define FRC971_CONTROL_LOOPS_CLAW_UNAUGMENTED_BOTTOM_CLAW_MOTOR_PLANT_H_
-
-#include "frc971/control_loops/state_feedback_loop.h"
-
-namespace frc971 {
-namespace control_loops {
-
-StateFeedbackPlantCoefficients<2, 1, 1> MakeRawBottomClawPlantCoefficients();
-
-StateFeedbackController<2, 1, 1> MakeRawBottomClawController();
-
-StateFeedbackPlant<2, 1, 1> MakeRawBottomClawPlant();
-
-StateFeedbackLoop<2, 1, 1> MakeRawBottomClawLoop();
-
-} // namespace control_loops
-} // namespace frc971
-
-#endif // FRC971_CONTROL_LOOPS_CLAW_UNAUGMENTED_BOTTOM_CLAW_MOTOR_PLANT_H_
diff --git a/frc971/control_loops/claw/unaugmented_top_claw_motor_plant.cc b/frc971/control_loops/claw/unaugmented_top_claw_motor_plant.cc
deleted file mode 100644
index 8ab4bbf..0000000
--- a/frc971/control_loops/claw/unaugmented_top_claw_motor_plant.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-#include "frc971/control_loops/claw/unaugmented_top_claw_motor_plant.h"
-
-#include <vector>
-
-#include "frc971/control_loops/state_feedback_loop.h"
-
-namespace frc971 {
-namespace control_loops {
-
-StateFeedbackPlantCoefficients<2, 1, 1> MakeRawTopClawPlantCoefficients() {
- Eigen::Matrix<double, 2, 2> A;
- A << 1.0, 0.00904786878843, 0.0, 0.815818233346;
- Eigen::Matrix<double, 2, 1> B;
- B << 0.000326582411818, 0.0631746179893;
- 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> MakeRawTopClawController() {
- Eigen::Matrix<double, 2, 1> L;
- L << 1.71581823335, 64.8264890043;
- Eigen::Matrix<double, 1, 2> K;
- K << 130.590421637, 7.48987035533;
- return StateFeedbackController<2, 1, 1>(L, K, MakeRawTopClawPlantCoefficients());
-}
-
-StateFeedbackPlant<2, 1, 1> MakeRawTopClawPlant() {
- ::std::vector<StateFeedbackPlantCoefficients<2, 1, 1> *> plants(1);
- plants[0] = new StateFeedbackPlantCoefficients<2, 1, 1>(MakeRawTopClawPlantCoefficients());
- return StateFeedbackPlant<2, 1, 1>(plants);
-}
-
-StateFeedbackLoop<2, 1, 1> MakeRawTopClawLoop() {
- ::std::vector<StateFeedbackController<2, 1, 1> *> controllers(1);
- controllers[0] = new StateFeedbackController<2, 1, 1>(MakeRawTopClawController());
- return StateFeedbackLoop<2, 1, 1>(controllers);
-}
-
-} // namespace control_loops
-} // namespace frc971
diff --git a/frc971/control_loops/claw/unaugmented_top_claw_motor_plant.h b/frc971/control_loops/claw/unaugmented_top_claw_motor_plant.h
deleted file mode 100644
index c87d3ca..0000000
--- a/frc971/control_loops/claw/unaugmented_top_claw_motor_plant.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef FRC971_CONTROL_LOOPS_CLAW_UNAUGMENTED_TOP_CLAW_MOTOR_PLANT_H_
-#define FRC971_CONTROL_LOOPS_CLAW_UNAUGMENTED_TOP_CLAW_MOTOR_PLANT_H_
-
-#include "frc971/control_loops/state_feedback_loop.h"
-
-namespace frc971 {
-namespace control_loops {
-
-StateFeedbackPlantCoefficients<2, 1, 1> MakeRawTopClawPlantCoefficients();
-
-StateFeedbackController<2, 1, 1> MakeRawTopClawController();
-
-StateFeedbackPlant<2, 1, 1> MakeRawTopClawPlant();
-
-StateFeedbackLoop<2, 1, 1> MakeRawTopClawLoop();
-
-} // namespace control_loops
-} // namespace frc971
-
-#endif // FRC971_CONTROL_LOOPS_CLAW_UNAUGMENTED_TOP_CLAW_MOTOR_PLANT_H_
diff --git a/frc971/control_loops/control_loops.gyp b/frc971/control_loops/control_loops.gyp
index 94c73d0..273063c 100644
--- a/frc971/control_loops/control_loops.gyp
+++ b/frc971/control_loops/control_loops.gyp
@@ -1,6 +1,30 @@
{
'targets': [
{
+ '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': 'state_feedback_loop',
'type': 'static_library',
'sources': [
diff --git a/frc971/control_loops/control_loops.q b/frc971/control_loops/control_loops.q
new file mode 100644
index 0000000..ccc8388
--- /dev/null
+++ b/frc971/control_loops/control_loops.q
@@ -0,0 +1,7 @@
+package frc971;
+
+struct HallEffectStruct {
+ bool current;
+ int32_t posedge_count;
+ int32_t negedge_count;
+};
diff --git a/frc971/control_loops/drivetrain/drivetrain.cc b/frc971/control_loops/drivetrain/drivetrain.cc
index 982c6e9..e7aae01 100644
--- a/frc971/control_loops/drivetrain/drivetrain.cc
+++ b/frc971/control_loops/drivetrain/drivetrain.cc
@@ -10,11 +10,12 @@
#include "aos/common/queue.h"
#include "aos/controls/polytope.h"
#include "aos/common/commonmath.h"
+#include "aos/common/logging/queue_logging.h"
+
#include "frc971/control_loops/state_feedback_loop.h"
#include "frc971/control_loops/drivetrain/polydrivetrain_cim_plant.h"
#include "frc971/control_loops/drivetrain/drivetrain.q.h"
#include "frc971/queues/gyro_angle.q.h"
-#include "frc971/queues/piston.q.h"
#include "frc971/constants.h"
using frc971::sensors::gyro;
@@ -121,7 +122,6 @@
}
}
void PrintMotors() const {
- // LOG(DEBUG, "Left Power %f Right Power %f lg %f rg %f le %f re %f gyro %f\n", U[0], U[1], R[0], R[2], Y[0], Y[1], _gyro);
::Eigen::Matrix<double, 4, 1> E = loop_->R - loop_->X_hat;
LOG(DEBUG, "E[0, 0]: %f E[1, 0] %f E[2, 0] %f E[3, 0] %f\n", E(0, 0), E(1, 0), E(2, 0), E(3, 0));
}
@@ -308,53 +308,31 @@
}
if (position) {
+ GearLogging gear_logging;
// Switch to the correct controller.
// TODO(austin): Un-hard code 0.57
if (position->left_shifter_position < 0.57) {
if (position->right_shifter_position < 0.57 || right_gear_ == LOW) {
- LOG(DEBUG, "Loop Left low, Right low\n");
- loop_->set_controller_index(0);
+ gear_logging.left_loop_high = false;
+ gear_logging.right_loop_high = false;
+ loop_->set_controller_index(gear_logging.controller_index = 0);
} else {
- LOG(DEBUG, "Loop Left low, Right high\n");
- loop_->set_controller_index(1);
+ 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 < 0.57 || left_gear_ == LOW) {
- LOG(DEBUG, "Loop Left high, Right low\n");
- loop_->set_controller_index(2);
+ gear_logging.left_loop_high = true;
+ gear_logging.right_loop_high = false;
+ loop_->set_controller_index(gear_logging.controller_index = 2);
} else {
- LOG(DEBUG, "Loop Left high, Right high\n");
- loop_->set_controller_index(3);
+ gear_logging.left_loop_high = true;
+ gear_logging.right_loop_high = true;
+ loop_->set_controller_index(gear_logging.controller_index = 3);
}
}
- switch (left_gear_) {
- case LOW:
- LOG(DEBUG, "Left is in low\n");
- break;
- case HIGH:
- LOG(DEBUG, "Left is in high\n");
- break;
- case SHIFTING_UP:
- LOG(DEBUG, "Left is shifting up\n");
- break;
- case SHIFTING_DOWN:
- LOG(DEBUG, "Left is shifting down\n");
- break;
- }
- switch (right_gear_) {
- case LOW:
- LOG(DEBUG, "Right is in low\n");
- break;
- case HIGH:
- LOG(DEBUG, "Right is in high\n");
- break;
- case SHIFTING_UP:
- LOG(DEBUG, "Right is shifting up\n");
- break;
- case SHIFTING_DOWN:
- LOG(DEBUG, "Right is shifting down\n");
- break;
- }
+
// TODO(austin): Constants.
if (position->left_shifter_position > 0.9 && left_gear_ == SHIFTING_UP) {
left_gear_ = HIGH;
@@ -368,6 +346,10 @@
if (position->right_shifter_position < 0.1 && 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);
}
}
@@ -433,19 +415,30 @@
const double right_motor_speed =
MotorSpeed(position_.right_shifter_position, current_right_velocity);
- // Reset the CIM model to the current conditions to be ready for when we shift.
- if (IsInGear(left_gear_)) {
- left_cim_->X_hat(0, 0) = left_motor_speed;
- LOG(DEBUG, "Setting left CIM to %f at robot speed %f\n", left_motor_speed,
- current_left_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;
+ left_cim_->X_hat(0, 0) = left_motor_speed;
+ } 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;
+ right_cim_->X_hat(0, 0) = right_motor_speed;
+ } else {
+ logging.right_in_gear = false;
+ }
+ logging.right_motor_speed = right_motor_speed;
+ logging.right_velocity = current_right_velocity;
+
+ LOG_STRUCT(DEBUG, "currently", logging);
}
- if (IsInGear(right_gear_)) {
- right_cim_->X_hat(0, 0) = right_motor_speed;
- LOG(DEBUG, "Setting right CIM to %f at robot speed %f\n",
- right_motor_speed, current_right_velocity);
- }
- LOG(DEBUG, "robot speed l=%f r=%f\n", current_left_velocity,
- current_right_velocity);
if (IsInGear(left_gear_) && IsInGear(right_gear_)) {
// FF * X = U (steady state)
@@ -505,7 +498,8 @@
// Any motor is not in gear. Speed match.
::Eigen::Matrix<double, 1, 1> R_left;
R_left(0, 0) = left_motor_speed;
- const double wiggle = (static_cast<double>((counter_ % 4) / 2) - 0.5) * 3.5;
+ const double wiggle =
+ (static_cast<double>((counter_ % 4) / 2) - 0.5) * 3.5;
loop_->U(0, 0) =
::aos::Clip((R_left / Kv)(0, 0) + wiggle, -position_.battery_voltage,
@@ -528,14 +522,8 @@
if (output != NULL) {
output->left_voltage = loop_->U(0, 0);
output->right_voltage = loop_->U(1, 0);
- }
- // Go in high gear if anything wants to be in high gear.
- // TODO(austin): Seperate these.
- if (left_gear_ == HIGH || left_gear_ == SHIFTING_UP ||
- right_gear_ == HIGH || right_gear_ == SHIFTING_UP) {
- shifters.MakeWithBuilder().set(false).Send();
- } else {
- shifters.MakeWithBuilder().set(true).Send();
+ output->left_high = left_gear_ == HIGH || left_gear_ == SHIFTING_UP;
+ output->right_high = right_gear_ == HIGH || right_gear_ == SHIFTING_UP;
}
}
@@ -581,7 +569,7 @@
bool bad_pos = false;
if (position == nullptr) {
- LOG(WARNING, "no position\n");
+ LOG_INTERVAL(no_position_);
bad_pos = true;
}
@@ -600,7 +588,7 @@
const double left_encoder = position->left_encoder;
const double right_encoder = position->right_encoder;
if (gyro.FetchLatest()) {
- LOG(DEBUG, "gyro %f\n", gyro->angle);
+ LOG_STRUCT(DEBUG, "using", *gyro);
dt_closedloop.SetPosition(left_encoder, right_encoder, gyro->angle,
control_loop_driving);
} else {
diff --git a/frc971/control_loops/drivetrain/drivetrain.gyp b/frc971/control_loops/drivetrain/drivetrain.gyp
index 7821c39..adb9f94 100644
--- a/frc971/control_loops/drivetrain/drivetrain.gyp
+++ b/frc971/control_loops/drivetrain/drivetrain.gyp
@@ -47,6 +47,8 @@
'<(DEPTH)/aos/build/externals.gyp:libcdd',
'<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
'<(DEPTH)/frc971/queues/queues.gyp:queues',
+ '<(AOS)/common/util/util.gyp:log_interval',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
],
'export_dependent_settings': [
'<(DEPTH)/aos/build/externals.gyp:libcdd',
diff --git a/frc971/control_loops/drivetrain/drivetrain.h b/frc971/control_loops/drivetrain/drivetrain.h
index 249de70..9ee7a27 100644
--- a/frc971/control_loops/drivetrain/drivetrain.h
+++ b/frc971/control_loops/drivetrain/drivetrain.h
@@ -7,6 +7,7 @@
#include "aos/common/control_loop/ControlLoop.h"
#include "aos/controls/polytope.h"
#include "frc971/control_loops/drivetrain/drivetrain.q.h"
+#include "aos/common/util/log_interval.h"
namespace frc971 {
namespace control_loops {
@@ -35,6 +36,10 @@
const control_loops::Drivetrain::Position *position,
control_loops::Drivetrain::Output *output,
control_loops::Drivetrain::Status *status);
+
+ typedef ::aos::util::SimpleLogInterval SimpleLogInterval;
+ SimpleLogInterval no_position_ = SimpleLogInterval(
+ ::aos::time::Time::InSeconds(0.25), WARNING, "no position");
};
} // namespace control_loops
diff --git a/frc971/control_loops/drivetrain/drivetrain.q b/frc971/control_loops/drivetrain/drivetrain.q
index 1dcc947..443282c 100644
--- a/frc971/control_loops/drivetrain/drivetrain.q
+++ b/frc971/control_loops/drivetrain/drivetrain.q
@@ -2,6 +2,23 @@
import "aos/common/control_loop/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 Drivetrain {
implements aos.control_loops.ControlLoop;
@@ -28,6 +45,8 @@
message Output {
float left_voltage;
float right_voltage;
+ bool left_high;
+ bool right_high;
};
message Status {
diff --git a/frc971/control_loops/hall_effect_tracker.h b/frc971/control_loops/hall_effect_tracker.h
new file mode 100644
index 0000000..79d14af
--- /dev/null
+++ b/frc971/control_loops/hall_effect_tracker.h
@@ -0,0 +1,52 @@
+#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 {
+
+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_; }
+
+ void Update(const HallEffectStruct &position) {
+ value_ = position.current;
+ posedges_.update(position.posedge_count);
+ negedges_.update(position.negedge_count);
+ }
+
+ private:
+ class {
+ public:
+ void update(int32_t count) {
+ previous_count_ = 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;
+};
+
+} // namespace frc971
+
+#endif // FRC971_CONTROL_LOOPS_HALL_EFFECT_H_
diff --git a/frc971/control_loops/python/claw.py b/frc971/control_loops/python/claw.py
index 3d6b9fc..98d79ea 100755
--- a/frc971/control_loops/python/claw.py
+++ b/frc971/control_loops/python/claw.py
@@ -1,6 +1,7 @@
#!/usr/bin/python
import control_loop
+import controls
import numpy
import sys
from matplotlib import pylab
@@ -32,28 +33,57 @@
# Control loop time step
self.dt = 0.01
+ # State is [bottom position, top - bottom position,
+ # bottom velocity, top - bottom velocity]
+ # Input is [bottom power, top power]
+ # Motor time constant.
+ self.motor_timeconstant = self.Kt / self.Kv / (self.J * self.G * self.G * self.R)
# State feedback matrices
self.A_continuous = numpy.matrix(
- [[0, 1],
- [0, -self.Kt / self.Kv / (self.J * self.G * self.G * self.R)]])
+ [[0, 0, 1, 0],
+ [0, 0, 0, 1],
+ [0, 0, -self.motor_timeconstant, 0],
+ [0, 0, 0, -self.motor_timeconstant]])
+
+ self.motor_feedforward = self.Kt / (self.J * 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]])
+ [[0, 0],
+ [0, 0],
+ [self.motor_feedforward, 0],
+ [0.0, self.motor_feedforward]])
+ 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.PlaceControllerPoles([0.85, 0.45])
+ #controlability = controls.ctrb(self.A, self.B);
+ #print "Rank of controlability matrix.", numpy.linalg.matrix_rank(controlability)
+
+ self.Q = numpy.matrix([[(1.0 / (0.10 ** 2.0)), 0.0, 0.0, 0.0],
+ [0.0, (1.0 / (0.03 ** 2.0)), 0.0, 0.0],
+ [0.0, 0.0, 0.2, 0.0],
+ [0.0, 0.0, 0.0, 2.00]])
+
+ self.R = numpy.matrix([[(1.0 / (20.0 ** 2.0)), 0.0],
+ [0.0, (1.0 / (20.0 ** 2.0))]])
+ self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
+
+ print "K unaugmented"
+ print self.K
self.rpl = .05
self.ipl = 0.008
self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
+ self.rpl - 1j * self.ipl,
+ self.rpl + 1j * self.ipl,
self.rpl - 1j * self.ipl])
- self.U_max = numpy.matrix([[12.0]])
- self.U_min = numpy.matrix([[-12.0]])
+ self.U_max = numpy.matrix([[12.0], [24.0]])
+ self.U_min = numpy.matrix([[-12.0], [-24.0]])
self.InitializeState()
@@ -63,108 +93,202 @@
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, 1.0]])
- self.A[0:2, 0:2] = A_unaugmented
- self.A[0:2, 2] = B_unaugmented
+ 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],
- [1.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.matrix([[1.0, 0.0, 0.0]])
- self.D = numpy.matrix([[0.0]])
+ 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.80])
+ #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,
+ self.PlaceObserverPoles([self.rpl + 1j * self.ipl, 0.10, 0.09,
self.rpl - 1j * self.ipl, 0.90])
- print "Placed observer poles are"
- print numpy.linalg.eig(self.A - self.L * self.C)[0]
+ #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
- self.U_max = numpy.matrix([[12.0]])
- self.U_min = numpy.matrix([[-12.0]])
+ #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 ClipDeltaU(claw, delta_u):
- old_u = numpy.matrix([[claw.X[2, 0]]])
- new_u = numpy.clip(old_u + delta_u, claw.U_min, claw.U_max)
- return new_u - old_u
+def FullSeparationPriority(claw, U):
+ bottom_u = U[0, 0]
+ top_u = U[1, 0] + bottom_u
+
+ #print "Bottom is", new_unclipped_bottom_u, "Top is", top_u
+ if bottom_u > claw.U_max[0, 0]:
+ #print "Bottom is too big. Was", new_unclipped_bottom_u, "changing top by", new_unclipped_bottom_u - claw.U_max[0, 0]
+ top_u -= bottom_u - claw.U_max[0, 0]
+ if top_u < claw.U_min[1, 0]:
+ top_u = claw.U_min[1, 0]
+
+ bottom_u = claw.U_max[0, 0]
+ if top_u > claw.U_max[1, 0]:
+ bottom_u -= top_u - claw.U_max[1, 0]
+ if bottom_u < claw.U_min[0, 0]:
+ bottom_u = claw.U_min[0, 0]
+
+ top_u = claw.U_max[1, 0]
+ if top_u < claw.U_min[1, 0]:
+ bottom_u -= top_u - claw.U_min[1, 0]
+ if bottom_u > claw.U_max[0, 0]:
+ bottom_u = claw.U_max[0, 0]
+
+ top_u = claw.U_min[1, 0]
+ if bottom_u < claw.U_min[0, 0]:
+ top_u -= bottom_u - claw.U_min[0, 0]
+ if top_u > claw.U_max[1, 0]:
+ top_u = claw.U_max[1, 0]
+
+ bottom_u = claw.U_min[0, 0]
+
+ return numpy.matrix([[bottom_u], [top_u - bottom_u]])
+
+def AverageUFix(claw, U):
+ bottom_u = U[0, 0]
+ top_u = U[1, 0] + bottom_u
+
+ #print "Bottom is", new_unclipped_bottom_u, "Top is", top_u
+ if (bottom_u > claw.U_max[0, 0] or top_u > claw.U_max[1, 0] or
+ top_u < claw.U_min[1, 0] or bottom_u < claw.U_min[0, 0]):
+ scalar = 12.0 / max(numpy.abs(top_u), numpy.abs(bottom_u))
+ top_u *= scalar
+ bottom_u *= scalar
+
+ return numpy.matrix([[bottom_u], [top_u - bottom_u]])
+
+def ClipDeltaU(claw, U):
+ delta_u = U[0, 0]
+ top_u = U[1, 0]
+ old_bottom_u = claw.X[4, 0]
+
+ # TODO(austin): Preserve the difference between the top and bottom power.
+ new_unclipped_bottom_u = old_bottom_u + delta_u
+
+ #print "Bottom is", new_unclipped_bottom_u, "Top is", top_u
+ if new_unclipped_bottom_u > claw.U_max[0, 0]:
+ #print "Bottom is too big. Was", new_unclipped_bottom_u, "changing top by", new_unclipped_bottom_u - claw.U_max[0, 0]
+ top_u -= new_unclipped_bottom_u - claw.U_max[0, 0]
+ new_unclipped_bottom_u = claw.U_max[0, 0]
+ if top_u > claw.U_max[1, 0]:
+ new_unclipped_bottom_u -= top_u - claw.U_max[1, 0]
+ top_u = claw.U_max[1, 0]
+ if top_u < claw.U_min[1, 0]:
+ new_unclipped_bottom_u -= top_u - claw.U_min[1, 0]
+ top_u = claw.U_min[1, 0]
+ if new_unclipped_bottom_u < claw.U_min[0, 0]:
+ top_u -= new_unclipped_bottom_u - claw.U_min[0, 0]
+ new_unclipped_bottom_u = claw.U_min[0, 0]
+
+ new_bottom_u = numpy.clip(new_unclipped_bottom_u, claw.U_min[0, 0], claw.U_max[0, 0])
+ new_top_u = numpy.clip(top_u, claw.U_min[1, 0], claw.U_max[1, 0])
+
+ return numpy.matrix([[new_bottom_u - old_bottom_u], [new_top_u]])
def main(argv):
# Simulate the response of the system to a step input.
- claw = ClawDeltaU()
- simulated_x = []
- for _ in xrange(100):
- claw.Update(numpy.matrix([[12.0]]))
- simulated_x.append(claw.X[0, 0])
+ #claw = ClawDeltaU()
+ #simulated_x = []
+ #for _ in xrange(100):
+ # claw.Update(numpy.matrix([[12.0]]))
+ # simulated_x.append(claw.X[0, 0])
- pylab.plot(range(100), simulated_x)
- pylab.show()
+ #pylab.plot(range(100), simulated_x)
+ #pylab.show()
# Simulate the closed loop response of the system to a step input.
- top_claw = ClawDeltaU("TopClaw")
- close_loop_x = []
- close_loop_u = []
- R = numpy.matrix([[1.0], [0.0], [0.0]])
- top_claw.X[2, 0] = -5
- for _ in xrange(100):
- U = numpy.clip(top_claw.K * (R - top_claw.X_hat), top_claw.U_min, top_claw.U_max)
- U = ClipDeltaU(top_claw, U)
- top_claw.UpdateObserver(U)
- top_claw.Update(U)
- close_loop_x.append(top_claw.X[0, 0] * 10)
- close_loop_u.append(top_claw.X[2, 0])
+ claw = Claw("TopClaw")
+ t = []
+ close_loop_x_bottom = []
+ close_loop_x_sep = []
+ close_loop_u_bottom = []
+ close_loop_u_top = []
+ R = numpy.matrix([[1.0], [1.0], [0.0], [0.0]])
+ claw.X[0, 0] = 0
+ for i in xrange(100):
+ #print "Error is", (R - claw.X_hat)
+ U = claw.K * (R - claw.X_hat)
+ #U = numpy.clip(claw.K * (R - claw.X_hat), claw.U_min, claw.U_max)
+ #U = FullSeparationPriority(claw, U)
+ U = AverageUFix(claw, U)
+ #U = claw.K * (R - claw.X_hat)
+ #U = ClipDeltaU(claw, U)
+ claw.UpdateObserver(U)
+ claw.Update(U)
+ close_loop_x_bottom.append(claw.X[0, 0] * 10)
+ close_loop_u_bottom.append(U[0, 0])
+ close_loop_x_sep.append(claw.X[1, 0] * 10)
+ close_loop_u_top.append(U[1, 0] + U[0, 0])
+ t.append(0.01 * i)
- pylab.plot(range(100), close_loop_x)
- pylab.plot(range(100), close_loop_u)
+ pylab.plot(t, close_loop_x_bottom, label='x bottom')
+ pylab.plot(t, close_loop_x_sep, label='seperation')
+ pylab.plot(t, close_loop_u_bottom, label='u bottom')
+ pylab.plot(t, close_loop_u_top, label='u top')
+ pylab.legend()
pylab.show()
# Write the generated constants out to a file.
- if len(argv) != 9:
- print "Expected .h file name and .cc file name for"
- print "both the plant and unaugmented plant"
+ if len(argv) != 3:
+ print "Expected .h file name and .cc file name for the claw."
else:
- top_unaug_claw = Claw("RawTopClaw")
- top_unaug_loop_writer = control_loop.ControlLoopWriter("RawTopClaw",
- [top_unaug_claw])
+ claw = Claw("Claw")
+ loop_writer = control_loop.ControlLoopWriter("Claw", [claw])
if argv[1][-3:] == '.cc':
- top_unaug_loop_writer.Write(argv[2], argv[1])
+ loop_writer.Write(argv[2], argv[1])
else:
- top_unaug_loop_writer.Write(argv[1], argv[2])
-
- top_loop_writer = control_loop.ControlLoopWriter("TopClaw", [top_claw])
- if argv[3][-3:] == '.cc':
- top_loop_writer.Write(argv[4], argv[3])
- else:
- top_loop_writer.Write(argv[3], argv[4])
-
- bottom_claw = ClawDeltaU("BottomClaw")
- bottom_unaug_claw = Claw("RawBottomClaw")
- bottom_unaug_loop_writer = control_loop.ControlLoopWriter(
- "RawBottomClaw", [bottom_unaug_claw])
- if argv[5][-3:] == '.cc':
- bottom_unaug_loop_writer.Write(argv[6], argv[5])
- else:
- bottom_unaug_loop_writer.Write(argv[5], argv[6])
-
- bottom_loop_writer = control_loop.ControlLoopWriter("BottomClaw",
- [bottom_claw])
- if argv[7][-3:] == '.cc':
- bottom_loop_writer.Write(argv[8], argv[7])
- else:
- bottom_loop_writer.Write(argv[7], argv[8])
+ loop_writer.Write(argv[1], argv[2])
if __name__ == '__main__':
sys.exit(main(sys.argv))
diff --git a/frc971/control_loops/python/control_loop.py b/frc971/control_loops/python/control_loop.py
index fffbe0d..4b63aec 100644
--- a/frc971/control_loops/python/control_loop.py
+++ b/frc971/control_loops/python/control_loop.py
@@ -208,7 +208,7 @@
first = True
for x in xrange(matrix.shape[0]):
for y in xrange(matrix.shape[1]):
- element = matrix[x, y]
+ element = matrix[x, y]
if first:
ans.append(' %s << ' % matrix_name)
first = False
diff --git a/frc971/control_loops/python/controls.py b/frc971/control_loops/python/controls.py
index a40bfe2..967be3c 100644
--- a/frc971/control_loops/python/controls.py
+++ b/frc971/control_loops/python/controls.py
@@ -99,3 +99,31 @@
diage[count, count] = (numpy.exp(eig * dt) - 1.0) / eig
return (P * diag * numpy.linalg.inv(P), P * diage * numpy.linalg.inv(P) * 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(A.shape[0], B.shape[1], A, B, Q, R, 'D')
+
+ F = numpy.linalg.inv(R + B.T * P *B) * B.T * P * A
+ return F
diff --git a/frc971/control_loops/state_feedback_loop.h b/frc971/control_loops/state_feedback_loop.h
index 420a0e7..0384747 100644
--- a/frc971/control_loops/state_feedback_loop.h
+++ b/frc971/control_loops/state_feedback_loop.h
@@ -4,9 +4,12 @@
#include <assert.h>
#include <vector>
+#include <iostream>
#include "Eigen/Dense"
+#include "aos/common/logging/logging.h"
+
template <int number_of_states, int number_of_inputs, int number_of_outputs>
class StateFeedbackPlantCoefficients {
public:
@@ -129,8 +132,8 @@
// Assert that U is within the hardware range.
virtual void CheckU() {
for (int i = 0; i < kNumOutputs; ++i) {
- assert(U(i, 0) <= U_max(i, 0));
- assert(U(i, 0) >= U_min(i, 0));
+ assert(U(i, 0) <= U_max(i, 0) + 0.00001);
+ assert(U(i, 0) >= U_min(i, 0) - 0.00001);
}
}
@@ -286,6 +289,23 @@
// Corrects X_hat given the observation in Y.
void Correct(const Eigen::Matrix<double, number_of_outputs, 1> &Y) {
+ /*
+ auto eye =
+ Eigen::Matrix<double, number_of_states, number_of_states>::Identity();
+ //LOG(DEBUG, "X_hat(2, 0) = %f\n", X_hat(2, 0));
+ ::std::cout << "Identity " << eye << ::std::endl;
+ ::std::cout << "X_hat " << X_hat << ::std::endl;
+ ::std::cout << "LC " << L() * C() << ::std::endl;
+ ::std::cout << "L " << L() << ::std::endl;
+ ::std::cout << "C " << C() << ::std::endl;
+ ::std::cout << "y " << Y << ::std::endl;
+ ::std::cout << "z " << (Y - C() * X_hat) << ::std::endl;
+ ::std::cout << "correction " << L() * (Y - C() * X_hat) << ::std::endl;
+ X_hat = (eye - L() * C()) * X_hat + L() * Y;
+ ::std::cout << "X_hat after " << X_hat << ::std::endl;
+ ::std::cout << ::std::endl;
+ */
+ //LOG(DEBUG, "X_hat(2, 0) = %f\n", X_hat(2, 0));
Y_ = Y;
new_y_ = true;
}
@@ -299,12 +319,16 @@
CapU();
}
+ //::std::cout << "Predict xhat before " << X_hat;
+ //::std::cout << "Measurement error is " << Y_ - C() * X_hat;
+ //X_hat = A() * X_hat + B() * U;
if (new_y_) {
X_hat = (A() - L() * C()) * X_hat + L() * Y_ + B() * U;
new_y_ = false;
} else {
X_hat = A() * X_hat + B() * U;
}
+ //::std::cout << "Predict xhat after " << X_hat;
}
// Sets the current controller to be index and verifies that it isn't out of
diff --git a/frc971/control_loops/update_claw.sh b/frc971/control_loops/update_claw.sh
index 083850a..2800d2a 100755
--- a/frc971/control_loops/update_claw.sh
+++ b/frc971/control_loops/update_claw.sh
@@ -4,11 +4,5 @@
cd $(dirname $0)
-./python/claw.py claw/unaugmented_top_claw_motor_plant.h \
- claw/unaugmented_top_claw_motor_plant.cc \
- claw/top_claw_motor_plant.h \
- claw/top_claw_motor_plant.cc \
- claw/unaugmented_bottom_claw_motor_plant.h \
- claw/unaugmented_bottom_claw_motor_plant.cc \
- claw/bottom_claw_motor_plant.h \
- claw/bottom_claw_motor_plant.cc
+./python/claw.py claw/claw_motor_plant.h \
+ claw/claw_motor_plant.cc
diff --git a/frc971/input/input.gyp b/frc971/input/input.gyp
index 0fef215..e87063b 100644
--- a/frc971/input/input.gyp
+++ b/frc971/input/input.gyp
@@ -31,6 +31,7 @@
'<(DEPTH)/frc971/frc971.gyp:constants',
'<(DEPTH)/bbb_cape/src/bbb/bbb.gyp:sensor_reader',
'<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
],
},
],
diff --git a/frc971/input/joystick_reader.cc b/frc971/input/joystick_reader.cc
index 4372098..e078e53 100644
--- a/frc971/input/joystick_reader.cc
+++ b/frc971/input/joystick_reader.cc
@@ -9,11 +9,9 @@
#include "frc971/control_loops/drivetrain/drivetrain.q.h"
#include "frc971/queues/gyro_angle.q.h"
-#include "frc971/queues/piston.q.h"
#include "frc971/autonomous/auto.q.h"
using ::frc971::control_loops::drivetrain;
-using ::frc971::control_loops::shifters;
using ::frc971::sensors::gyro;
using ::aos::input::driver_station::ButtonLocation;
@@ -32,9 +30,7 @@
class Reader : public ::aos::input::JoystickInput {
public:
- Reader() {
- shifters.MakeWithBuilder().set(true).Send();
- }
+ Reader() {}
virtual void RunIteration(const ::aos::input::driver_station::Data &data) {
static bool is_high_gear = false;
diff --git a/frc971/input/sensor_receiver.cc b/frc971/input/sensor_receiver.cc
index 80c1c9f..2d7af87 100644
--- a/frc971/input/sensor_receiver.cc
+++ b/frc971/input/sensor_receiver.cc
@@ -4,12 +4,14 @@
#include "aos/common/logging/logging.h"
#include "aos/common/util/wrapping_counter.h"
#include "aos/common/time.h"
+#include "aos/common/logging/queue_logging.h"
#include "bbb/sensor_reader.h"
#include "frc971/control_loops/drivetrain/drivetrain.q.h"
#include "frc971/queues/gyro_angle.q.h"
#include "frc971/constants.h"
+#include "frc971/queues/to_log.q.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
@@ -55,8 +57,9 @@
void PacketReceived(const ::bbb::DataStruct *data,
const ::aos::time::Time &cape_timestamp) {
- LOG(DEBUG, "cape timestamp %010" PRId32 ".%09" PRId32 "s\n",
- cape_timestamp.sec(), cape_timestamp.nsec());
+ ::frc971::logging_structs::CapeReading reading_to_log(cape_timestamp.sec(),
+ cape_timestamp.nsec());
+ LOG_STRUCT(DEBUG, "cape reading", reading_to_log);
bool bad_gyro;
if (data->uninitialized_gyro) {
LOG(DEBUG, "uninitialized gyro\n");
diff --git a/frc971/output/motor_writer.cc b/frc971/output/motor_writer.cc
index c6dbf63..0af5e2b 100644
--- a/frc971/output/motor_writer.cc
+++ b/frc971/output/motor_writer.cc
@@ -5,12 +5,15 @@
#include "aos/prime/output/motor_output.h"
#include "aos/common/logging/logging.h"
#include "aos/linux_code/init.h"
+#include "aos/common/util/log_interval.h"
+#include "aos/common/time.h"
+#include "aos/common/logging/queue_logging.h"
-#include "frc971/queues/piston.q.h"
#include "frc971/control_loops/drivetrain/drivetrain.q.h"
+using ::aos::util::SimpleLogInterval;
+
using ::frc971::control_loops::drivetrain;
-using ::frc971::control_loops::shifters;
namespace frc971 {
namespace output {
@@ -18,7 +21,8 @@
class MotorWriter : public ::aos::MotorOutput {
// Maximum age of an output packet before the motors get zeroed instead.
static const int kOutputMaxAgeMS = 20;
- static const int kEnableDrivetrain = true;
+ static constexpr ::aos::time::Time kOldLogInterval =
+ ::aos::time::Time::InSeconds(0.5);
virtual void RunIteration() {
values_.digital_module = 0;
@@ -26,29 +30,34 @@
values_.compressor_channel = 1;
values_.solenoid_module = 0;
- drivetrain.output.FetchLatest();
- if (drivetrain.output.IsNewerThanMS(kOutputMaxAgeMS) && kEnableDrivetrain) {
- SetPWMOutput(2, drivetrain.output->right_voltage / 12.0, kTalonBounds);
- SetPWMOutput(3, drivetrain.output->right_voltage / 12.0, kTalonBounds);
- SetPWMOutput(5, -drivetrain.output->left_voltage / 12.0, kTalonBounds);
- SetPWMOutput(6, -drivetrain.output->left_voltage / 12.0, kTalonBounds);
- } else {
- DisablePWMOutput(2);
- DisablePWMOutput(3);
- DisablePWMOutput(5);
- DisablePWMOutput(6);
- if (kEnableDrivetrain) {
- LOG(WARNING, "drivetrain not new enough\n");
+ if (true) {
+ drivetrain.output.FetchLatest();
+ if (drivetrain.output.get()) {
+ LOG_STRUCT(DEBUG, "will output", *drivetrain.output.get());
+ }
+ if (drivetrain.output.IsNewerThanMS(kOutputMaxAgeMS)) {
+ SetPWMOutput(2, drivetrain.output->right_voltage / 12.0, kTalonBounds);
+ SetPWMOutput(3, drivetrain.output->right_voltage / 12.0, kTalonBounds);
+ SetPWMOutput(5, -drivetrain.output->left_voltage / 12.0, kTalonBounds);
+ SetPWMOutput(6, -drivetrain.output->left_voltage / 12.0, kTalonBounds);
+ SetSolenoid(1, drivetrain.output->left_high);
+ SetSolenoid(2, drivetrain.output->right_high);
+ } else {
+ DisablePWMOutput(2);
+ DisablePWMOutput(3);
+ DisablePWMOutput(5);
+ DisablePWMOutput(6);
+ LOG_INTERVAL(drivetrain_old_);
}
}
- shifters.FetchLatest();
- if (shifters.get()) {
- SetSolenoid(1, shifters->set);
- SetSolenoid(2, !shifters->set);
- }
}
+
+ SimpleLogInterval drivetrain_old_ =
+ SimpleLogInterval(kOldLogInterval, WARNING, "drivetrain too old");
};
+constexpr ::aos::time::Time MotorWriter::kOldLogInterval;
+
} // namespace output
} // namespace frc971
diff --git a/frc971/output/output.gyp b/frc971/output/output.gyp
index 1d81c69..19fb6d1 100644
--- a/frc971/output/output.gyp
+++ b/frc971/output/output.gyp
@@ -35,6 +35,9 @@
'<(DEPTH)/frc971/control_loops/drivetrain/drivetrain.gyp:drivetrain_loop',
'<(AOS)/common/common.gyp:controls',
'<(DEPTH)/frc971/queues/queues.gyp:queues',
+ '<(AOS)/common/util/util.gyp:log_interval',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
],
},
],
diff --git a/frc971/prime/build.sh b/frc971/prime/build.sh
index 8130f01..e3d02a2 100755
--- a/frc971/prime/build.sh
+++ b/frc971/prime/build.sh
@@ -2,5 +2,32 @@
cd $(dirname $0)
-../../aos/build/build.sh linux-amd64 prime.gyp no prime-amd64 "$@" || exit 1
-../../aos/build/build.sh linux prime.gyp no prime "$@"
+if [[ "$1" == "amd64" ]]; then
+ NO_TARGET=1
+ shift 1
+elif [[ "$1" == "arm" ]]; then
+ NO_AMD64=1
+ shift 1
+fi
+
+[[ "$1" == "tests" ]] && NO_TARGET=1
+[[ "$1" == "deploy" ]] && NO_AMD64=1
+
+if [[ ! ${NO_TARGET} ]]; then
+ echo 'Building code for target...' 1>&2
+ ../../aos/build/build.sh linux prime.gyp no prime "$@"
+ if [[ $? -ne 0 ]]; then
+ echo 'Building code for target failed!' 1>&2
+ exit 1
+ fi
+ echo 'Building code for target succeeded.' 1>&2
+fi
+if [[ ! ${NO_AMD64} ]]; then
+ echo 'Building code for amd64...' 1>&2
+ ../../aos/build/build.sh linux-amd64 prime.gyp yes prime-amd64 "$@"
+ if [[ $? -ne 0 ]]; then
+ echo 'Building code for amd64 failed!' 1>&2
+ exit 1
+ fi
+ echo 'Building code for amd64 succeeded.' 1>&2
+fi
diff --git a/frc971/prime/prime.gyp b/frc971/prime/prime.gyp
index 0b59c0b..42a9894 100644
--- a/frc971/prime/prime.gyp
+++ b/frc971/prime/prime.gyp
@@ -7,13 +7,19 @@
'<(AOS)/build/aos_all.gyp:Prime',
'../control_loops/drivetrain/drivetrain.gyp:drivetrain',
'../control_loops/drivetrain/drivetrain.gyp:drivetrain_lib_test',
+ '../control_loops/claw/claw.gyp:claw',
+ '../control_loops/claw/claw.gyp:claw_lib_test',
'../control_loops/shooter/shooter.gyp:shooter',
'../control_loops/shooter/shooter.gyp:shooter_lib_test',
'../autonomous/autonomous.gyp:auto',
'../input/input.gyp:joystick_reader',
'../output/output.gyp:motor_writer',
'../input/input.gyp:sensor_receiver',
- '<(DEPTH)/bbb_cape/src/bbb/bbb.gyp:*',
+ '<(DEPTH)/bbb_cape/src/bbb/bbb.gyp:uart_reader_main',
+ '<(DEPTH)/bbb_cape/src/bbb/bbb.gyp:test_sensor_receiver',
+ '<(DEPTH)/bbb_cape/src/bbb/bbb.gyp:packet_finder_test',
+ '<(DEPTH)/bbb_cape/src/bbb/bbb.gyp:cows_test',
+ '<(DEPTH)/bbb_cape/src/flasher/flasher.gyp:stm32_flasher',
],
'copies': [
{
diff --git a/frc971/prime/scripts/start_list.txt b/frc971/prime/scripts/start_list.txt
index 79f9490..d7d20f4 100644
--- a/frc971/prime/scripts/start_list.txt
+++ b/frc971/prime/scripts/start_list.txt
@@ -1,4 +1,4 @@
-BinaryLogReader
+binary_log_writer
motor_writer
joystick_reader
drivetrain
diff --git a/frc971/queues/piston.q b/frc971/queues/piston.q
deleted file mode 100644
index 5811958..0000000
--- a/frc971/queues/piston.q
+++ /dev/null
@@ -1,8 +0,0 @@
-package frc971.control_loops;
-
-message Piston {
- bool set;
-};
-
-queue Piston shifters;
-queue Piston hangers;
diff --git a/frc971/queues/queues.gyp b/frc971/queues/queues.gyp
index 277c91c..a67ffe2 100644
--- a/frc971/queues/queues.gyp
+++ b/frc971/queues/queues.gyp
@@ -3,7 +3,7 @@
'queue_files': [
'gyro_angle.q',
'photo_sensor.q',
- 'piston.q',
+ 'to_log.q',
]
},
'targets': [
diff --git a/frc971/queues/to_log.q b/frc971/queues/to_log.q
new file mode 100644
index 0000000..c3cc076
--- /dev/null
+++ b/frc971/queues/to_log.q
@@ -0,0 +1,6 @@
+package frc971.logging_structs;
+
+struct CapeReading {
+ uint32_t sec;
+ uint32_t nsec;
+};
diff --git a/output/downloaded/.gitignore b/output/downloaded/.gitignore
index df9fd00..d91a4c2 100644
--- a/output/downloaded/.gitignore
+++ b/output/downloaded/.gitignore
@@ -4,11 +4,9 @@
/eigen-3.1.3/
/gccdist.zip
/gccdist/
-/gtest-1.6.0/
+/gtest-1.6.0-p1/
/gtest-1.6.0.zip
/gyp-1738/
-/javacv-0.2-bin.zip
-/javacv-bin/
/jpeg-8d/
/jpegsrc.v8d.tar.gz
/libjpeg/