Merge remote-tracking branch 'brian/devel' into claw
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 b7775a1..12ac2a1 100644
--- a/aos/build/aos.gyp
+++ b/aos/build/aos.gyp
@@ -50,23 +50,8 @@
'<(AOS)/common/common.gyp:time',
'<(AOS)/common/common.gyp:once',
'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 3006820..2584ee2 100644
--- a/aos/build/aos_all.gyp
+++ b/aos/build/aos_all.gyp
@@ -22,6 +22,7 @@
'../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 bf9d2eb..cf79fc3 100755
--- a/aos/build/build.sh
+++ b/aos/build/build.sh
@@ -86,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 7ae0d89..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': {
@@ -118,6 +93,8 @@
},
'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',
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 508f94f..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',
@@ -42,6 +42,52 @@
],
},
{
+ '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': [
@@ -136,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',
@@ -143,6 +191,8 @@
'timing',
'time',
'control_loop_queues',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
+ '<(AOS)/common/util/util.gyp:log_interval',
],
},
{
@@ -154,8 +204,9 @@
'dependencies': [
'<(EXTERNALS):gtest',
'queue_testutils',
- 'queue_test_queue',
+ 'test_queue',
'<(AOS)/common/util/util.gyp:thread',
+ 'die',
],
},
{
@@ -267,6 +318,7 @@
'dependencies': [
'<(EXTERNALS):gtest',
'mutex',
+ 'die',
],
},
{
@@ -284,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 c71ee46..673cc9a 100644
--- a/aos/common/logging/logging.h
+++ b/aos/common/logging/logging.h
@@ -71,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
}
@@ -154,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); \
}
@@ -163,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
@@ -186,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))
@@ -208,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 fa76c86..40909c2 100644
--- a/aos/common/logging/logging_impl.cc
+++ b/aos/common/logging/logging_impl.cc
@@ -6,6 +6,7 @@
#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 {
@@ -14,7 +15,6 @@
using internal::Context;
using internal::global_top_implementation;
-
// The root LogImplementation. It only logs to stderr/stdout.
// Some of the things specified in the LogImplementation documentation doesn't
// apply here (mostly the parts about being able to use LOG) because this is the
@@ -57,16 +57,15 @@
} // namespace
namespace internal {
+namespace {
-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();
@@ -75,16 +74,100 @@
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];
+ 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>(printed_bytes), printed);
+}
+
StreamLogImplementation::StreamLogImplementation(FILE *stream)
: stream_(stream) {}
diff --git a/aos/common/logging/logging_impl.h b/aos/common/logging/logging_impl.h
index ca19dbc..3d5b387 100644
--- a/aos/common/logging/logging_impl.h
+++ b/aos/common/logging/logging_impl.h
@@ -7,13 +7,21 @@
#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
@@ -32,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");
@@ -85,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
@@ -107,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_;
};
@@ -208,8 +254,16 @@
} 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);
@@ -218,8 +272,13 @@
// 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);
+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
diff --git a/aos/common/logging/logging_interface.cc b/aos/common/logging/logging_interface.cc
index 01fb708..b4244fb 100644
--- a/aos/common/logging/logging_interface.cc
+++ b/aos/common/logging/logging_interface.cc
@@ -4,6 +4,8 @@
#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).
@@ -21,30 +23,28 @@
cork_data.Reset();
}
-void ExecuteFormat(char *output, size_t output_size,
- const char *format, va_list ap) {
- static const char *continued = "...\n";
+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<uintmax_t>(ret) >= static_cast<uintmax_t>(size)) {
+ } 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);
}
-} // namespace internal
-
-using internal::Context;
-
-void LogImplementation::DoVLog(log_level level, const char *format, va_list ap,
- int levels) {
+void RunWithCurrentImplementation(
+ int levels, ::std::function<void(LogImplementation *)> function) {
Context *context = Context::Get();
- LogImplementation *top_implementation = context->implementation;
+ LogImplementation *const top_implementation = context->implementation;
LogImplementation *new_implementation = top_implementation;
LogImplementation *implementation = NULL;
assert(levels >= 1);
@@ -56,12 +56,24 @@
new_implementation = new_implementation->next();
}
context->implementation = new_implementation;
- implementation->DoLog(level, format, ap);
+ function(implementation);
context->implementation = top_implementation;
+}
- if (level == FATAL) {
- VDie(format, ap);
- }
+} // 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) {
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/queue.h b/aos/common/queue.h
index 77fdf0d..65bfa95 100644
--- a/aos/common/queue.h
+++ b/aos/common/queue.h
@@ -18,55 +18,11 @@
namespace aos {
-#ifndef SWIG
-
-// Prints the value from 1 message field into output.
-// output is where to write the text representation.
-// output_bytes should point to the number of bytes available to write in
-// output. It will be changed to the number of bytes that were actually written.
-// input is where to read the data in from.
-// input_bytes should point to the number of bytes available to read from input.
-// It will be changed to the number of bytes that were actually read.
-// type is the ID of a type to print. It must be a primitive type.
-//
-// The implementation of this is generated by the ruby code.
-// TODO(brians): Actually do that.
-bool PrintField(char *output, size_t *output_bytes, void *input,
- size_t *input_bytes, uint32_t type);
-
-// 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 added.
-//
-// Serializing/deserializing includes all of the fields too.
-struct MessageType {
- struct Field {
- uint32_t type;
- const char *name;
- };
-
- ~MessageType() {
- for (int i = 0; i < number_fields; ++i) {
- delete fields[i];
- }
- }
-
- // Returns -1 for error.
- ssize_t Serialize(char *buffer, size_t max_bytes) const;
- // bytes should start out as the number of bytes available in buffer and gets
- // set to the number actually read before returning.
- // Returns a new instance allocated with new or NULL for error.
- static MessageType *Deserialize(const char *buffer, size_t *bytes);
-
- uint32_t id;
- const char *name;
-
- int number_fields;
- const Field *fields[];
-};
-
-#endif // SWIG
+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;
@@ -76,17 +32,13 @@
Message() : sent_time(0, 0) {}
// Zeros out the time.
- // Overriden to zero the whole message.
void Zero();
// Returns the size of the common fields.
- // Overriden to return the size of the whole message.
static size_t Size() { return sizeof(Time); }
// Deserializes the common fields from the buffer.
- // Overriden to deserialize the whole message.
size_t Deserialize(const char *buffer);
// Serializes the common fields into the buffer.
- // Overriden to serialize the whole message.
size_t Serialize(char *buffer) const;
// Populates sent_time with the current time.
@@ -95,9 +47,7 @@
// Writes the contents of the message to the provided buffer.
size_t Print(char *buffer, int length) const;
-#ifndef SWIG
- const MessageType &GetType() const;
-#endif // SWIG
+ static const MessageType *GetType();
};
template <class T> class Queue;
diff --git a/aos/common/queue_test.cc b/aos/common/queue_test.cc
index 32d1d23..ebb0bba 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
diff --git a/aos/common/queue_types.cc b/aos/common/queue_types.cc
new file mode 100644
index 0000000..e2310fc
--- /dev/null
+++ b/aos/common/queue_types.cc
@@ -0,0 +1,282 @@
+#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 < 1) return false;
+ *output_bytes -= 1;
+ *(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..c665333
--- /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 79275c0..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': [
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/configuration.cc b/aos/linux_code/configuration.cc
index ef6ae3e..e5e5583 100644
--- a/aos/linux_code/configuration.cc
+++ b/aos/linux_code/configuration.cc
@@ -26,13 +26,16 @@
const in_addr *DoGetOwnIPAddress() {
static const char *kOverrideVariable = "FRC971_IP_OVERRIDE";
const char *override_ip = getenv(kOverrideVariable);
- if (override_ip != nullptr) {
+ if (override_ip != NULL) {
+ LOG(INFO, "Override IP is %s\n", override_ip);
static in_addr r;
if (inet_aton(override_ip, &r) != 0) {
return &r;
} else {
LOG(WARNING, "error parsing %s value '%s'\n", kOverrideVariable, override_ip);
}
+ } else {
+ LOG(INFO, "Couldn't get environmental variable.\n");
}
ifaddrs *addrs;
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 df7b658..08e8df8 100644
--- a/aos/linux_code/ipc_lib/ipc_lib.gyp
+++ b/aos/linux_code/ipc_lib/ipc_lib.gyp
@@ -60,6 +60,7 @@
'core_lib',
'<(AOS)/common/common.gyp:queue_testutils',
'<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/common.gyp:die',
],
},
{
@@ -76,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/logging/binary_log_file.cc b/aos/linux_code/logging/binary_log_file.cc
index e1e99b4..8c8c8b4 100644
--- a/aos/linux_code/logging/binary_log_file.cc
+++ b/aos/linux_code/logging/binary_log_file.cc
@@ -71,6 +71,9 @@
}
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;
}
@@ -78,15 +81,19 @@
msync(current_, kPageSize, MS_ASYNC | MS_INVALIDATE);
}
-void LogFileAccessor::MoveToEnd() {
- Unmap(current_);
+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));
}
- offset_ = info.st_size - kPageSize;
- MapNextPage();
+ bool r = offset_ == static_cast<off_t>(info.st_size - kPageSize);
+ is_last_page_ = r ? 2 : 1;
+ return r;
}
void LogFileAccessor::MapNextPage() {
@@ -116,6 +123,7 @@
LOG(FATAL, "munmap(%p, %zd) failed with %d: %s. aborting\n", location,
kPageSize, errno, strerror(errno));
}
+ is_last_page_ = 0;
}
} // namespace linux_code
diff --git a/aos/linux_code/logging/binary_log_file.h b/aos/linux_code/logging/binary_log_file.h
index 83f8dde..0f8c3fb 100644
--- a/aos/linux_code/logging/binary_log_file.h
+++ b/aos/linux_code/logging/binary_log_file.h
@@ -35,7 +35,7 @@
LogFileMessageHeader {
// Represents the type of an individual message.
enum class MessageType : uint16_t {
- // '\0'-terminated string.
+ // char[] (no '\0' on the end).
kString,
kStructType,
kStruct,
@@ -63,8 +63,7 @@
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 (ie any '\0's
- // etc).
+ // Both including all of the bytes in that part of the message.
uint32_t name_size, message_size;
uint16_t sequence;
@@ -94,8 +93,7 @@
// Asynchronously syncs all open mappings.
void Sync() const;
- // TODO(brians): This won't work any more.
- void MoveToEnd();
+ bool IsLastPage();
private:
// The size of the chunks that get mmaped/munmapped together. Large enough so
@@ -115,6 +113,9 @@
char *current_;
size_t position_;
+ // 0 = unknown, 1 = no, 2 = yes
+ int is_last_page_ = 0;
+
void MapNextPage();
void Unmap(void *location);
diff --git a/aos/linux_code/logging/binary_log_writer.cc b/aos/linux_code/logging/binary_log_writer.cc
index 22c635e..67cb166 100644
--- a/aos/linux_code/logging/binary_log_writer.cc
+++ b/aos/linux_code/logging/binary_log_writer.cc
@@ -10,17 +10,54 @@
#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();
@@ -81,23 +118,52 @@
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);
+ 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 = name_size;
- output->message_size = message_size;
+ 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;
- output->type = LogFileMessageHeader::MessageType::kString;
- memcpy(output_strings, msg->name, name_size);
- memcpy(output_strings + name_size, msg->message, message_size);
+ 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);
diff --git a/aos/linux_code/logging/linux_logging.cc b/aos/linux_code/logging/linux_logging.cc
index 985577b..676e3b7 100644
--- a/aos/linux_code/logging/linux_logging.cc
+++ b/aos/linux_code/logging/linux_logging.cc
@@ -25,15 +25,29 @@
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);
}
};
@@ -48,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
index dc27c80..98021b5 100644
--- a/aos/linux_code/logging/log_displayer.cc
+++ b/aos/linux_code/logging/log_displayer.cc
@@ -7,8 +7,13 @@
#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 {
@@ -45,10 +50,16 @@
int main(int argc, char **argv) {
const char *filter_name = NULL;
+ size_t filter_length = 0;
log_level filter_level = INFO;
- bool follow = false, start_at_beginning = true;
+ 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'},
@@ -80,6 +91,7 @@
abort();
case 'n':
filter_name = optarg;
+ filter_length = strlen(filter_name);
break;
case 'l':
filter_level = ::aos::logging::str_log(optarg);
@@ -93,16 +105,16 @@
break;
case 'f':
follow = true;
- start_at_beginning = false;
+ skip_to_end = true;
break;
case 't':
follow = false;
break;
case 'b':
- start_at_beginning = true;
+ skip_to_end = false;
break;
case 'e':
- start_at_beginning = false;
+ skip_to_end = true;
break;
case 'm':
abort();
@@ -138,10 +150,12 @@
exit(EXIT_FAILURE);
}
::aos::logging::linux_code::LogFileAccessor accessor(fd, false);
- if (!start_at_beginning) {
- accessor.MoveToEnd();
+
+ if (skip_to_end) {
+ fputs("skipping old logs...\n", stderr);
}
- const ::aos::logging::linux_code::LogFileMessageHeader *msg;
+
+ const LogFileMessageHeader *msg;
::aos::logging::LogMessage log_message;
do {
msg = accessor.ReadNextMessage(follow);
@@ -149,25 +163,81 @@
fputs("reached end of file\n", stderr);
return 0;
}
- 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) {
+
+ 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;
- 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));
+ 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/logging/logging.gyp b/aos/linux_code/logging/logging.gyp
index 95459f3..32df43d 100644
--- a/aos/linux_code/logging/logging.gyp
+++ b/aos/linux_code/logging/logging.gyp
@@ -12,6 +12,7 @@
'<(AOS)/linux_code/linux_code.gyp:init',
'<(AOS)/linux_code/linux_code.gyp:configuration',
'binary_log_file',
+ '<(AOS)/common/common.gyp:queue_types',
],
},
{
@@ -37,6 +38,7 @@
'<(AOS)/build/aos.gyp:logging',
'<(AOS)/linux_code/linux_code.gyp:init',
'binary_log_file',
+ '<(AOS)/common/common.gyp:queue_types',
],
},
{
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/src/bbb/bbb.gyp b/bbb_cape/src/bbb/bbb.gyp
index 25db9a5..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',
],
},
{
diff --git a/bbb_cape/src/bbb/cape_manager.cc b/bbb_cape/src/bbb/cape_manager.cc
index b680c4a..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);
diff --git a/bbb_cape/src/bbb/packet_finder.cc b/bbb_cape/src/bbb/packet_finder.cc
index 747eaf8..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),
@@ -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 f9395c2..fd4b79d 100644
--- a/bbb_cape/src/bbb/sensor_reader.cc
+++ b/bbb_cape/src/bbb/sensor_reader.cc
@@ -39,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;
@@ -67,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/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/flasher/stm32_flasher.cc b/bbb_cape/src/flasher/stm32_flasher.cc
index c2e8bf2..48887d2 100644
--- a/bbb_cape/src/flasher/stm32_flasher.cc
+++ b/bbb_cape/src/flasher/stm32_flasher.cc
@@ -174,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) {
@@ -187,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));
@@ -219,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);
}
diff --git a/frc971/constants.cc b/frc971/constants.cc
index 66ff80f..672d5ef 100755
--- a/frc971/constants.cc
+++ b/frc971/constants.cc
@@ -38,6 +38,13 @@
0.47};
const ShifterHallEffect kPracticeRightDriveShifter{2.070124, 0.838993, 0.62,
0.55};
+const double shooter_lower_physical_limit=0.0;
+const double shooter_upper_physical_limit=0.0;
+const double shooter_voltage=0.0;
+const double shooter_hall_effect_start_position=0.0;
+const double shooter_zeroing_off_speed=0.0;
+const double shooter_zeroing_speed=0.0;
+const double position=0.0;
const Values *DoGetValues() {
uint16_t team = ::aos::network::GetTeamNumber();
@@ -53,6 +60,16 @@
true,
control_loops::MakeVClutchDrivetrainLoop,
control_loops::MakeClutchDrivetrainLoop,
+ // ShooterLimits
+ // TODO(ben): make these real numbers
+ {0.0, 100.0, {-3.0, 2.0}, {-1, 4.0}, {2.0, 7.0}},
+ shooter_voltage,
+ // shooter_total_length
+ 100.0,
+ shooter_hall_effect_start_position,
+ shooter_zeroing_off_speed,
+ shooter_zeroing_speed,
+ position,
{0.5,
0.1,
0.1,
@@ -76,6 +93,16 @@
false,
control_loops::MakeVDogDrivetrainLoop,
control_loops::MakeDogDrivetrainLoop,
+ // ShooterLimits
+ // TODO(ben): make these real numbers
+ {0.0, 100.0, {-3.0, 2.0}, {-1, 4.0}, {2.0, 7.0}},
+ shooter_voltage,
+ // shooter_total_length
+ 100.0,
+ shooter_hall_effect_start_position,
+ shooter_zeroing_off_speed,
+ shooter_zeroing_speed,
+ position,
{0.5,
0.2,
0.1,
diff --git a/frc971/constants.h b/frc971/constants.h
index cb59a96..68dfea4 100755
--- a/frc971/constants.h
+++ b/frc971/constants.h
@@ -25,6 +25,12 @@
// This structure contains current values for all of the things that change.
struct Values {
+ // This is useful for representing the 2 sides of a hall effect sensor etc.
+ struct Pair {
+ double lower_limit;
+ double upper_limit;
+ };
+
// The ratio from the encoder shaft to the drivetrain wheels.
double drivetrain_encoder_ratio;
@@ -39,6 +45,23 @@
::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;
+ Pair plunger_back;
+ Pair pusher_distal;
+ Pair pusher_proximal;
+ };
+
+ ShooterLimits shooter;
+
+ double shooter_voltage;
+ double shooter_total_length;
+ double shooter_hall_effect_start_position;
+ double shooter_zeroing_off_speed;
+ double shooter_zeroing_speed;
+ double position;
struct Claws {
double claw_zeroing_off_speed;
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/python/shooter.py b/frc971/control_loops/python/shooter.py
index 11699ac..bb88523 100755
--- a/frc971/control_loops/python/shooter.py
+++ b/frc971/control_loops/python/shooter.py
@@ -132,17 +132,17 @@
pylab.show()
# Write the generated constants out to a file.
- if len(argv) != 3:
+ if len(argv) != 5:
print "Expected .h file name and .cc file name for"
print "both the plant and unaugmented plant"
else:
unaug_shooter = Shooter("RawShooter")
unaug_loop_writer = control_loop.ControlLoopWriter("RawShooter",
[unaug_shooter])
- #if argv[3][-3:] == '.cc':
- # unaug_loop_writer.Write(argv[4], argv[3])
- #else:
- # unaug_loop_writer.Write(argv[3], argv[4])
+ if argv[3][-3:] == '.cc':
+ unaug_loop_writer.Write(argv[4], argv[3])
+ else:
+ unaug_loop_writer.Write(argv[3], argv[4])
loop_writer = control_loop.ControlLoopWriter("Shooter", [shooter])
if argv[1][-3:] == '.cc':
diff --git a/frc971/control_loops/shooter/shooter.cc b/frc971/control_loops/shooter/shooter.cc
old mode 100644
new mode 100755
index 56fd74b..86991e8
--- a/frc971/control_loops/shooter/shooter.cc
+++ b/frc971/control_loops/shooter/shooter.cc
@@ -1,79 +1,305 @@
-#include "frc971/control_loops/shooters/shooters.h"
+#include "frc971/control_loops/shooter/shooter.h"
#include <stdio.h>
#include <algorithm>
#include "aos/common/control_loop/control_loops.q.h"
+#include "aos/common/control_loop/control_loops.q.h"
#include "aos/common/logging/logging.h"
#include "frc971/constants.h"
-#include "frc971/control_loops/shooters/top_shooter_motor_plant.h"
-#include "frc971/control_loops/shooters/bottom_shooter_motor_plant.h"
+#include "frc971/control_loops/shooter/shooter_motor_plant.h"
namespace frc971 {
namespace control_loops {
-ShooterMotor::ShooterMotor(control_loops::ShooterLoop *my_shooter)
- : aos::control_loops::ControlLoop<control_loops::ShooterLoop>(my_shooter),
- zeroed_joint_(MakeShooterLoop()) {
- {
- using ::frc971::constants::GetValues;
- ZeroedJoint<1>::ConfigurationData config_data;
+using ::aos::time::Time;
+
+void ZeroedStateFeedbackLoop::CapU() {
+ const double old_voltage = voltage_;
+ voltage_ += U(0, 0);
- config_data.lower_limit = GetValues().shooter_lower_limit;
- config_data.upper_limit = GetValues().shooter_upper_limit;
- config_data.hall_effect_start_position[0] =
- GetValues().shooter_hall_effect_start_position;
- config_data.zeroing_off_speed = GetValues().shooter_zeroing_off_speed;
- config_data.zeroing_speed = GetValues().shooter_zeroing_speed;
+ uncapped_voltage_ = voltage_;
- config_data.max_zeroing_voltage = 5.0;
- config_data.deadband_voltage = 0.0;
+ double limit = zeroing_state() != UNKNOWN_POSITION ? 12.0 : kZeroingMaxVoltage;
- zeroed_joint_.set_config_data(config_data);
+ // 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));
}
+
+ voltage_ = std::min(limit, voltage_);
+ voltage_ = std::max(-limit, voltage_);
+ U(0, 0) = voltage_ - old_voltage;
+ LOG(DEBUG, "abc %f\n", X_hat(2, 0) - voltage_);
+ LOG(DEBUG, "error %f\n", X_hat(0, 0) - R(0, 0));
+
+ last_voltage_ = voltage_;
}
-// Positive is up, and positive power is up.
+ShooterMotor::ShooterMotor(control_loops::ShooterGroup *my_shooter)
+ : aos::control_loops::ControlLoop<control_loops::ShooterGroup>(my_shooter),
+ shooter_(MakeShooterLoop()),
+ calibration_position_(0.0),
+ state_(STATE_INITIALIZE),
+ loading_problem_end_time_(0,0),
+ shooter_brake_set_time_(0,0),
+ prepare_fire_end_time_(0,0),
+ shot_end_time_(0,0),
+ cycles_not_moved_(0) { }
+
+
+// Positive is out, and positive power is out.
void ShooterMotor::RunIteration(
- const ::aos::control_loops::Goal *goal,
- const control_loops::ShooterLoop::Position *position,
- ::aos::control_loops::Output *output,
- ::aos::control_loops::Status * status) {
+ const control_loops::ShooterGroup::Goal *goal,
+ const control_loops::ShooterGroup::Position *position,
+ control_loops::ShooterGroup::Output *output,
+ control_loops::ShooterGroup::Status * status) {
+ constexpr double dt = 0.01;
+
+ // we must always have these or we have issues.
+ if (goal == NULL || status == NULL) {
+ if (output) output->voltage = 0;
+ LOG(ERROR, "Thought I would just check for null and die.\n");
+ return;
+ }
// Disable the motors now so that all early returns will return with the
// motors disabled.
- if (output) {
- output->voltage = 0;
- }
+ if (output) output->voltage = 0;
- ZeroedJoint<1>::PositionData transformed_position;
- ZeroedJoint<1>::PositionData *transformed_position_ptr =
- &transformed_position;
- if (!position) {
- transformed_position_ptr = NULL;
- } else {
- transformed_position.position = position->pos;
- transformed_position.hall_effects[0] = position->hall_effect;
- transformed_position.hall_effect_positions[0] = position->calibration;
- }
+ const frc971::constants::Values &values = constants::GetValues();
- const double voltage = zeroed_joint_.Update(transformed_position_ptr,
- output != NULL,
- goal->goal, 0.0);
+ double real_position = position->position - calibration_position_;
+
+ // don't even let the control loops run
+ bool shooter_loop_disable = false;
+
+ // adds voltage to take up slack in gears before shot
+ bool apply_some_voltage = false;
+
+ switch (state_) {
+ case STATE_INITIALIZE:
+ // start off with the assumption that we are at the value
+ // futhest back given our sensors
+ if (position && position->pusher_distal_hall_effect){
+ calibration_position_ = position->position -
+ values.shooter.pusher_distal.lower_limit;
+ } else if (position && position->pusher_proximal_hall_effect) {
+ calibration_position_ = position->position -
+ values.shooter.pusher_proximal.lower_limit;
+ } else {
+ calibration_position_ = values.shooter_total_length;
+ }
+
+ state_ = STATE_REQUEST_LOAD;
+
+ // zero out initial goal
+ shooter_.SetGoalPosition(0.0, 0.0);
+ if (position) {
+ output->latch_piston = position->plunger_back_hall_effect;
+ } else {
+ // we don't know what is going on so just close the latch to be safe
+ output->latch_piston = true;
+ }
+ output->brake_piston = false;
+ break;
+ case STATE_REQUEST_LOAD:
+ if (position->plunger_back_hall_effect && position->latch_hall_effect) {
+ // already latched
+ state_ = STATE_PREPARE_SHOT;
+ } else if (position->pusher_distal_hall_effect ||
+ (real_position) < 0) {
+ state_ = STATE_LOAD_BACKTRACK;
+ if (position) {
+ calibration_position_ = position->position;
+ }
+ } else {
+ state_ = STATE_LOAD;
+ }
+
+ shooter_.SetGoalPosition(0.0, 0.0);
+ if (position && output) output->latch_piston = position->plunger_back_hall_effect;
+ output->brake_piston = false;
+ break;
+ case STATE_LOAD_BACKTRACK:
+ if (real_position < values.shooter.pusher_distal.upper_limit + 0.01) {
+ shooter_.SetGoalPosition(position->position + values.shooter_zeroing_speed*dt,
+ values.shooter_zeroing_speed);
+ } else {
+ state_ = STATE_LOAD;
+ }
+
+ output->latch_piston = false;
+ output->brake_piston = true;
+ break;
+ case STATE_LOAD:
+ if (position->pusher_proximal_hall_effect &&
+ !last_position_.pusher_proximal_hall_effect) {
+ calibration_position_ = position->position -
+ values.shooter.pusher_proximal.upper_limit;
+ }
+ if (position->pusher_distal_hall_effect &&
+ !last_position_.pusher_distal_hall_effect) {
+ calibration_position_ = position->position -
+ values.shooter.pusher_distal.lower_limit;
+ }
+
+ shooter_.SetGoalPosition(calibration_position_, 0.0);
+ if (position && output) output->latch_piston = position->plunger_back_hall_effect;
+ if(output) output->brake_piston = false;
+
+ if (position->plunger_back_hall_effect && position->latch_hall_effect) {
+ state_ = STATE_PREPARE_SHOT;
+ } else if (position->plunger_back_hall_effect &&
+ position->position == PowerToPosition(goal->shot_power)) {
+ //TODO_ben: I'm worried it will bounce states if the position is drifting slightly
+ state_ = STATE_LOADING_PROBLEM;
+ loading_problem_end_time_ = Time::Now(Time::kDefaultClock) + Time::InSeconds(3.0);
+ }
+ break;
+ case STATE_LOADING_PROBLEM:
+ if (position->plunger_back_hall_effect && position->latch_hall_effect) {
+ state_ = STATE_PREPARE_SHOT;
+ } else if (real_position < -0.02 || Time::Now() > loading_problem_end_time_) {
+ state_ = STATE_UNLOAD;
+ }
+
+ shooter_.SetGoalPosition(position->position - values.shooter_zeroing_speed*dt,
+ values.shooter_zeroing_speed);
+ if (output) output->latch_piston = true;
+ if (output) output->brake_piston = false;
+ break;
+ case STATE_PREPARE_SHOT:
+ shooter_.SetGoalPosition(
+ PowerToPosition(goal->shot_power), 0.0);
+ //TODO_ben: I'm worried it will bounce states if the position is drifting slightly
+ if (position->position == PowerToPosition(goal->shot_power)) {
+ state_ = STATE_READY;
+ output->latch_piston = true;
+ output->brake_piston = true;
+ shooter_brake_set_time_ = Time::Now(Time::kDefaultClock) + Time::InSeconds(3.0);
+ } else {
+ output->latch_piston =true;
+ output->brake_piston = false;
+ }
+ break;
+ case STATE_READY:
+ if (Time::Now() > shooter_brake_set_time_) {
+ shooter_loop_disable = true;
+ if (goal->unload_requested) {
+ state_ = STATE_UNLOAD;
+ } else if (PowerToPosition(goal->shot_power)
+ != position->position) {
+ //TODO_ben: I'm worried it will bounce states if the position is drifting slightly
+ state_ = STATE_PREPARE_SHOT;
+ }else if (goal->shot_requested) {
+ state_ = STATE_REQUEST_FIRE;
+ }
+
+ }
+ output->latch_piston = true;
+ output->brake_piston = true;
+ break;
+ case STATE_REQUEST_FIRE:
+ shooter_loop_disable = true;
+ if (position->plunger_back_hall_effect) {
+ prepare_fire_end_time_ = Time::Now(Time::kDefaultClock)
+ + Time::InMS(40.0);
+ apply_some_voltage = true;
+ state_ = STATE_PREPARE_FIRE;
+ } else {
+ state_ = STATE_REQUEST_LOAD;
+ }
+ break;
+ case STATE_PREPARE_FIRE:
+ shooter_loop_disable = true;
+ if (Time::Now(Time::kDefaultClock) < prepare_fire_end_time_) {
+ apply_some_voltage = true;
+ } else {
+ state_ = STATE_FIRE;
+ cycles_not_moved_ = 0;
+ shot_end_time_ = Time::Now(Time::kDefaultClock) +
+ Time::InMS(500);
+ }
+
+ output->latch_piston = true;
+ output->brake_piston = true;
+
+ break;
+ case STATE_FIRE:
+ shooter_loop_disable = true;
+ //TODO_ben: need approamately equal
+ if (last_position_.position - position->position < 7) {
+ cycles_not_moved_++;
+ } else {
+ cycles_not_moved_ = 0;
+ }
+ if ((real_position < 10.0 && cycles_not_moved_ > 5) ||
+ Time::Now(Time::kDefaultClock) > shot_end_time_) {
+ state_ = STATE_REQUEST_LOAD;
+ }
+ output->latch_piston = true;
+ output->brake_piston = true;
+ break;
+ case STATE_UNLOAD:
+ if (position->plunger_back_hall_effect && position->latch_hall_effect) {
+ shooter_.SetGoalPosition(0.02, 0.0);
+ if (real_position == 0.02) {
+ output->latch_piston = false;
+ }
+ } else {
+ output->latch_piston = false;
+ state_ = STATE_UNLOAD_MOVE;
+ }
+
+ output->brake_piston = false;
+ break;
+ case STATE_UNLOAD_MOVE:
+ if (position->position > values.shooter_total_length - 0.03) {
+ shooter_.SetGoalPosition(position->position, 0.0);
+ state_ = STATE_READY_UNLOAD;
+ } else {
+ shooter_.SetGoalPosition(
+ position->position + values.shooter_zeroing_speed*dt,
+ values.shooter_zeroing_speed);
+ }
+
+ output->latch_piston = false;
+ output->brake_piston = false;
+ break;
+ case STATE_READY_UNLOAD:
+ if (!goal->unload_requested) {
+ state_ = STATE_REQUEST_LOAD;
+ }
+
+ output->latch_piston = false;
+ output->brake_piston = false;
+ break;
+ }
if (position) {
- LOG(DEBUG, "pos: %f hall: %s absolute: %f\n",
- position->pos,
- position->hall_effect ? "true" : "false",
- zeroed_joint_.absolute_position());
+ last_position_ = *position;
+ LOG(DEBUG, "pos: hall: real: %.2f absolute: %.2f\n",
+ real_position, position->position);
}
- if (output) {
- output->voltage = voltage;
+ if (apply_some_voltage) {
+ output->voltage = 2.0;
+ } else if (!shooter_loop_disable) {
+ output->voltage = shooter_.voltage();
+ } else {
+ output->voltage = 0.0;
}
- status->done = ::std::abs(zeroed_joint_.absolute_position() - goal->goal) < 0.004;
+
+ status->done = ::std::abs(position->position - PowerToPosition(goal->shot_power)) < 0.004;
}
} // namespace control_loops
diff --git a/frc971/control_loops/shooter/shooter.gyp b/frc971/control_loops/shooter/shooter.gyp
old mode 100644
new mode 100755
index c4e9971..9811918
--- a/frc971/control_loops/shooter/shooter.gyp
+++ b/frc971/control_loops/shooter/shooter.gyp
@@ -23,6 +23,7 @@
'sources': [
'shooter.cc',
'shooter_motor_plant.cc',
+ 'unaugmented_shooter_motor_plant.cc',
],
'dependencies': [
'shooter_loop',
diff --git a/frc971/control_loops/shooter/shooter.h b/frc971/control_loops/shooter/shooter.h
new file mode 100755
index 0000000..4705f86
--- /dev/null
+++ b/frc971/control_loops/shooter/shooter.h
@@ -0,0 +1,215 @@
+#ifndef FRC971_CONTROL_LOOPS_shooter_shooter_H_
+#define FRC971_CONTROL_LOOPS_shooter_shooter_H_
+
+#include <memory>
+
+#include "aos/common/control_loop/ControlLoop.h"
+#include "frc971/control_loops/state_feedback_loop.h"
+#include "aos/common/time.h"
+
+#include "frc971/constants.h"
+#include "frc971/control_loops/shooter/shooter_motor_plant.h"
+#include "frc971/control_loops/shooter/shooter.q.h"
+
+namespace frc971 {
+namespace control_loops {
+namespace testing {
+class ShooterTest_NoWindupPositive_Test;
+class ShooterTest_NoWindupNegative_Test;
+};
+
+using ::aos::time::Time;
+
+// Note: Everything in this file assumes that there is a 1 cycle delay between
+// power being requested and it showing up at the motor. It assumes that
+// X_hat(2, 1) is the voltage being applied as well. It will go unstable if
+// that isn't true.
+
+// This class implements the CapU function correctly given all the extra
+// information that we know about 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> {
+ 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),
+ 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,
+ // Ready for use during teleop.
+ CALIBRATED
+ };
+
+
+ void set_zeroing_state(JointZeroingState zeroing_state) {
+ zeroing_state_ = zeroing_state;
+ }
+
+
+ JointZeroingState zeroing_state() const { return zeroing_state_; }
+
+
+ // Sets the calibration offset given the absolute angle and the corrisponding
+ // encoder value.
+ void SetCalibration(double encoder_val, double known_position) {
+ offset_ = known_position - encoder_val;
+ }
+
+
+ bool SetCalibrationOnEdge(const constants::Values::ShooterLimits &shooter_values,
+ JointZeroingState zeroing_state) {
+ double edge_encoder;
+ double known_position;
+ if (GetPositionOfEdge(shooter_values, &edge_encoder, &known_position)) {
+ LOG(INFO, "Calibration edge.\n");
+ SetCalibration(edge_encoder, known_position);
+ set_zeroing_state(zeroing_state);
+ return true;
+ }
+ return false;
+ }
+
+
+ void SetPositionValues(double position) {
+ Eigen::Matrix<double, 1, 1> Y;
+ Y << position;
+ Correct(Y);
+ }
+
+
+ void SetGoalPosition(double desired_position,
+ double desired_velocity) {
+ // austin said something about which matrix to set, but I didn't under
+ // very much of it
+ //some_matrix = {desired_position, desired_velocity};
+ R << desired_position, desired_velocity, 0;
+ }
+
+ 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::ShooterLimits &shooter,
+ double *edge_encoder, double *known_position);
+
+#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_;
+
+ JointZeroingState zeroing_state_;
+ double encoder_;
+ double last_encoder_;
+};
+
+
+class ShooterMotor
+ : public aos::control_loops::ControlLoop<control_loops::ShooterGroup> {
+ public:
+ explicit ShooterMotor(
+ control_loops::ShooterGroup *my_shooter = &control_loops::shooter_queue_group);
+
+ // True if the goal was moved to avoid goal windup.
+ //bool capped_goal() const { return shooter_.capped_goal(); }
+
+ double PowerToPosition(double power) {
+ LOG(WARNING, "power to position not correctly implemented");
+ const frc971::constants::Values &values = constants::GetValues();
+ double new_pos =
+ (power > values.shooter.upper_limit) ? values.shooter.upper_limit
+ : (power < 0.0) ? 0.0 : power;
+
+ return new_pos;
+ }
+
+typedef enum {
+ STATE_INITIALIZE,
+ STATE_REQUEST_LOAD,
+ STATE_LOAD_BACKTRACK,
+ STATE_LOAD,
+ STATE_LOADING_PROBLEM,
+ STATE_PREPARE_SHOT,
+ STATE_READY,
+ STATE_REQUEST_FIRE,
+ STATE_PREPARE_FIRE,
+ STATE_FIRE,
+ STATE_UNLOAD,
+ STATE_UNLOAD_MOVE,
+ STATE_READY_UNLOAD
+} State;
+
+ protected:
+
+ virtual void RunIteration(
+ const ShooterGroup::Goal *goal,
+ const control_loops::ShooterGroup::Position *position,
+ ShooterGroup::Output *output,
+ ShooterGroup::Status *status);
+
+ private:
+ // Friend the test classes for acces to the internal state.
+ friend class testing::ShooterTest_NoWindupPositive_Test;
+ friend class testing::ShooterTest_NoWindupNegative_Test;
+
+ control_loops::ShooterGroup::Position last_position_;
+
+ ZeroedStateFeedbackLoop shooter_;
+
+ // position need to zero
+ double calibration_position_;
+
+ // state machine state
+ State state_;
+
+ // time to giving up on loading problem
+ Time loading_problem_end_time_;
+
+ // wait for brake to set
+ Time shooter_brake_set_time_;
+
+ // we are attempting to take up some of the backlash
+ // in the gears before the plunger hits
+ Time prepare_fire_end_time_;
+
+ // time that shot must have completed
+ Time shot_end_time_;
+
+ // track cycles that we are stuck to detect errors
+ int cycles_not_moved_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShooterMotor);
+};
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // FRC971_CONTROL_LOOPS_shooter_shooter_H_
diff --git a/frc971/control_loops/shooter/shooter.q b/frc971/control_loops/shooter/shooter.q
old mode 100644
new mode 100755
index 860162e..670b10f
--- a/frc971/control_loops/shooter/shooter.q
+++ b/frc971/control_loops/shooter/shooter.q
@@ -1,35 +1,77 @@
package frc971.control_loops;
-import "aos/common/control_loop/control_looops.q";
+import "aos/common/control_loop/control_loops.q";
-queue_group ShooterLoop {
+queue_group ShooterGroup {
implements aos.control_loops.ControlLoop;
+ message Output {
+ double voltage;
+ // true: latch engaged, false: latch open
+ bool latch_piston;
+ // true: brake engaged false: brake released
+ bool brake_piston;
+ };
message Goal {
- // The energy to load to in joules.
- double energy;
+ // encoder ticks of shot energy.
+ double shot_power;
// Shoots as soon as this is true.
- bool shoot;
+ bool shot_requested;
+ bool unload_requested;
};
message Position {
- bool back_hall_effect;
+ // back on the plunger
+ bool plunger_back_hall_effect;
+ // truely back on the pusher
+ bool pusher_distal_hall_effect;
+ // warning that we are back on the pusher
+ bool pusher_proximal_hall_effect;
+ // the latch is closed
+ bool latch_hall_effect;
+ // the brake is closed
+ bool brake_hall_effect;
+
+ // count of positive edges
+ int64_t plunger_back_hall_effect_posedge_count;
+ // count of negative edges
+ int64_t plunger_back_hall_effect_negedge_count;
+ // count of positive edges
+ int64_t pusher_distal_hall_effect_posedge_count;
+ // count of negative edges
+ int64_t pusher_distal_hall_effect_negedge_count;
+ // count of positive edges
+ int64_t pusher_proximal_hall_effect_posedge_count;
+ // count of negative edges
+ int64_t pusher_proximal_hall_effect_negedge_count;
+ // count of positive edges
+ int64_t latch_hall_effect_posedge_count;
+ // count of negative edges
+ int64_t latch_hall_effect_negedge_count;
+
// In meters, out is positive.
double position;
- double back_calibration;
+
+ // last positive edge
+ double posedge_value;
+ // last negative edge
+ double negedge_value;
};
+ // I don't think this is needed, but it is here
+ // so I won't delete it yet.
message Status {
// Whether it's ready to shoot right now.
bool ready;
// Whether the plunger is in and out of the way of grabbing a ball.
bool cocked;
// How many times we've shot.
- int shots;
+ int32_t shots;
+ bool done;
};
queue Goal goal;
queue Position position;
- queue aos.control_loops.Output output;
+ queue Output output;
queue Status status;
};
-queue_group ShooterLoop shooter;
+queue_group ShooterGroup shooter_queue_group;
diff --git a/frc971/control_loops/shooter/shooter_lib_test.cc b/frc971/control_loops/shooter/shooter_lib_test.cc
new file mode 100755
index 0000000..b2da13e
--- /dev/null
+++ b/frc971/control_loops/shooter/shooter_lib_test.cc
@@ -0,0 +1,255 @@
+#include <unistd.h>
+
+#include <memory>
+
+#include "gtest/gtest.h"
+#include "aos/common/queue.h"
+#include "aos/common/queue_testutils.h"
+#include "frc971/control_loops/shooter/shooter.q.h"
+#include "frc971/control_loops/shooter/shooter.h"
+#include "frc971/control_loops/shooter/unaugmented_shooter_motor_plant.h"
+#include "frc971/constants.h"
+
+
+using ::aos::time::Time;
+
+namespace frc971 {
+namespace control_loops {
+namespace testing {
+
+// Class which simulates the shooter and sends out queue messages containing the
+// position.
+class ShooterSimulation {
+ public:
+ // Constructs a motor simulation.
+ ShooterSimulation(double initial_position)
+ : shooter_plant_(new StateFeedbackPlant<3, 1, 1>(MakeShooterPlant())),
+ shooter_queue_group_(
+ ".frc971.control_loops.shooter_queue_group", 0xcbf22ba9,
+ ".frc971.control_loops.shooter_queue_group.goal",
+ ".frc971.control_loops.shooter_queue_group.position",
+ ".frc971.control_loops.shooter_queue_group.output",
+ ".frc971.control_loops.shooter_queue_group.status") {
+ Reinitialize(initial_position);
+ }
+ void Reinitialize(double initial_position) {
+ LOG(INFO, "Reinitializing to {top: %f}\n", initial_position);
+ StateFeedbackPlant<3, 1, 1>* plant = shooter_plant_.get();
+ initial_position_ = initial_position;
+ plant->X(0, 0) = initial_position_;
+ plant->X(1, 0) = 0.0;
+ plant->Y = plant->C() * plant->X;
+ last_voltage_ = 0.0;
+ last_plant_position_ = 0.0;
+ SetPhysicalSensors(&last_position_message_);
+ }
+
+
+ // Returns the absolute angle of the wrist.
+ double GetAbsolutePosition() const {
+ return shooter_plant_->Y(0,0);
+ }
+
+
+ // Returns the adjusted angle of the wrist.
+ double GetPosition() const {
+ return GetAbsolutePosition() - initial_position_;
+ }
+
+
+ // Makes sure pos is inside range (inclusive)
+ bool CheckRange(double pos, struct constants::Values::Pair pair) {
+ return (pos >= pair.lower_limit && pos <= pair.upper_limit);
+ }
+
+
+ // Sets the values of the physical sensors that can be directly observed
+ // (encoder, hall effect).
+ void SetPhysicalSensors(control_loops::ShooterGroup::Position *position) {
+ const frc971::constants::Values& values = constants::GetValues();
+ position->position = GetPosition();
+
+ LOG(DEBUG, "Physical shooter at {%f}\n", position->position);
+
+ // Signal that the hall effect sensor has been triggered if it is within
+ // the correct range.
+ position->plunger_back_hall_effect =
+ CheckRange(position->position, values.shooter.plunger_back);
+ position->pusher_distal_hall_effect =
+ CheckRange(position->position, values.shooter.pusher_distal);
+ position->pusher_proximal_hall_effect =
+ CheckRange(position->position, values.shooter.pusher_proximal);
+ }
+
+ void UpdateEffectEdge(bool effect, bool last_effect, double upper_angle,
+ double lower_angle, double position,
+ double &posedge_value, double &negedge_value,
+ int64_t &posedge_count, int64_t &negedge_count) {
+ if (effect && !last_effect) {
+ ++posedge_count;
+ if (last_position_message_.position < lower_angle) {
+ posedge_value = lower_angle - initial_position_;
+ } else {
+ posedge_value = upper_angle - initial_position_;
+ }
+ }
+
+ if (!effect && last_effect) {
+ ++negedge_count;
+ if (position < lower_angle) {
+ negedge_value = lower_angle - initial_position_;
+ } else {
+ negedge_value = upper_angle - initial_position_;
+ }
+ }
+ }
+
+
+ // Sends out the position queue messages.
+ void SendPositionMessage() {
+ const frc971::constants::Values& values = constants::GetValues();
+ ::aos::ScopedMessagePtr<control_loops::ShooterGroup::Position> position =
+ shooter_queue_group_.position.MakeMessage();
+
+ SetPhysicalSensors(position.get());
+
+ // Handle plunger hall effect
+ UpdateEffectEdge(position->plunger_back_hall_effect,
+ last_position_message_.plunger_back_hall_effect,
+ values.shooter.plunger_back.upper_limit,
+ values.shooter.plunger_back.lower_limit,
+ position->position, position->posedge_value,
+ position->negedge_value,
+ position->plunger_back_hall_effect_posedge_count,
+ position->plunger_back_hall_effect_negedge_count);
+
+ // Handle pusher distal hall effect
+ UpdateEffectEdge(position->pusher_distal_hall_effect,
+ last_position_message_.pusher_distal_hall_effect,
+ values.shooter.pusher_distal.upper_limit,
+ values.shooter.pusher_distal.lower_limit,
+ position->position, position->posedge_value,
+ position->negedge_value,
+ position->pusher_distal_hall_effect_posedge_count,
+ position->pusher_distal_hall_effect_negedge_count);
+
+ // Handle pusher proximal hall effect
+ UpdateEffectEdge(position->pusher_proximal_hall_effect,
+ last_position_message_.pusher_proximal_hall_effect,
+ values.shooter.pusher_proximal.upper_limit,
+ values.shooter.pusher_proximal.lower_limit,
+ position->position, position->posedge_value,
+ position->negedge_value,
+ position->pusher_proximal_hall_effect_posedge_count,
+ position->pusher_proximal_hall_effect_negedge_count);
+
+ last_position_message_ = *position;
+ position.Send();
+ }
+
+
+ // Simulates the claw moving for one timestep.
+ void Simulate() {
+ last_plant_position_ = shooter_plant_->Y(0, 0);
+ EXPECT_TRUE(shooter_queue_group_.output.FetchLatest());
+ shooter_plant_->U << last_voltage_;
+ shooter_plant_->Update();
+
+ EXPECT_GE(constants::GetValues().shooter.upper_limit,
+ shooter_plant_->Y(0, 0));
+ EXPECT_LE(constants::GetValues().shooter.lower_limit,
+ shooter_plant_->Y(0, 0));
+ last_voltage_ = shooter_queue_group_.output->voltage;
+ }
+
+
+ // pointer to plant
+ ::std::unique_ptr<StateFeedbackPlant<3, 1, 1>> shooter_plant_;
+
+
+ private:
+
+ ShooterGroup shooter_queue_group_;
+ double initial_position_;
+ double last_voltage_;
+
+ control_loops::ShooterGroup::Position last_position_message_;
+ double last_plant_position_;
+};
+
+
+class ShooterTest : public ::testing::Test {
+ protected:
+ ::aos::common::testing::GlobalCoreInstance my_core;
+
+ // Create a new instance of the test queue so that it invalidates the queue
+ // that it points to. Otherwise, we will have a pointer to shared memory that
+ // is no longer valid.
+ ShooterGroup shooter_queue_group_;
+
+ // Create a loop and simulation plant.
+ ShooterMotor shooter_motor_;
+ ShooterSimulation shooter_motor_plant_;
+
+ ShooterTest()
+ : shooter_queue_group_(
+ ".frc971.control_loops.shooter_queue_group", 0xcbf22ba9,
+ ".frc971.control_loops.shooter_queue_group.goal",
+ ".frc971.control_loops.shooter_queue_group.position",
+ ".frc971.control_loops.shooter_queue_group.output",
+ ".frc971.control_loops.shooter_queue_group.status"),
+ shooter_motor_(&shooter_queue_group_),
+ shooter_motor_plant_(0.5) {
+ // Flush the robot state queue so we can use clean shared memory for this
+ // test.
+ ::aos::robot_state.Clear();
+ SendDSPacket(true);
+ }
+
+
+ void SendDSPacket(bool enabled) {
+ ::aos::robot_state.MakeWithBuilder().enabled(enabled)
+ .autonomous(false)
+ .team_id(971).Send();
+ ::aos::robot_state.FetchLatest();
+ }
+
+ void VerifyNearGoal() {
+ shooter_queue_group_.goal.FetchLatest();
+ shooter_queue_group_.position.FetchLatest();
+ double pos = shooter_motor_plant_.GetAbsolutePosition();
+ EXPECT_NEAR(shooter_queue_group_.goal->shot_power, pos, 1e-4);
+ }
+
+ virtual ~ShooterTest() { ::aos::robot_state.Clear(); }
+};
+
+
+TEST_F(ShooterTest, PowerConversion) {
+ // test a couple of values return the right thing
+ EXPECT_EQ(2.1, shooter_motor_.PowerToPosition(2.1));
+ EXPECT_EQ(50.99, shooter_motor_.PowerToPosition(50.99));
+
+ const frc971::constants::Values &values = constants::GetValues();
+ // value too large should get max
+ EXPECT_EQ(values.shooter.upper_limit,
+ shooter_motor_.PowerToPosition(505050.99));
+ // negative values should zero
+ EXPECT_EQ(0.0, shooter_motor_.PowerToPosition(-123.4));
+}
+
+// Tests that the wrist zeros correctly and goes to a position.
+TEST_F(ShooterTest, ZerosCorrectly) {
+ shooter_queue_group_.goal.MakeWithBuilder().shot_power(2.1).Send();
+ for (int i = 0; i < 400; ++i) {
+ shooter_motor_plant_.SendPositionMessage();
+ shooter_motor_.Iterate();
+ shooter_motor_plant_.Simulate();
+ SendDSPacket(true);
+ }
+ VerifyNearGoal();
+}
+
+} // namespace testing
+} // namespace control_loops
+} // namespace frc971
diff --git a/frc971/control_loops/shooter/shooter_main.cc b/frc971/control_loops/shooter/shooter_main.cc
old mode 100644
new mode 100755
index cb6fc8b..e4e25ad
--- a/frc971/control_loops/shooter/shooter_main.cc
+++ b/frc971/control_loops/shooter/shooter_main.cc
@@ -4,7 +4,7 @@
int main() {
::aos::Init();
- frc971::control_loops::ShooterLoop shooter;
+ frc971::control_loops::ShooterMotor shooter;
shooter.Run();
::aos::Cleanup();
return 0;
diff --git a/frc971/control_loops/shooter/shooter_motor_plant.cc b/frc971/control_loops/shooter/shooter_motor_plant.cc
old mode 100644
new mode 100755
diff --git a/frc971/control_loops/shooter/shooter_motor_plant.h b/frc971/control_loops/shooter/shooter_motor_plant.h
old mode 100644
new mode 100755
diff --git a/frc971/control_loops/shooter/unaugmented_shooter_motor_plant.cc b/frc971/control_loops/shooter/unaugmented_shooter_motor_plant.cc
new file mode 100755
index 0000000..d395200
--- /dev/null
+++ b/frc971/control_loops/shooter/unaugmented_shooter_motor_plant.cc
@@ -0,0 +1,47 @@
+#include "frc971/control_loops/shooter/unaugmented_shooter_motor_plant.h"
+
+#include <vector>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<2, 1, 1> MakeRawShooterPlantCoefficients() {
+ Eigen::Matrix<double, 2, 2> A;
+ A << 1.0, 0.00988697090637, 0.0, 0.977479674375;
+ Eigen::Matrix<double, 2, 1> B;
+ B << 0.000120553991591, 0.0240196135246;
+ 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> MakeRawShooterController() {
+ Eigen::Matrix<double, 2, 1> L;
+ L << 1.87747967438, 87.0117404538;
+ Eigen::Matrix<double, 1, 2> K;
+ K << 343.469306513, 26.4814035344;
+ return StateFeedbackController<2, 1, 1>(L, K, MakeRawShooterPlantCoefficients());
+}
+
+StateFeedbackPlant<2, 1, 1> MakeRawShooterPlant() {
+ ::std::vector<StateFeedbackPlantCoefficients<2, 1, 1> *> plants(1);
+ plants[0] = new StateFeedbackPlantCoefficients<2, 1, 1>(MakeRawShooterPlantCoefficients());
+ return StateFeedbackPlant<2, 1, 1>(plants);
+}
+
+StateFeedbackLoop<2, 1, 1> MakeRawShooterLoop() {
+ ::std::vector<StateFeedbackController<2, 1, 1> *> controllers(1);
+ controllers[0] = new StateFeedbackController<2, 1, 1>(MakeRawShooterController());
+ return StateFeedbackLoop<2, 1, 1>(controllers);
+}
+
+} // namespace control_loops
+} // namespace frc971
diff --git a/frc971/control_loops/shooter/unaugmented_shooter_motor_plant.h b/frc971/control_loops/shooter/unaugmented_shooter_motor_plant.h
new file mode 100755
index 0000000..4deea48
--- /dev/null
+++ b/frc971/control_loops/shooter/unaugmented_shooter_motor_plant.h
@@ -0,0 +1,20 @@
+#ifndef FRC971_CONTROL_LOOPS_SHOOTER_UNAUGMENTED_SHOOTER_MOTOR_PLANT_H_
+#define FRC971_CONTROL_LOOPS_SHOOTER_UNAUGMENTED_SHOOTER_MOTOR_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<2, 1, 1> MakeRawShooterPlantCoefficients();
+
+StateFeedbackController<2, 1, 1> MakeRawShooterController();
+
+StateFeedbackPlant<2, 1, 1> MakeRawShooterPlant();
+
+StateFeedbackLoop<2, 1, 1> MakeRawShooterLoop();
+
+} // namespace control_loops
+} // namespace frc971
+
+#endif // FRC971_CONTROL_LOOPS_SHOOTER_UNAUGMENTED_SHOOTER_MOTOR_PLANT_H_
diff --git a/frc971/control_loops/update_shooter.sh b/frc971/control_loops/update_shooter.sh
index 8a18289..a9c5807 100755
--- a/frc971/control_loops/update_shooter.sh
+++ b/frc971/control_loops/update_shooter.sh
@@ -5,4 +5,6 @@
cd $(dirname $0)
./python/shooter.py shooter/shooter_motor_plant.h \
- shooter/shooter_motor_plant.cc
+ shooter/shooter_motor_plant.cc \
+ shooter/unaugmented_shooter_motor_plant.h \
+ shooter/unaugmented_shooter_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/prime.gyp b/frc971/prime/prime.gyp
index 3b699ad..e23272b 100644
--- a/frc971/prime/prime.gyp
+++ b/frc971/prime/prime.gyp
@@ -7,8 +7,8 @@
'<(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',
@@ -17,6 +17,7 @@
'<(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/