blob: 6c453243d637567f4b8d2f317a18d51f8572d83b [file] [log] [blame]
Austin Schuh010eb812014-10-25 18:06:49 -07001#include <stdio.h>
2#include <string.h>
3#include <thread>
4#include <mutex>
5#include <unistd.h>
6#include <inttypes.h>
7
8#include "aos/prime/output/motor_output.h"
9#include "aos/common/controls/output_check.q.h"
10#include "aos/common/controls/sensor_generation.q.h"
11#include "aos/common/logging/logging.h"
12#include "aos/common/logging/queue_logging.h"
13#include "aos/common/messages/robot_state.q.h"
14#include "aos/common/time.h"
15#include "aos/common/util/log_interval.h"
16#include "aos/common/util/phased_loop.h"
17#include "aos/common/util/wrapping_counter.h"
18#include "aos/common/network/team_number.h"
19#include "aos/linux_code/init.h"
20
21#include "frc971/control_loops/drivetrain/drivetrain.q.h"
22#include "frc971/control_loops/claw/claw.q.h"
23#include "frc971/control_loops/shooter/shooter.q.h"
24#include "frc971/constants.h"
25#include "frc971/queues/other_sensors.q.h"
26#include "frc971/queues/to_log.q.h"
Daniel Pettiaece37f2014-10-25 17:13:44 -070027#include "frc971/shifter_hall_effect.h"
Austin Schuh010eb812014-10-25 18:06:49 -070028
29#include <WPILib.h>
30
31#ifndef M_PI
32#define M_PI 3.14159265358979323846
33#endif
34
35using ::aos::util::SimpleLogInterval;
36using ::frc971::control_loops::drivetrain;
37using ::frc971::sensors::other_sensors;
38using ::frc971::sensors::gyro_reading;
39using ::aos::util::WrappingCounter;
40
41namespace frc971 {
42namespace output {
43
44void SetThreadRealtimePriority(int priority) {
45 struct sched_param param;
46 param.sched_priority = priority;
47 if (sched_setscheduler(0, SCHED_FIFO, &param) == -1) {
48 PLOG(FATAL, "sched_setscheduler failed");
49 }
50}
51
52class priority_mutex {
53 public:
54 typedef pthread_mutex_t *native_handle_type;
55
56 // TODO(austin): Write a test case for the mutex, and make the constructor
57 // constexpr.
58 priority_mutex() {
59 pthread_mutexattr_t attr;
60 // Turn on priority inheritance.
61#ifdef NDEBUG
62#error "Won't let perror be no-op ed"
63#endif
64 assert_perror(pthread_mutexattr_init(&attr));
65 assert_perror(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL));
66 assert_perror(pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT));
67
68 assert_perror(pthread_mutex_init(native_handle(), &attr));
69
70 assert_perror(pthread_mutexattr_destroy(&attr));
71 }
72
73 ~priority_mutex() { pthread_mutex_destroy(&handle_); }
74
75 void lock() { assert_perror(pthread_mutex_lock(&handle_)); }
76 bool try_lock() {
77 int ret = pthread_mutex_trylock(&handle_);
78 if (ret == 0) {
79 return true;
80 } else if (ret == EBUSY) {
81 return false;
82 } else {
83 assert_perror(ret);
84 }
85 }
86 void unlock() { assert_perror(pthread_mutex_unlock(&handle_)); }
87
88 native_handle_type native_handle() { return &handle_; }
89
90 private:
91 DISALLOW_COPY_AND_ASSIGN(priority_mutex);
92 pthread_mutex_t handle_;
93};
94
95class HallEffect : public DigitalInput {
96 public:
97 HallEffect(int index) : DigitalInput(index) {}
98 bool GetHall() { return !Get(); }
99};
100
101class EdgeCounter {
102 public:
103 EdgeCounter(int priority, Encoder *encoder, HallEffect *input,
104 priority_mutex *mutex)
105 : priority_(priority),
106 encoder_(encoder),
107 input_(input),
108 mutex_(mutex),
109 run_(true),
110 any_interrupt_count_(0) {
111 thread_.reset(new ::std::thread(::std::ref(*this)));
112 }
113
114 // Waits for interrupts, locks the mutex, and updates the internal state.
115 // Updates the any_interrupt_count count when the interrupt comes in without
116 // the lock.
117 void operator ()() {
118 SetThreadRealtimePriority(priority_);
119
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
128 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
214// This class will synchronize sampling edges on a bunch of DigitalInputs with
215// the periodic poll.
216//
217// The data is provided to subclasses by calling SaveState when the state is
218// consistent and ready.
219template <int num_sensors>
220class PeriodicHallSynchronizer {
221 public:
222 PeriodicHallSynchronizer(
223 const char *name, int priority, int interrupt_priority,
224 ::std::unique_ptr<Encoder> encoder,
225 ::std::array<::std::unique_ptr<HallEffect>, num_sensors> *sensors)
226 : name_(name),
227 priority_(priority),
228 encoder_(::std::move(encoder)),
229 run_(true) {
230 for (int i = 0; i < num_sensors; ++i) {
231 sensors_[i] = ::std::move((*sensors)[i]);
232 edge_counters_[i] = ::std::unique_ptr<EdgeCounter>(new EdgeCounter(
233 interrupt_priority, encoder_.get(), sensors_[i].get(), &mutex_));
234 }
235 }
236
237 const char *name() const { return name_.c_str(); }
238
239 void StartThread() { thread_.reset(new ::std::thread(::std::ref(*this))); }
240
241 // Called when the state is consistent and up to date.
242 virtual void SaveState() = 0;
243
244 // Starts a sampling iteration. See RunIteration for usage.
245 void StartIteration() {
246 // Start by capturing the current interrupt counts.
247 for (int i = 0; i < num_sensors; ++i) {
248 interrupt_counts_[i] = edge_counters_[i]->any_interrupt_count();
249 }
250
251 {
252 // Now, update the encoder and sensor values.
253 ::std::unique_lock<priority_mutex> mutex_guard(mutex_);
254 encoder_value_ = encoder_->GetRaw();
255 for (int i = 0; i < num_sensors; ++i) {
256 edge_counters_[i]->set_polled_value(sensors_[i]->GetHall());
257 }
258 }
259 }
260
261 // Attempts to finish a sampling iteration. See RunIteration for usage.
262 // Returns true if the iteration succeeded, and false otherwise.
263 bool TryFinishingIteration() {
264 // Make sure no interrupts have occurred while we were waiting. If they
265 // have, we are in an inconsistent state and need to try again.
266 ::std::unique_lock<priority_mutex> mutex_guard(mutex_);
267 bool retry = false;
268 for (int i = 0; i < num_sensors; ++i) {
269 retry = retry || (interrupt_counts_[i] !=
270 edge_counters_[i]->any_interrupt_count());
271 }
272 if (!retry) {
273 SaveState();
274 return true;
275 }
276 LOG(WARNING, "Got an interrupt while sampling encoder %s, retrying\n",
277 name());
278 return false;
279 }
280
281 void RunIteration() {
282 while (true) {
283 StartIteration();
284
285 // Wait more than the amount of time it takes for a digital input change
286 // to go from visible to software to having triggered an interrupt.
287 ::aos::time::SleepFor(::aos::time::Time::InUS(120));
288
289 if (TryFinishingIteration()) {
290 return;
291 }
292 }
293 }
294
295 void operator()() {
296 SetThreadRealtimePriority(priority_);
297 while (run_) {
298 ::aos::time::PhasedLoopXMS(10, 9000);
299 RunIteration();
300 }
301 }
302
303 void Quit() {
304 run_ = false;
305 for (int i = 0; i < num_sensors; ++i) {
306 edge_counters_[i]->Quit();
307 }
308 if (thread_) {
309 thread_->join();
310 }
311 }
312
313 protected:
314 // These values are only safe to fetch from inside SaveState()
315 int32_t encoder_value() const { return encoder_value_; }
316 ::std::array<::std::unique_ptr<EdgeCounter>, num_sensors> &edge_counters() {
317 return edge_counters_;
318 }
319
320 private:
321 // A descriptive name for error messages.
322 ::std::string name_;
323 // The priority of the polling thread.
324 int priority_;
325 // The Encoder to sample.
326 ::std::unique_ptr<Encoder> encoder_;
327 // A list of all the digital inputs.
328 ::std::array<::std::unique_ptr<HallEffect>, num_sensors> sensors_;
329 // The mutex used to synchronize all the state.
330 priority_mutex mutex_;
331 ::std::atomic<bool> run_;
332
333 // The state.
334 // The current encoder value.
335 int32_t encoder_value_ = 0;
336 // The current edge counters.
337 ::std::array<::std::unique_ptr<EdgeCounter>, num_sensors> edge_counters_;
338
339 ::std::unique_ptr<::std::thread> thread_;
340 ::std::array<int, num_sensors> interrupt_counts_;
341};
342
343double drivetrain_translate(int32_t in) {
344 return static_cast<double>(in)
345 / (256.0 /*cpr*/ * 2.0 /*2x. Stupid WPILib*/)
346 * (18.0 / 50.0 /*output stage*/) * (56.0 / 30.0 /*encoder gears*/)
347 // * constants::GetValues().drivetrain_encoder_ratio
348 * (3.5 /*wheel diameter*/ * 2.54 / 100.0 * M_PI);
349}
350
351static const double kVcc = 5.15;
352
353double gyro_translate(int64_t in) {
354 return in / 16.0 / 1000.0 / (180.0 / M_PI);
355}
356
357float 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) {
375 return static_cast<double>(in)
376 / (256.0 /*cpr*/ * 4.0 /*quad*/)
377 / (18.0 / 48.0 /*encoder gears*/)
378 / (12.0 / 60.0 /*chain reduction*/)
379 * (M_PI / 180.0)
380 * 2.0 /*TODO(austin): Debug this, encoders read too little*/;
381}
382
383double shooter_translate(int32_t in) {
384 return static_cast<double>(in)
385 / (256.0 /*cpr*/ * 4.0 /*quad*/)
386 * 16 /*sprocket teeth*/ * 0.375 /*chain pitch*/
387 * (2.54 / 100.0 /*in to m*/);
388}
389
390
391// This class sends out half of the claw position state at 100 hz.
392class HalfClawHallSynchronizer : public PeriodicHallSynchronizer<3> {
393 public:
394 // Constructs a HalfClawHallSynchronizer.
395 //
396 // priority is the priority of the polling thread.
397 // interrupt_priority is the priority of the interrupt threads.
398 // encoder is the encoder to read.
399 // sensors is an array of hall effect sensors. The sensors[0] is the front
400 // sensor, sensors[1] is the calibration sensor, sensors[2] is the back sensor.
401 HalfClawHallSynchronizer(
402 const char *name, int priority, int interrupt_priority,
403 ::std::unique_ptr<Encoder> encoder,
404 ::std::array<::std::unique_ptr<HallEffect>, 3> *sensors, bool reversed)
405 : PeriodicHallSynchronizer<3>(name, priority, interrupt_priority,
406 ::std::move(encoder), sensors),
407 reversed_(reversed) {}
408
409 void set_position(control_loops::HalfClawPosition *half_claw_position) {
410 half_claw_position_ = half_claw_position;
411 }
412
413 // Saves the state so that it can be sent if it was synchronized.
414 virtual void SaveState() {
415 const auto &front = edge_counters()[0];
416 half_claw_position_->front.current = front->polled_value();
417 half_claw_position_->front.posedge_count =
418 front->positive_interrupt_count();
419 half_claw_position_->front.negedge_count =
420 front->negative_interrupt_count();
421
422 const auto &calibration = edge_counters()[1];
423 half_claw_position_->calibration.current = calibration->polled_value();
424 half_claw_position_->calibration.posedge_count =
425 calibration->positive_interrupt_count();
426 half_claw_position_->calibration.negedge_count =
427 calibration->negative_interrupt_count();
428
429 const auto &back = edge_counters()[2];
430 half_claw_position_->back.current = back->polled_value();
431 half_claw_position_->back.posedge_count = back->positive_interrupt_count();
432 half_claw_position_->back.negedge_count = back->negative_interrupt_count();
433
434 const double multiplier = reversed_ ? -1.0 : 1.0;
435
436 half_claw_position_->position =
437 multiplier * claw_translate(encoder_value());
438
439 // We assume here that we can only have 1 sensor have a posedge per cycle.
440 {
441 half_claw_position_->posedge_value =
442 last_half_claw_position_.posedge_value;
443 int posedge_changes = 0;
444 if (half_claw_position_->front.posedge_count !=
445 last_half_claw_position_.front.posedge_count) {
446 ++posedge_changes;
447 half_claw_position_->posedge_value =
448 multiplier * claw_translate(front->last_positive_encoder_value());
449 LOG(INFO, "Got a front posedge\n");
450 }
451
452 if (half_claw_position_->back.posedge_count !=
453 last_half_claw_position_.back.posedge_count) {
454 ++posedge_changes;
455 half_claw_position_->posedge_value =
456 multiplier * claw_translate(back->last_positive_encoder_value());
457 LOG(INFO, "Got a back posedge\n");
458 }
459
460 if (half_claw_position_->calibration.posedge_count !=
461 last_half_claw_position_.calibration.posedge_count) {
462 ++posedge_changes;
463 half_claw_position_->posedge_value =
464 multiplier *
465 claw_translate(calibration->last_positive_encoder_value());
466 LOG(INFO, "Got a calibration posedge\n");
467 }
468
469 if (posedge_changes > 1) {
470 LOG(WARNING, "Found more than 1 positive edge on %s\n", name());
471 }
472 }
473
474 {
475 half_claw_position_->negedge_value =
476 last_half_claw_position_.negedge_value;
477 int negedge_changes = 0;
478 if (half_claw_position_->front.negedge_count !=
479 last_half_claw_position_.front.negedge_count) {
480 ++negedge_changes;
481 half_claw_position_->negedge_value =
482 multiplier * claw_translate(front->last_negative_encoder_value());
483 LOG(INFO, "Got a front negedge\n");
484 }
485
486 if (half_claw_position_->back.negedge_count !=
487 last_half_claw_position_.back.negedge_count) {
488 ++negedge_changes;
489 half_claw_position_->negedge_value =
490 multiplier * claw_translate(back->last_negative_encoder_value());
491 LOG(INFO, "Got a back negedge\n");
492 }
493
494 if (half_claw_position_->calibration.negedge_count !=
495 last_half_claw_position_.calibration.negedge_count) {
496 ++negedge_changes;
497 half_claw_position_->negedge_value =
498 multiplier *
499 claw_translate(calibration->last_negative_encoder_value());
500 LOG(INFO, "Got a calibration negedge\n");
501 }
502
503 if (negedge_changes > 1) {
504 LOG(WARNING, "Found more than 1 negative edge on %s\n", name());
505 }
506 }
507
508 last_half_claw_position_ = *half_claw_position_;
509 }
510
511 private:
512 control_loops::HalfClawPosition *half_claw_position_;
513 control_loops::HalfClawPosition last_half_claw_position_;
514 bool reversed_;
515};
516
517
518// This class sends out the shooter position state at 100 hz.
519class ShooterHallSynchronizer : public PeriodicHallSynchronizer<2> {
520 public:
521 // Constructs a ShooterHallSynchronizer.
522 //
523 // priority is the priority of the polling thread.
524 // interrupt_priority is the priority of the interrupt threads.
525 // encoder is the encoder to read.
526 // sensors is an array of hall effect sensors. The sensors[0] is the proximal
527 // sensor, sensors[1] is the distal sensor.
528 // shooter_plunger is the plunger.
529 // shooter_latch is the latch.
530 ShooterHallSynchronizer(
531 int priority, int interrupt_priority, ::std::unique_ptr<Encoder> encoder,
532 ::std::array<::std::unique_ptr<HallEffect>, 2> *sensors,
533 ::std::unique_ptr<HallEffect> shooter_plunger,
534 ::std::unique_ptr<HallEffect> shooter_latch)
535 : PeriodicHallSynchronizer<2>("shooter", priority, interrupt_priority,
536 ::std::move(encoder), sensors),
537 shooter_plunger_(::std::move(shooter_plunger)),
538 shooter_latch_(::std::move(shooter_latch)) {}
539
540 // Saves the state so that it can be sent if it was synchronized.
541 virtual void SaveState() {
542 auto shooter_position =
543 control_loops::shooter_queue_group.position.MakeMessage();
544
545 shooter_position->plunger = shooter_plunger_->GetHall();
546 shooter_position->latch = shooter_latch_->GetHall();
547 shooter_position->position = shooter_translate(encoder_value());
548
549 {
550 const auto &proximal_edge_counter = edge_counters()[0];
551 shooter_position->pusher_proximal.current =
552 proximal_edge_counter->polled_value();
553 shooter_position->pusher_proximal.posedge_count =
554 proximal_edge_counter->positive_interrupt_count();
555 shooter_position->pusher_proximal.negedge_count =
556 proximal_edge_counter->negative_interrupt_count();
557 shooter_position->pusher_proximal.posedge_value = shooter_translate(
558 proximal_edge_counter->last_positive_encoder_value());
559 }
560
561 {
562 const auto &distal_edge_counter = edge_counters()[1];
563 shooter_position->pusher_distal.current =
564 distal_edge_counter->polled_value();
565 shooter_position->pusher_distal.posedge_count =
566 distal_edge_counter->positive_interrupt_count();
567 shooter_position->pusher_distal.negedge_count =
568 distal_edge_counter->negative_interrupt_count();
569 shooter_position->pusher_distal.posedge_value =
570 shooter_translate(distal_edge_counter->last_positive_encoder_value());
571 }
572
573 shooter_position.Send();
574 }
575
576 private:
577 ::std::unique_ptr<HallEffect> shooter_plunger_;
578 ::std::unique_ptr<HallEffect> shooter_latch_;
579};
580
581
582class SensorReader {
583 public:
584 SensorReader()
585 : auto_selector_analog_(new AnalogInput(4)),
586 left_encoder_(new Encoder(10, 11, false, Encoder::k2X)), // E0
587 right_encoder_(new Encoder(12, 13, false, Encoder::k2X)), // E1
588 low_left_drive_hall_(new AnalogInput(2)),
589 high_left_drive_hall_(new AnalogInput(3)),
590 low_right_drive_hall_(new AnalogInput(1)),
591 high_right_drive_hall_(new AnalogInput(0)),
592 shooter_plunger_(new HallEffect(1)), // S3
593 shooter_latch_(new HallEffect(0)), // S4
594 shooter_distal_(new HallEffect(2)), // S2
595 shooter_proximal_(new HallEffect(3)), // S1
596 shooter_encoder_(new Encoder(19, 18)), // E2
597 claw_top_front_hall_(new HallEffect(5)), // R2
598 claw_top_calibration_hall_(new HallEffect(6)), // R3
599 claw_top_back_hall_(new HallEffect(4)), // R2
600 claw_top_encoder_(new Encoder(20, 21)), // E3
601 // L2 Middle Left hall effect sensor.
602 claw_bottom_front_hall_(new HallEffect(8)),
603 // L3 Bottom Left hall effect sensor
604 claw_bottom_calibration_hall_(new HallEffect(9)),
605 // L1 Top Left hall effect sensor
606 claw_bottom_back_hall_(new HallEffect(7)),
607 claw_bottom_encoder_(new Encoder(22, 23)), // E5
608 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()() {
626 const int kPriority = 30;
627 const int kInterruptPriority = 55;
628 SetThreadRealtimePriority(kPriority);
629
630 ::std::array<::std::unique_ptr<HallEffect>, 2> shooter_sensors;
631 shooter_sensors[0] = ::std::move(shooter_proximal_);
632 shooter_sensors[1] = ::std::move(shooter_distal_);
633 ShooterHallSynchronizer shooter(
634 kPriority, kInterruptPriority, ::std::move(shooter_encoder_),
635 &shooter_sensors, ::std::move(shooter_plunger_),
636 ::std::move(shooter_latch_));
637 shooter.StartThread();
638
639 ::std::array<::std::unique_ptr<HallEffect>, 3> claw_top_sensors;
640 claw_top_sensors[0] = ::std::move(claw_top_front_hall_);
641 claw_top_sensors[1] = ::std::move(claw_top_calibration_hall_);
642 claw_top_sensors[2] = ::std::move(claw_top_back_hall_);
643 HalfClawHallSynchronizer top_claw("top_claw", kPriority, kInterruptPriority,
644 ::std::move(claw_top_encoder_),
645 &claw_top_sensors, false);
646
647 ::std::array<::std::unique_ptr<HallEffect>, 3> claw_bottom_sensors;
648 claw_bottom_sensors[0] = ::std::move(claw_bottom_front_hall_);
649 claw_bottom_sensors[1] = ::std::move(claw_bottom_calibration_hall_);
650 claw_bottom_sensors[2] = ::std::move(claw_bottom_back_hall_);
651 HalfClawHallSynchronizer bottom_claw(
652 "bottom_claw", kPriority, kInterruptPriority,
653 ::std::move(claw_bottom_encoder_), &claw_bottom_sensors, true);
654
655 while (run_) {
656 ::aos::time::PhasedLoopXMS(10, 9000);
657 RunIteration();
658
659 auto claw_position =
660 control_loops::claw_queue_group.position.MakeMessage();
661 bottom_claw.set_position(&claw_position->bottom);
662 top_claw.set_position(&claw_position->top);
663 while (true) {
664 bottom_claw.StartIteration();
665 top_claw.StartIteration();
666
667 // Wait more than the amount of time it takes for a digital input change
668 // to go from visible to software to having triggered an interrupt.
669 ::aos::time::SleepFor(::aos::time::Time::InUS(120));
670
671 if (bottom_claw.TryFinishingIteration() && top_claw.TryFinishingIteration()) {
672 break;
673 }
674 }
675
676 claw_position.Send();
677 }
678 shooter.Quit();
679 top_claw.Quit();
680 bottom_claw.Quit();
681 }
682
683 void RunIteration() {
684 //::aos::time::TimeFreezer time_freezer;
685 DriverStation *ds = DriverStation::GetInstance();
686
687 bool bad_gyro = true;
688 // TODO(brians): Switch to LogInterval for these things.
689 /*
690 if (data->uninitialized_gyro) {
691 LOG(DEBUG, "uninitialized gyro\n");
692 bad_gyro = true;
693 } else if (data->zeroing_gyro) {
694 LOG(DEBUG, "zeroing gyro\n");
695 bad_gyro = true;
696 } else if (data->bad_gyro) {
697 LOG(ERROR, "bad gyro\n");
698 bad_gyro = true;
699 } else if (data->old_gyro_reading) {
700 LOG(WARNING, "old/bad gyro reading\n");
701 bad_gyro = true;
702 } else {
703 bad_gyro = false;
704 }
705 */
706
707 if (!bad_gyro) {
708 // TODO(austin): Read the gyro.
709 gyro_reading.MakeWithBuilder().angle(0).Send();
710 }
711
712 if (ds->SystemActive()) {
713 auto message = ::aos::controls::output_check_received.MakeMessage();
714 // TODO(brians): Actually read a pulse value from the roboRIO.
715 message->pwm_value = 0;
716 message->pulse_length = -1;
717 LOG_STRUCT(DEBUG, "received", *message);
718 message.Send();
719 }
720
721 ::frc971::sensors::auto_mode.MakeWithBuilder()
722 .voltage(auto_selector_analog_->GetVoltage())
723 .Send();
724
725 // TODO(austin): Calibrate the shifter constants again.
726 drivetrain.position.MakeWithBuilder()
727 .right_encoder(drivetrain_translate(right_encoder_->GetRaw()))
728 .left_encoder(-drivetrain_translate(left_encoder_->GetRaw()))
729 .left_shifter_position(
730 hall_translate(constants::GetValues().left_drive,
731 low_left_drive_hall_->GetVoltage(),
732 high_left_drive_hall_->GetVoltage()))
733 .right_shifter_position(
734 hall_translate(constants::GetValues().right_drive,
735 low_right_drive_hall_->GetVoltage(),
736 high_right_drive_hall_->GetVoltage()))
737 .battery_voltage(ds->GetBatteryVoltage())
738 .Send();
739
740 // Signal that we are allive.
741 ::aos::controls::sensor_generation.MakeWithBuilder()
742 .reader_pid(getpid())
743 .cape_resets(0)
744 .Send();
745 }
746
747 void Quit() { run_ = false; }
748
749 private:
750 ::std::unique_ptr<AnalogInput> auto_selector_analog_;
751
752 ::std::unique_ptr<Encoder> left_encoder_;
753 ::std::unique_ptr<Encoder> right_encoder_;
754 ::std::unique_ptr<AnalogInput> low_left_drive_hall_;
755 ::std::unique_ptr<AnalogInput> high_left_drive_hall_;
756 ::std::unique_ptr<AnalogInput> low_right_drive_hall_;
757 ::std::unique_ptr<AnalogInput> high_right_drive_hall_;
758
759 ::std::unique_ptr<HallEffect> shooter_plunger_;
760 ::std::unique_ptr<HallEffect> shooter_latch_;
761 ::std::unique_ptr<HallEffect> shooter_distal_;
762 ::std::unique_ptr<HallEffect> shooter_proximal_;
763 ::std::unique_ptr<Encoder> shooter_encoder_;
764
765 ::std::unique_ptr<HallEffect> claw_top_front_hall_;
766 ::std::unique_ptr<HallEffect> claw_top_calibration_hall_;
767 ::std::unique_ptr<HallEffect> claw_top_back_hall_;
768 ::std::unique_ptr<Encoder> claw_top_encoder_;
769
770 ::std::unique_ptr<HallEffect> claw_bottom_front_hall_;
771 ::std::unique_ptr<HallEffect> claw_bottom_calibration_hall_;
772 ::std::unique_ptr<HallEffect> claw_bottom_back_hall_;
773 ::std::unique_ptr<Encoder> claw_bottom_encoder_;
774
775 ::std::atomic<bool> run_;
776 DigitalGlitchFilter filter_;
777};
778
779class MotorWriter {
780 public:
781 MotorWriter()
782 : right_drivetrain_talon_(new Talon(2)),
783 left_drivetrain_talon_(new Talon(5)),
784 shooter_talon_(new Talon(6)),
785 top_claw_talon_(new Talon(1)),
786 bottom_claw_talon_(new Talon(0)),
787 left_tusk_talon_(new Talon(4)),
788 right_tusk_talon_(new Talon(3)),
789 intake1_talon_(new Talon(7)),
790 intake2_talon_(new Talon(8)),
791 left_shifter_(new Solenoid(6)),
792 right_shifter_(new Solenoid(7)),
793 shooter_latch_(new Solenoid(5)),
794 shooter_brake_(new Solenoid(4)),
795 compressor_(new Compressor()) {
796 compressor_->SetClosedLoopControl(true);
797 //right_drivetrain_talon_->EnableDeadbandElimination(true);
798 //left_drivetrain_talon_->EnableDeadbandElimination(true);
799 //shooter_talon_->EnableDeadbandElimination(true);
800 //top_claw_talon_->EnableDeadbandElimination(true);
801 //bottom_claw_talon_->EnableDeadbandElimination(true);
802 //left_tusk_talon_->EnableDeadbandElimination(true);
803 //right_tusk_talon_->EnableDeadbandElimination(true);
804 //intake1_talon_->EnableDeadbandElimination(true);
805 //intake2_talon_->EnableDeadbandElimination(true);
806 }
807
808 // Maximum age of an output packet before the motors get zeroed instead.
809 static const int kOutputMaxAgeMS = 20;
810 static constexpr ::aos::time::Time kOldLogInterval =
811 ::aos::time::Time::InSeconds(0.5);
812
813 void Run() {
814 //::aos::time::Time::EnableMockTime();
815 while (true) {
816 //::aos::time::Time::UpdateMockTime();
817 // 200 hz loop
818 ::aos::time::PhasedLoopXMS(5, 1000);
819 //::aos::time::Time::UpdateMockTime();
820
821 no_robot_state_.Print();
822 fake_robot_state_.Print();
823 sending_failed_.Print();
824
825 RunIteration();
826 }
827 }
828
829 virtual void RunIteration() {
830 ::aos::robot_state.FetchLatest();
831 if (!::aos::robot_state.get()) {
832 LOG_INTERVAL(no_robot_state_);
833 return;
834 }
835 if (::aos::robot_state->fake) {
836 LOG_INTERVAL(fake_robot_state_);
837 return;
838 }
839
840 // TODO(austin): Write the motor values out when they change! One thread
841 // per queue.
842 // TODO(austin): Figure out how to synchronize everything to the PWM update
843 // rate, or get the pulse to go out clocked off of this code. That would be
844 // awesome.
845 {
846 static auto &drivetrain = ::frc971::control_loops::drivetrain.output;
847 drivetrain.FetchLatest();
848 if (drivetrain.IsNewerThanMS(kOutputMaxAgeMS)) {
849 LOG_STRUCT(DEBUG, "will output", *drivetrain);
850 left_drivetrain_talon_->Set(-drivetrain->left_voltage / 12.0);
851 right_drivetrain_talon_->Set(drivetrain->right_voltage / 12.0);
852 left_shifter_->Set(drivetrain->left_high);
853 right_shifter_->Set(drivetrain->right_high);
854 } else {
855 left_drivetrain_talon_->Disable();
856 right_drivetrain_talon_->Disable();
857 LOG_INTERVAL(drivetrain_old_);
858 }
859 drivetrain_old_.Print();
860 }
861
862 {
863 static auto &shooter =
864 ::frc971::control_loops::shooter_queue_group.output;
865 shooter.FetchLatest();
866 if (shooter.IsNewerThanMS(kOutputMaxAgeMS)) {
867 LOG_STRUCT(DEBUG, "will output", *shooter);
868 shooter_talon_->Set(shooter->voltage / 12.0);
869 shooter_latch_->Set(!shooter->latch_piston);
870 shooter_brake_->Set(!shooter->brake_piston);
871 } else {
872 shooter_talon_->Disable();
873 shooter_brake_->Set(false); // engage the brake
874 LOG_INTERVAL(shooter_old_);
875 }
876 shooter_old_.Print();
877 }
878
879 {
880 static auto &claw = ::frc971::control_loops::claw_queue_group.output;
881 claw.FetchLatest();
882 if (claw.IsNewerThanMS(kOutputMaxAgeMS)) {
883 LOG_STRUCT(DEBUG, "will output", *claw);
884 intake1_talon_->Set(claw->intake_voltage / 12.0);
885 intake2_talon_->Set(claw->intake_voltage / 12.0);
886 bottom_claw_talon_->Set(-claw->bottom_claw_voltage / 12.0);
887 top_claw_talon_->Set(claw->top_claw_voltage / 12.0);
888 left_tusk_talon_->Set(claw->tusk_voltage / 12.0);
889 right_tusk_talon_->Set(-claw->tusk_voltage / 12.0);
890 } else {
891 intake1_talon_->Disable();
892 intake2_talon_->Disable();
893 bottom_claw_talon_->Disable();
894 top_claw_talon_->Disable();
895 left_tusk_talon_->Disable();
896 right_tusk_talon_->Disable();
897 LOG_INTERVAL(claw_old_);
898 }
899 claw_old_.Print();
900 }
901 }
902
903 SimpleLogInterval drivetrain_old_ =
904 SimpleLogInterval(kOldLogInterval, WARNING, "drivetrain too old");
905 SimpleLogInterval shooter_old_ =
906 SimpleLogInterval(kOldLogInterval, WARNING, "shooter too old");
907 SimpleLogInterval claw_old_ =
908 SimpleLogInterval(kOldLogInterval, WARNING, "claw too old");
909
910 ::std::unique_ptr<Talon> right_drivetrain_talon_;
911 ::std::unique_ptr<Talon> left_drivetrain_talon_;
912 ::std::unique_ptr<Talon> shooter_talon_;
913 ::std::unique_ptr<Talon> top_claw_talon_;
914 ::std::unique_ptr<Talon> bottom_claw_talon_;
915 ::std::unique_ptr<Talon> left_tusk_talon_;
916 ::std::unique_ptr<Talon> right_tusk_talon_;
917 ::std::unique_ptr<Talon> intake1_talon_;
918 ::std::unique_ptr<Talon> intake2_talon_;
919
920 ::std::unique_ptr<Solenoid> left_shifter_;
921 ::std::unique_ptr<Solenoid> right_shifter_;
922 ::std::unique_ptr<Solenoid> shooter_latch_;
923 ::std::unique_ptr<Solenoid> shooter_brake_;
924
925 ::std::unique_ptr<Compressor> compressor_;
926
927 ::aos::util::SimpleLogInterval no_robot_state_ =
928 ::aos::util::SimpleLogInterval(::aos::time::Time::InSeconds(0.5), INFO,
929 "no robot state -> not outputting");
930 ::aos::util::SimpleLogInterval fake_robot_state_ =
931 ::aos::util::SimpleLogInterval(::aos::time::Time::InSeconds(0.5), DEBUG,
932 "fake robot state -> not outputting");
933 ::aos::util::SimpleLogInterval sending_failed_ =
934 ::aos::util::SimpleLogInterval(::aos::time::Time::InSeconds(0.1), WARNING,
935 "sending outputs failed");
936};
937
938constexpr ::aos::time::Time MotorWriter::kOldLogInterval;
939
940class JoystickSender {
941 public:
942 JoystickSender() : run_(true) {}
943
944 void operator()() {
945 DriverStation *ds = DriverStation::GetInstance();
946 SetThreadRealtimePriority(29);
947 uint16_t team_id = ::aos::network::GetTeamNumber();
948
949 while (run_) {
950 ds->WaitForData();
951 auto new_state = ::aos::robot_state.MakeMessage();
952
953 new_state->test_mode = ds->IsAutonomous();
954 new_state->fms_attached = ds->IsFMSAttached();
955 new_state->enabled = ds->IsEnabled();
956 new_state->autonomous = ds->IsAutonomous();
957 new_state->team_id = team_id;
958 new_state->fake = false;
959
960 for (int i = 0; i < 4; ++i) {
961 new_state->joysticks[i].buttons = ds->GetStickButtons(i);
962 for (int j = 0; j < 4; ++j) {
963 new_state->joysticks[i].axis[j] = ds->GetStickAxis(i, j + 1);
964 }
965 }
966 LOG_STRUCT(DEBUG, "robot_state", *new_state);
967
968 if (!new_state.Send()) {
969 LOG(WARNING, "sending robot_state failed\n");
970 }
971 }
972 }
973
974 void Quit() { run_ = false; }
975
976 private:
977 ::std::atomic<bool> run_;
978};
979
980} // namespace output
981} // namespace frc971
982
983class WPILibRobot : public RobotBase {
984 public:
985 virtual void StartCompetition() {
986 ::aos::Init();
987 ::frc971::output::MotorWriter writer;
988 ::frc971::output::SensorReader reader;
989 ::std::thread reader_thread(::std::ref(reader));
990 ::frc971::output::JoystickSender joystick_sender;
991 ::std::thread joystick_thread(::std::ref(joystick_sender));
992 writer.Run();
993 LOG(ERROR, "Exiting WPILibRobot\n");
994 reader.Quit();
995 reader_thread.join();
996 joystick_sender.Quit();
997 joystick_thread.join();
998 ::aos::Cleanup();
999 }
1000};
1001
1002START_ROBOT_CLASS(WPILibRobot);