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