blob: 56e58cc10f98bb43d0ad7f52d6d1e67eb9868d8d [file] [log] [blame]
milind-u086d7262022-01-19 20:44:18 -08001#include "y2022/control_loops/superstructure/superstructure.h"
2
3#include "aos/events/event_loop.h"
Ravago Jones3283ce02022-03-09 19:31:29 -08004#include "aos/flatbuffer_merge.h"
Milind Upadhyay225156b2022-02-25 22:42:12 -08005#include "y2022/control_loops/superstructure/collision_avoidance.h"
milind-u086d7262022-01-19 20:44:18 -08006
7namespace y2022 {
8namespace control_loops {
9namespace superstructure {
10
11using frc971::control_loops::AbsoluteEncoderProfiledJointStatus;
12using frc971::control_loops::PotAndAbsoluteEncoderProfiledJointStatus;
Henry Speiser55aa3ba2022-02-21 23:21:12 -080013using frc971::control_loops::RelativeEncoderProfiledJointStatus;
milind-u086d7262022-01-19 20:44:18 -080014
15Superstructure::Superstructure(::aos::EventLoop *event_loop,
Henry Speiser55aa3ba2022-02-21 23:21:12 -080016 std::shared_ptr<const constants::Values> values,
milind-u086d7262022-01-19 20:44:18 -080017 const ::std::string &name)
18 : frc971::controls::ControlLoop<Goal, Position, Status, Output>(event_loop,
Siddhant Kanwar0e37f592022-02-21 19:26:50 -080019 name),
Austin Schuh39f26f62022-02-24 21:34:46 -080020 values_(values),
21 climber_(values_->climber.subsystem_params),
22 intake_front_(values_->intake_front.subsystem_params),
23 intake_back_(values_->intake_back.subsystem_params),
24 turret_(values_->turret.subsystem_params),
Ravago Jones5da06352022-03-04 20:26:24 -080025 catapult_(*values_),
Henry Speiser55aa3ba2022-02-21 23:21:12 -080026 drivetrain_status_fetcher_(
27 event_loop->MakeFetcher<frc971::control_loops::drivetrain::Status>(
Austin Schuh39f26f62022-02-24 21:34:46 -080028 "/drivetrain")),
Ravago Jones5da06352022-03-04 20:26:24 -080029 can_position_fetcher_(
30 event_loop->MakeFetcher<CANPosition>("/superstructure")) {
milind-u086d7262022-01-19 20:44:18 -080031 event_loop->SetRuntimeRealtimePriority(30);
32}
33
34void Superstructure::RunIteration(const Goal *unsafe_goal,
Siddhant Kanwar0e37f592022-02-21 19:26:50 -080035 const Position *position,
milind-u086d7262022-01-19 20:44:18 -080036 aos::Sender<Output>::Builder *output,
37 aos::Sender<Status>::Builder *status) {
38 if (WasReset()) {
39 AOS_LOG(ERROR, "WPILib reset, restarting\n");
Henry Speiser55aa3ba2022-02-21 23:21:12 -080040 intake_front_.Reset();
41 intake_back_.Reset();
42 turret_.Reset();
43 climber_.Reset();
Austin Schuh39f26f62022-02-24 21:34:46 -080044 catapult_.Reset();
Henry Speiser55aa3ba2022-02-21 23:21:12 -080045 }
46
Ravago Jones5da06352022-03-04 20:26:24 -080047 OutputT output_struct;
Milind Upadhyay225156b2022-02-25 22:42:12 -080048
Ravago Jones5da06352022-03-04 20:26:24 -080049 aos::FlatbufferFixedAllocatorArray<
50 frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal, 64>
51 turret_goal_buffer;
Ravago Jones3283ce02022-03-09 19:31:29 -080052 aos::FlatbufferFixedAllocatorArray<CatapultGoal, 64> catapult_goal_buffer;
Ravago Jones5da06352022-03-04 20:26:24 -080053
54 const aos::monotonic_clock::time_point timestamp =
55 event_loop()->context().monotonic_event_time;
Milind Upadhyay225156b2022-02-25 22:42:12 -080056
Henry Speiser55aa3ba2022-02-21 23:21:12 -080057 drivetrain_status_fetcher_.Fetch();
58 const float velocity = robot_velocity();
59
James Kuszmaul84083f42022-02-27 17:24:38 -080060 const turret::Aimer::Goal *auto_aim_goal = nullptr;
61 if (drivetrain_status_fetcher_.get() != nullptr) {
62 aimer_.Update(drivetrain_status_fetcher_.get(),
63 turret::Aimer::ShotMode::kShootOnTheFly);
64 auto_aim_goal = aimer_.TurretGoal();
65 }
66
Ravago Jones5da06352022-03-04 20:26:24 -080067 const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal
68 *turret_goal = nullptr;
Ravago Jones3283ce02022-03-09 19:31:29 -080069 const CatapultGoal *catapult_goal = nullptr;
Ravago Jones5da06352022-03-04 20:26:24 -080070 double roller_speed_compensated_front = 0.0;
71 double roller_speed_compensated_back = 0.0;
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -080072 double transfer_roller_speed_front = 0.0;
73 double transfer_roller_speed_back = 0.0;
Ravago Jones5da06352022-03-04 20:26:24 -080074 double flipper_arms_voltage = 0.0;
Henry Speiser55aa3ba2022-02-21 23:21:12 -080075
76 if (unsafe_goal != nullptr) {
77 roller_speed_compensated_front =
78 unsafe_goal->roller_speed_front() +
79 std::max(velocity * unsafe_goal->roller_speed_compensation(), 0.0);
80
81 roller_speed_compensated_back =
82 unsafe_goal->roller_speed_back() -
83 std::min(velocity * unsafe_goal->roller_speed_compensation(), 0.0);
84
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -080085 transfer_roller_speed_front = unsafe_goal->transfer_roller_speed_front();
86 transfer_roller_speed_back = unsafe_goal->transfer_roller_speed_back();
milind-u086d7262022-01-19 20:44:18 -080087
James Kuszmaul84083f42022-02-27 17:24:38 -080088 turret_goal =
89 unsafe_goal->auto_aim() ? auto_aim_goal : unsafe_goal->turret();
Ravago Jones3283ce02022-03-09 19:31:29 -080090
91 catapult_goal = unsafe_goal->catapult();
92
93 constants::Values::ShotParams shot_params;
94 const double distance_to_goal = aimer_.DistanceToGoal();
95 if (unsafe_goal->auto_aim() && values_->shot_interpolation_table.GetInRange(
96 distance_to_goal, &shot_params)) {
97 std::optional<flatbuffers::Offset<
98 frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal>>
99 return_position_offset;
100 if (unsafe_goal != nullptr && unsafe_goal->has_catapult() &&
101 unsafe_goal->catapult()->has_return_position()) {
102 return_position_offset = {
103 aos::CopyFlatBuffer(unsafe_goal->catapult()->return_position(),
104 catapult_goal_buffer.fbb())};
105 }
106 CatapultGoal::Builder builder(*catapult_goal_buffer.fbb());
107 builder.add_shot_position(shot_params.shot_angle);
108 builder.add_shot_velocity(shot_params.shot_velocity);
109 if (return_position_offset.has_value()) {
110 builder.add_return_position(return_position_offset.value());
111 }
112 catapult_goal_buffer.Finish(builder.Finish());
113 catapult_goal = &catapult_goal_buffer.message();
114 }
James Kuszmaul84083f42022-02-27 17:24:38 -0800115 }
Austin Schuh39f26f62022-02-24 21:34:46 -0800116
Milind Upadhyay803bbf02022-03-11 17:56:26 -0800117 // Superstructure state machine:
Ravago Jones5da06352022-03-04 20:26:24 -0800118 // 1. IDLE: Wait until an intake beambreak is triggerred, meaning that a ball
119 // is being intaked. This means that the transfer rollers have a ball. If
120 // we've been waiting here for too long without any beambreak triggered, the
121 // ball got lost, so reset.
122 // 2. TRANSFERRING: Until the turret reaches the loading position where the
123 // ball can be transferred into the catapult, wiggle the ball in place.
124 // Once the turret reaches the loading position, send the ball forward with
125 // the transfer rollers until the turret beambreak is triggered.
126 // If we have been in this state for too long, the ball probably got lost so
127 // reset back to IDLE.
128 // 3. LOADING: To load the ball into the catapult, put the flippers at the
129 // feeding speed. Wait for a timeout, and then wait until the ball has gone
130 // past the turret beambreak and the flippers have stopped moving, meaning
131 // that the ball is fully loaded in the catapult.
132 // 4. LOADED: Wait until the user asks us to fire to transition to the
133 // shooting stage. If asked to cancel the shot, reset back to the IDLE state.
134 // 5. SHOOTING: Open the flippers to get ready for the shot. If they don't
135 // open quickly enough, try reseating the ball and going back to the LOADING
136 // stage, which moves the flippers in the opposite direction first.
137 // Now, hold the flippers open and wait until the turret has reached its
138 // aiming goal. Once the turret is ready, tell the catapult to fire.
139 // If the flippers move back for some reason now, it could damage the
140 // catapult, so estop it. Otherwise, wait until the catapult shoots a ball and
141 // goes back to its return position. We have now finished the shot, so return
142 // to IDLE.
143
Milind Upadhyay803bbf02022-03-11 17:56:26 -0800144 // If we started off preloaded, skip to the loaded state.
145 // Make sure we weren't already there just in case.
146 if (unsafe_goal != nullptr && unsafe_goal->preloaded()) {
147 switch (state_) {
148 case SuperstructureState::IDLE:
149 case SuperstructureState::TRANSFERRING:
150 case SuperstructureState::LOADING:
151 state_ = SuperstructureState::LOADED;
152 loading_timer_ = timestamp;
153 break;
154 case SuperstructureState::LOADED:
155 case SuperstructureState::SHOOTING:
156 break;
157 }
158 }
159
Ravago Jones5da06352022-03-04 20:26:24 -0800160 const bool is_spitting = ((intake_state_ == IntakeState::INTAKE_FRONT_BALL &&
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800161 transfer_roller_speed_front < 0) ||
Ravago Jones5da06352022-03-04 20:26:24 -0800162 (intake_state_ == IntakeState::INTAKE_BACK_BALL &&
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800163 transfer_roller_speed_back < 0));
Ravago Jones5da06352022-03-04 20:26:24 -0800164
165 // Intake handling should happen regardless of the turret state
166 if (position->intake_beambreak_front() || position->intake_beambreak_back()) {
167 if (intake_state_ == IntakeState::NO_BALL) {
168 if (position->intake_beambreak_front()) {
169 intake_state_ = IntakeState::INTAKE_FRONT_BALL;
170 } else if (position->intake_beambreak_back()) {
171 intake_state_ = IntakeState::INTAKE_BACK_BALL;
172 }
173 }
174
175 intake_beambreak_timer_ = timestamp;
176 }
177
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800178 if (timestamp >
179 intake_beambreak_timer_ + constants::Values::kBallLostTime()) {
180 intake_state_ = IntakeState::NO_BALL;
181 }
182
Ravago Jones5da06352022-03-04 20:26:24 -0800183 if (intake_state_ != IntakeState::NO_BALL) {
Ravago Jones5da06352022-03-04 20:26:24 -0800184 roller_speed_compensated_front = 0.0;
185 roller_speed_compensated_back = 0.0;
186
187 const double wiggle_voltage =
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800188 constants::Values::kTransferRollerWiggleVoltage();
Ravago Jones5da06352022-03-04 20:26:24 -0800189 // Wiggle transfer rollers: send the ball back and forth while waiting
190 // for the turret or waiting for another shot to be completed
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800191 if (intake_state_ == IntakeState::INTAKE_FRONT_BALL) {
192 if (position->intake_beambreak_front()) {
193 transfer_roller_speed_front = -wiggle_voltage;
194 transfer_roller_speed_back = wiggle_voltage;
195 } else {
196 transfer_roller_speed_front = wiggle_voltage;
197 transfer_roller_speed_back = -wiggle_voltage;
198 }
Ravago Jones5da06352022-03-04 20:26:24 -0800199 } else {
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800200 if (position->intake_beambreak_back()) {
201 transfer_roller_speed_back = -wiggle_voltage;
202 transfer_roller_speed_front = wiggle_voltage;
203 } else {
204 transfer_roller_speed_back = wiggle_voltage;
205 transfer_roller_speed_front = -wiggle_voltage;
206 }
Ravago Jones5da06352022-03-04 20:26:24 -0800207 }
208 }
209
Milind Upadhyay918ecf02022-03-12 15:55:46 -0800210 // Send the turret to the loading position if we have a ball in the intake, or
211 // are trying to intake one
212 double turret_loading_position = std::numeric_limits<double>::quiet_NaN();
213 if (state_ == SuperstructureState::TRANSFERRING &&
214 intake_state_ != IntakeState::NO_BALL) {
215 turret_loading_position = (intake_state_ == IntakeState::INTAKE_FRONT_BALL
216 ? constants::Values::kTurretFrontIntakePos()
217 : constants::Values::kTurretBackIntakePos());
218 } else if (state_ == SuperstructureState::IDLE && unsafe_goal != nullptr) {
219 if (unsafe_goal->roller_speed_front() > 0) {
220 turret_loading_position = constants::Values::kTurretFrontIntakePos();
221 } else if (unsafe_goal->roller_speed_back() > 0) {
222 turret_loading_position = constants::Values::kTurretBackIntakePos();
223 }
224 }
225
226 if (!std::isnan(turret_loading_position)) {
227 // Turn to the loading position as close to the current position as
228 // possible
229 // Strategy is copied from frc971/control_loops/aiming/aiming.cc
230 turret_loading_position =
231 turret_.estimated_position() +
232 aos::math::NormalizeAngle(turret_loading_position -
233 turret_.estimated_position());
234 // if out of range, reset back to within +/- pi of zero.
235 if (turret_loading_position > constants::Values::kTurretRange().upper ||
236 turret_loading_position < constants::Values::kTurretRange().lower) {
237 turret_loading_position =
238 aos::math::NormalizeAngle(turret_loading_position);
239 }
240
241 turret_goal_buffer.Finish(
242 frc971::control_loops::
243 CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
244 *turret_goal_buffer.fbb(), turret_loading_position));
245 turret_goal = &turret_goal_buffer.message();
246 }
247
Ravago Jones5da06352022-03-04 20:26:24 -0800248 switch (state_) {
249 case SuperstructureState::IDLE: {
Ravago Jones5da06352022-03-04 20:26:24 -0800250 if (is_spitting) {
251 intake_state_ = IntakeState::NO_BALL;
252 }
253
254 if (intake_state_ == IntakeState::NO_BALL ||
255 !(position->intake_beambreak_front() ||
256 position->intake_beambreak_back())) {
257 break;
258 }
259
260 state_ = SuperstructureState::TRANSFERRING;
Ravago Jones5da06352022-03-04 20:26:24 -0800261 // Save the side the ball is on for later
262
263 break;
264 }
265 case SuperstructureState::TRANSFERRING: {
266 // If we've been transferring for too long, the ball probably got lost
Ravago Jones5da06352022-03-04 20:26:24 -0800267 if (intake_state_ == IntakeState::NO_BALL) {
268 state_ = SuperstructureState::IDLE;
269 break;
270 }
271
Ravago Jones5da06352022-03-04 20:26:24 -0800272 const bool turret_near_goal =
273 std::abs(turret_.estimated_position() - turret_loading_position) <
274 kTurretGoalThreshold;
275 if (!turret_near_goal) {
276 break; // Wait for turret to reach the chosen intake
277 }
278
279 // Transfer rollers and flipper arm belt on
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800280 if (intake_state_ == IntakeState::INTAKE_FRONT_BALL) {
281 transfer_roller_speed_front =
282 constants::Values::kTransferRollerVoltage();
283 transfer_roller_speed_back =
284 -constants::Values::kTransferRollerVoltage();
285 } else {
286 transfer_roller_speed_back =
287 constants::Values::kTransferRollerVoltage();
288 transfer_roller_speed_front =
289 -constants::Values::kTransferRollerVoltage();
290 }
Ravago Jones5da06352022-03-04 20:26:24 -0800291 flipper_arms_voltage = constants::Values::kFlipperFeedVoltage();
292
293 // Ball is in catapult
294 if (position->turret_beambreak()) {
295 intake_state_ = IntakeState::NO_BALL;
296 state_ = SuperstructureState::LOADING;
297 loading_timer_ = timestamp;
298 }
299 break;
300 }
301 case SuperstructureState::LOADING: {
302 flipper_arms_voltage = constants::Values::kFlipperFeedVoltage();
303
304 // Keep feeding for kExtraLoadingTime
305
Ravago Jones5da06352022-03-04 20:26:24 -0800306 // The ball should go past the turret beambreak to be loaded.
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800307 // If we got a CAN reading not too long ago, the flippers should have
308 // also stopped.
Milind Upadhyay47e0d032022-03-05 23:06:25 -0800309 if (position->turret_beambreak()) {
310 loading_timer_ = timestamp;
311 } else if (timestamp >
312 loading_timer_ + constants::Values::kExtraLoadingTime()) {
Ravago Jones5da06352022-03-04 20:26:24 -0800313 state_ = SuperstructureState::LOADED;
314 reseating_in_catapult_ = false;
315 }
316 break;
317 }
318 case SuperstructureState::LOADED: {
319 if (unsafe_goal != nullptr) {
320 if (unsafe_goal->cancel_shot()) {
321 // Cancel the shot process
322 state_ = SuperstructureState::IDLE;
323 } else if (unsafe_goal->fire()) {
324 // Start if we were asked to and the turret is at goal
325 state_ = SuperstructureState::SHOOTING;
326 prev_shot_count_ = catapult_.shot_count();
327
328 // Reset opening timeout
329 flipper_opening_start_time_ = timestamp;
330 }
331 }
332 break;
333 }
334 case SuperstructureState::SHOOTING: {
335 // Opening flipper arms could fail, wait until they are open using their
336 // potentiometers (the member below is just named encoder).
337 // Be a little more lenient if the flippers were already open in case of
338 // noise or collisions.
339 const double flipper_open_position =
340 (flippers_open_ ? constants::Values::kReseatFlipperPosition()
341 : constants::Values::kFlipperOpenPosition());
milind-u37dc40b2022-03-08 19:51:27 -0800342
343 // TODO(milind): add left arm back once it's fixed
Ravago Jones5da06352022-03-04 20:26:24 -0800344 flippers_open_ =
Ravago Jones5da06352022-03-04 20:26:24 -0800345 position->flipper_arm_right()->encoder() >= flipper_open_position;
346
347 if (flippers_open_) {
348 // Hold at kFlipperHoldVoltage
349 flipper_arms_voltage = constants::Values::kFlipperHoldVoltage();
350 } else {
351 // Open at kFlipperOpenVoltage
352 flipper_arms_voltage = constants::Values::kFlipperOpenVoltage();
353 }
354
355 if (!flippers_open_ &&
356 timestamp >
357 loading_timer_ + constants::Values::kFlipperOpeningTimeout()) {
358 // Reseat the ball and try again
359 state_ = SuperstructureState::LOADING;
360 loading_timer_ = timestamp;
361 reseating_in_catapult_ = true;
362 break;
363 }
364
365 const bool turret_near_goal =
366 turret_goal != nullptr &&
367 std::abs(turret_goal->unsafe_goal() - turret_.position()) <
368 kTurretGoalThreshold;
Henry Speiser28288cc2022-03-09 22:59:24 -0800369 const bool collided = collision_avoidance_.IsCollided(
370 {.intake_front_position = intake_front_.estimated_position(),
371 .intake_back_position = intake_back_.estimated_position(),
372 .turret_position = turret_.estimated_position(),
373 .shooting = true});
Ravago Jones5da06352022-03-04 20:26:24 -0800374
Henry Speiser28288cc2022-03-09 22:59:24 -0800375 // If the turret reached the aiming goal and the catapult is safe to move
376 // up, fire!
377 if (flippers_open_ && turret_near_goal && !collided) {
Ravago Jones5da06352022-03-04 20:26:24 -0800378 fire_ = true;
379 }
380
381 // If we started firing and the flippers closed a bit, estop to prevent
382 // damage
383 if (fire_ && !flippers_open_) {
384 catapult_.Estop();
385 }
386
387 const bool near_return_position =
388 (unsafe_goal != nullptr && unsafe_goal->has_catapult() &&
389 unsafe_goal->catapult()->has_return_position() &&
390 std::abs(unsafe_goal->catapult()->return_position()->unsafe_goal() -
391 catapult_.estimated_position()) < kCatapultGoalThreshold);
392
393 // Once the shot is complete and the catapult is back to its return
394 // position, go back to IDLE
395 if (catapult_.shot_count() > prev_shot_count_ && near_return_position) {
396 prev_shot_count_ = catapult_.shot_count();
397 fire_ = false;
398 state_ = SuperstructureState::IDLE;
399 }
400
401 break;
402 }
403 }
404
405 collision_avoidance_.UpdateGoal(
406 {.intake_front_position = intake_front_.estimated_position(),
407 .intake_back_position = intake_back_.estimated_position(),
Henry Speiser28288cc2022-03-09 22:59:24 -0800408 .turret_position = turret_.estimated_position(),
409 .shooting = state_ == SuperstructureState::SHOOTING},
Ravago Jones5da06352022-03-04 20:26:24 -0800410 turret_goal);
411
412 turret_.set_min_position(collision_avoidance_.min_turret_goal());
413 turret_.set_max_position(collision_avoidance_.max_turret_goal());
414 intake_front_.set_min_position(collision_avoidance_.min_intake_front_goal());
415 intake_front_.set_max_position(collision_avoidance_.max_intake_front_goal());
416 intake_back_.set_min_position(collision_avoidance_.min_intake_back_goal());
417 intake_back_.set_max_position(collision_avoidance_.max_intake_back_goal());
418
James Kuszmaul84083f42022-02-27 17:24:38 -0800419 const flatbuffers::Offset<AimerStatus> aimer_offset =
420 aimer_.PopulateStatus(status->fbb());
421
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800422 // Disable the catapult if we want to restart to prevent damage with
423 // flippers
Austin Schuh39f26f62022-02-24 21:34:46 -0800424 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Austin Schuh97410d72022-03-12 15:37:23 -0800425 catapult_status_offset = catapult_.Iterate(
Ravago Jones3283ce02022-03-09 19:31:29 -0800426 catapult_goal, position, robot_state().voltage_battery(),
Austin Schuh97410d72022-03-12 15:37:23 -0800427 output != nullptr && !catapult_.estopped()
428 ? &(output_struct.catapult_voltage)
429 : nullptr,
430 fire_, status->fbb());
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800431
Ravago Jones5da06352022-03-04 20:26:24 -0800432 const flatbuffers::Offset<RelativeEncoderProfiledJointStatus>
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800433 climber_status_offset = climber_.Iterate(
434 unsafe_goal != nullptr ? unsafe_goal->climber() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800435 position->climber(),
436 output != nullptr ? &output_struct.climber_voltage : nullptr,
437 status->fbb());
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800438
Ravago Jones5da06352022-03-04 20:26:24 -0800439 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800440 intake_status_offset_front = intake_front_.Iterate(
441 unsafe_goal != nullptr ? unsafe_goal->intake_front() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800442 position->intake_front(),
443 output != nullptr ? &output_struct.intake_voltage_front : nullptr,
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800444 status->fbb());
445
Ravago Jones5da06352022-03-04 20:26:24 -0800446 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800447 intake_status_offset_back = intake_back_.Iterate(
448 unsafe_goal != nullptr ? unsafe_goal->intake_back() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800449 position->intake_back(),
450 output != nullptr ? &output_struct.intake_voltage_back : nullptr,
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800451 status->fbb());
452
Ravago Jones5da06352022-03-04 20:26:24 -0800453 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Austin Schuh7f120362022-03-05 17:10:11 -0800454 turret_status_offset = turret_.Iterate(
455 turret_goal, position->turret(),
456 output != nullptr ? &output_struct.turret_voltage : nullptr,
457 status->fbb());
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800458
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800459 if (output != nullptr) {
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800460 output_struct.roller_voltage_front = roller_speed_compensated_front;
461 output_struct.roller_voltage_back = roller_speed_compensated_back;
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800462 output_struct.transfer_roller_voltage_front = transfer_roller_speed_front;
463 output_struct.transfer_roller_voltage_back = transfer_roller_speed_back;
Ravago Jones5da06352022-03-04 20:26:24 -0800464 output_struct.flipper_arms_voltage = flipper_arms_voltage;
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800465
milind-u086d7262022-01-19 20:44:18 -0800466 output->CheckOk(output->Send(Output::Pack(*output->fbb(), &output_struct)));
467 }
468
469 Status::Builder status_builder = status->MakeBuilder<Status>();
470
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800471 const bool zeroed = intake_front_.zeroed() && intake_back_.zeroed() &&
Ravago Jones5da06352022-03-04 20:26:24 -0800472 turret_.zeroed() && climber_.zeroed() &&
473 catapult_.zeroed();
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800474 const bool estopped = intake_front_.estopped() || intake_back_.estopped() ||
Ravago Jones5da06352022-03-04 20:26:24 -0800475 turret_.estopped() || climber_.estopped() ||
476 catapult_.estopped();
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800477
478 status_builder.add_zeroed(zeroed);
479 status_builder.add_estopped(estopped);
480
481 status_builder.add_intake_front(intake_status_offset_front);
482 status_builder.add_intake_back(intake_status_offset_back);
483 status_builder.add_turret(turret_status_offset);
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800484 status_builder.add_climber(climber_status_offset);
Ravago Jones5da06352022-03-04 20:26:24 -0800485
Austin Schuh39f26f62022-02-24 21:34:46 -0800486 status_builder.add_catapult(catapult_status_offset);
487 status_builder.add_solve_time(catapult_.solve_time());
Austin Schuh80fc2752022-02-25 13:33:56 -0800488 status_builder.add_shot_count(catapult_.shot_count());
Ravago Jones5da06352022-03-04 20:26:24 -0800489 status_builder.add_mpc_active(catapult_.mpc_active());
Ravago Jones3283ce02022-03-09 19:31:29 -0800490 if (catapult_goal != nullptr) {
491 status_builder.add_shot_position(catapult_goal->shot_position());
492 status_builder.add_shot_velocity(catapult_goal->shot_velocity());
493 }
Ravago Jones5da06352022-03-04 20:26:24 -0800494
495 status_builder.add_flippers_open(flippers_open_);
496 status_builder.add_reseating_in_catapult(reseating_in_catapult_);
497 status_builder.add_fire(fire_);
498 status_builder.add_state(state_);
499 status_builder.add_intake_state(intake_state_);
milind-u086d7262022-01-19 20:44:18 -0800500
James Kuszmaul84083f42022-02-27 17:24:38 -0800501 status_builder.add_aimer(aimer_offset);
502
milind-u086d7262022-01-19 20:44:18 -0800503 (void)status->Send(status_builder.Finish());
504}
505
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800506double Superstructure::robot_velocity() const {
507 return (drivetrain_status_fetcher_.get() != nullptr
508 ? drivetrain_status_fetcher_->robot_speed()
509 : 0.0);
510}
511
milind-u086d7262022-01-19 20:44:18 -0800512} // namespace superstructure
513} // namespace control_loops
514} // namespace y2022