Add PWM bounds for victor.

- Rename AtomMotorWriter.cc to be styleguide compliant.
- Make initial modifications to this file for third robot.
- Copy entire output directory from frc971 to bot3.
- Get rid of aos_module.ko.
diff --git a/bot3/atom_code/atom_code.gyp b/bot3/atom_code/atom_code.gyp
index 23f4e1f..6dbcc77 100644
--- a/bot3/atom_code/atom_code.gyp
+++ b/bot3/atom_code/atom_code.gyp
@@ -13,8 +13,8 @@
         '<(DEPTH)/bot3/input/input.gyp:joystick_reader',
         '<(DEPTH)/bot3/input/input.gyp:gyro_reader',
         #'../input/input.gyp:AutoMode',
-        '<(DEPTH)/frc971/output/output.gyp:MotorWriter',
-        '<(DEPTH)/frc971/output/output.gyp:CameraServer',
+        '<(DEPTH)/bot3/output/output.gyp:MotorWriter',
+        '<(DEPTH)/bot3/output/output.gyp:CameraServer',
         #'camera/camera.gyp:frc971',
         '<(DEPTH)/gyro_board/src/libusb-driver/libusb-driver.gyp:get',
         '<(DEPTH)/bot3/input/input.gyp:gyro_board_reader',
@@ -24,7 +24,6 @@
         {
           'destination': '<(rsync_dir)',
           'files': [
-            'scripts/aos_module.ko',
             'scripts/start_list.txt',
           ],
         },
diff --git a/bot3/atom_code/scripts/aos_module.ko b/bot3/atom_code/scripts/aos_module.ko
deleted file mode 100644
index cdaddb7..0000000
--- a/bot3/atom_code/scripts/aos_module.ko
+++ /dev/null
Binary files differ
diff --git a/bot3/output/CameraServer.cc b/bot3/output/CameraServer.cc
new file mode 100644
index 0000000..939731d
--- /dev/null
+++ b/bot3/output/CameraServer.cc
@@ -0,0 +1,93 @@
+#include <string.h>
+
+#include "aos/atom_code/output/HTTPServer.h"
+#include "aos/atom_code/output/evhttp_ctemplate_emitter.h"
+#include "aos/atom_code/output/ctemplate_cache.h"
+#include "aos/common/messages/RobotState.q.h"
+#include "ctemplate/template.h"
+#include "aos/atom_code/init.h"
+#include "aos/common/logging/logging.h"
+#include "aos/atom_code/configuration.h"
+
+#include "frc971/constants.h"
+
+RegisterTemplateFilename(ROBOT_HTML, "robot.html.tpl");
+
+namespace frc971 {
+
+class CameraServer : public aos::http::HTTPServer {
+ public:
+  CameraServer() : HTTPServer(::aos::configuration::GetRootDirectory(), 8080),
+      buf_(NULL) {
+    AddPage<CameraServer>("/robot.html", &CameraServer::RobotHTML, this);
+  }
+
+ private:
+  evbuffer *buf_;
+  bool Setup(evhttp_request *request, const char *content_type) {
+    if (evhttp_add_header(evhttp_request_get_output_headers(request),
+                          "Content-Type", content_type) == -1) {
+      LOG(WARNING, "adding Content-Type failed\n");
+      evhttp_send_error(request, HTTP_INTERNAL, NULL);
+      return false;
+    }
+    if (buf_ == NULL) buf_ = evbuffer_new();
+    if (buf_ == NULL) {
+      LOG(WARNING, "evbuffer_new() failed\n");
+      evhttp_send_error(request, HTTP_INTERNAL, NULL);
+      return false;
+    }
+    return true;
+  }
+  void RobotHTML(evhttp_request *request) {
+    if (!Setup(request, "text/html")) return;
+
+    ctemplate::TemplateDictionary dict(ROBOT_HTML);
+    const char *host = evhttp_find_header(
+        evhttp_request_get_input_headers(request), "Host");
+    if (host == NULL) {
+      evhttp_send_error(request, HTTP_BADREQUEST, "no Host header");
+      return;
+    }
+    const char *separator = strchrnul(host, ':');
+    size_t length = separator - host;
+    // Don't include the last ':' (or the terminating '\0') or anything else
+    // after it.
+    dict.SetValue("HOST", ctemplate::TemplateString(host, length));
+
+    if (!aos::robot_state.FetchLatest()) {
+      LOG(WARNING, "getting a RobotState message failed\n");
+      evhttp_send_error(request, HTTP_INTERNAL, NULL);
+      return;
+    }
+    int center;
+    if (!constants::camera_center(&center)) {
+      evhttp_send_error(request, HTTP_INTERNAL, NULL);
+      return;
+    }
+    dict.SetIntValue("CENTER", center);
+
+    aos::http::EvhttpCtemplateEmitter emitter(buf_);
+    if (!aos::http::get_template_cache()->
+        ExpandWithData(ROBOT_HTML, ctemplate::STRIP_WHITESPACE,
+                       &dict, NULL, &emitter)) {
+      LOG(ERROR, "expanding the template failed\n");
+      evhttp_send_error(request, HTTP_INTERNAL, NULL);
+      return;
+    }
+    if (emitter.error()) {
+      evhttp_send_error(request, HTTP_INTERNAL, NULL);
+      return;
+    }
+    evhttp_send_reply(request, HTTP_OK, NULL, buf_);
+  }
+};
+
+}  // namespace frc971
+
+int main() {
+  ::aos::InitNRT();
+  ::frc971::CameraServer server;
+  server.Run();
+  ::aos::Cleanup();
+}
diff --git a/bot3/output/atom_motor_writer.cc b/bot3/output/atom_motor_writer.cc
new file mode 100644
index 0000000..c2aeeb2
--- /dev/null
+++ b/bot3/output/atom_motor_writer.cc
@@ -0,0 +1,78 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "aos/atom_code/output/motor_output.h"
+#include "aos/common/logging/logging.h"
+#include "aos/atom_code/init.h"
+
+#include "frc971/queues/Piston.q.h"
+#include "frc971/control_loops/drivetrain/drivetrain.q.h"
+#include "frc971/control_loops/shooter/shooter_motor.q.h"
+
+using ::frc971::control_loops::drivetrain;
+using ::frc971::control_loops::shifters;
+using ::frc971::control_loops::shooter;
+using ::frc971::control_loops::hangers;
+
+namespace bot3 {
+namespace output {
+
+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;
+
+  virtual void RunIteration() {
+    values_.digital_module = 0;
+    values_.pressure_switch_channel = 14;
+    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");
+      }
+    }
+    shifters.FetchLatest();
+    if (shifters.get()) {
+      SetSolenoid(1, shifters->set);
+      SetSolenoid(2, !shifters->set);
+    }
+
+    shooter.output.FetchLatest();
+    if (shooter.output.IsNewerThanMS(kOutputMaxAgeMS)) {
+      SetPWMOutput(4, shooter.output->voltage / 12.0, kVictorBounds);
+    } else {
+      DisablePWMOutput(4);
+      LOG(WARNING, "shooter not new enough\n");
+    }
+    // TODO(danielp): Add stuff for intake.
+
+    hangers.FetchLatest();
+    if (hangers.get()) {
+      SetSolenoid(4, hangers->set);
+      SetSolenoid(5, hangers->set);
+    }
+  }
+};
+
+}  // namespace output
+}  // namespace bot3
+
+int main() {
+  ::aos::Init();
+  ::bot3::output::MotorWriter writer;
+  writer.Run();
+  ::aos::Cleanup();
+}
diff --git a/bot3/output/output.gyp b/bot3/output/output.gyp
new file mode 100644
index 0000000..139997b
--- /dev/null
+++ b/bot3/output/output.gyp
@@ -0,0 +1,46 @@
+{
+  'targets': [
+    {
+      'target_name': 'CameraServer',
+      'type': 'executable',
+      'sources': [
+        'CameraServer.cc',
+      ],
+      'dependencies': [
+        '<(AOS)/atom_code/output/output.gyp:http_server',
+        '<(DEPTH)/frc971/frc971.gyp:common',
+        '<(AOS)/atom_code/atom_code.gyp:init',
+        '<(AOS)/build/aos.gyp:logging',
+        '<(AOS)/common/messages/messages.gyp:aos_queues',
+        '<(AOS)/atom_code/atom_code.gyp:configuration',
+      ],
+      'copies': [
+        {
+          'destination': '<(rsync_dir)',
+          'files': [
+            'robot.html.tpl',
+          ],
+        },
+      ],
+    },
+    {
+      'target_name': 'MotorWriter',
+      'type': '<(aos_target)',
+      'sources': [
+        'atom_motor_writer.cc'
+      ],
+      'dependencies': [
+        '<(AOS)/atom_code/output/output.gyp:motor_output',
+        '<(AOS)/atom_code/atom_code.gyp:init',
+        '<(AOS)/build/aos.gyp:logging',
+        '<(DEPTH)/frc971/control_loops/angle_adjust/angle_adjust.gyp:angle_adjust_loop',
+        '<(DEPTH)/frc971/control_loops/wrist/wrist.gyp:wrist_loop',
+        '<(DEPTH)/frc971/control_loops/index/index.gyp:index_loop',
+        '<(DEPTH)/frc971/control_loops/shooter/shooter.gyp:shooter_loop',
+        '<(DEPTH)/frc971/control_loops/drivetrain/drivetrain.gyp:drivetrain_loop',
+        '<(AOS)/common/common.gyp:controls',
+        '<(DEPTH)/frc971/queues/queues.gyp:queues',
+      ],
+    },
+  ],
+}
diff --git a/bot3/output/robot.html.tpl b/bot3/output/robot.html.tpl
new file mode 100644
index 0000000..7ae51a6
--- /dev/null
+++ b/bot3/output/robot.html.tpl
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <title>971 Camera Code: Robot Stream</title>
+    <style type="text/css">
+      #body {
+        display: block;
+        margin: 0px;
+        margin-top: 0px;
+        margin-right: 0px;
+        margin-bottom: 0px;
+        margin-left: 0px;
+      }
+      #img {
+        position: absolute;
+        left: 50%;
+        top: 0%;
+        margin: 0 0 0 -320px;
+      }
+      #center {
+        left: 50%;
+        position: absolute;
+        width: 2px;
+        height: 100%;
+        background-color: red;
+      }
+      #middle {
+        top: 240px;
+        margin-top: -1px;
+        width: 100%;
+        position: absolute;
+        height: 2px;
+        background-color: red;
+      }
+      #footer {
+        top: 482px;
+        left: 10px;
+        position: absolute;
+      }
+      #center {
+        margin-left: {{CENTER}}px;
+      }
+    </style>
+  </head>
+  <body id="body">
+        <img id="img" src="http://{{HOST}}:9714" />
+        <div id="center"></div>
+        <div id="middle"></div>
+        <div id="footer">
+          <!--<form>
+            <input type="button" value="Camera Controls"
+           onclick="window.open('control.htm', 'Camera_Controls')">
+   </form>-->
+        </div>
+  </body>
+</html>