copied everything over from 2012 and removed all of the actual robot code except the drivetrain stuff
git-svn-id: https://robotics.mvla.net/svn/frc971/2013/trunk/src@4078 f308d9b7-e957-4cde-b6ac-9a88185e7312
diff --git a/aos/atom_code/output/HTTPServer.cpp b/aos/atom_code/output/HTTPServer.cpp
new file mode 100644
index 0000000..88e3c6f
--- /dev/null
+++ b/aos/atom_code/output/HTTPServer.cpp
@@ -0,0 +1,153 @@
+#include "aos/atom_code/output/HTTPServer.h"
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <memory>
+
+#include "event2/event.h"
+
+#include "aos/aos_core.h"
+#include "aos/common/scoped_fd.h"
+#include "aos/common/unique_malloc_ptr.h"
+
+namespace aos {
+namespace http {
+
+HTTPServer::HTTPServer(const char *directory, uint16_t port) :
+ directory_(directory), base_(event_base_new()), http_(evhttp_new(base_)) {
+ if (base_ == NULL) {
+ LOG(FATAL, "couldn't create an event_base\n");
+ }
+ if (http_ == NULL) {
+ LOG(FATAL, "couldn't create an evhttp\n");
+ }
+ if (evhttp_bind_socket(http_, "0.0.0.0", port) != 0) {
+ LOG(FATAL, "evhttp_bind_socket(%p, \"0.0.0.0\", %"PRIu16") failed\n",
+ http_, port);
+ }
+ evhttp_set_gencb(http_, StaticServeFile, this);
+}
+
+void HTTPServer::AddPage(const std::string &path,
+ void (*handler)(evhttp_request *, void *), void *data) {
+ switch (evhttp_set_cb(http_, path.c_str(), handler, data)) {
+ case 0:
+ LOG(DEBUG, "set callback handler for '%s'\n", path.c_str());
+ break;
+ case -1:
+ LOG(INFO, "changed callback handler for '%s'\n", path.c_str());
+ break;
+ default:
+ LOG(WARNING, "evhttp_set_cb(%p, %s, %p, %p) failed\n", http_, path.c_str(),
+ handler, data);
+ break;
+ }
+}
+
+void HTTPServer::AddStandardHeaders(evhttp_request *request) {
+ if (evhttp_add_header(evhttp_request_get_output_headers(request),
+ "Server", "aos::HTTPServer/0.0") == -1) {
+ LOG(WARNING, "adding Server header failed\n");
+ }
+}
+
+namespace {
+// All of these functions return false, NULL, or -1 if they fail (and send back
+// an error).
+
+// Returns the path of the file that is being requested.
+const char *GetPath(evhttp_request *request) {
+ // Docs are unclear whether this needs freeing, but it looks like it just
+ // returns an internal field of the request.
+ // Running valgrind with no freeing of uri or path doesn't report anything
+ // related to this code.
+ const evhttp_uri *uri = evhttp_request_get_evhttp_uri(request);
+ const char *path = evhttp_uri_get_path(uri);
+ if (path == NULL) {
+ evhttp_send_error(request, HTTP_BADREQUEST, "need a path");
+ return NULL;
+ }
+ if (strstr(path, "..") != NULL) {
+ evhttp_send_error(request, HTTP_NOTFOUND, "no .. allowed!!");
+ return NULL;
+ }
+ return path;
+}
+// Returns an fd open for reading for the file at "directory/path".
+int OpenFile(evhttp_request *request, const char *path,
+ const char *directory) {
+ char *temp;
+ if (asprintf(&temp, "%s/%s", directory, path) == -1) {
+ LOG(WARNING, "asprintf(%p, \"%%s/%%s\", %p, %p) failed with %d: %s\n",
+ &temp, directory, path, errno, strerror(errno));
+ evhttp_send_error(request, HTTP_INTERNAL, NULL);
+ return -1;
+ }
+ const unique_c_ptr<char> filename(temp);
+ ScopedFD file(open(filename.get(), O_RDONLY));
+ if (!file) {
+ if (errno == ENOENT) {
+ evhttp_send_error(request, HTTP_NOTFOUND, NULL);
+ return -1;
+ }
+ LOG(ERROR, "open('%s', 0) failed with %d: %s\n", filename.get(),
+ errno, strerror(errno));
+ evhttp_send_error(request, HTTP_INTERNAL, NULL);
+ return -1;
+ }
+ return file.release();
+}
+// Returns the size of the file specified by the given fd.
+off_t GetSize(int file) {
+ struct stat info;
+ if (fstat(file, &info) == -1) {
+ LOG(ERROR, "stat(%d, %p) failed with %d: %s\n", file, &info,
+ errno, strerror(errno));
+ return -1;
+ }
+ return info.st_size;
+}
+bool SendFileResponse(evhttp_request *request, int file_num) {
+ ScopedFD file(file_num);
+ const off_t size = GetSize(file.get());
+ if (size == -1) {
+ evhttp_send_error(request, HTTP_INTERNAL, NULL);
+ return false;
+ }
+ evbuffer *const buf = evhttp_request_get_output_buffer(request);
+ if (evbuffer_add_file(buf, file.get(), 0, size) == -1) {
+ LOG(WARNING, "evbuffer_add_file(%p, %d, 0, %jd) failed\n", buf,
+ file.get(), static_cast<intmax_t>(size));
+ evhttp_send_error(request, HTTP_INTERNAL, NULL);
+ return false;
+ } else {
+ // it succeeded, so evhttp takes ownership
+ file.release();
+ }
+ evhttp_send_reply(request, HTTP_OK, NULL, NULL);
+ return true;
+}
+
+} // namespace
+void HTTPServer::ServeFile(evhttp_request *request) {
+ AddStandardHeaders(request);
+
+ const char *path = GetPath(request);
+ if (path == NULL) return;
+
+ ScopedFD file(OpenFile(request, path, directory_));
+ if (!file) return;
+
+ if (!SendFileResponse(request, file.release())) return;
+}
+
+void HTTPServer::Run() {
+ event_base_dispatch(base_);
+ LOG(FATAL, "event_base_dispatch returned\n");
+}
+
+} // namespace http
+} // namespace aos
diff --git a/aos/atom_code/output/HTTPServer.h b/aos/atom_code/output/HTTPServer.h
new file mode 100644
index 0000000..99eb295
--- /dev/null
+++ b/aos/atom_code/output/HTTPServer.h
@@ -0,0 +1,58 @@
+#include "event2/buffer.h"
+#include "event2/http.h"
+
+#include <string>
+
+namespace aos {
+namespace http {
+
+// An HTTP server that serves files from a directory using libevent.
+// Also allows configuring certain URLs to be dynamically generated.
+class HTTPServer {
+ public:
+ HTTPServer(const char *directory, uint16_t port);
+ // Starts serving pages.
+ // Might not clean up everything before returning.
+ void Run();
+ protected:
+ template<class T> class MemberHandler {
+ public:
+ typedef void (T::*Handler)(evhttp_request *);
+ struct Holder {
+ T *self;
+ Handler handler;
+ };
+ static void Call(evhttp_request *request, void *handler_in) {
+ const Holder *const holder = static_cast<Holder *>(handler_in);
+ AddStandardHeaders(request);
+ ((holder->self)->*(holder->handler))(request);
+ }
+ };
+ void AddPage(const std::string &path, void (*handler)(evhttp_request *, void *),
+ void *data);
+ template<class T> void AddPage(const std::string &path,
+ typename MemberHandler<T>::Handler handler,
+ T *self) {
+ // have to put "typename" in, so the typedef makes it clearer
+ typedef typename MemberHandler<T>::Holder HolderType;
+ AddPage(path, MemberHandler<T>::Call, new HolderType{self, handler});
+ }
+ // This gets set up as the generic handler.
+ // It can also be called separately to serve the file that the request is
+ // requesting from the filesystem.
+ void ServeFile(evhttp_request *request);
+ private:
+ // The directory where files to be served come from.
+ const char *directory_;
+ // The main libevent structure.
+ event_base *const base_;
+ // The libevent HTTP server handle.
+ evhttp *const http_;
+ static void AddStandardHeaders(evhttp_request *request);
+ static void StaticServeFile(evhttp_request *request, void *self) {
+ static_cast<HTTPServer *>(self)->ServeFile(request);
+ }
+};
+
+} // namespace http
+} // namespace aos
diff --git a/aos/atom_code/output/MotorOutput.cpp b/aos/atom_code/output/MotorOutput.cpp
new file mode 100644
index 0000000..ecbdf8b
--- /dev/null
+++ b/aos/atom_code/output/MotorOutput.cpp
@@ -0,0 +1,78 @@
+#include "aos/atom_code/output/MotorOutput.h"
+
+#include <math.h>
+
+#include "aos/common/Configuration.h"
+#include "aos/aos_core.h"
+#include "aos/common/byteorder.h"
+#include "aos/common/control_loop/Timing.h"
+#include "aos/atom_code/messages/DriverStationDisplay.h"
+
+namespace aos {
+
+MotorOutput::MotorOutput() : sock(NetworkPort::kMotors,
+ configuration::GetIPAddress(configuration::NetworkDevice::kCRIO)) {}
+
+void MotorOutput::Run() {
+ while (true) {
+ time::PhasedLoopXMS(5, 1000);
+ RunIteration();
+
+ // doesn't matter if multiple instances end up running this loop at the same
+ // time because the queue handles the race conditions
+ while (true) {
+ const aos::DriverStationDisplay *line = aos::DriverStationDisplay::GetNext();
+ if (line != NULL) {
+ AddDSLine(line->line, line->data);
+ LOG(DEBUG, "got a line %hhd that said '%s'\n", line->line, line->data);
+ aos::DriverStationDisplay::Free(line);
+ } else {
+ break;
+ }
+ }
+
+ if (sock.SendHoldMsg() == -1) {
+ LOG(WARNING, "sending outputs failed\n");
+ continue;
+ } else {
+ LOG(DEBUG, "sent outputs\n");
+ }
+ }
+}
+
+int MotorOutput::AddMotor(char type, uint8_t num, float value) {
+ if (sock.hold_msg_len_ + 4 > sock.MAX_MSG) {
+ return -1;
+ }
+ sock.hold_msg_[sock.hold_msg_len_ ++] = type;
+ sock.hold_msg_[sock.hold_msg_len_ ++] = num;
+ to_network(&value, &sock.hold_msg_[sock.hold_msg_len_]);
+ sock.hold_msg_len_ += 4;
+ return 0;
+}
+int MotorOutput::AddSolenoid(uint8_t port, bool on) {
+ if (sock.hold_msg_len_ + 3 > sock.MAX_MSG) {
+ return -1;
+ }
+ sock.hold_msg_[sock.hold_msg_len_ ++] = 's';
+ sock.hold_msg_[sock.hold_msg_len_ ++] = port;
+ sock.hold_msg_[sock.hold_msg_len_ ++] = on ? 1 : 0;
+ return 0;
+}
+
+int MotorOutput::AddDSLine(uint8_t line, const char *data) {
+ size_t data_len = strlen(data); // doesn't include terminating NULL
+ if (sock.hold_msg_len_ + 3 + data_len > sock.MAX_MSG) {
+ return -1;
+ }
+
+ sock.hold_msg_[sock.hold_msg_len_ ++] = 'd';
+ sock.hold_msg_[sock.hold_msg_len_ ++] = line;
+ sock.hold_msg_[sock.hold_msg_len_ ++] = data_len;
+ memcpy(&sock.hold_msg_[sock.hold_msg_len_], data, data_len);
+ sock.hold_msg_len_ += data_len;
+ return 0;
+}
+
+} // namespace aos
+
diff --git a/aos/atom_code/output/MotorOutput.h b/aos/atom_code/output/MotorOutput.h
new file mode 100644
index 0000000..925139a
--- /dev/null
+++ b/aos/atom_code/output/MotorOutput.h
@@ -0,0 +1,74 @@
+#ifndef AOS_ATOM_CODE_OUTPUT_MOTOR_OUTPUT_H_
+#define AOS_ATOM_CODE_OUTPUT_MOTOR_OUTPUT_H_
+
+#include <stdint.h>
+#include <string.h>
+#include <algorithm>
+#include <string>
+
+#include "aos/common/network/SendSocket.h"
+#include "aos/common/byteorder.h"
+#include "aos/common/type_traits.h"
+
+namespace aos {
+
+class MotorOutput {
+ public:
+ MotorOutput();
+ void Run();
+
+ // Constants for the first argument of AddMotor.
+ static const char VICTOR = 'v';
+ static const char TALON = 't';
+
+ protected:
+ // num is 1-indexed
+ int AddMotor(char type, uint8_t num, float value);
+ int AddSolenoid(uint8_t num, bool on);
+ int AddDSLine(uint8_t line, const char *data);
+ // loop_queuegroup is expected to be a control loop queue group.
+ // This function will fetch the latest goal and serve it.
+ template <class T>
+ int AddControlLoopGoal(T *loop_queuegroup);
+
+ virtual void RunIteration() = 0;
+
+ private:
+ SendSocket sock;
+};
+
+template <class T>
+int MotorOutput::AddControlLoopGoal(T *loop_queuegroup) {
+ typedef typename std::remove_reference<
+ decltype(*(loop_queuegroup->goal.MakeMessage().get()))>::type GoalType;
+ // TODO(aschuh): This assumes a static serialized message size.
+ const uint16_t size = GoalType::Size();
+ if (sock.hold_msg_len_ + 4 + 1 + size > sock.MAX_MSG) {
+ return -1;
+ }
+
+ const bool zero = !loop_queuegroup->goal.FetchLatest();
+
+ sock.hold_msg_[sock.hold_msg_len_ ++] = 'g';
+ const uint32_t hash = loop_queuegroup->hash();
+
+ // Place the loop hash at the front.
+ to_network(&hash, &sock.hold_msg_[sock.hold_msg_len_]);
+ sock.hold_msg_len_ += 4;
+
+ if (zero) {
+ GoalType zero_message;
+ zero_message.Zero();
+ zero_message.Serialize(&sock.hold_msg_[sock.hold_msg_len_]);
+ sock.hold_msg_len_ += size;
+ return -1;
+ } else {
+ loop_queuegroup->goal->Serialize(&sock.hold_msg_[sock.hold_msg_len_]);
+ sock.hold_msg_len_ += size;
+ return 0;
+ }
+}
+
+} // namespace aos
+
+#endif
diff --git a/aos/atom_code/output/evhttp_ctemplate_emitter.cc b/aos/atom_code/output/evhttp_ctemplate_emitter.cc
new file mode 100644
index 0000000..ed2a83f
--- /dev/null
+++ b/aos/atom_code/output/evhttp_ctemplate_emitter.cc
@@ -0,0 +1,18 @@
+#include "aos/atom_code/output/evhttp_ctemplate_emitter.h"
+
+#include "aos/aos_core.h"
+
+namespace aos {
+namespace http {
+
+void EvhttpCtemplateEmitter::Emit(const char *s, size_t slen) {
+ if (error_) return;
+ if (evbuffer_add(buf_, s, slen) != 0) {
+ LOG(ERROR, "evbuffer_add(%p, %p, %zd) failed\n",
+ buf_, s, slen);
+ error_ = true;
+ }
+}
+
+} // namespace http
+} // namespace aos
diff --git a/aos/atom_code/output/evhttp_ctemplate_emitter.h b/aos/atom_code/output/evhttp_ctemplate_emitter.h
new file mode 100644
index 0000000..d20d2cb
--- /dev/null
+++ b/aos/atom_code/output/evhttp_ctemplate_emitter.h
@@ -0,0 +1,34 @@
+#ifndef AOS_ATOM_CODE_OUTPUT_EVHTTP_CTEMPLATE_EMITTER_H_
+#define AOS_ATOM_CODE_OUTPUT_EVHTTP_CTEMPLATE_EMITTER_H_
+
+#include <string.h>
+
+#include "event2/buffer.h"
+#include "ctemplate/template_emitter.h"
+
+namespace aos {
+namespace http {
+
+// Writes everything directly into an evbuffer*.
+// Handles errors by refusing to write anything else into the buffer and storing
+// the state (which can be retrieved with error()).
+class EvhttpCtemplateEmitter : public ctemplate::ExpandEmitter {
+ public:
+ EvhttpCtemplateEmitter(evbuffer *buf) : buf_(buf), error_(false) {}
+ virtual void Emit(char c) { Emit(&c, 1); };
+ virtual void Emit(const std::string& s) { Emit(s.data(), s.size()); };
+ virtual void Emit(const char* s) { Emit(s, strlen(s)); }
+ virtual void Emit(const char* s, size_t slen);
+ // Retrieves whether or not there has been an error. If true, the error will
+ // already have been logged.
+ bool error() { return error_; }
+
+ private:
+ evbuffer *const buf_;
+ bool error_;
+};
+
+} // namespace http
+} // namespace aos
+
+#endif // AOS_ATOM_CODE_OUTPUT_EVHTTP_CTEMPLATE_EMITTER_H_
diff --git a/aos/atom_code/output/output.gyp b/aos/atom_code/output/output.gyp
new file mode 100644
index 0000000..3e4abd4
--- /dev/null
+++ b/aos/atom_code/output/output.gyp
@@ -0,0 +1,35 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'http_server',
+ 'type': 'static_library',
+ 'sources': [
+ 'HTTPServer.cpp',
+ 'evhttp_ctemplate_emitter.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:libaos',
+ '<(EXTERNALS):libevent',
+ '<(EXTERNALS):ctemplate',
+ ],
+ 'export_dependent_settings': [
+# Our headers #include headers from both of these.
+ '<(EXTERNALS):libevent',
+ '<(EXTERNALS):ctemplate',
+ ],
+ },
+ {
+ 'target_name': 'motor_output',
+ 'type': 'static_library',
+ 'sources': [
+ 'MotorOutput.cpp',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:libaos',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/build/aos.gyp:libaos',
+ ],
+ },
+ ],
+}