blob: 6f13205703b3c492923a7a03b118b10661e0f82b [file] [log] [blame]
Austin Schuh010eb812014-10-25 18:06:49 -07001#include <stdio.h>
2#include <string.h>
Austin Schuh010eb812014-10-25 18:06:49 -07003#include <unistd.h>
4#include <inttypes.h>
5
Brian Silvermand8f403a2014-12-13 19:12:04 -05006#include <thread>
7#include <mutex>
8#include <functional>
9
Austin Schuh010eb812014-10-25 18:06:49 -070010#include "aos/prime/output/motor_output.h"
11#include "aos/common/controls/output_check.q.h"
12#include "aos/common/controls/sensor_generation.q.h"
13#include "aos/common/logging/logging.h"
14#include "aos/common/logging/queue_logging.h"
Brian Silvermand8f403a2014-12-13 19:12:04 -050015#include "aos/common/messages/robot_state.q.h"
Austin Schuh010eb812014-10-25 18:06:49 -070016#include "aos/common/time.h"
17#include "aos/common/util/log_interval.h"
18#include "aos/common/util/phased_loop.h"
19#include "aos/common/util/wrapping_counter.h"
Austin Schuh010eb812014-10-25 18:06:49 -070020#include "aos/linux_code/init.h"
21
22#include "frc971/control_loops/drivetrain/drivetrain.q.h"
23#include "frc971/control_loops/claw/claw.q.h"
24#include "frc971/control_loops/shooter/shooter.q.h"
25#include "frc971/constants.h"
26#include "frc971/queues/other_sensors.q.h"
27#include "frc971/queues/to_log.q.h"
Daniel Pettiaece37f2014-10-25 17:13:44 -070028#include "frc971/shifter_hall_effect.h"
Austin Schuh010eb812014-10-25 18:06:49 -070029
Brian Silvermanda45b6c2014-12-28 11:36:50 -080030#include "frc971/wpilib/hall_effect.h"
31#include "frc971/wpilib/joystick_sender.h"
Brian Silvermand8f403a2014-12-13 19:12:04 -050032#include "frc971/wpilib/loop_output_handler.h"
33#include "frc971/wpilib/buffered_solenoid.h"
34#include "frc971/wpilib/buffered_pcm.h"
Brian Silverman07ec88e2014-12-28 00:13:08 -080035#include "frc971/wpilib/gyro_sender.h"
Brian Silvermanda45b6c2014-12-28 11:36:50 -080036
Brian Silvermancb77f232014-12-19 21:48:36 -080037#include "Encoder.h"
Brian Silvermancb77f232014-12-19 21:48:36 -080038#include "Talon.h"
39#include "DriverStation.h"
40#include "AnalogInput.h"
Brian Silvermancb77f232014-12-19 21:48:36 -080041#include "Compressor.h"
42#include "RobotBase.h"
Austin Schuh010eb812014-10-25 18:06:49 -070043
44#ifndef M_PI
45#define M_PI 3.14159265358979323846
46#endif
47
48using ::aos::util::SimpleLogInterval;
49using ::frc971::control_loops::drivetrain;
Austin Schuh010eb812014-10-25 18:06:49 -070050using ::aos::util::WrappingCounter;
51
52namespace frc971 {
Brian Silvermanda45b6c2014-12-28 11:36:50 -080053namespace wpilib {
Austin Schuh010eb812014-10-25 18:06:49 -070054
55class priority_mutex {
56 public:
57 typedef pthread_mutex_t *native_handle_type;
58
59 // TODO(austin): Write a test case for the mutex, and make the constructor
60 // constexpr.
61 priority_mutex() {
62 pthread_mutexattr_t attr;
Austin Schuh010eb812014-10-25 18:06:49 -070063#ifdef NDEBUG
Brian Silvermanda45b6c2014-12-28 11:36:50 -080064#error "Won't let assert_perror be no-op ed"
Austin Schuh010eb812014-10-25 18:06:49 -070065#endif
Austin Schuhdb516032014-12-28 00:12:38 -080066 // Turn on priority inheritance.
Austin Schuh010eb812014-10-25 18:06:49 -070067 assert_perror(pthread_mutexattr_init(&attr));
68 assert_perror(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL));
69 assert_perror(pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT));
70
71 assert_perror(pthread_mutex_init(native_handle(), &attr));
72
73 assert_perror(pthread_mutexattr_destroy(&attr));
74 }
75
76 ~priority_mutex() { pthread_mutex_destroy(&handle_); }
77
78 void lock() { assert_perror(pthread_mutex_lock(&handle_)); }
79 bool try_lock() {
80 int ret = pthread_mutex_trylock(&handle_);
81 if (ret == 0) {
82 return true;
83 } else if (ret == EBUSY) {
84 return false;
85 } else {
86 assert_perror(ret);
87 }
88 }
89 void unlock() { assert_perror(pthread_mutex_unlock(&handle_)); }
90
91 native_handle_type native_handle() { return &handle_; }
92
93 private:
94 DISALLOW_COPY_AND_ASSIGN(priority_mutex);
95 pthread_mutex_t handle_;
96};
97
Brian Silvermanda45b6c2014-12-28 11:36:50 -080098// TODO(brian): Split this out into a separate file once DMA is in.
Austin Schuh010eb812014-10-25 18:06:49 -070099class EdgeCounter {
100 public:
101 EdgeCounter(int priority, Encoder *encoder, HallEffect *input,
102 priority_mutex *mutex)
103 : priority_(priority),
104 encoder_(encoder),
105 input_(input),
106 mutex_(mutex),
107 run_(true),
108 any_interrupt_count_(0) {
109 thread_.reset(new ::std::thread(::std::ref(*this)));
110 }
111
112 // Waits for interrupts, locks the mutex, and updates the internal state.
113 // Updates the any_interrupt_count count when the interrupt comes in without
114 // the lock.
Austin Schuhdb516032014-12-28 00:12:38 -0800115 void operator()() {
Brian Silverman2fe007c2014-12-28 12:20:01 -0800116 ::aos::SetCurrentThreadName("EdgeCounter_" +
117 ::std::to_string(input_->GetChannel()));
Austin Schuh010eb812014-10-25 18:06:49 -0700118
119 input_->RequestInterrupts();
120 input_->SetUpSourceEdge(true, true);
121
122 {
123 ::std::unique_lock<priority_mutex> mutex_guard(*mutex_);
124 current_value_ = input_->GetHall();
125 }
126
Brian Silverman2fe007c2014-12-28 12:20:01 -0800127 ::aos::SetCurrentThreadRealtimePriority(priority_);
Austin Schuh010eb812014-10-25 18:06:49 -0700128 InterruptableSensorBase::WaitResult result = InterruptableSensorBase::kBoth;
129 while (run_) {
130 result = input_->WaitForInterrupt(
131 0.1, result != InterruptableSensorBase::kTimeout);
132 if (result == InterruptableSensorBase::kTimeout) {
133 continue;
134 }
135 ++any_interrupt_count_;
136
137 ::std::unique_lock<priority_mutex> mutex_guard(*mutex_);
138 int32_t encoder_value = encoder_->GetRaw();
139 bool hall_value = input_->GetHall();
140 if (current_value_ != hall_value) {
141 if (hall_value) {
142 ++positive_interrupt_count_;
143 last_positive_encoder_value_ = encoder_value;
144 } else {
145 ++negative_interrupt_count_;
146 last_negative_encoder_value_ = encoder_value;
147 }
148 } else {
149 LOG(WARNING, "Detected spurious edge on %d. Dropping it.\n",
150 input_->GetChannel());
151 }
152
153 current_value_ = hall_value;
154 }
155 }
156
157 // Updates the internal hall effect value given this new observation.
158 // The mutex provided at construction time must be held during this operation.
159 void set_polled_value(bool value) {
160 polled_value_ = value;
161 bool miss_match = (value != current_value_);
162 if (miss_match && last_miss_match_) {
163 current_value_ = value;
164 last_miss_match_ = false;
165 } else {
166 last_miss_match_ = miss_match;
167 }
168 }
169
170 // Signals the thread to quit next time it gets an interrupt.
171 void Quit() {
172 run_ = false;
173 thread_->join();
174 }
175
176 // Returns the total number of interrupts since construction time. This
177 // should be done without the mutex held.
178 int any_interrupt_count() const { return any_interrupt_count_; }
179 // Returns the current interrupt edge counts and encoder values.
180 // The mutex provided at construction time must be held during this operation.
181 int positive_interrupt_count() const { return positive_interrupt_count_; }
182 int negative_interrupt_count() const { return negative_interrupt_count_; }
183 int32_t last_positive_encoder_value() const {
184 return last_positive_encoder_value_;
185 }
186 int32_t last_negative_encoder_value() const {
187 return last_negative_encoder_value_;
188 }
189 // Returns the current polled value.
190 bool polled_value() const { return polled_value_; }
191
192 private:
193 int priority_;
194 Encoder *encoder_;
195 HallEffect *input_;
196 priority_mutex *mutex_;
197 ::std::atomic<bool> run_;
198
199 ::std::atomic<int> any_interrupt_count_;
200
201 // The following variables represent the current state. They must be
202 // synchronized by mutex_;
203 bool current_value_ = false;
204 bool polled_value_ = false;
205 bool last_miss_match_ = true;
206 int positive_interrupt_count_ = 0;
207 int negative_interrupt_count_ = 0;
208 int32_t last_positive_encoder_value_ = 0;
209 int32_t last_negative_encoder_value_ = 0;
210
211 ::std::unique_ptr<::std::thread> thread_;
212};
213
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800214// This class will synchronize sampling edges on a bunch of HallEffects with
Austin Schuh010eb812014-10-25 18:06:49 -0700215// the periodic poll.
216//
217// The data is provided to subclasses by calling SaveState when the state is
218// consistent and ready.
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800219//
220// TODO(brian): Split this out into a separate file once DMA is in.
Austin Schuh010eb812014-10-25 18:06:49 -0700221template <int num_sensors>
222class PeriodicHallSynchronizer {
223 public:
224 PeriodicHallSynchronizer(
225 const char *name, int priority, int interrupt_priority,
226 ::std::unique_ptr<Encoder> encoder,
227 ::std::array<::std::unique_ptr<HallEffect>, num_sensors> *sensors)
228 : name_(name),
229 priority_(priority),
230 encoder_(::std::move(encoder)),
231 run_(true) {
232 for (int i = 0; i < num_sensors; ++i) {
233 sensors_[i] = ::std::move((*sensors)[i]);
234 edge_counters_[i] = ::std::unique_ptr<EdgeCounter>(new EdgeCounter(
235 interrupt_priority, encoder_.get(), sensors_[i].get(), &mutex_));
236 }
237 }
238
239 const char *name() const { return name_.c_str(); }
240
241 void StartThread() { thread_.reset(new ::std::thread(::std::ref(*this))); }
242
243 // Called when the state is consistent and up to date.
244 virtual void SaveState() = 0;
245
246 // Starts a sampling iteration. See RunIteration for usage.
247 void StartIteration() {
248 // Start by capturing the current interrupt counts.
249 for (int i = 0; i < num_sensors; ++i) {
250 interrupt_counts_[i] = edge_counters_[i]->any_interrupt_count();
251 }
252
253 {
254 // Now, update the encoder and sensor values.
255 ::std::unique_lock<priority_mutex> mutex_guard(mutex_);
256 encoder_value_ = encoder_->GetRaw();
257 for (int i = 0; i < num_sensors; ++i) {
258 edge_counters_[i]->set_polled_value(sensors_[i]->GetHall());
259 }
260 }
261 }
262
263 // Attempts to finish a sampling iteration. See RunIteration for usage.
264 // Returns true if the iteration succeeded, and false otherwise.
265 bool TryFinishingIteration() {
266 // Make sure no interrupts have occurred while we were waiting. If they
267 // have, we are in an inconsistent state and need to try again.
268 ::std::unique_lock<priority_mutex> mutex_guard(mutex_);
269 bool retry = false;
270 for (int i = 0; i < num_sensors; ++i) {
271 retry = retry || (interrupt_counts_[i] !=
272 edge_counters_[i]->any_interrupt_count());
273 }
274 if (!retry) {
275 SaveState();
276 return true;
277 }
278 LOG(WARNING, "Got an interrupt while sampling encoder %s, retrying\n",
279 name());
280 return false;
281 }
282
283 void RunIteration() {
284 while (true) {
285 StartIteration();
286
287 // Wait more than the amount of time it takes for a digital input change
288 // to go from visible to software to having triggered an interrupt.
289 ::aos::time::SleepFor(::aos::time::Time::InUS(120));
290
291 if (TryFinishingIteration()) {
292 return;
293 }
294 }
295 }
296
297 void operator()() {
Brian Silverman2fe007c2014-12-28 12:20:01 -0800298 ::aos::SetCurrentThreadName("HallSync" + ::std::to_string(num_sensors));
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800299 ::aos::SetCurrentThreadRealtimePriority(priority_);
Austin Schuh010eb812014-10-25 18:06:49 -0700300 while (run_) {
301 ::aos::time::PhasedLoopXMS(10, 9000);
302 RunIteration();
303 }
304 }
305
306 void Quit() {
307 run_ = false;
308 for (int i = 0; i < num_sensors; ++i) {
309 edge_counters_[i]->Quit();
310 }
311 if (thread_) {
312 thread_->join();
313 }
314 }
315
316 protected:
317 // These values are only safe to fetch from inside SaveState()
318 int32_t encoder_value() const { return encoder_value_; }
319 ::std::array<::std::unique_ptr<EdgeCounter>, num_sensors> &edge_counters() {
320 return edge_counters_;
321 }
322
323 private:
324 // A descriptive name for error messages.
325 ::std::string name_;
326 // The priority of the polling thread.
327 int priority_;
328 // The Encoder to sample.
329 ::std::unique_ptr<Encoder> encoder_;
330 // A list of all the digital inputs.
331 ::std::array<::std::unique_ptr<HallEffect>, num_sensors> sensors_;
332 // The mutex used to synchronize all the state.
333 priority_mutex mutex_;
334 ::std::atomic<bool> run_;
335
336 // The state.
337 // The current encoder value.
338 int32_t encoder_value_ = 0;
339 // The current edge counters.
340 ::std::array<::std::unique_ptr<EdgeCounter>, num_sensors> edge_counters_;
341
342 ::std::unique_ptr<::std::thread> thread_;
343 ::std::array<int, num_sensors> interrupt_counts_;
344};
345
346double drivetrain_translate(int32_t in) {
Austin Schuhdb516032014-12-28 00:12:38 -0800347 return static_cast<double>(in) /
348 (256.0 /*cpr*/ * 2.0 /*2x. Stupid WPILib*/) *
349 (18.0 / 50.0 /*output stage*/) * (56.0 / 30.0 /*encoder gears*/)
350 // * constants::GetValues().drivetrain_encoder_ratio
351 *
352 (3.5 /*wheel diameter*/ * 2.54 / 100.0 * M_PI);
Austin Schuh010eb812014-10-25 18:06:49 -0700353}
354
355static const double kVcc = 5.15;
356
Austin Schuh010eb812014-10-25 18:06:49 -0700357float hall_translate(const constants::ShifterHallEffect &k, float in_low,
358 float in_high) {
359 const float low_ratio =
360 0.5 * (in_low - static_cast<float>(k.low_gear_low)) /
361 static_cast<float>(k.low_gear_middle - k.low_gear_low);
362 const float high_ratio =
363 0.5 + 0.5 * (in_high - static_cast<float>(k.high_gear_middle)) /
364 static_cast<float>(k.high_gear_high - k.high_gear_middle);
365
366 // Return low when we are below 1/2, and high when we are above 1/2.
367 if (low_ratio + high_ratio < 1.0) {
368 return low_ratio;
369 } else {
370 return high_ratio;
371 }
372}
373
374double claw_translate(int32_t in) {
Austin Schuhdb516032014-12-28 00:12:38 -0800375 return static_cast<double>(in) / (256.0 /*cpr*/ * 4.0 /*quad*/) /
376 (18.0 / 48.0 /*encoder gears*/) / (12.0 / 60.0 /*chain reduction*/) *
377 (M_PI / 180.0) *
378 2.0 /*TODO(austin): Debug this, encoders read too little*/;
Austin Schuh010eb812014-10-25 18:06:49 -0700379}
380
381double shooter_translate(int32_t in) {
Austin Schuhdb516032014-12-28 00:12:38 -0800382 return static_cast<double>(in) / (256.0 /*cpr*/ * 4.0 /*quad*/) *
383 16 /*sprocket teeth*/ * 0.375 /*chain pitch*/
384 * (2.54 / 100.0 /*in to m*/);
Austin Schuh010eb812014-10-25 18:06:49 -0700385}
386
Austin Schuh010eb812014-10-25 18:06:49 -0700387// This class sends out half of the claw position state at 100 hz.
388class HalfClawHallSynchronizer : public PeriodicHallSynchronizer<3> {
389 public:
390 // Constructs a HalfClawHallSynchronizer.
391 //
392 // priority is the priority of the polling thread.
393 // interrupt_priority is the priority of the interrupt threads.
394 // encoder is the encoder to read.
395 // sensors is an array of hall effect sensors. The sensors[0] is the front
Austin Schuhdb516032014-12-28 00:12:38 -0800396 // sensor, sensors[1] is the calibration sensor, sensors[2] is the back
397 // sensor.
Austin Schuh010eb812014-10-25 18:06:49 -0700398 HalfClawHallSynchronizer(
399 const char *name, int priority, int interrupt_priority,
400 ::std::unique_ptr<Encoder> encoder,
401 ::std::array<::std::unique_ptr<HallEffect>, 3> *sensors, bool reversed)
402 : PeriodicHallSynchronizer<3>(name, priority, interrupt_priority,
403 ::std::move(encoder), sensors),
404 reversed_(reversed) {}
405
406 void set_position(control_loops::HalfClawPosition *half_claw_position) {
407 half_claw_position_ = half_claw_position;
408 }
409
410 // Saves the state so that it can be sent if it was synchronized.
411 virtual void SaveState() {
412 const auto &front = edge_counters()[0];
413 half_claw_position_->front.current = front->polled_value();
414 half_claw_position_->front.posedge_count =
415 front->positive_interrupt_count();
416 half_claw_position_->front.negedge_count =
417 front->negative_interrupt_count();
418
419 const auto &calibration = edge_counters()[1];
420 half_claw_position_->calibration.current = calibration->polled_value();
421 half_claw_position_->calibration.posedge_count =
422 calibration->positive_interrupt_count();
423 half_claw_position_->calibration.negedge_count =
424 calibration->negative_interrupt_count();
425
426 const auto &back = edge_counters()[2];
427 half_claw_position_->back.current = back->polled_value();
428 half_claw_position_->back.posedge_count = back->positive_interrupt_count();
429 half_claw_position_->back.negedge_count = back->negative_interrupt_count();
430
431 const double multiplier = reversed_ ? -1.0 : 1.0;
432
433 half_claw_position_->position =
434 multiplier * claw_translate(encoder_value());
435
436 // We assume here that we can only have 1 sensor have a posedge per cycle.
437 {
438 half_claw_position_->posedge_value =
439 last_half_claw_position_.posedge_value;
440 int posedge_changes = 0;
441 if (half_claw_position_->front.posedge_count !=
442 last_half_claw_position_.front.posedge_count) {
443 ++posedge_changes;
444 half_claw_position_->posedge_value =
445 multiplier * claw_translate(front->last_positive_encoder_value());
446 LOG(INFO, "Got a front posedge\n");
447 }
448
449 if (half_claw_position_->back.posedge_count !=
450 last_half_claw_position_.back.posedge_count) {
451 ++posedge_changes;
452 half_claw_position_->posedge_value =
453 multiplier * claw_translate(back->last_positive_encoder_value());
454 LOG(INFO, "Got a back posedge\n");
455 }
456
457 if (half_claw_position_->calibration.posedge_count !=
458 last_half_claw_position_.calibration.posedge_count) {
459 ++posedge_changes;
460 half_claw_position_->posedge_value =
461 multiplier *
462 claw_translate(calibration->last_positive_encoder_value());
463 LOG(INFO, "Got a calibration posedge\n");
464 }
465
466 if (posedge_changes > 1) {
467 LOG(WARNING, "Found more than 1 positive edge on %s\n", name());
468 }
469 }
470
471 {
472 half_claw_position_->negedge_value =
473 last_half_claw_position_.negedge_value;
474 int negedge_changes = 0;
475 if (half_claw_position_->front.negedge_count !=
476 last_half_claw_position_.front.negedge_count) {
477 ++negedge_changes;
478 half_claw_position_->negedge_value =
479 multiplier * claw_translate(front->last_negative_encoder_value());
480 LOG(INFO, "Got a front negedge\n");
481 }
482
483 if (half_claw_position_->back.negedge_count !=
484 last_half_claw_position_.back.negedge_count) {
485 ++negedge_changes;
486 half_claw_position_->negedge_value =
487 multiplier * claw_translate(back->last_negative_encoder_value());
488 LOG(INFO, "Got a back negedge\n");
489 }
490
491 if (half_claw_position_->calibration.negedge_count !=
492 last_half_claw_position_.calibration.negedge_count) {
493 ++negedge_changes;
494 half_claw_position_->negedge_value =
495 multiplier *
496 claw_translate(calibration->last_negative_encoder_value());
497 LOG(INFO, "Got a calibration negedge\n");
498 }
499
500 if (negedge_changes > 1) {
501 LOG(WARNING, "Found more than 1 negative edge on %s\n", name());
502 }
503 }
504
505 last_half_claw_position_ = *half_claw_position_;
506 }
507
508 private:
509 control_loops::HalfClawPosition *half_claw_position_;
510 control_loops::HalfClawPosition last_half_claw_position_;
511 bool reversed_;
512};
513
Austin Schuh010eb812014-10-25 18:06:49 -0700514// This class sends out the shooter position state at 100 hz.
515class ShooterHallSynchronizer : public PeriodicHallSynchronizer<2> {
516 public:
517 // Constructs a ShooterHallSynchronizer.
518 //
519 // priority is the priority of the polling thread.
520 // interrupt_priority is the priority of the interrupt threads.
521 // encoder is the encoder to read.
522 // sensors is an array of hall effect sensors. The sensors[0] is the proximal
523 // sensor, sensors[1] is the distal sensor.
524 // shooter_plunger is the plunger.
525 // shooter_latch is the latch.
526 ShooterHallSynchronizer(
527 int priority, int interrupt_priority, ::std::unique_ptr<Encoder> encoder,
528 ::std::array<::std::unique_ptr<HallEffect>, 2> *sensors,
529 ::std::unique_ptr<HallEffect> shooter_plunger,
530 ::std::unique_ptr<HallEffect> shooter_latch)
531 : PeriodicHallSynchronizer<2>("shooter", priority, interrupt_priority,
532 ::std::move(encoder), sensors),
533 shooter_plunger_(::std::move(shooter_plunger)),
534 shooter_latch_(::std::move(shooter_latch)) {}
535
536 // Saves the state so that it can be sent if it was synchronized.
537 virtual void SaveState() {
538 auto shooter_position =
539 control_loops::shooter_queue_group.position.MakeMessage();
540
541 shooter_position->plunger = shooter_plunger_->GetHall();
542 shooter_position->latch = shooter_latch_->GetHall();
543 shooter_position->position = shooter_translate(encoder_value());
544
545 {
546 const auto &proximal_edge_counter = edge_counters()[0];
547 shooter_position->pusher_proximal.current =
548 proximal_edge_counter->polled_value();
549 shooter_position->pusher_proximal.posedge_count =
550 proximal_edge_counter->positive_interrupt_count();
551 shooter_position->pusher_proximal.negedge_count =
552 proximal_edge_counter->negative_interrupt_count();
553 shooter_position->pusher_proximal.posedge_value = shooter_translate(
554 proximal_edge_counter->last_positive_encoder_value());
555 }
556
557 {
Austin Schuhdb516032014-12-28 00:12:38 -0800558 const auto &distal_edge_counter = edge_counters()[1];
559 shooter_position->pusher_distal.current =
560 distal_edge_counter->polled_value();
561 shooter_position->pusher_distal.posedge_count =
562 distal_edge_counter->positive_interrupt_count();
563 shooter_position->pusher_distal.negedge_count =
564 distal_edge_counter->negative_interrupt_count();
565 shooter_position->pusher_distal.posedge_value =
566 shooter_translate(distal_edge_counter->last_positive_encoder_value());
Austin Schuh010eb812014-10-25 18:06:49 -0700567 }
568
569 shooter_position.Send();
570 }
571
572 private:
573 ::std::unique_ptr<HallEffect> shooter_plunger_;
574 ::std::unique_ptr<HallEffect> shooter_latch_;
575};
576
Austin Schuh010eb812014-10-25 18:06:49 -0700577class SensorReader {
578 public:
579 SensorReader()
580 : auto_selector_analog_(new AnalogInput(4)),
Brian Silvermancb77f232014-12-19 21:48:36 -0800581 left_encoder_(new Encoder(11, 10, false, Encoder::k2X)), // E0
582 right_encoder_(new Encoder(13, 12, false, Encoder::k2X)), // E1
583 low_left_drive_hall_(new AnalogInput(1)),
584 high_left_drive_hall_(new AnalogInput(0)),
585 low_right_drive_hall_(new AnalogInput(2)),
586 high_right_drive_hall_(new AnalogInput(3)),
587 shooter_plunger_(new HallEffect(8)), // S3
588 shooter_latch_(new HallEffect(9)), // S4
589 shooter_distal_(new HallEffect(7)), // S2
590 shooter_proximal_(new HallEffect(6)), // S1
591 shooter_encoder_(new Encoder(14, 15)), // E2
592 claw_top_front_hall_(new HallEffect(4)), // R2
593 claw_top_calibration_hall_(new HallEffect(3)), // R3
594 claw_top_back_hall_(new HallEffect(5)), // R1
595 claw_top_encoder_(new Encoder(17, 16)), // E3
Austin Schuh010eb812014-10-25 18:06:49 -0700596 // L2 Middle Left hall effect sensor.
Brian Silvermancb77f232014-12-19 21:48:36 -0800597 claw_bottom_front_hall_(new HallEffect(1)),
Austin Schuh010eb812014-10-25 18:06:49 -0700598 // L3 Bottom Left hall effect sensor
Brian Silvermancb77f232014-12-19 21:48:36 -0800599 claw_bottom_calibration_hall_(new HallEffect(0)),
Austin Schuh010eb812014-10-25 18:06:49 -0700600 // L1 Top Left hall effect sensor
Brian Silvermancb77f232014-12-19 21:48:36 -0800601 claw_bottom_back_hall_(new HallEffect(2)),
602 claw_bottom_encoder_(new Encoder(21, 20)), // E5
Austin Schuh010eb812014-10-25 18:06:49 -0700603 run_(true) {
604 filter_.SetPeriodNanoSeconds(100000);
605 filter_.Add(shooter_plunger_.get());
606 filter_.Add(shooter_latch_.get());
607 filter_.Add(shooter_distal_.get());
608 filter_.Add(shooter_proximal_.get());
609 filter_.Add(claw_top_front_hall_.get());
610 filter_.Add(claw_top_calibration_hall_.get());
611 filter_.Add(claw_top_back_hall_.get());
612 filter_.Add(claw_bottom_front_hall_.get());
613 filter_.Add(claw_bottom_calibration_hall_.get());
614 filter_.Add(claw_bottom_back_hall_.get());
Austin Schuh010eb812014-10-25 18:06:49 -0700615 }
616
617 void operator()() {
Brian Silverman2fe007c2014-12-28 12:20:01 -0800618 ::aos::SetCurrentThreadName("SensorReader");
619
Austin Schuh010eb812014-10-25 18:06:49 -0700620 const int kPriority = 30;
621 const int kInterruptPriority = 55;
Austin Schuh010eb812014-10-25 18:06:49 -0700622
623 ::std::array<::std::unique_ptr<HallEffect>, 2> shooter_sensors;
624 shooter_sensors[0] = ::std::move(shooter_proximal_);
625 shooter_sensors[1] = ::std::move(shooter_distal_);
626 ShooterHallSynchronizer shooter(
627 kPriority, kInterruptPriority, ::std::move(shooter_encoder_),
628 &shooter_sensors, ::std::move(shooter_plunger_),
629 ::std::move(shooter_latch_));
630 shooter.StartThread();
631
632 ::std::array<::std::unique_ptr<HallEffect>, 3> claw_top_sensors;
633 claw_top_sensors[0] = ::std::move(claw_top_front_hall_);
634 claw_top_sensors[1] = ::std::move(claw_top_calibration_hall_);
635 claw_top_sensors[2] = ::std::move(claw_top_back_hall_);
636 HalfClawHallSynchronizer top_claw("top_claw", kPriority, kInterruptPriority,
637 ::std::move(claw_top_encoder_),
638 &claw_top_sensors, false);
639
640 ::std::array<::std::unique_ptr<HallEffect>, 3> claw_bottom_sensors;
641 claw_bottom_sensors[0] = ::std::move(claw_bottom_front_hall_);
642 claw_bottom_sensors[1] = ::std::move(claw_bottom_calibration_hall_);
643 claw_bottom_sensors[2] = ::std::move(claw_bottom_back_hall_);
644 HalfClawHallSynchronizer bottom_claw(
645 "bottom_claw", kPriority, kInterruptPriority,
646 ::std::move(claw_bottom_encoder_), &claw_bottom_sensors, true);
647
Brian Silverman2fe007c2014-12-28 12:20:01 -0800648 ::aos::SetCurrentThreadRealtimePriority(kPriority);
Austin Schuh010eb812014-10-25 18:06:49 -0700649 while (run_) {
650 ::aos::time::PhasedLoopXMS(10, 9000);
651 RunIteration();
652
653 auto claw_position =
654 control_loops::claw_queue_group.position.MakeMessage();
655 bottom_claw.set_position(&claw_position->bottom);
656 top_claw.set_position(&claw_position->top);
657 while (true) {
658 bottom_claw.StartIteration();
659 top_claw.StartIteration();
660
661 // Wait more than the amount of time it takes for a digital input change
662 // to go from visible to software to having triggered an interrupt.
663 ::aos::time::SleepFor(::aos::time::Time::InUS(120));
664
Austin Schuhdb516032014-12-28 00:12:38 -0800665 if (bottom_claw.TryFinishingIteration() &&
666 top_claw.TryFinishingIteration()) {
Austin Schuh010eb812014-10-25 18:06:49 -0700667 break;
668 }
669 }
670
671 claw_position.Send();
672 }
673 shooter.Quit();
674 top_claw.Quit();
675 bottom_claw.Quit();
676 }
677
678 void RunIteration() {
679 //::aos::time::TimeFreezer time_freezer;
680 DriverStation *ds = DriverStation::GetInstance();
681
Austin Schuhdb516032014-12-28 00:12:38 -0800682 if (ds->IsSysActive()) {
Austin Schuh010eb812014-10-25 18:06:49 -0700683 auto message = ::aos::controls::output_check_received.MakeMessage();
684 // TODO(brians): Actually read a pulse value from the roboRIO.
685 message->pwm_value = 0;
686 message->pulse_length = -1;
687 LOG_STRUCT(DEBUG, "received", *message);
688 message.Send();
689 }
690
691 ::frc971::sensors::auto_mode.MakeWithBuilder()
692 .voltage(auto_selector_analog_->GetVoltage())
693 .Send();
694
695 // TODO(austin): Calibrate the shifter constants again.
696 drivetrain.position.MakeWithBuilder()
697 .right_encoder(drivetrain_translate(right_encoder_->GetRaw()))
698 .left_encoder(-drivetrain_translate(left_encoder_->GetRaw()))
699 .left_shifter_position(
700 hall_translate(constants::GetValues().left_drive,
701 low_left_drive_hall_->GetVoltage(),
702 high_left_drive_hall_->GetVoltage()))
703 .right_shifter_position(
704 hall_translate(constants::GetValues().right_drive,
705 low_right_drive_hall_->GetVoltage(),
706 high_right_drive_hall_->GetVoltage()))
707 .battery_voltage(ds->GetBatteryVoltage())
708 .Send();
709
Brian Silvermand8f403a2014-12-13 19:12:04 -0500710 // Signal that we are alive.
Austin Schuh010eb812014-10-25 18:06:49 -0700711 ::aos::controls::sensor_generation.MakeWithBuilder()
712 .reader_pid(getpid())
713 .cape_resets(0)
714 .Send();
715 }
716
717 void Quit() { run_ = false; }
718
719 private:
720 ::std::unique_ptr<AnalogInput> auto_selector_analog_;
721
722 ::std::unique_ptr<Encoder> left_encoder_;
723 ::std::unique_ptr<Encoder> right_encoder_;
724 ::std::unique_ptr<AnalogInput> low_left_drive_hall_;
725 ::std::unique_ptr<AnalogInput> high_left_drive_hall_;
726 ::std::unique_ptr<AnalogInput> low_right_drive_hall_;
727 ::std::unique_ptr<AnalogInput> high_right_drive_hall_;
728
729 ::std::unique_ptr<HallEffect> shooter_plunger_;
730 ::std::unique_ptr<HallEffect> shooter_latch_;
731 ::std::unique_ptr<HallEffect> shooter_distal_;
732 ::std::unique_ptr<HallEffect> shooter_proximal_;
733 ::std::unique_ptr<Encoder> shooter_encoder_;
734
735 ::std::unique_ptr<HallEffect> claw_top_front_hall_;
736 ::std::unique_ptr<HallEffect> claw_top_calibration_hall_;
737 ::std::unique_ptr<HallEffect> claw_top_back_hall_;
738 ::std::unique_ptr<Encoder> claw_top_encoder_;
739
740 ::std::unique_ptr<HallEffect> claw_bottom_front_hall_;
741 ::std::unique_ptr<HallEffect> claw_bottom_calibration_hall_;
742 ::std::unique_ptr<HallEffect> claw_bottom_back_hall_;
743 ::std::unique_ptr<Encoder> claw_bottom_encoder_;
744
745 ::std::atomic<bool> run_;
746 DigitalGlitchFilter filter_;
747};
748
Brian Silvermand8f403a2014-12-13 19:12:04 -0500749class SolenoidWriter {
Austin Schuh010eb812014-10-25 18:06:49 -0700750 public:
Brian Silvermand8f403a2014-12-13 19:12:04 -0500751 SolenoidWriter(const ::std::unique_ptr<BufferedPcm> &pcm)
752 : pcm_(pcm),
753 drivetrain_(".frc971.control_loops.drivetrain.output"),
754 shooter_(".frc971.control_loops.shooter_queue_group.output") {}
755
756 void set_drivetrain_left(::std::unique_ptr<BufferedSolenoid> s) {
757 drivetrain_left_ = ::std::move(s);
Austin Schuh010eb812014-10-25 18:06:49 -0700758 }
759
Brian Silvermand8f403a2014-12-13 19:12:04 -0500760 void set_drivetrain_right(::std::unique_ptr<BufferedSolenoid> s) {
761 drivetrain_right_ = ::std::move(s);
762 }
Austin Schuh010eb812014-10-25 18:06:49 -0700763
Brian Silvermand8f403a2014-12-13 19:12:04 -0500764 void set_shooter_latch(::std::unique_ptr<BufferedSolenoid> s) {
765 shooter_latch_ = ::std::move(s);
766 }
Austin Schuh010eb812014-10-25 18:06:49 -0700767
Brian Silvermand8f403a2014-12-13 19:12:04 -0500768 void set_shooter_brake(::std::unique_ptr<BufferedSolenoid> s) {
769 shooter_brake_ = ::std::move(s);
770 }
Austin Schuh010eb812014-10-25 18:06:49 -0700771
Brian Silvermand8f403a2014-12-13 19:12:04 -0500772 void operator()() {
773 ::aos::SetCurrentThreadName("Solenoids");
774 ::aos::SetCurrentThreadRealtimePriority(30);
775
776 while (run_) {
777 ::aos::time::PhasedLoopXMS(20, 1000);
778
779 {
780 drivetrain_.FetchLatest();
781 if (drivetrain_.get()) {
782 LOG_STRUCT(DEBUG, "solenoids", *drivetrain_);
783 drivetrain_left_->Set(drivetrain_->left_high);
784 drivetrain_right_->Set(drivetrain_->right_high);
785 }
786 }
787
788 {
789 shooter_.FetchLatest();
790 if (shooter_.get()) {
791 LOG_STRUCT(DEBUG, "solenoids", *shooter_);
792 shooter_latch_->Set(!shooter_->latch_piston);
793 shooter_brake_->Set(!shooter_->brake_piston);
794 }
795 }
796
797 pcm_->Flush();
Austin Schuh010eb812014-10-25 18:06:49 -0700798 }
799 }
800
Brian Silvermand8f403a2014-12-13 19:12:04 -0500801 void Quit() { run_ = false; }
Austin Schuh010eb812014-10-25 18:06:49 -0700802
Brian Silvermand8f403a2014-12-13 19:12:04 -0500803 private:
804 const ::std::unique_ptr<BufferedPcm> &pcm_;
805 ::std::unique_ptr<BufferedSolenoid> drivetrain_left_;
806 ::std::unique_ptr<BufferedSolenoid> drivetrain_right_;
807 ::std::unique_ptr<BufferedSolenoid> shooter_latch_;
808 ::std::unique_ptr<BufferedSolenoid> shooter_brake_;
Austin Schuh010eb812014-10-25 18:06:49 -0700809
Brian Silvermand8f403a2014-12-13 19:12:04 -0500810 ::aos::Queue<::frc971::control_loops::Drivetrain::Output> drivetrain_;
811 ::aos::Queue<::frc971::control_loops::ShooterGroup::Output> shooter_;
Austin Schuh010eb812014-10-25 18:06:49 -0700812
Brian Silvermand8f403a2014-12-13 19:12:04 -0500813 ::std::atomic<bool> run_{true};
814};
815
816class DrivetrainWriter : public LoopOutputHandler {
817 public:
818 void set_left_drivetrain_talon(::std::unique_ptr<Talon> t) {
819 left_drivetrain_talon_ = ::std::move(t);
Austin Schuh010eb812014-10-25 18:06:49 -0700820 }
821
Brian Silvermand8f403a2014-12-13 19:12:04 -0500822 void set_right_drivetrain_talon(::std::unique_ptr<Talon> t) {
823 right_drivetrain_talon_ = ::std::move(t);
824 }
Austin Schuh010eb812014-10-25 18:06:49 -0700825
Brian Silvermand8f403a2014-12-13 19:12:04 -0500826 private:
827 virtual void Read() override {
828 ::frc971::control_loops::drivetrain.output.FetchAnother();
829 }
830
831 virtual void Write() override {
832 auto &queue = ::frc971::control_loops::drivetrain.output;
833 LOG_STRUCT(DEBUG, "will output", *queue);
834 left_drivetrain_talon_->Set(-queue->left_voltage / 12.0);
835 right_drivetrain_talon_->Set(queue->right_voltage / 12.0);
836 }
837
838 virtual void Stop() override {
839 LOG(WARNING, "drivetrain output too old\n");
840 left_drivetrain_talon_->Disable();
841 right_drivetrain_talon_->Disable();
842 }
843
Austin Schuh010eb812014-10-25 18:06:49 -0700844 ::std::unique_ptr<Talon> left_drivetrain_talon_;
Brian Silvermand8f403a2014-12-13 19:12:04 -0500845 ::std::unique_ptr<Talon> right_drivetrain_talon_;
846};
847
848class ShooterWriter : public LoopOutputHandler {
849 public:
850 void set_shooter_talon(::std::unique_ptr<Talon> t) {
851 shooter_talon_ = ::std::move(t);
852 }
853
854 private:
855 virtual void Read() override {
856 ::frc971::control_loops::shooter_queue_group.output.FetchAnother();
857 }
858
859 virtual void Write() override {
860 auto &queue = ::frc971::control_loops::shooter_queue_group.output;
861 LOG_STRUCT(DEBUG, "will output", *queue);
862 shooter_talon_->Set(queue->voltage / 12.0);
863 }
864
865 virtual void Stop() override {
866 LOG(WARNING, "shooter output too old\n");
867 shooter_talon_->Disable();
868 }
869
Austin Schuh010eb812014-10-25 18:06:49 -0700870 ::std::unique_ptr<Talon> shooter_talon_;
Brian Silvermand8f403a2014-12-13 19:12:04 -0500871};
872
873class ClawWriter : public LoopOutputHandler {
874 public:
875 void set_top_claw_talon(::std::unique_ptr<Talon> t) {
876 top_claw_talon_ = ::std::move(t);
877 }
878
879 void set_bottom_claw_talon(::std::unique_ptr<Talon> t) {
880 bottom_claw_talon_ = ::std::move(t);
881 }
882
883 void set_left_tusk_talon(::std::unique_ptr<Talon> t) {
884 left_tusk_talon_ = ::std::move(t);
885 }
886
887 void set_right_tusk_talon(::std::unique_ptr<Talon> t) {
888 right_tusk_talon_ = ::std::move(t);
889 }
890
891 void set_intake1_talon(::std::unique_ptr<Talon> t) {
892 intake1_talon_ = ::std::move(t);
893 }
894
895 void set_intake2_talon(::std::unique_ptr<Talon> t) {
896 intake2_talon_ = ::std::move(t);
897 }
898
899 private:
900 virtual void Read() override {
901 ::frc971::control_loops::claw_queue_group.output.FetchAnother();
902 }
903
904 virtual void Write() override {
905 auto &queue = ::frc971::control_loops::claw_queue_group.output;
906 LOG_STRUCT(DEBUG, "will output", *queue);
907 intake1_talon_->Set(queue->intake_voltage / 12.0);
908 intake2_talon_->Set(queue->intake_voltage / 12.0);
909 bottom_claw_talon_->Set(-queue->bottom_claw_voltage / 12.0);
910 top_claw_talon_->Set(queue->top_claw_voltage / 12.0);
911 left_tusk_talon_->Set(queue->tusk_voltage / 12.0);
912 right_tusk_talon_->Set(-queue->tusk_voltage / 12.0);
913 }
914
915 virtual void Stop() override {
916 LOG(WARNING, "claw output too old\n");
917 intake1_talon_->Disable();
918 intake2_talon_->Disable();
919 bottom_claw_talon_->Disable();
920 top_claw_talon_->Disable();
921 left_tusk_talon_->Disable();
922 right_tusk_talon_->Disable();
923 }
924
Austin Schuh010eb812014-10-25 18:06:49 -0700925 ::std::unique_ptr<Talon> top_claw_talon_;
926 ::std::unique_ptr<Talon> bottom_claw_talon_;
927 ::std::unique_ptr<Talon> left_tusk_talon_;
928 ::std::unique_ptr<Talon> right_tusk_talon_;
929 ::std::unique_ptr<Talon> intake1_talon_;
930 ::std::unique_ptr<Talon> intake2_talon_;
Austin Schuh010eb812014-10-25 18:06:49 -0700931};
932
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800933} // namespace wpilib
Austin Schuh010eb812014-10-25 18:06:49 -0700934} // namespace frc971
935
936class WPILibRobot : public RobotBase {
937 public:
938 virtual void StartCompetition() {
Brian Silvermand8f403a2014-12-13 19:12:04 -0500939 ::aos::InitNRT();
Brian Silverman2fe007c2014-12-28 12:20:01 -0800940 ::aos::SetCurrentThreadName("StartCompetition");
Brian Silvermand8f403a2014-12-13 19:12:04 -0500941
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800942 ::frc971::wpilib::JoystickSender joystick_sender;
Austin Schuh010eb812014-10-25 18:06:49 -0700943 ::std::thread joystick_thread(::std::ref(joystick_sender));
Brian Silvermand8f403a2014-12-13 19:12:04 -0500944 ::frc971::wpilib::SensorReader reader;
945 ::std::thread reader_thread(::std::ref(reader));
Brian Silverman07ec88e2014-12-28 00:13:08 -0800946 ::frc971::wpilib::GyroSender gyro_sender;
947 ::std::thread gyro_thread(::std::ref(gyro_sender));
Brian Silvermand8f403a2014-12-13 19:12:04 -0500948 ::std::unique_ptr<Compressor> compressor(new Compressor());
949 compressor->SetClosedLoopControl(true);
950
951 ::frc971::wpilib::DrivetrainWriter drivetrain_writer;
952 drivetrain_writer.set_left_drivetrain_talon(
953 ::std::unique_ptr<Talon>(new Talon(5)));
954 drivetrain_writer.set_right_drivetrain_talon(
955 ::std::unique_ptr<Talon>(new Talon(2)));
956 ::std::thread drivetrain_writer_thread(::std::ref(drivetrain_writer));
957
Brian Silvermand8f403a2014-12-13 19:12:04 -0500958 ::frc971::wpilib::ClawWriter claw_writer;
959 claw_writer.set_top_claw_talon(::std::unique_ptr<Talon>(new Talon(1)));
960 claw_writer.set_bottom_claw_talon(::std::unique_ptr<Talon>(new Talon(0)));
961 claw_writer.set_left_tusk_talon(::std::unique_ptr<Talon>(new Talon(4)));
962 claw_writer.set_right_tusk_talon(::std::unique_ptr<Talon>(new Talon(3)));
963 claw_writer.set_intake1_talon(::std::unique_ptr<Talon>(new Talon(7)));
964 claw_writer.set_intake2_talon(::std::unique_ptr<Talon>(new Talon(8)));
965 ::std::thread claw_writer_thread(::std::ref(claw_writer));
966
967 ::frc971::wpilib::ShooterWriter shooter_writer;
968 shooter_writer.set_shooter_talon(::std::unique_ptr<Talon>(new Talon(6)));
969 ::std::thread shooter_writer_thread(::std::ref(shooter_writer));
970
971 ::std::unique_ptr<::frc971::wpilib::BufferedPcm> pcm(
972 new ::frc971::wpilib::BufferedPcm());
973 ::frc971::wpilib::SolenoidWriter solenoid_writer(pcm);
974 solenoid_writer.set_drivetrain_left(pcm->MakeSolenoid(6));
975 solenoid_writer.set_drivetrain_right(pcm->MakeSolenoid(7));
976 solenoid_writer.set_shooter_latch(pcm->MakeSolenoid(5));
977 solenoid_writer.set_shooter_brake(pcm->MakeSolenoid(4));
978 ::std::thread solenoid_thread(::std::ref(solenoid_writer));
979
980 // Wait forever. Not much else to do...
981 PCHECK(select(0, nullptr, nullptr, nullptr, nullptr));
982
Austin Schuh010eb812014-10-25 18:06:49 -0700983 LOG(ERROR, "Exiting WPILibRobot\n");
Brian Silverman07ec88e2014-12-28 00:13:08 -0800984
Austin Schuh010eb812014-10-25 18:06:49 -0700985 joystick_sender.Quit();
986 joystick_thread.join();
Brian Silvermand8f403a2014-12-13 19:12:04 -0500987 reader.Quit();
988 reader_thread.join();
Brian Silverman07ec88e2014-12-28 00:13:08 -0800989 gyro_sender.Quit();
990 gyro_thread.join();
Brian Silvermand8f403a2014-12-13 19:12:04 -0500991
992 drivetrain_writer.Quit();
993 drivetrain_writer_thread.join();
994 claw_writer.Quit();
995 claw_writer_thread.join();
996 shooter_writer.Quit();
997 shooter_writer_thread.join();
998 solenoid_writer.Quit();
999 solenoid_thread.join();
1000
Austin Schuh010eb812014-10-25 18:06:49 -07001001 ::aos::Cleanup();
1002 }
1003};
1004
Austin Schuhdb516032014-12-28 00:12:38 -08001005
Austin Schuh010eb812014-10-25 18:06:49 -07001006START_ROBOT_CLASS(WPILibRobot);