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