blob: 7b2c3a38cbcca7b6376084b559f5a263531b207a [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 Silvermanda45b6c2014-12-28 11:36:50 -080035
Brian Silvermancb77f232014-12-19 21:48:36 -080036#include "Encoder.h"
Brian Silvermancb77f232014-12-19 21:48:36 -080037#include "Talon.h"
38#include "DriverStation.h"
39#include "AnalogInput.h"
Brian Silvermancb77f232014-12-19 21:48:36 -080040#include "Compressor.h"
41#include "RobotBase.h"
Austin Schuh010eb812014-10-25 18:06:49 -070042
43#ifndef M_PI
44#define M_PI 3.14159265358979323846
45#endif
46
47using ::aos::util::SimpleLogInterval;
48using ::frc971::control_loops::drivetrain;
49using ::frc971::sensors::other_sensors;
50using ::frc971::sensors::gyro_reading;
51using ::aos::util::WrappingCounter;
52
53namespace frc971 {
Brian Silvermanda45b6c2014-12-28 11:36:50 -080054namespace wpilib {
Austin Schuh010eb812014-10-25 18:06:49 -070055
56class priority_mutex {
57 public:
58 typedef pthread_mutex_t *native_handle_type;
59
60 // TODO(austin): Write a test case for the mutex, and make the constructor
61 // constexpr.
62 priority_mutex() {
63 pthread_mutexattr_t attr;
Austin Schuh010eb812014-10-25 18:06:49 -070064#ifdef NDEBUG
Brian Silvermanda45b6c2014-12-28 11:36:50 -080065#error "Won't let assert_perror be no-op ed"
Austin Schuh010eb812014-10-25 18:06:49 -070066#endif
Austin Schuhdb516032014-12-28 00:12:38 -080067 // Turn on priority inheritance.
Austin Schuh010eb812014-10-25 18:06:49 -070068 assert_perror(pthread_mutexattr_init(&attr));
69 assert_perror(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL));
70 assert_perror(pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT));
71
72 assert_perror(pthread_mutex_init(native_handle(), &attr));
73
74 assert_perror(pthread_mutexattr_destroy(&attr));
75 }
76
77 ~priority_mutex() { pthread_mutex_destroy(&handle_); }
78
79 void lock() { assert_perror(pthread_mutex_lock(&handle_)); }
80 bool try_lock() {
81 int ret = pthread_mutex_trylock(&handle_);
82 if (ret == 0) {
83 return true;
84 } else if (ret == EBUSY) {
85 return false;
86 } else {
87 assert_perror(ret);
88 }
89 }
90 void unlock() { assert_perror(pthread_mutex_unlock(&handle_)); }
91
92 native_handle_type native_handle() { return &handle_; }
93
94 private:
95 DISALLOW_COPY_AND_ASSIGN(priority_mutex);
96 pthread_mutex_t handle_;
97};
98
Brian Silvermanda45b6c2014-12-28 11:36:50 -080099// TODO(brian): Split this out into a separate file once DMA is in.
Austin Schuh010eb812014-10-25 18:06:49 -0700100class EdgeCounter {
101 public:
102 EdgeCounter(int priority, Encoder *encoder, HallEffect *input,
103 priority_mutex *mutex)
104 : priority_(priority),
105 encoder_(encoder),
106 input_(input),
107 mutex_(mutex),
108 run_(true),
109 any_interrupt_count_(0) {
110 thread_.reset(new ::std::thread(::std::ref(*this)));
111 }
112
113 // Waits for interrupts, locks the mutex, and updates the internal state.
114 // Updates the any_interrupt_count count when the interrupt comes in without
115 // the lock.
Austin Schuhdb516032014-12-28 00:12:38 -0800116 void operator()() {
Brian Silverman2fe007c2014-12-28 12:20:01 -0800117 ::aos::SetCurrentThreadName("EdgeCounter_" +
118 ::std::to_string(input_->GetChannel()));
Austin Schuh010eb812014-10-25 18:06:49 -0700119
120 input_->RequestInterrupts();
121 input_->SetUpSourceEdge(true, true);
122
123 {
124 ::std::unique_lock<priority_mutex> mutex_guard(*mutex_);
125 current_value_ = input_->GetHall();
126 }
127
Brian Silverman2fe007c2014-12-28 12:20:01 -0800128 ::aos::SetCurrentThreadRealtimePriority(priority_);
Austin Schuh010eb812014-10-25 18:06:49 -0700129 InterruptableSensorBase::WaitResult result = InterruptableSensorBase::kBoth;
130 while (run_) {
131 result = input_->WaitForInterrupt(
132 0.1, result != InterruptableSensorBase::kTimeout);
133 if (result == InterruptableSensorBase::kTimeout) {
134 continue;
135 }
136 ++any_interrupt_count_;
137
138 ::std::unique_lock<priority_mutex> mutex_guard(*mutex_);
139 int32_t encoder_value = encoder_->GetRaw();
140 bool hall_value = input_->GetHall();
141 if (current_value_ != hall_value) {
142 if (hall_value) {
143 ++positive_interrupt_count_;
144 last_positive_encoder_value_ = encoder_value;
145 } else {
146 ++negative_interrupt_count_;
147 last_negative_encoder_value_ = encoder_value;
148 }
149 } else {
150 LOG(WARNING, "Detected spurious edge on %d. Dropping it.\n",
151 input_->GetChannel());
152 }
153
154 current_value_ = hall_value;
155 }
156 }
157
158 // Updates the internal hall effect value given this new observation.
159 // The mutex provided at construction time must be held during this operation.
160 void set_polled_value(bool value) {
161 polled_value_ = value;
162 bool miss_match = (value != current_value_);
163 if (miss_match && last_miss_match_) {
164 current_value_ = value;
165 last_miss_match_ = false;
166 } else {
167 last_miss_match_ = miss_match;
168 }
169 }
170
171 // Signals the thread to quit next time it gets an interrupt.
172 void Quit() {
173 run_ = false;
174 thread_->join();
175 }
176
177 // Returns the total number of interrupts since construction time. This
178 // should be done without the mutex held.
179 int any_interrupt_count() const { return any_interrupt_count_; }
180 // Returns the current interrupt edge counts and encoder values.
181 // The mutex provided at construction time must be held during this operation.
182 int positive_interrupt_count() const { return positive_interrupt_count_; }
183 int negative_interrupt_count() const { return negative_interrupt_count_; }
184 int32_t last_positive_encoder_value() const {
185 return last_positive_encoder_value_;
186 }
187 int32_t last_negative_encoder_value() const {
188 return last_negative_encoder_value_;
189 }
190 // Returns the current polled value.
191 bool polled_value() const { return polled_value_; }
192
193 private:
194 int priority_;
195 Encoder *encoder_;
196 HallEffect *input_;
197 priority_mutex *mutex_;
198 ::std::atomic<bool> run_;
199
200 ::std::atomic<int> any_interrupt_count_;
201
202 // The following variables represent the current state. They must be
203 // synchronized by mutex_;
204 bool current_value_ = false;
205 bool polled_value_ = false;
206 bool last_miss_match_ = true;
207 int positive_interrupt_count_ = 0;
208 int negative_interrupt_count_ = 0;
209 int32_t last_positive_encoder_value_ = 0;
210 int32_t last_negative_encoder_value_ = 0;
211
212 ::std::unique_ptr<::std::thread> thread_;
213};
214
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800215// This class will synchronize sampling edges on a bunch of HallEffects with
Austin Schuh010eb812014-10-25 18:06:49 -0700216// the periodic poll.
217//
218// The data is provided to subclasses by calling SaveState when the state is
219// consistent and ready.
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800220//
221// TODO(brian): Split this out into a separate file once DMA is in.
Austin Schuh010eb812014-10-25 18:06:49 -0700222template <int num_sensors>
223class PeriodicHallSynchronizer {
224 public:
225 PeriodicHallSynchronizer(
226 const char *name, int priority, int interrupt_priority,
227 ::std::unique_ptr<Encoder> encoder,
228 ::std::array<::std::unique_ptr<HallEffect>, num_sensors> *sensors)
229 : name_(name),
230 priority_(priority),
231 encoder_(::std::move(encoder)),
232 run_(true) {
233 for (int i = 0; i < num_sensors; ++i) {
234 sensors_[i] = ::std::move((*sensors)[i]);
235 edge_counters_[i] = ::std::unique_ptr<EdgeCounter>(new EdgeCounter(
236 interrupt_priority, encoder_.get(), sensors_[i].get(), &mutex_));
237 }
238 }
239
240 const char *name() const { return name_.c_str(); }
241
242 void StartThread() { thread_.reset(new ::std::thread(::std::ref(*this))); }
243
244 // Called when the state is consistent and up to date.
245 virtual void SaveState() = 0;
246
247 // Starts a sampling iteration. See RunIteration for usage.
248 void StartIteration() {
249 // Start by capturing the current interrupt counts.
250 for (int i = 0; i < num_sensors; ++i) {
251 interrupt_counts_[i] = edge_counters_[i]->any_interrupt_count();
252 }
253
254 {
255 // Now, update the encoder and sensor values.
256 ::std::unique_lock<priority_mutex> mutex_guard(mutex_);
257 encoder_value_ = encoder_->GetRaw();
258 for (int i = 0; i < num_sensors; ++i) {
259 edge_counters_[i]->set_polled_value(sensors_[i]->GetHall());
260 }
261 }
262 }
263
264 // Attempts to finish a sampling iteration. See RunIteration for usage.
265 // Returns true if the iteration succeeded, and false otherwise.
266 bool TryFinishingIteration() {
267 // Make sure no interrupts have occurred while we were waiting. If they
268 // have, we are in an inconsistent state and need to try again.
269 ::std::unique_lock<priority_mutex> mutex_guard(mutex_);
270 bool retry = false;
271 for (int i = 0; i < num_sensors; ++i) {
272 retry = retry || (interrupt_counts_[i] !=
273 edge_counters_[i]->any_interrupt_count());
274 }
275 if (!retry) {
276 SaveState();
277 return true;
278 }
279 LOG(WARNING, "Got an interrupt while sampling encoder %s, retrying\n",
280 name());
281 return false;
282 }
283
284 void RunIteration() {
285 while (true) {
286 StartIteration();
287
288 // Wait more than the amount of time it takes for a digital input change
289 // to go from visible to software to having triggered an interrupt.
290 ::aos::time::SleepFor(::aos::time::Time::InUS(120));
291
292 if (TryFinishingIteration()) {
293 return;
294 }
295 }
296 }
297
298 void operator()() {
Brian Silverman2fe007c2014-12-28 12:20:01 -0800299 ::aos::SetCurrentThreadName("HallSync" + ::std::to_string(num_sensors));
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800300 ::aos::SetCurrentThreadRealtimePriority(priority_);
Austin Schuh010eb812014-10-25 18:06:49 -0700301 while (run_) {
302 ::aos::time::PhasedLoopXMS(10, 9000);
303 RunIteration();
304 }
305 }
306
307 void Quit() {
308 run_ = false;
309 for (int i = 0; i < num_sensors; ++i) {
310 edge_counters_[i]->Quit();
311 }
312 if (thread_) {
313 thread_->join();
314 }
315 }
316
317 protected:
318 // These values are only safe to fetch from inside SaveState()
319 int32_t encoder_value() const { return encoder_value_; }
320 ::std::array<::std::unique_ptr<EdgeCounter>, num_sensors> &edge_counters() {
321 return edge_counters_;
322 }
323
324 private:
325 // A descriptive name for error messages.
326 ::std::string name_;
327 // The priority of the polling thread.
328 int priority_;
329 // The Encoder to sample.
330 ::std::unique_ptr<Encoder> encoder_;
331 // A list of all the digital inputs.
332 ::std::array<::std::unique_ptr<HallEffect>, num_sensors> sensors_;
333 // The mutex used to synchronize all the state.
334 priority_mutex mutex_;
335 ::std::atomic<bool> run_;
336
337 // The state.
338 // The current encoder value.
339 int32_t encoder_value_ = 0;
340 // The current edge counters.
341 ::std::array<::std::unique_ptr<EdgeCounter>, num_sensors> edge_counters_;
342
343 ::std::unique_ptr<::std::thread> thread_;
344 ::std::array<int, num_sensors> interrupt_counts_;
345};
346
347double drivetrain_translate(int32_t in) {
Austin Schuhdb516032014-12-28 00:12:38 -0800348 return static_cast<double>(in) /
349 (256.0 /*cpr*/ * 2.0 /*2x. Stupid WPILib*/) *
350 (18.0 / 50.0 /*output stage*/) * (56.0 / 30.0 /*encoder gears*/)
351 // * constants::GetValues().drivetrain_encoder_ratio
352 *
353 (3.5 /*wheel diameter*/ * 2.54 / 100.0 * M_PI);
Austin Schuh010eb812014-10-25 18:06:49 -0700354}
355
356static const double kVcc = 5.15;
357
358double gyro_translate(int64_t in) {
359 return in / 16.0 / 1000.0 / (180.0 / M_PI);
360}
361
362float hall_translate(const constants::ShifterHallEffect &k, float in_low,
363 float in_high) {
364 const float low_ratio =
365 0.5 * (in_low - static_cast<float>(k.low_gear_low)) /
366 static_cast<float>(k.low_gear_middle - k.low_gear_low);
367 const float high_ratio =
368 0.5 + 0.5 * (in_high - static_cast<float>(k.high_gear_middle)) /
369 static_cast<float>(k.high_gear_high - k.high_gear_middle);
370
371 // Return low when we are below 1/2, and high when we are above 1/2.
372 if (low_ratio + high_ratio < 1.0) {
373 return low_ratio;
374 } else {
375 return high_ratio;
376 }
377}
378
379double claw_translate(int32_t in) {
Austin Schuhdb516032014-12-28 00:12:38 -0800380 return static_cast<double>(in) / (256.0 /*cpr*/ * 4.0 /*quad*/) /
381 (18.0 / 48.0 /*encoder gears*/) / (12.0 / 60.0 /*chain reduction*/) *
382 (M_PI / 180.0) *
383 2.0 /*TODO(austin): Debug this, encoders read too little*/;
Austin Schuh010eb812014-10-25 18:06:49 -0700384}
385
386double shooter_translate(int32_t in) {
Austin Schuhdb516032014-12-28 00:12:38 -0800387 return static_cast<double>(in) / (256.0 /*cpr*/ * 4.0 /*quad*/) *
388 16 /*sprocket teeth*/ * 0.375 /*chain pitch*/
389 * (2.54 / 100.0 /*in to m*/);
Austin Schuh010eb812014-10-25 18:06:49 -0700390}
391
Austin Schuh010eb812014-10-25 18:06:49 -0700392// This class sends out half of the claw position state at 100 hz.
393class HalfClawHallSynchronizer : public PeriodicHallSynchronizer<3> {
394 public:
395 // Constructs a HalfClawHallSynchronizer.
396 //
397 // priority is the priority of the polling thread.
398 // interrupt_priority is the priority of the interrupt threads.
399 // encoder is the encoder to read.
400 // sensors is an array of hall effect sensors. The sensors[0] is the front
Austin Schuhdb516032014-12-28 00:12:38 -0800401 // sensor, sensors[1] is the calibration sensor, sensors[2] is the back
402 // sensor.
Austin Schuh010eb812014-10-25 18:06:49 -0700403 HalfClawHallSynchronizer(
404 const char *name, int priority, int interrupt_priority,
405 ::std::unique_ptr<Encoder> encoder,
406 ::std::array<::std::unique_ptr<HallEffect>, 3> *sensors, bool reversed)
407 : PeriodicHallSynchronizer<3>(name, priority, interrupt_priority,
408 ::std::move(encoder), sensors),
409 reversed_(reversed) {}
410
411 void set_position(control_loops::HalfClawPosition *half_claw_position) {
412 half_claw_position_ = half_claw_position;
413 }
414
415 // Saves the state so that it can be sent if it was synchronized.
416 virtual void SaveState() {
417 const auto &front = edge_counters()[0];
418 half_claw_position_->front.current = front->polled_value();
419 half_claw_position_->front.posedge_count =
420 front->positive_interrupt_count();
421 half_claw_position_->front.negedge_count =
422 front->negative_interrupt_count();
423
424 const auto &calibration = edge_counters()[1];
425 half_claw_position_->calibration.current = calibration->polled_value();
426 half_claw_position_->calibration.posedge_count =
427 calibration->positive_interrupt_count();
428 half_claw_position_->calibration.negedge_count =
429 calibration->negative_interrupt_count();
430
431 const auto &back = edge_counters()[2];
432 half_claw_position_->back.current = back->polled_value();
433 half_claw_position_->back.posedge_count = back->positive_interrupt_count();
434 half_claw_position_->back.negedge_count = back->negative_interrupt_count();
435
436 const double multiplier = reversed_ ? -1.0 : 1.0;
437
438 half_claw_position_->position =
439 multiplier * claw_translate(encoder_value());
440
441 // We assume here that we can only have 1 sensor have a posedge per cycle.
442 {
443 half_claw_position_->posedge_value =
444 last_half_claw_position_.posedge_value;
445 int posedge_changes = 0;
446 if (half_claw_position_->front.posedge_count !=
447 last_half_claw_position_.front.posedge_count) {
448 ++posedge_changes;
449 half_claw_position_->posedge_value =
450 multiplier * claw_translate(front->last_positive_encoder_value());
451 LOG(INFO, "Got a front posedge\n");
452 }
453
454 if (half_claw_position_->back.posedge_count !=
455 last_half_claw_position_.back.posedge_count) {
456 ++posedge_changes;
457 half_claw_position_->posedge_value =
458 multiplier * claw_translate(back->last_positive_encoder_value());
459 LOG(INFO, "Got a back posedge\n");
460 }
461
462 if (half_claw_position_->calibration.posedge_count !=
463 last_half_claw_position_.calibration.posedge_count) {
464 ++posedge_changes;
465 half_claw_position_->posedge_value =
466 multiplier *
467 claw_translate(calibration->last_positive_encoder_value());
468 LOG(INFO, "Got a calibration posedge\n");
469 }
470
471 if (posedge_changes > 1) {
472 LOG(WARNING, "Found more than 1 positive edge on %s\n", name());
473 }
474 }
475
476 {
477 half_claw_position_->negedge_value =
478 last_half_claw_position_.negedge_value;
479 int negedge_changes = 0;
480 if (half_claw_position_->front.negedge_count !=
481 last_half_claw_position_.front.negedge_count) {
482 ++negedge_changes;
483 half_claw_position_->negedge_value =
484 multiplier * claw_translate(front->last_negative_encoder_value());
485 LOG(INFO, "Got a front negedge\n");
486 }
487
488 if (half_claw_position_->back.negedge_count !=
489 last_half_claw_position_.back.negedge_count) {
490 ++negedge_changes;
491 half_claw_position_->negedge_value =
492 multiplier * claw_translate(back->last_negative_encoder_value());
493 LOG(INFO, "Got a back negedge\n");
494 }
495
496 if (half_claw_position_->calibration.negedge_count !=
497 last_half_claw_position_.calibration.negedge_count) {
498 ++negedge_changes;
499 half_claw_position_->negedge_value =
500 multiplier *
501 claw_translate(calibration->last_negative_encoder_value());
502 LOG(INFO, "Got a calibration negedge\n");
503 }
504
505 if (negedge_changes > 1) {
506 LOG(WARNING, "Found more than 1 negative edge on %s\n", name());
507 }
508 }
509
510 last_half_claw_position_ = *half_claw_position_;
511 }
512
513 private:
514 control_loops::HalfClawPosition *half_claw_position_;
515 control_loops::HalfClawPosition last_half_claw_position_;
516 bool reversed_;
517};
518
Austin Schuh010eb812014-10-25 18:06:49 -0700519// This class sends out the shooter position state at 100 hz.
520class ShooterHallSynchronizer : public PeriodicHallSynchronizer<2> {
521 public:
522 // Constructs a ShooterHallSynchronizer.
523 //
524 // priority is the priority of the polling thread.
525 // interrupt_priority is the priority of the interrupt threads.
526 // encoder is the encoder to read.
527 // sensors is an array of hall effect sensors. The sensors[0] is the proximal
528 // sensor, sensors[1] is the distal sensor.
529 // shooter_plunger is the plunger.
530 // shooter_latch is the latch.
531 ShooterHallSynchronizer(
532 int priority, int interrupt_priority, ::std::unique_ptr<Encoder> encoder,
533 ::std::array<::std::unique_ptr<HallEffect>, 2> *sensors,
534 ::std::unique_ptr<HallEffect> shooter_plunger,
535 ::std::unique_ptr<HallEffect> shooter_latch)
536 : PeriodicHallSynchronizer<2>("shooter", priority, interrupt_priority,
537 ::std::move(encoder), sensors),
538 shooter_plunger_(::std::move(shooter_plunger)),
539 shooter_latch_(::std::move(shooter_latch)) {}
540
541 // Saves the state so that it can be sent if it was synchronized.
542 virtual void SaveState() {
543 auto shooter_position =
544 control_loops::shooter_queue_group.position.MakeMessage();
545
546 shooter_position->plunger = shooter_plunger_->GetHall();
547 shooter_position->latch = shooter_latch_->GetHall();
548 shooter_position->position = shooter_translate(encoder_value());
549
550 {
551 const auto &proximal_edge_counter = edge_counters()[0];
552 shooter_position->pusher_proximal.current =
553 proximal_edge_counter->polled_value();
554 shooter_position->pusher_proximal.posedge_count =
555 proximal_edge_counter->positive_interrupt_count();
556 shooter_position->pusher_proximal.negedge_count =
557 proximal_edge_counter->negative_interrupt_count();
558 shooter_position->pusher_proximal.posedge_value = shooter_translate(
559 proximal_edge_counter->last_positive_encoder_value());
560 }
561
562 {
Austin Schuhdb516032014-12-28 00:12:38 -0800563 const auto &distal_edge_counter = edge_counters()[1];
564 shooter_position->pusher_distal.current =
565 distal_edge_counter->polled_value();
566 shooter_position->pusher_distal.posedge_count =
567 distal_edge_counter->positive_interrupt_count();
568 shooter_position->pusher_distal.negedge_count =
569 distal_edge_counter->negative_interrupt_count();
570 shooter_position->pusher_distal.posedge_value =
571 shooter_translate(distal_edge_counter->last_positive_encoder_value());
Austin Schuh010eb812014-10-25 18:06:49 -0700572 }
573
574 shooter_position.Send();
575 }
576
577 private:
578 ::std::unique_ptr<HallEffect> shooter_plunger_;
579 ::std::unique_ptr<HallEffect> shooter_latch_;
580};
581
Austin Schuh010eb812014-10-25 18:06:49 -0700582class SensorReader {
583 public:
584 SensorReader()
585 : auto_selector_analog_(new AnalogInput(4)),
Brian Silvermancb77f232014-12-19 21:48:36 -0800586 left_encoder_(new Encoder(11, 10, false, Encoder::k2X)), // E0
587 right_encoder_(new Encoder(13, 12, false, Encoder::k2X)), // E1
588 low_left_drive_hall_(new AnalogInput(1)),
589 high_left_drive_hall_(new AnalogInput(0)),
590 low_right_drive_hall_(new AnalogInput(2)),
591 high_right_drive_hall_(new AnalogInput(3)),
592 shooter_plunger_(new HallEffect(8)), // S3
593 shooter_latch_(new HallEffect(9)), // S4
594 shooter_distal_(new HallEffect(7)), // S2
595 shooter_proximal_(new HallEffect(6)), // S1
596 shooter_encoder_(new Encoder(14, 15)), // E2
597 claw_top_front_hall_(new HallEffect(4)), // R2
598 claw_top_calibration_hall_(new HallEffect(3)), // R3
599 claw_top_back_hall_(new HallEffect(5)), // R1
600 claw_top_encoder_(new Encoder(17, 16)), // E3
Austin Schuh010eb812014-10-25 18:06:49 -0700601 // L2 Middle Left hall effect sensor.
Brian Silvermancb77f232014-12-19 21:48:36 -0800602 claw_bottom_front_hall_(new HallEffect(1)),
Austin Schuh010eb812014-10-25 18:06:49 -0700603 // L3 Bottom Left hall effect sensor
Brian Silvermancb77f232014-12-19 21:48:36 -0800604 claw_bottom_calibration_hall_(new HallEffect(0)),
Austin Schuh010eb812014-10-25 18:06:49 -0700605 // L1 Top Left hall effect sensor
Brian Silvermancb77f232014-12-19 21:48:36 -0800606 claw_bottom_back_hall_(new HallEffect(2)),
607 claw_bottom_encoder_(new Encoder(21, 20)), // E5
Austin Schuh010eb812014-10-25 18:06:49 -0700608 run_(true) {
609 filter_.SetPeriodNanoSeconds(100000);
610 filter_.Add(shooter_plunger_.get());
611 filter_.Add(shooter_latch_.get());
612 filter_.Add(shooter_distal_.get());
613 filter_.Add(shooter_proximal_.get());
614 filter_.Add(claw_top_front_hall_.get());
615 filter_.Add(claw_top_calibration_hall_.get());
616 filter_.Add(claw_top_back_hall_.get());
617 filter_.Add(claw_bottom_front_hall_.get());
618 filter_.Add(claw_bottom_calibration_hall_.get());
619 filter_.Add(claw_bottom_back_hall_.get());
620 printf("Filtering all hall effect sensors with a %" PRIu64
621 " nanosecond window\n",
622 filter_.GetPeriodNanoSeconds());
623 }
624
625 void operator()() {
Brian Silverman2fe007c2014-12-28 12:20:01 -0800626 ::aos::SetCurrentThreadName("SensorReader");
627
Austin Schuh010eb812014-10-25 18:06:49 -0700628 const int kPriority = 30;
629 const int kInterruptPriority = 55;
Austin Schuh010eb812014-10-25 18:06:49 -0700630
631 ::std::array<::std::unique_ptr<HallEffect>, 2> shooter_sensors;
632 shooter_sensors[0] = ::std::move(shooter_proximal_);
633 shooter_sensors[1] = ::std::move(shooter_distal_);
634 ShooterHallSynchronizer shooter(
635 kPriority, kInterruptPriority, ::std::move(shooter_encoder_),
636 &shooter_sensors, ::std::move(shooter_plunger_),
637 ::std::move(shooter_latch_));
638 shooter.StartThread();
639
640 ::std::array<::std::unique_ptr<HallEffect>, 3> claw_top_sensors;
641 claw_top_sensors[0] = ::std::move(claw_top_front_hall_);
642 claw_top_sensors[1] = ::std::move(claw_top_calibration_hall_);
643 claw_top_sensors[2] = ::std::move(claw_top_back_hall_);
644 HalfClawHallSynchronizer top_claw("top_claw", kPriority, kInterruptPriority,
645 ::std::move(claw_top_encoder_),
646 &claw_top_sensors, false);
647
648 ::std::array<::std::unique_ptr<HallEffect>, 3> claw_bottom_sensors;
649 claw_bottom_sensors[0] = ::std::move(claw_bottom_front_hall_);
650 claw_bottom_sensors[1] = ::std::move(claw_bottom_calibration_hall_);
651 claw_bottom_sensors[2] = ::std::move(claw_bottom_back_hall_);
652 HalfClawHallSynchronizer bottom_claw(
653 "bottom_claw", kPriority, kInterruptPriority,
654 ::std::move(claw_bottom_encoder_), &claw_bottom_sensors, true);
655
Brian Silverman2fe007c2014-12-28 12:20:01 -0800656 ::aos::SetCurrentThreadRealtimePriority(kPriority);
Austin Schuh010eb812014-10-25 18:06:49 -0700657 while (run_) {
658 ::aos::time::PhasedLoopXMS(10, 9000);
659 RunIteration();
660
661 auto claw_position =
662 control_loops::claw_queue_group.position.MakeMessage();
663 bottom_claw.set_position(&claw_position->bottom);
664 top_claw.set_position(&claw_position->top);
665 while (true) {
666 bottom_claw.StartIteration();
667 top_claw.StartIteration();
668
669 // Wait more than the amount of time it takes for a digital input change
670 // to go from visible to software to having triggered an interrupt.
671 ::aos::time::SleepFor(::aos::time::Time::InUS(120));
672
Austin Schuhdb516032014-12-28 00:12:38 -0800673 if (bottom_claw.TryFinishingIteration() &&
674 top_claw.TryFinishingIteration()) {
Austin Schuh010eb812014-10-25 18:06:49 -0700675 break;
676 }
677 }
678
679 claw_position.Send();
680 }
681 shooter.Quit();
682 top_claw.Quit();
683 bottom_claw.Quit();
684 }
685
686 void RunIteration() {
687 //::aos::time::TimeFreezer time_freezer;
688 DriverStation *ds = DriverStation::GetInstance();
689
690 bool bad_gyro = true;
691 // TODO(brians): Switch to LogInterval for these things.
692 /*
693 if (data->uninitialized_gyro) {
694 LOG(DEBUG, "uninitialized gyro\n");
695 bad_gyro = true;
696 } else if (data->zeroing_gyro) {
697 LOG(DEBUG, "zeroing gyro\n");
698 bad_gyro = true;
699 } else if (data->bad_gyro) {
700 LOG(ERROR, "bad gyro\n");
701 bad_gyro = true;
702 } else if (data->old_gyro_reading) {
703 LOG(WARNING, "old/bad gyro reading\n");
704 bad_gyro = true;
705 } else {
706 bad_gyro = false;
707 }
708 */
709
710 if (!bad_gyro) {
711 // TODO(austin): Read the gyro.
712 gyro_reading.MakeWithBuilder().angle(0).Send();
713 }
714
Austin Schuhdb516032014-12-28 00:12:38 -0800715 if (ds->IsSysActive()) {
Austin Schuh010eb812014-10-25 18:06:49 -0700716 auto message = ::aos::controls::output_check_received.MakeMessage();
717 // TODO(brians): Actually read a pulse value from the roboRIO.
718 message->pwm_value = 0;
719 message->pulse_length = -1;
720 LOG_STRUCT(DEBUG, "received", *message);
721 message.Send();
722 }
723
724 ::frc971::sensors::auto_mode.MakeWithBuilder()
725 .voltage(auto_selector_analog_->GetVoltage())
726 .Send();
727
728 // TODO(austin): Calibrate the shifter constants again.
729 drivetrain.position.MakeWithBuilder()
730 .right_encoder(drivetrain_translate(right_encoder_->GetRaw()))
731 .left_encoder(-drivetrain_translate(left_encoder_->GetRaw()))
732 .left_shifter_position(
733 hall_translate(constants::GetValues().left_drive,
734 low_left_drive_hall_->GetVoltage(),
735 high_left_drive_hall_->GetVoltage()))
736 .right_shifter_position(
737 hall_translate(constants::GetValues().right_drive,
738 low_right_drive_hall_->GetVoltage(),
739 high_right_drive_hall_->GetVoltage()))
740 .battery_voltage(ds->GetBatteryVoltage())
741 .Send();
742
Brian Silvermand8f403a2014-12-13 19:12:04 -0500743 // Signal that we are alive.
Austin Schuh010eb812014-10-25 18:06:49 -0700744 ::aos::controls::sensor_generation.MakeWithBuilder()
745 .reader_pid(getpid())
746 .cape_resets(0)
747 .Send();
748 }
749
750 void Quit() { run_ = false; }
751
752 private:
753 ::std::unique_ptr<AnalogInput> auto_selector_analog_;
754
755 ::std::unique_ptr<Encoder> left_encoder_;
756 ::std::unique_ptr<Encoder> right_encoder_;
757 ::std::unique_ptr<AnalogInput> low_left_drive_hall_;
758 ::std::unique_ptr<AnalogInput> high_left_drive_hall_;
759 ::std::unique_ptr<AnalogInput> low_right_drive_hall_;
760 ::std::unique_ptr<AnalogInput> high_right_drive_hall_;
761
762 ::std::unique_ptr<HallEffect> shooter_plunger_;
763 ::std::unique_ptr<HallEffect> shooter_latch_;
764 ::std::unique_ptr<HallEffect> shooter_distal_;
765 ::std::unique_ptr<HallEffect> shooter_proximal_;
766 ::std::unique_ptr<Encoder> shooter_encoder_;
767
768 ::std::unique_ptr<HallEffect> claw_top_front_hall_;
769 ::std::unique_ptr<HallEffect> claw_top_calibration_hall_;
770 ::std::unique_ptr<HallEffect> claw_top_back_hall_;
771 ::std::unique_ptr<Encoder> claw_top_encoder_;
772
773 ::std::unique_ptr<HallEffect> claw_bottom_front_hall_;
774 ::std::unique_ptr<HallEffect> claw_bottom_calibration_hall_;
775 ::std::unique_ptr<HallEffect> claw_bottom_back_hall_;
776 ::std::unique_ptr<Encoder> claw_bottom_encoder_;
777
778 ::std::atomic<bool> run_;
779 DigitalGlitchFilter filter_;
780};
781
Brian Silvermand8f403a2014-12-13 19:12:04 -0500782class SolenoidWriter {
Austin Schuh010eb812014-10-25 18:06:49 -0700783 public:
Brian Silvermand8f403a2014-12-13 19:12:04 -0500784 SolenoidWriter(const ::std::unique_ptr<BufferedPcm> &pcm)
785 : pcm_(pcm),
786 drivetrain_(".frc971.control_loops.drivetrain.output"),
787 shooter_(".frc971.control_loops.shooter_queue_group.output") {}
788
789 void set_drivetrain_left(::std::unique_ptr<BufferedSolenoid> s) {
790 drivetrain_left_ = ::std::move(s);
Austin Schuh010eb812014-10-25 18:06:49 -0700791 }
792
Brian Silvermand8f403a2014-12-13 19:12:04 -0500793 void set_drivetrain_right(::std::unique_ptr<BufferedSolenoid> s) {
794 drivetrain_right_ = ::std::move(s);
795 }
Austin Schuh010eb812014-10-25 18:06:49 -0700796
Brian Silvermand8f403a2014-12-13 19:12:04 -0500797 void set_shooter_latch(::std::unique_ptr<BufferedSolenoid> s) {
798 shooter_latch_ = ::std::move(s);
799 }
Austin Schuh010eb812014-10-25 18:06:49 -0700800
Brian Silvermand8f403a2014-12-13 19:12:04 -0500801 void set_shooter_brake(::std::unique_ptr<BufferedSolenoid> s) {
802 shooter_brake_ = ::std::move(s);
803 }
Austin Schuh010eb812014-10-25 18:06:49 -0700804
Brian Silvermand8f403a2014-12-13 19:12:04 -0500805 void operator()() {
806 ::aos::SetCurrentThreadName("Solenoids");
807 ::aos::SetCurrentThreadRealtimePriority(30);
808
809 while (run_) {
810 ::aos::time::PhasedLoopXMS(20, 1000);
811
812 {
813 drivetrain_.FetchLatest();
814 if (drivetrain_.get()) {
815 LOG_STRUCT(DEBUG, "solenoids", *drivetrain_);
816 drivetrain_left_->Set(drivetrain_->left_high);
817 drivetrain_right_->Set(drivetrain_->right_high);
818 }
819 }
820
821 {
822 shooter_.FetchLatest();
823 if (shooter_.get()) {
824 LOG_STRUCT(DEBUG, "solenoids", *shooter_);
825 shooter_latch_->Set(!shooter_->latch_piston);
826 shooter_brake_->Set(!shooter_->brake_piston);
827 }
828 }
829
830 pcm_->Flush();
Austin Schuh010eb812014-10-25 18:06:49 -0700831 }
832 }
833
Brian Silvermand8f403a2014-12-13 19:12:04 -0500834 void Quit() { run_ = false; }
Austin Schuh010eb812014-10-25 18:06:49 -0700835
Brian Silvermand8f403a2014-12-13 19:12:04 -0500836 private:
837 const ::std::unique_ptr<BufferedPcm> &pcm_;
838 ::std::unique_ptr<BufferedSolenoid> drivetrain_left_;
839 ::std::unique_ptr<BufferedSolenoid> drivetrain_right_;
840 ::std::unique_ptr<BufferedSolenoid> shooter_latch_;
841 ::std::unique_ptr<BufferedSolenoid> shooter_brake_;
Austin Schuh010eb812014-10-25 18:06:49 -0700842
Brian Silvermand8f403a2014-12-13 19:12:04 -0500843 ::aos::Queue<::frc971::control_loops::Drivetrain::Output> drivetrain_;
844 ::aos::Queue<::frc971::control_loops::ShooterGroup::Output> shooter_;
Austin Schuh010eb812014-10-25 18:06:49 -0700845
Brian Silvermand8f403a2014-12-13 19:12:04 -0500846 ::std::atomic<bool> run_{true};
847};
848
849class DrivetrainWriter : public LoopOutputHandler {
850 public:
851 void set_left_drivetrain_talon(::std::unique_ptr<Talon> t) {
852 left_drivetrain_talon_ = ::std::move(t);
Austin Schuh010eb812014-10-25 18:06:49 -0700853 }
854
Brian Silvermand8f403a2014-12-13 19:12:04 -0500855 void set_right_drivetrain_talon(::std::unique_ptr<Talon> t) {
856 right_drivetrain_talon_ = ::std::move(t);
857 }
Austin Schuh010eb812014-10-25 18:06:49 -0700858
Brian Silvermand8f403a2014-12-13 19:12:04 -0500859 private:
860 virtual void Read() override {
861 ::frc971::control_loops::drivetrain.output.FetchAnother();
862 }
863
864 virtual void Write() override {
865 auto &queue = ::frc971::control_loops::drivetrain.output;
866 LOG_STRUCT(DEBUG, "will output", *queue);
867 left_drivetrain_talon_->Set(-queue->left_voltage / 12.0);
868 right_drivetrain_talon_->Set(queue->right_voltage / 12.0);
869 }
870
871 virtual void Stop() override {
872 LOG(WARNING, "drivetrain output too old\n");
873 left_drivetrain_talon_->Disable();
874 right_drivetrain_talon_->Disable();
875 }
876
Austin Schuh010eb812014-10-25 18:06:49 -0700877 ::std::unique_ptr<Talon> left_drivetrain_talon_;
Brian Silvermand8f403a2014-12-13 19:12:04 -0500878 ::std::unique_ptr<Talon> right_drivetrain_talon_;
879};
880
881class ShooterWriter : public LoopOutputHandler {
882 public:
883 void set_shooter_talon(::std::unique_ptr<Talon> t) {
884 shooter_talon_ = ::std::move(t);
885 }
886
887 private:
888 virtual void Read() override {
889 ::frc971::control_loops::shooter_queue_group.output.FetchAnother();
890 }
891
892 virtual void Write() override {
893 auto &queue = ::frc971::control_loops::shooter_queue_group.output;
894 LOG_STRUCT(DEBUG, "will output", *queue);
895 shooter_talon_->Set(queue->voltage / 12.0);
896 }
897
898 virtual void Stop() override {
899 LOG(WARNING, "shooter output too old\n");
900 shooter_talon_->Disable();
901 }
902
Austin Schuh010eb812014-10-25 18:06:49 -0700903 ::std::unique_ptr<Talon> shooter_talon_;
Brian Silvermand8f403a2014-12-13 19:12:04 -0500904};
905
906class ClawWriter : public LoopOutputHandler {
907 public:
908 void set_top_claw_talon(::std::unique_ptr<Talon> t) {
909 top_claw_talon_ = ::std::move(t);
910 }
911
912 void set_bottom_claw_talon(::std::unique_ptr<Talon> t) {
913 bottom_claw_talon_ = ::std::move(t);
914 }
915
916 void set_left_tusk_talon(::std::unique_ptr<Talon> t) {
917 left_tusk_talon_ = ::std::move(t);
918 }
919
920 void set_right_tusk_talon(::std::unique_ptr<Talon> t) {
921 right_tusk_talon_ = ::std::move(t);
922 }
923
924 void set_intake1_talon(::std::unique_ptr<Talon> t) {
925 intake1_talon_ = ::std::move(t);
926 }
927
928 void set_intake2_talon(::std::unique_ptr<Talon> t) {
929 intake2_talon_ = ::std::move(t);
930 }
931
932 private:
933 virtual void Read() override {
934 ::frc971::control_loops::claw_queue_group.output.FetchAnother();
935 }
936
937 virtual void Write() override {
938 auto &queue = ::frc971::control_loops::claw_queue_group.output;
939 LOG_STRUCT(DEBUG, "will output", *queue);
940 intake1_talon_->Set(queue->intake_voltage / 12.0);
941 intake2_talon_->Set(queue->intake_voltage / 12.0);
942 bottom_claw_talon_->Set(-queue->bottom_claw_voltage / 12.0);
943 top_claw_talon_->Set(queue->top_claw_voltage / 12.0);
944 left_tusk_talon_->Set(queue->tusk_voltage / 12.0);
945 right_tusk_talon_->Set(-queue->tusk_voltage / 12.0);
946 }
947
948 virtual void Stop() override {
949 LOG(WARNING, "claw output too old\n");
950 intake1_talon_->Disable();
951 intake2_talon_->Disable();
952 bottom_claw_talon_->Disable();
953 top_claw_talon_->Disable();
954 left_tusk_talon_->Disable();
955 right_tusk_talon_->Disable();
956 }
957
Austin Schuh010eb812014-10-25 18:06:49 -0700958 ::std::unique_ptr<Talon> top_claw_talon_;
959 ::std::unique_ptr<Talon> bottom_claw_talon_;
960 ::std::unique_ptr<Talon> left_tusk_talon_;
961 ::std::unique_ptr<Talon> right_tusk_talon_;
962 ::std::unique_ptr<Talon> intake1_talon_;
963 ::std::unique_ptr<Talon> intake2_talon_;
Austin Schuh010eb812014-10-25 18:06:49 -0700964};
965
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800966} // namespace wpilib
Austin Schuh010eb812014-10-25 18:06:49 -0700967} // namespace frc971
968
969class WPILibRobot : public RobotBase {
970 public:
971 virtual void StartCompetition() {
Brian Silvermand8f403a2014-12-13 19:12:04 -0500972 ::aos::InitNRT();
Brian Silverman2fe007c2014-12-28 12:20:01 -0800973 ::aos::SetCurrentThreadName("StartCompetition");
Brian Silvermand8f403a2014-12-13 19:12:04 -0500974
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800975 ::frc971::wpilib::JoystickSender joystick_sender;
Austin Schuh010eb812014-10-25 18:06:49 -0700976 ::std::thread joystick_thread(::std::ref(joystick_sender));
Brian Silvermand8f403a2014-12-13 19:12:04 -0500977 ::frc971::wpilib::SensorReader reader;
978 ::std::thread reader_thread(::std::ref(reader));
979 ::std::unique_ptr<Compressor> compressor(new Compressor());
980 compressor->SetClosedLoopControl(true);
981
982 ::frc971::wpilib::DrivetrainWriter drivetrain_writer;
983 drivetrain_writer.set_left_drivetrain_talon(
984 ::std::unique_ptr<Talon>(new Talon(5)));
985 drivetrain_writer.set_right_drivetrain_talon(
986 ::std::unique_ptr<Talon>(new Talon(2)));
987 ::std::thread drivetrain_writer_thread(::std::ref(drivetrain_writer));
988
989
990 ::frc971::wpilib::ClawWriter claw_writer;
991 claw_writer.set_top_claw_talon(::std::unique_ptr<Talon>(new Talon(1)));
992 claw_writer.set_bottom_claw_talon(::std::unique_ptr<Talon>(new Talon(0)));
993 claw_writer.set_left_tusk_talon(::std::unique_ptr<Talon>(new Talon(4)));
994 claw_writer.set_right_tusk_talon(::std::unique_ptr<Talon>(new Talon(3)));
995 claw_writer.set_intake1_talon(::std::unique_ptr<Talon>(new Talon(7)));
996 claw_writer.set_intake2_talon(::std::unique_ptr<Talon>(new Talon(8)));
997 ::std::thread claw_writer_thread(::std::ref(claw_writer));
998
999 ::frc971::wpilib::ShooterWriter shooter_writer;
1000 shooter_writer.set_shooter_talon(::std::unique_ptr<Talon>(new Talon(6)));
1001 ::std::thread shooter_writer_thread(::std::ref(shooter_writer));
1002
1003 ::std::unique_ptr<::frc971::wpilib::BufferedPcm> pcm(
1004 new ::frc971::wpilib::BufferedPcm());
1005 ::frc971::wpilib::SolenoidWriter solenoid_writer(pcm);
1006 solenoid_writer.set_drivetrain_left(pcm->MakeSolenoid(6));
1007 solenoid_writer.set_drivetrain_right(pcm->MakeSolenoid(7));
1008 solenoid_writer.set_shooter_latch(pcm->MakeSolenoid(5));
1009 solenoid_writer.set_shooter_brake(pcm->MakeSolenoid(4));
1010 ::std::thread solenoid_thread(::std::ref(solenoid_writer));
1011
1012 // Wait forever. Not much else to do...
1013 PCHECK(select(0, nullptr, nullptr, nullptr, nullptr));
1014
Austin Schuh010eb812014-10-25 18:06:49 -07001015 LOG(ERROR, "Exiting WPILibRobot\n");
Austin Schuh010eb812014-10-25 18:06:49 -07001016 joystick_sender.Quit();
1017 joystick_thread.join();
Brian Silvermand8f403a2014-12-13 19:12:04 -05001018 reader.Quit();
1019 reader_thread.join();
1020
1021 drivetrain_writer.Quit();
1022 drivetrain_writer_thread.join();
1023 claw_writer.Quit();
1024 claw_writer_thread.join();
1025 shooter_writer.Quit();
1026 shooter_writer_thread.join();
1027 solenoid_writer.Quit();
1028 solenoid_thread.join();
1029
Austin Schuh010eb812014-10-25 18:06:49 -07001030 ::aos::Cleanup();
1031 }
1032};
1033
Austin Schuhdb516032014-12-28 00:12:38 -08001034
Austin Schuh010eb812014-10-25 18:06:49 -07001035START_ROBOT_CLASS(WPILibRobot);