blob: c87cc9505a98d55b1ac7130f8057b6573a43f618 [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"
Milind Upadhyay225156b2022-02-25 22:42:12 -08004#include "y2022/control_loops/superstructure/collision_avoidance.h"
milind-u086d7262022-01-19 20:44:18 -08005
6namespace y2022 {
7namespace control_loops {
8namespace superstructure {
9
10using frc971::control_loops::AbsoluteEncoderProfiledJointStatus;
11using frc971::control_loops::PotAndAbsoluteEncoderProfiledJointStatus;
Henry Speiser55aa3ba2022-02-21 23:21:12 -080012using frc971::control_loops::RelativeEncoderProfiledJointStatus;
milind-u086d7262022-01-19 20:44:18 -080013
14Superstructure::Superstructure(::aos::EventLoop *event_loop,
Henry Speiser55aa3ba2022-02-21 23:21:12 -080015 std::shared_ptr<const constants::Values> values,
milind-u086d7262022-01-19 20:44:18 -080016 const ::std::string &name)
17 : frc971::controls::ControlLoop<Goal, Position, Status, Output>(event_loop,
Siddhant Kanwar0e37f592022-02-21 19:26:50 -080018 name),
Austin Schuh39f26f62022-02-24 21:34:46 -080019 values_(values),
20 climber_(values_->climber.subsystem_params),
21 intake_front_(values_->intake_front.subsystem_params),
22 intake_back_(values_->intake_back.subsystem_params),
23 turret_(values_->turret.subsystem_params),
Ravago Jones5da06352022-03-04 20:26:24 -080024 catapult_(*values_),
Henry Speiser55aa3ba2022-02-21 23:21:12 -080025 drivetrain_status_fetcher_(
26 event_loop->MakeFetcher<frc971::control_loops::drivetrain::Status>(
Austin Schuh39f26f62022-02-24 21:34:46 -080027 "/drivetrain")),
Ravago Jones5da06352022-03-04 20:26:24 -080028 can_position_fetcher_(
29 event_loop->MakeFetcher<CANPosition>("/superstructure")) {
milind-u086d7262022-01-19 20:44:18 -080030 event_loop->SetRuntimeRealtimePriority(30);
31}
32
33void Superstructure::RunIteration(const Goal *unsafe_goal,
Siddhant Kanwar0e37f592022-02-21 19:26:50 -080034 const Position *position,
milind-u086d7262022-01-19 20:44:18 -080035 aos::Sender<Output>::Builder *output,
36 aos::Sender<Status>::Builder *status) {
37 if (WasReset()) {
38 AOS_LOG(ERROR, "WPILib reset, restarting\n");
Henry Speiser55aa3ba2022-02-21 23:21:12 -080039 intake_front_.Reset();
40 intake_back_.Reset();
41 turret_.Reset();
42 climber_.Reset();
Austin Schuh39f26f62022-02-24 21:34:46 -080043 catapult_.Reset();
Henry Speiser55aa3ba2022-02-21 23:21:12 -080044 }
45
Ravago Jones5da06352022-03-04 20:26:24 -080046 OutputT output_struct;
Milind Upadhyay225156b2022-02-25 22:42:12 -080047
Ravago Jones5da06352022-03-04 20:26:24 -080048 aos::FlatbufferFixedAllocatorArray<
49 frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal, 64>
50 turret_goal_buffer;
51
52 const aos::monotonic_clock::time_point timestamp =
53 event_loop()->context().monotonic_event_time;
Milind Upadhyay225156b2022-02-25 22:42:12 -080054
Henry Speiser55aa3ba2022-02-21 23:21:12 -080055 drivetrain_status_fetcher_.Fetch();
56 const float velocity = robot_velocity();
57
James Kuszmaul84083f42022-02-27 17:24:38 -080058 const turret::Aimer::Goal *auto_aim_goal = nullptr;
59 if (drivetrain_status_fetcher_.get() != nullptr) {
60 aimer_.Update(drivetrain_status_fetcher_.get(),
61 turret::Aimer::ShotMode::kShootOnTheFly);
62 auto_aim_goal = aimer_.TurretGoal();
63 }
64
Ravago Jones5da06352022-03-04 20:26:24 -080065 const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal
66 *turret_goal = nullptr;
67 double roller_speed_compensated_front = 0.0;
68 double roller_speed_compensated_back = 0.0;
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -080069 double transfer_roller_speed_front = 0.0;
70 double transfer_roller_speed_back = 0.0;
Ravago Jones5da06352022-03-04 20:26:24 -080071 double flipper_arms_voltage = 0.0;
Henry Speiser55aa3ba2022-02-21 23:21:12 -080072
73 if (unsafe_goal != nullptr) {
74 roller_speed_compensated_front =
75 unsafe_goal->roller_speed_front() +
76 std::max(velocity * unsafe_goal->roller_speed_compensation(), 0.0);
77
78 roller_speed_compensated_back =
79 unsafe_goal->roller_speed_back() -
80 std::min(velocity * unsafe_goal->roller_speed_compensation(), 0.0);
81
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -080082 transfer_roller_speed_front = unsafe_goal->transfer_roller_speed_front();
83 transfer_roller_speed_back = unsafe_goal->transfer_roller_speed_back();
milind-u086d7262022-01-19 20:44:18 -080084
James Kuszmaul84083f42022-02-27 17:24:38 -080085 turret_goal =
86 unsafe_goal->auto_aim() ? auto_aim_goal : unsafe_goal->turret();
87 }
Austin Schuh39f26f62022-02-24 21:34:46 -080088
Milind Upadhyay803bbf02022-03-11 17:56:26 -080089 // Superstructure state machine:
Ravago Jones5da06352022-03-04 20:26:24 -080090 // 1. IDLE: Wait until an intake beambreak is triggerred, meaning that a ball
91 // is being intaked. This means that the transfer rollers have a ball. If
92 // we've been waiting here for too long without any beambreak triggered, the
93 // ball got lost, so reset.
94 // 2. TRANSFERRING: Until the turret reaches the loading position where the
95 // ball can be transferred into the catapult, wiggle the ball in place.
96 // Once the turret reaches the loading position, send the ball forward with
97 // the transfer rollers until the turret beambreak is triggered.
98 // If we have been in this state for too long, the ball probably got lost so
99 // reset back to IDLE.
100 // 3. LOADING: To load the ball into the catapult, put the flippers at the
101 // feeding speed. Wait for a timeout, and then wait until the ball has gone
102 // past the turret beambreak and the flippers have stopped moving, meaning
103 // that the ball is fully loaded in the catapult.
104 // 4. LOADED: Wait until the user asks us to fire to transition to the
105 // shooting stage. If asked to cancel the shot, reset back to the IDLE state.
106 // 5. SHOOTING: Open the flippers to get ready for the shot. If they don't
107 // open quickly enough, try reseating the ball and going back to the LOADING
108 // stage, which moves the flippers in the opposite direction first.
109 // Now, hold the flippers open and wait until the turret has reached its
110 // aiming goal. Once the turret is ready, tell the catapult to fire.
111 // If the flippers move back for some reason now, it could damage the
112 // catapult, so estop it. Otherwise, wait until the catapult shoots a ball and
113 // goes back to its return position. We have now finished the shot, so return
114 // to IDLE.
115
Milind Upadhyay803bbf02022-03-11 17:56:26 -0800116 // If we started off preloaded, skip to the loaded state.
117 // Make sure we weren't already there just in case.
118 if (unsafe_goal != nullptr && unsafe_goal->preloaded()) {
119 switch (state_) {
120 case SuperstructureState::IDLE:
121 case SuperstructureState::TRANSFERRING:
122 case SuperstructureState::LOADING:
123 state_ = SuperstructureState::LOADED;
124 loading_timer_ = timestamp;
125 break;
126 case SuperstructureState::LOADED:
127 case SuperstructureState::SHOOTING:
128 break;
129 }
130 }
131
Ravago Jones5da06352022-03-04 20:26:24 -0800132 const bool is_spitting = ((intake_state_ == IntakeState::INTAKE_FRONT_BALL &&
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800133 transfer_roller_speed_front < 0) ||
Ravago Jones5da06352022-03-04 20:26:24 -0800134 (intake_state_ == IntakeState::INTAKE_BACK_BALL &&
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800135 transfer_roller_speed_back < 0));
Ravago Jones5da06352022-03-04 20:26:24 -0800136
137 // Intake handling should happen regardless of the turret state
138 if (position->intake_beambreak_front() || position->intake_beambreak_back()) {
139 if (intake_state_ == IntakeState::NO_BALL) {
140 if (position->intake_beambreak_front()) {
141 intake_state_ = IntakeState::INTAKE_FRONT_BALL;
142 } else if (position->intake_beambreak_back()) {
143 intake_state_ = IntakeState::INTAKE_BACK_BALL;
144 }
145 }
146
147 intake_beambreak_timer_ = timestamp;
148 }
149
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800150 if (timestamp >
151 intake_beambreak_timer_ + constants::Values::kBallLostTime()) {
152 intake_state_ = IntakeState::NO_BALL;
153 }
154
Ravago Jones5da06352022-03-04 20:26:24 -0800155 if (intake_state_ != IntakeState::NO_BALL) {
Ravago Jones5da06352022-03-04 20:26:24 -0800156 roller_speed_compensated_front = 0.0;
157 roller_speed_compensated_back = 0.0;
158
159 const double wiggle_voltage =
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800160 constants::Values::kTransferRollerWiggleVoltage();
Ravago Jones5da06352022-03-04 20:26:24 -0800161 // Wiggle transfer rollers: send the ball back and forth while waiting
162 // for the turret or waiting for another shot to be completed
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800163 if (intake_state_ == IntakeState::INTAKE_FRONT_BALL) {
164 if (position->intake_beambreak_front()) {
165 transfer_roller_speed_front = -wiggle_voltage;
166 transfer_roller_speed_back = wiggle_voltage;
167 } else {
168 transfer_roller_speed_front = wiggle_voltage;
169 transfer_roller_speed_back = -wiggle_voltage;
170 }
Ravago Jones5da06352022-03-04 20:26:24 -0800171 } else {
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800172 if (position->intake_beambreak_back()) {
173 transfer_roller_speed_back = -wiggle_voltage;
174 transfer_roller_speed_front = wiggle_voltage;
175 } else {
176 transfer_roller_speed_back = wiggle_voltage;
177 transfer_roller_speed_front = -wiggle_voltage;
178 }
Ravago Jones5da06352022-03-04 20:26:24 -0800179 }
180 }
181
Milind Upadhyay918ecf02022-03-12 15:55:46 -0800182 // Send the turret to the loading position if we have a ball in the intake, or
183 // are trying to intake one
184 double turret_loading_position = std::numeric_limits<double>::quiet_NaN();
185 if (state_ == SuperstructureState::TRANSFERRING &&
186 intake_state_ != IntakeState::NO_BALL) {
187 turret_loading_position = (intake_state_ == IntakeState::INTAKE_FRONT_BALL
188 ? constants::Values::kTurretFrontIntakePos()
189 : constants::Values::kTurretBackIntakePos());
190 } else if (state_ == SuperstructureState::IDLE && unsafe_goal != nullptr) {
191 if (unsafe_goal->roller_speed_front() > 0) {
192 turret_loading_position = constants::Values::kTurretFrontIntakePos();
193 } else if (unsafe_goal->roller_speed_back() > 0) {
194 turret_loading_position = constants::Values::kTurretBackIntakePos();
195 }
196 }
197
198 if (!std::isnan(turret_loading_position)) {
199 // Turn to the loading position as close to the current position as
200 // possible
201 // Strategy is copied from frc971/control_loops/aiming/aiming.cc
202 turret_loading_position =
203 turret_.estimated_position() +
204 aos::math::NormalizeAngle(turret_loading_position -
205 turret_.estimated_position());
206 // if out of range, reset back to within +/- pi of zero.
207 if (turret_loading_position > constants::Values::kTurretRange().upper ||
208 turret_loading_position < constants::Values::kTurretRange().lower) {
209 turret_loading_position =
210 aos::math::NormalizeAngle(turret_loading_position);
211 }
212
213 turret_goal_buffer.Finish(
214 frc971::control_loops::
215 CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
216 *turret_goal_buffer.fbb(), turret_loading_position));
217 turret_goal = &turret_goal_buffer.message();
218 }
219
Ravago Jones5da06352022-03-04 20:26:24 -0800220 switch (state_) {
221 case SuperstructureState::IDLE: {
Ravago Jones5da06352022-03-04 20:26:24 -0800222 if (is_spitting) {
223 intake_state_ = IntakeState::NO_BALL;
224 }
225
226 if (intake_state_ == IntakeState::NO_BALL ||
227 !(position->intake_beambreak_front() ||
228 position->intake_beambreak_back())) {
229 break;
230 }
231
232 state_ = SuperstructureState::TRANSFERRING;
Ravago Jones5da06352022-03-04 20:26:24 -0800233 // Save the side the ball is on for later
234
235 break;
236 }
237 case SuperstructureState::TRANSFERRING: {
238 // If we've been transferring for too long, the ball probably got lost
Ravago Jones5da06352022-03-04 20:26:24 -0800239 if (intake_state_ == IntakeState::NO_BALL) {
240 state_ = SuperstructureState::IDLE;
241 break;
242 }
243
Ravago Jones5da06352022-03-04 20:26:24 -0800244 const bool turret_near_goal =
245 std::abs(turret_.estimated_position() - turret_loading_position) <
246 kTurretGoalThreshold;
247 if (!turret_near_goal) {
248 break; // Wait for turret to reach the chosen intake
249 }
250
251 // Transfer rollers and flipper arm belt on
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800252 if (intake_state_ == IntakeState::INTAKE_FRONT_BALL) {
253 transfer_roller_speed_front =
254 constants::Values::kTransferRollerVoltage();
255 transfer_roller_speed_back =
256 -constants::Values::kTransferRollerVoltage();
257 } else {
258 transfer_roller_speed_back =
259 constants::Values::kTransferRollerVoltage();
260 transfer_roller_speed_front =
261 -constants::Values::kTransferRollerVoltage();
262 }
Ravago Jones5da06352022-03-04 20:26:24 -0800263 flipper_arms_voltage = constants::Values::kFlipperFeedVoltage();
264
265 // Ball is in catapult
266 if (position->turret_beambreak()) {
267 intake_state_ = IntakeState::NO_BALL;
268 state_ = SuperstructureState::LOADING;
269 loading_timer_ = timestamp;
270 }
271 break;
272 }
273 case SuperstructureState::LOADING: {
274 flipper_arms_voltage = constants::Values::kFlipperFeedVoltage();
275
276 // Keep feeding for kExtraLoadingTime
277
Ravago Jones5da06352022-03-04 20:26:24 -0800278 // The ball should go past the turret beambreak to be loaded.
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800279 // If we got a CAN reading not too long ago, the flippers should have
280 // also stopped.
Milind Upadhyay47e0d032022-03-05 23:06:25 -0800281 if (position->turret_beambreak()) {
282 loading_timer_ = timestamp;
283 } else if (timestamp >
284 loading_timer_ + constants::Values::kExtraLoadingTime()) {
Ravago Jones5da06352022-03-04 20:26:24 -0800285 state_ = SuperstructureState::LOADED;
286 reseating_in_catapult_ = false;
287 }
288 break;
289 }
290 case SuperstructureState::LOADED: {
291 if (unsafe_goal != nullptr) {
292 if (unsafe_goal->cancel_shot()) {
293 // Cancel the shot process
294 state_ = SuperstructureState::IDLE;
295 } else if (unsafe_goal->fire()) {
296 // Start if we were asked to and the turret is at goal
297 state_ = SuperstructureState::SHOOTING;
298 prev_shot_count_ = catapult_.shot_count();
299
300 // Reset opening timeout
301 flipper_opening_start_time_ = timestamp;
302 }
303 }
304 break;
305 }
306 case SuperstructureState::SHOOTING: {
307 // Opening flipper arms could fail, wait until they are open using their
308 // potentiometers (the member below is just named encoder).
309 // Be a little more lenient if the flippers were already open in case of
310 // noise or collisions.
311 const double flipper_open_position =
312 (flippers_open_ ? constants::Values::kReseatFlipperPosition()
313 : constants::Values::kFlipperOpenPosition());
milind-u37dc40b2022-03-08 19:51:27 -0800314
315 // TODO(milind): add left arm back once it's fixed
Ravago Jones5da06352022-03-04 20:26:24 -0800316 flippers_open_ =
Ravago Jones5da06352022-03-04 20:26:24 -0800317 position->flipper_arm_right()->encoder() >= flipper_open_position;
318
319 if (flippers_open_) {
320 // Hold at kFlipperHoldVoltage
321 flipper_arms_voltage = constants::Values::kFlipperHoldVoltage();
322 } else {
323 // Open at kFlipperOpenVoltage
324 flipper_arms_voltage = constants::Values::kFlipperOpenVoltage();
325 }
326
327 if (!flippers_open_ &&
328 timestamp >
329 loading_timer_ + constants::Values::kFlipperOpeningTimeout()) {
330 // Reseat the ball and try again
331 state_ = SuperstructureState::LOADING;
332 loading_timer_ = timestamp;
333 reseating_in_catapult_ = true;
334 break;
335 }
336
337 const bool turret_near_goal =
338 turret_goal != nullptr &&
339 std::abs(turret_goal->unsafe_goal() - turret_.position()) <
340 kTurretGoalThreshold;
Henry Speiser28288cc2022-03-09 22:59:24 -0800341 const bool collided = collision_avoidance_.IsCollided(
342 {.intake_front_position = intake_front_.estimated_position(),
343 .intake_back_position = intake_back_.estimated_position(),
344 .turret_position = turret_.estimated_position(),
345 .shooting = true});
Ravago Jones5da06352022-03-04 20:26:24 -0800346
Henry Speiser28288cc2022-03-09 22:59:24 -0800347 // If the turret reached the aiming goal and the catapult is safe to move
348 // up, fire!
349 if (flippers_open_ && turret_near_goal && !collided) {
Ravago Jones5da06352022-03-04 20:26:24 -0800350 fire_ = true;
351 }
352
353 // If we started firing and the flippers closed a bit, estop to prevent
354 // damage
355 if (fire_ && !flippers_open_) {
356 catapult_.Estop();
357 }
358
359 const bool near_return_position =
360 (unsafe_goal != nullptr && unsafe_goal->has_catapult() &&
361 unsafe_goal->catapult()->has_return_position() &&
362 std::abs(unsafe_goal->catapult()->return_position()->unsafe_goal() -
363 catapult_.estimated_position()) < kCatapultGoalThreshold);
364
365 // Once the shot is complete and the catapult is back to its return
366 // position, go back to IDLE
367 if (catapult_.shot_count() > prev_shot_count_ && near_return_position) {
368 prev_shot_count_ = catapult_.shot_count();
369 fire_ = false;
370 state_ = SuperstructureState::IDLE;
371 }
372
373 break;
374 }
375 }
376
377 collision_avoidance_.UpdateGoal(
378 {.intake_front_position = intake_front_.estimated_position(),
379 .intake_back_position = intake_back_.estimated_position(),
Henry Speiser28288cc2022-03-09 22:59:24 -0800380 .turret_position = turret_.estimated_position(),
381 .shooting = state_ == SuperstructureState::SHOOTING},
Ravago Jones5da06352022-03-04 20:26:24 -0800382 turret_goal);
383
384 turret_.set_min_position(collision_avoidance_.min_turret_goal());
385 turret_.set_max_position(collision_avoidance_.max_turret_goal());
386 intake_front_.set_min_position(collision_avoidance_.min_intake_front_goal());
387 intake_front_.set_max_position(collision_avoidance_.max_intake_front_goal());
388 intake_back_.set_min_position(collision_avoidance_.min_intake_back_goal());
389 intake_back_.set_max_position(collision_avoidance_.max_intake_back_goal());
390
James Kuszmaul84083f42022-02-27 17:24:38 -0800391 const flatbuffers::Offset<AimerStatus> aimer_offset =
392 aimer_.PopulateStatus(status->fbb());
393
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800394 // Disable the catapult if we want to restart to prevent damage with
395 // flippers
Austin Schuh39f26f62022-02-24 21:34:46 -0800396 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Ravago Jones5da06352022-03-04 20:26:24 -0800397 catapult_status_offset =
398 catapult_.Iterate(unsafe_goal, position,
399 output != nullptr && !catapult_.estopped()
400 ? &(output_struct.catapult_voltage)
401 : nullptr,
402 fire_, status->fbb());
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800403
Ravago Jones5da06352022-03-04 20:26:24 -0800404 const flatbuffers::Offset<RelativeEncoderProfiledJointStatus>
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800405 climber_status_offset = climber_.Iterate(
406 unsafe_goal != nullptr ? unsafe_goal->climber() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800407 position->climber(),
408 output != nullptr ? &output_struct.climber_voltage : nullptr,
409 status->fbb());
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800410
Ravago Jones5da06352022-03-04 20:26:24 -0800411 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800412 intake_status_offset_front = intake_front_.Iterate(
413 unsafe_goal != nullptr ? unsafe_goal->intake_front() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800414 position->intake_front(),
415 output != nullptr ? &output_struct.intake_voltage_front : nullptr,
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800416 status->fbb());
417
Ravago Jones5da06352022-03-04 20:26:24 -0800418 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800419 intake_status_offset_back = intake_back_.Iterate(
420 unsafe_goal != nullptr ? unsafe_goal->intake_back() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800421 position->intake_back(),
422 output != nullptr ? &output_struct.intake_voltage_back : nullptr,
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800423 status->fbb());
424
Ravago Jones5da06352022-03-04 20:26:24 -0800425 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Austin Schuh7f120362022-03-05 17:10:11 -0800426 turret_status_offset = turret_.Iterate(
427 turret_goal, position->turret(),
428 output != nullptr ? &output_struct.turret_voltage : nullptr,
429 status->fbb());
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800430
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800431 if (output != nullptr) {
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800432 output_struct.roller_voltage_front = roller_speed_compensated_front;
433 output_struct.roller_voltage_back = roller_speed_compensated_back;
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800434 output_struct.transfer_roller_voltage_front = transfer_roller_speed_front;
435 output_struct.transfer_roller_voltage_back = transfer_roller_speed_back;
Ravago Jones5da06352022-03-04 20:26:24 -0800436 output_struct.flipper_arms_voltage = flipper_arms_voltage;
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800437
milind-u086d7262022-01-19 20:44:18 -0800438 output->CheckOk(output->Send(Output::Pack(*output->fbb(), &output_struct)));
439 }
440
441 Status::Builder status_builder = status->MakeBuilder<Status>();
442
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800443 const bool zeroed = intake_front_.zeroed() && intake_back_.zeroed() &&
Ravago Jones5da06352022-03-04 20:26:24 -0800444 turret_.zeroed() && climber_.zeroed() &&
445 catapult_.zeroed();
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800446 const bool estopped = intake_front_.estopped() || intake_back_.estopped() ||
Ravago Jones5da06352022-03-04 20:26:24 -0800447 turret_.estopped() || climber_.estopped() ||
448 catapult_.estopped();
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800449
450 status_builder.add_zeroed(zeroed);
451 status_builder.add_estopped(estopped);
452
453 status_builder.add_intake_front(intake_status_offset_front);
454 status_builder.add_intake_back(intake_status_offset_back);
455 status_builder.add_turret(turret_status_offset);
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800456 status_builder.add_climber(climber_status_offset);
Ravago Jones5da06352022-03-04 20:26:24 -0800457
Austin Schuh39f26f62022-02-24 21:34:46 -0800458 status_builder.add_catapult(catapult_status_offset);
459 status_builder.add_solve_time(catapult_.solve_time());
Austin Schuh80fc2752022-02-25 13:33:56 -0800460 status_builder.add_shot_count(catapult_.shot_count());
Ravago Jones5da06352022-03-04 20:26:24 -0800461 status_builder.add_mpc_active(catapult_.mpc_active());
462
463 status_builder.add_flippers_open(flippers_open_);
464 status_builder.add_reseating_in_catapult(reseating_in_catapult_);
465 status_builder.add_fire(fire_);
466 status_builder.add_state(state_);
467 status_builder.add_intake_state(intake_state_);
milind-u086d7262022-01-19 20:44:18 -0800468
James Kuszmaul84083f42022-02-27 17:24:38 -0800469 status_builder.add_aimer(aimer_offset);
470
milind-u086d7262022-01-19 20:44:18 -0800471 (void)status->Send(status_builder.Finish());
472}
473
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800474double Superstructure::robot_velocity() const {
475 return (drivetrain_status_fetcher_.get() != nullptr
476 ? drivetrain_status_fetcher_->robot_speed()
477 : 0.0);
478}
479
milind-u086d7262022-01-19 20:44:18 -0800480} // namespace superstructure
481} // namespace control_loops
482} // namespace y2022