blob: da43495545311d4facb429c917eac7b489dda497 [file] [log] [blame]
Sabina Davis91b23602019-01-21 00:06:01 -08001#include <math.h>
2#include <stdio.h>
3#include <string.h>
4#include <unistd.h>
Tyler Chatow4fedeea2019-03-10 15:33:36 -07005#include <chrono>
Sabina Davis91b23602019-01-21 00:06:01 -08006
7#include "aos/actions/actions.h"
8#include "aos/init.h"
9#include "aos/input/action_joystick_input.h"
10#include "aos/input/driver_station_data.h"
11#include "aos/input/drivetrain_input.h"
12#include "aos/input/joystick_input.h"
13#include "aos/logging/logging.h"
Tyler Chatowe0241452019-03-08 21:07:50 -080014#include "aos/network/team_number.h"
Sabina Davis91b23602019-01-21 00:06:01 -080015#include "aos/util/log_interval.h"
Tyler Chatowe0241452019-03-08 21:07:50 -080016#include "aos/vision/events/udp.h"
17#include "external/com_google_protobuf/src/google/protobuf/stubs/stringprintf.h"
Sabina Davis91b23602019-01-21 00:06:01 -080018#include "frc971/autonomous/base_autonomous_actor.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070019#include "frc971/control_loops/drivetrain/localizer_generated.h"
Sabina Davis91b23602019-01-21 00:06:01 -080020
21#include "y2019/control_loops/drivetrain/drivetrain_base.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070022#include "y2019/control_loops/drivetrain/target_selector_generated.h"
23#include "y2019/control_loops/superstructure/superstructure_goal_generated.h"
24#include "y2019/control_loops/superstructure/superstructure_position_generated.h"
25#include "y2019/control_loops/superstructure/superstructure_status_generated.h"
26#include "y2019/status_light_generated.h"
Tyler Chatowe0241452019-03-08 21:07:50 -080027#include "y2019/vision.pb.h"
Sabina Davis91b23602019-01-21 00:06:01 -080028
Alex Perrycb7da4b2019-08-28 19:35:56 -070029using aos::events::ProtoTXUdpSocket;
30using aos::input::driver_station::ButtonLocation;
31using aos::input::driver_station::ControlBit;
32using aos::input::driver_station::JoystickAxis;
33using aos::input::driver_station::POVLocation;
34using frc971::CreateProfileParameters;
35using frc971::control_loops::CreateStaticZeroingSingleDOFProfiledSubsystemGoal;
36using frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal;
37using frc971::control_loops::drivetrain::LocalizerControl;
Sabina Davis91b23602019-01-21 00:06:01 -080038
Austin Schuhaab7e162019-03-13 22:44:58 -070039namespace chrono = ::std::chrono;
40
Sabina Davis91b23602019-01-21 00:06:01 -080041namespace y2019 {
42namespace input {
43namespace joysticks {
44
Alex Perrycb7da4b2019-08-28 19:35:56 -070045namespace superstructure = y2019::control_loops::superstructure;
46
Tyler Chatowe0241452019-03-08 21:07:50 -080047using google::protobuf::StringPrintf;
48
Tyler Chatow7bcb52f2019-02-24 00:16:54 -080049struct ElevatorWristPosition {
50 double elevator;
51 double wrist;
52};
Sabina Davis91b23602019-01-21 00:06:01 -080053
Tyler Chatow5eeee902019-04-12 20:47:48 -070054const ButtonLocation kSuctionBall(4, 2);
55const ButtonLocation kSuctionHatch(3, 15);
56const ButtonLocation kDeployStilt(4, 1);
57const ButtonLocation kHalfStilt(4, 3);
58const ButtonLocation kFallOver(3, 16);
59
Tyler Chatow7bcb52f2019-02-24 00:16:54 -080060const ButtonLocation kRocketForwardLower(5, 1);
61const ButtonLocation kRocketForwardMiddle(5, 2);
62const ButtonLocation kRocketForwardUpper(5, 4);
63const ButtonLocation kCargoForward(5, 3);
64
65const POVLocation kRocketBackwardUnpressed(5, -1);
66const POVLocation kRocketBackwardLower(5, 180);
67const POVLocation kRocketBackwardMiddle(5, 90);
68const POVLocation kRocketBackwardUpper(5, 0);
69const POVLocation kCargoBackward(5, 270);
70
71const ButtonLocation kPanelSwitch(5, 7);
72const ButtonLocation kCargoSwitch(5, 8);
73
74const ButtonLocation kBallHPIntakeForward(5, 6);
75const ButtonLocation kBallHPIntakeBackward(5, 5);
76const JoystickAxis kBallOutake(5, 3);
77const JoystickAxis kBallIntake(5, 4);
78
79const ButtonLocation kPanelHPIntakeForward(5, 6);
80const ButtonLocation kPanelHPIntakeBackward(5, 5);
81
82const ButtonLocation kRelease(2, 4);
James Kuszmaulccc368b2019-04-11 20:00:07 -070083// Reuse quickturn for the cancel button.
84const ButtonLocation kCancelAutoMode(2, 3);
Tyler Chatow5eeee902019-04-12 20:47:48 -070085const ButtonLocation kReleaseButtonBoard(3, 4);
86const ButtonLocation kResetLocalizerLeftForwards(3, 10);
87const ButtonLocation kResetLocalizerLeftBackwards(3, 9);
Tyler Chatow7bcb52f2019-02-24 00:16:54 -080088
Tyler Chatow5eeee902019-04-12 20:47:48 -070089const ButtonLocation kResetLocalizerRightForwards(3, 8);
90const ButtonLocation kResetLocalizerRightBackwards(3, 7);
Tyler Chatow07c906b2019-03-09 21:29:06 -080091
James Kuszmaul518640d2019-04-13 15:50:50 -070092const ButtonLocation kResetLocalizerLeft(3, 11);
93const ButtonLocation kResetLocalizerRight(3, 13);
94
Tyler Chatow5eeee902019-04-12 20:47:48 -070095const ButtonLocation kNearCargoHint(3, 3);
96const ButtonLocation kMidCargoHint(3, 5);
97const ButtonLocation kFarCargoHint(3, 6);
James Kuszmaul7d1ef442019-03-23 20:20:50 -070098
James Kuszmaul518640d2019-04-13 15:50:50 -070099const JoystickAxis kCargoSelectorY(5, 6);
100const JoystickAxis kCargoSelectorX(5, 5);
101
Tyler Chatow5eeee902019-04-12 20:47:48 -0700102const ButtonLocation kCameraLog(3, 14);
Austin Schuh4e2629d2019-03-28 14:44:37 -0700103
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800104const ElevatorWristPosition kStowPos{0.36, 0.0};
Austin Schuh02be8b92019-03-24 16:04:14 -0700105const ElevatorWristPosition kClimbPos{0.0, M_PI / 4.0};
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800106
Austin Schuh139f59d2019-03-17 18:16:13 -0700107const ElevatorWristPosition kPanelHPIntakeForwrdPos{0.01, M_PI / 2.0};
108const ElevatorWristPosition kPanelHPIntakeBackwardPos{0.015, -M_PI / 2.0};
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800109
110const ElevatorWristPosition kPanelForwardLowerPos{0.0, M_PI / 2.0};
111const ElevatorWristPosition kPanelBackwardLowerPos{0.0, -M_PI / 2.0};
112
Sabina Davise48004f2019-03-02 23:15:24 -0800113const ElevatorWristPosition kPanelForwardMiddlePos{0.75, M_PI / 2.0};
114const ElevatorWristPosition kPanelBackwardMiddlePos{0.78, -M_PI / 2.0};
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800115
Sabina Davise48004f2019-03-02 23:15:24 -0800116const ElevatorWristPosition kPanelForwardUpperPos{1.51, M_PI / 2.0};
117const ElevatorWristPosition kPanelBackwardUpperPos{1.50, -M_PI / 2.0};
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800118
Sabina Davise6fe6c52019-03-03 15:48:51 -0800119const ElevatorWristPosition kPanelCargoForwardPos{0.0, M_PI / 2.0};
120const ElevatorWristPosition kPanelCargoBackwardPos{0.0, -M_PI / 2.0};
121
Austin Schuh6c5deb12019-03-24 16:48:32 -0700122const ElevatorWristPosition kBallForwardLowerPos{0.21, 1.27};
Austin Schuh20dc0502019-03-30 07:24:07 -0700123const ElevatorWristPosition kBallBackwardLowerPos{0.43, -1.99};
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800124
Austin Schuh20dc0502019-03-30 07:24:07 -0700125const ElevatorWristPosition kBallForwardMiddlePos{0.925, 1.21};
126const ElevatorWristPosition kBallBackwardMiddlePos{1.19, -1.98};
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800127
Austin Schuh139f59d2019-03-17 18:16:13 -0700128const ElevatorWristPosition kBallForwardUpperPos{1.51, 0.961};
129const ElevatorWristPosition kBallBackwardUpperPos{1.44, -1.217};
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800130
Austin Schuh20dc0502019-03-30 07:24:07 -0700131const ElevatorWristPosition kBallCargoForwardPos{0.59, 1.2};
132const ElevatorWristPosition kBallCargoBackwardPos{0.868265, -2.1};
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800133
Sabina Davise6fe6c52019-03-03 15:48:51 -0800134const ElevatorWristPosition kBallHPIntakeForwardPos{0.55, 1.097};
135const ElevatorWristPosition kBallHPIntakeBackwardPos{0.89, -2.018};
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800136
Austin Schuh20dc0502019-03-30 07:24:07 -0700137const ElevatorWristPosition kBallIntakePos{0.309, 2.13};
Austin Schuh2cf16b82019-02-15 23:23:22 -0800138
Sabina Davis91b23602019-01-21 00:06:01 -0800139class Reader : public ::aos::input::ActionJoystickInput {
140 public:
141 Reader(::aos::EventLoop *event_loop)
142 : ::aos::input::ActionJoystickInput(
143 event_loop,
James Kuszmaulccc368b2019-04-11 20:00:07 -0700144 ::y2019::control_loops::drivetrain::GetDrivetrainConfig(),
Austin Schuhbfb04122019-05-22 21:16:51 -0700145 ::aos::input::DrivetrainInputReader::InputType::kPistol,
James Kuszmaulccc368b2019-04-11 20:00:07 -0700146 {.run_teleop_in_auto = true,
Austin Schuhc087b102019-05-12 15:33:12 -0700147 .cancel_auto_button = kCancelAutoMode}),
148 target_selector_hint_sender_(
149 event_loop->MakeSender<
150 ::y2019::control_loops::drivetrain::TargetSelectorHint>(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700151 "/drivetrain")),
Austin Schuheb99d072019-05-12 21:03:38 -0700152 localizer_control_sender_(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700153 event_loop->MakeSender<LocalizerControl>("/drivetrain")),
Austin Schuh5671a8c2019-05-19 17:01:04 -0700154 camera_log_sender_(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700155 event_loop->MakeSender<::y2019::CameraLog>("/camera")),
156 superstructure_goal_fetcher_(
157 event_loop->MakeFetcher<superstructure::Goal>("/superstructure")),
158 superstructure_goal_sender_(
159 event_loop->MakeSender<superstructure::Goal>("/superstructure")),
Austin Schuh170f4952019-06-29 18:58:30 -0700160 superstructure_position_fetcher_(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700161 event_loop->MakeFetcher<superstructure::Position>(
162 "/superstructure")),
Austin Schuh170f4952019-06-29 18:58:30 -0700163 superstructure_status_fetcher_(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700164 event_loop->MakeFetcher<superstructure::Status>(
165 "/superstructure")) {
Tyler Chatowe0241452019-03-08 21:07:50 -0800166 const uint16_t team = ::aos::network::GetTeamNumber();
Austin Schuh170f4952019-06-29 18:58:30 -0700167 superstructure_goal_fetcher_.Fetch();
168 if (superstructure_goal_fetcher_.get()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700169 grab_piece_ = superstructure_goal_fetcher_->has_suction()
170 ? superstructure_goal_fetcher_->suction()->grab_piece()
171 : false;
172 switch_ball_ =
173 superstructure_goal_fetcher_->has_suction()
174 ? (superstructure_goal_fetcher_->suction()->gamepiece_mode() == 0)
175 : true;
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800176 }
Tyler Chatowe0241452019-03-08 21:07:50 -0800177 video_tx_.reset(new ProtoTXUdpSocket<VisionControl>(
178 StringPrintf("10.%d.%d.179", team / 100, team % 100), 5000));
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800179 }
Sabina Davis91b23602019-01-21 00:06:01 -0800180
Austin Schuha78857f2019-03-13 22:43:41 -0700181 void AutoEnded() override {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700182 AOS_LOG(INFO, "Auto ended, assuming disc and have piece\n");
Austin Schuha78857f2019-03-13 22:43:41 -0700183 grab_piece_ = true;
184 switch_ball_ = false;
185 }
186
187 void HandleTeleop(const ::aos::input::driver_station::Data &data) override {
Austin Schuhaab7e162019-03-13 22:44:58 -0700188 ::aos::monotonic_clock::time_point monotonic_now =
189 ::aos::monotonic_clock::now();
Austin Schuh170f4952019-06-29 18:58:30 -0700190 superstructure_position_fetcher_.Fetch();
191 superstructure_status_fetcher_.Fetch();
192 if (!superstructure_status_fetcher_.get() ||
193 !superstructure_position_fetcher_.get()) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700194 AOS_LOG(ERROR, "Got no superstructure status or position packet.\n");
Sabina Davis91b23602019-01-21 00:06:01 -0800195 return;
196 }
197
Alex Perrycb7da4b2019-08-28 19:35:56 -0700198 CHECK(superstructure_status_fetcher_->has_stilts());
199
200 if (!superstructure_status_fetcher_->has_piece()) {
Austin Schuhaab7e162019-03-13 22:44:58 -0700201 last_not_has_piece_ = monotonic_now;
202 }
203
Alex Perrycb7da4b2019-08-28 19:35:56 -0700204 auto main_superstructure_goal_builder =
205 superstructure_goal_sender_.MakeBuilder();
206
207 flatbuffers::Offset<superstructure::Goal> superstructure_goal_offset;
Sabina Davis91b23602019-01-21 00:06:01 -0800208
James Kuszmaul7d1ef442019-03-23 20:20:50 -0700209 {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700210 flatbuffers::Offset<StaticZeroingSingleDOFProfiledSubsystemGoal>
211 elevator_offset = CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
212 *main_superstructure_goal_builder.fbb(), 0.0,
213 CreateProfileParameters(*main_superstructure_goal_builder.fbb(),
214 0.0, 0.0));
215
216 flatbuffers::Offset<StaticZeroingSingleDOFProfiledSubsystemGoal>
217 intake_offset = CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
218 *main_superstructure_goal_builder.fbb(), -1.2);
219
220 flatbuffers::Offset<StaticZeroingSingleDOFProfiledSubsystemGoal>
221 wrist_offset = CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
222 *main_superstructure_goal_builder.fbb(), 0.0,
223 CreateProfileParameters(*main_superstructure_goal_builder.fbb(),
224 0.0, 0.0));
225
226 flatbuffers::Offset<StaticZeroingSingleDOFProfiledSubsystemGoal>
227 stilts_offset = CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
228 *main_superstructure_goal_builder.fbb(), 0.0,
229 CreateProfileParameters(*main_superstructure_goal_builder.fbb(),
230 0.0, 0.0));
231
232 superstructure::Goal::Builder superstructure_goal_builder =
233 main_superstructure_goal_builder.MakeBuilder<superstructure::Goal>();
234
235 superstructure_goal_builder.add_elevator(elevator_offset);
236 superstructure_goal_builder.add_intake(intake_offset);
237 superstructure_goal_builder.add_wrist(wrist_offset);
238 superstructure_goal_builder.add_stilts(stilts_offset);
239 superstructure_goal_builder.add_roller_voltage(0.0);
240
241 superstructure_goal_offset = superstructure_goal_builder.Finish();
242 }
243 superstructure::Goal *mutable_superstructure_goal =
244 GetMutableTemporaryPointer(*main_superstructure_goal_builder.fbb(),
245 superstructure_goal_offset);
246
247 {
248 auto builder = target_selector_hint_sender_.MakeBuilder();
249 control_loops::drivetrain::TargetSelectorHint::Builder
250 target_selector_hint_builder =
251 builder
252 .MakeBuilder<control_loops::drivetrain::TargetSelectorHint>();
James Kuszmaul7d1ef442019-03-23 20:20:50 -0700253 if (data.IsPressed(kNearCargoHint)) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700254 target_selector_hint_builder.add_suggested_target(
255 control_loops::drivetrain::SelectionHint_NEAR_SHIP);
James Kuszmaul7d1ef442019-03-23 20:20:50 -0700256 } else if (data.IsPressed(kMidCargoHint)) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700257 target_selector_hint_builder.add_suggested_target(
258 control_loops::drivetrain::SelectionHint_MID_SHIP);
James Kuszmaul7d1ef442019-03-23 20:20:50 -0700259 } else if (data.IsPressed(kFarCargoHint)) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700260 target_selector_hint_builder.add_suggested_target(
261 control_loops::drivetrain::SelectionHint_FAR_SHIP);
James Kuszmaul7d1ef442019-03-23 20:20:50 -0700262 } else {
James Kuszmaul518640d2019-04-13 15:50:50 -0700263 const double cargo_joy_y = data.GetAxis(kCargoSelectorY);
264 const double cargo_joy_x = data.GetAxis(kCargoSelectorX);
265 if (cargo_joy_y > 0.5) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700266 target_selector_hint_builder.add_suggested_target(
267 control_loops::drivetrain::SelectionHint_NEAR_SHIP);
James Kuszmaul1b822b42019-04-14 14:27:35 -0700268 } else if (cargo_joy_y < -0.5) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700269 target_selector_hint_builder.add_suggested_target(
270 control_loops::drivetrain::SelectionHint_FAR_SHIP);
James Kuszmaul518640d2019-04-13 15:50:50 -0700271 } else if (::std::abs(cargo_joy_x) > 0.5) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700272 target_selector_hint_builder.add_suggested_target(
273 control_loops::drivetrain::SelectionHint_MID_SHIP);
James Kuszmaul518640d2019-04-13 15:50:50 -0700274 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700275 target_selector_hint_builder.add_suggested_target(
276 control_loops::drivetrain::SelectionHint_NONE);
James Kuszmaul518640d2019-04-13 15:50:50 -0700277 }
James Kuszmaul7d1ef442019-03-23 20:20:50 -0700278 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700279 if (!builder.Send(target_selector_hint_builder.Finish())) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700280 AOS_LOG(ERROR, "Failed to send target selector hint.\n");
James Kuszmaul7d1ef442019-03-23 20:20:50 -0700281 }
282 }
283
James Kuszmaul518640d2019-04-13 15:50:50 -0700284 if (data.PosEdge(kResetLocalizerLeft)) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700285 auto builder = localizer_control_sender_.MakeBuilder();
James Kuszmaul518640d2019-04-13 15:50:50 -0700286 // Start at the left feeder station.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700287 LocalizerControl::Builder localizer_control_builder =
288 builder.MakeBuilder<LocalizerControl>();
289 localizer_control_builder.add_x(0.6);
290 localizer_control_builder.add_y(3.4);
291 localizer_control_builder.add_keep_current_theta(true);
James Kuszmaul518640d2019-04-13 15:50:50 -0700292
Alex Perrycb7da4b2019-08-28 19:35:56 -0700293 if (!builder.Send(localizer_control_builder.Finish())) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700294 AOS_LOG(ERROR, "Failed to reset localizer.\n");
James Kuszmaul518640d2019-04-13 15:50:50 -0700295 }
296 }
297
298 if (data.PosEdge(kResetLocalizerRight)) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700299 auto builder = localizer_control_sender_.MakeBuilder();
300 // Start at the right feeder station.
301 LocalizerControl::Builder localizer_control_builder =
302 builder.MakeBuilder<LocalizerControl>();
303 localizer_control_builder.add_x(0.6);
304 localizer_control_builder.add_y(-3.4);
305 localizer_control_builder.add_keep_current_theta(true);
James Kuszmaul518640d2019-04-13 15:50:50 -0700306
Alex Perrycb7da4b2019-08-28 19:35:56 -0700307 if (!builder.Send(localizer_control_builder.Finish())) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700308 AOS_LOG(ERROR, "Failed to reset localizer.\n");
James Kuszmaul518640d2019-04-13 15:50:50 -0700309 }
310 }
311
Austin Schuhbb1df6f2019-03-17 18:15:43 -0700312 if (data.PosEdge(kResetLocalizerLeftForwards)) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700313 auto builder = localizer_control_sender_.MakeBuilder();
Austin Schuh3632f8d2019-03-22 21:14:14 -0700314 // Start at the left feeder station.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700315 LocalizerControl::Builder localizer_control_builder =
316 builder.MakeBuilder<LocalizerControl>();
317 localizer_control_builder.add_x(0.4);
318 localizer_control_builder.add_y(3.4);
319 localizer_control_builder.add_theta(0.0);
Austin Schuhbb1df6f2019-03-17 18:15:43 -0700320
Alex Perrycb7da4b2019-08-28 19:35:56 -0700321 if (!builder.Send(localizer_control_builder.Finish())) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700322 AOS_LOG(ERROR, "Failed to reset localizer.\n");
Austin Schuhbb1df6f2019-03-17 18:15:43 -0700323 }
324 }
325
326 if (data.PosEdge(kResetLocalizerLeftBackwards)) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700327 auto builder = localizer_control_sender_.MakeBuilder();
Austin Schuhbb1df6f2019-03-17 18:15:43 -0700328 // Start at the left feeder station.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700329 LocalizerControl::Builder localizer_control_builder =
330 builder.MakeBuilder<LocalizerControl>();
331 localizer_control_builder.add_x(0.4);
332 localizer_control_builder.add_y(3.4);
333 localizer_control_builder.add_theta(M_PI);
Austin Schuhbb1df6f2019-03-17 18:15:43 -0700334
Alex Perrycb7da4b2019-08-28 19:35:56 -0700335 if (!builder.Send(localizer_control_builder.Finish())) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700336 AOS_LOG(ERROR, "Failed to reset localizer.\n");
Austin Schuhbb1df6f2019-03-17 18:15:43 -0700337 }
338 }
339
Austin Schuhbb1df6f2019-03-17 18:15:43 -0700340 if (data.PosEdge(kResetLocalizerRightForwards)) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700341 auto builder = localizer_control_sender_.MakeBuilder();
Austin Schuh3632f8d2019-03-22 21:14:14 -0700342 // Start at the right feeder station.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700343 LocalizerControl::Builder localizer_control_builder =
344 builder.MakeBuilder<LocalizerControl>();
345 localizer_control_builder.add_x(0.4);
346 localizer_control_builder.add_y(-3.4);
347 localizer_control_builder.add_theta(0.0);
Austin Schuhbb1df6f2019-03-17 18:15:43 -0700348
Alex Perrycb7da4b2019-08-28 19:35:56 -0700349 if (!builder.Send(localizer_control_builder.Finish())) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700350 AOS_LOG(ERROR, "Failed to reset localizer.\n");
Austin Schuhbb1df6f2019-03-17 18:15:43 -0700351 }
352 }
353
354 if (data.PosEdge(kResetLocalizerRightBackwards)) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700355 auto builder = localizer_control_sender_.MakeBuilder();
Austin Schuh3632f8d2019-03-22 21:14:14 -0700356 // Start at the right feeder station.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700357 LocalizerControl::Builder localizer_control_builder =
358 builder.MakeBuilder<LocalizerControl>();
359 localizer_control_builder.add_x(0.4);
360 localizer_control_builder.add_y(-3.4);
361 localizer_control_builder.add_theta(M_PI);
Austin Schuhbb1df6f2019-03-17 18:15:43 -0700362
Alex Perrycb7da4b2019-08-28 19:35:56 -0700363 if (!builder.Send(localizer_control_builder.Finish())) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700364 AOS_LOG(ERROR, "Failed to reset localizer.\n");
James Kuszmauld8deb682019-03-10 10:38:42 -0700365 }
366 }
367
James Kuszmaul13738862019-04-14 10:48:00 -0700368 if (data.PosEdge(kRelease) &&
369 monotonic_now >
370 last_release_button_press_ + ::std::chrono::milliseconds(500)) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700371 if (superstructure_status_fetcher_->has_piece()) {
James Kuszmaul13738862019-04-14 10:48:00 -0700372 release_mode_ = ReleaseButtonMode::kRelease;
373 } else {
374 release_mode_ = ReleaseButtonMode::kBallIntake;
375 }
376 }
377
378 if (data.IsPressed(kRelease)) {
379 last_release_button_press_ = monotonic_now;
380 }
381
Alex Perrycb7da4b2019-08-28 19:35:56 -0700382 AOS_LOG(INFO, "has_piece: %d\n",
383 superstructure_status_fetcher_->has_piece());
Austin Schuh1a17e132019-02-17 15:05:06 -0800384 if (data.IsPressed(kSuctionBall)) {
Sabina Davisc6329342019-03-01 20:44:42 -0800385 grab_piece_ = true;
Austin Schuh1a17e132019-02-17 15:05:06 -0800386 } else if (data.IsPressed(kSuctionHatch)) {
Sabina Davisc6329342019-03-01 20:44:42 -0800387 grab_piece_ = true;
James Kuszmaul13738862019-04-14 10:48:00 -0700388 } else if ((release_mode_ == ReleaseButtonMode::kRelease &&
389 data.IsPressed(kRelease)) ||
Tyler Chatow5eeee902019-04-12 20:47:48 -0700390 data.IsPressed(kReleaseButtonBoard) ||
Alex Perrycb7da4b2019-08-28 19:35:56 -0700391 !superstructure_status_fetcher_->has_piece()) {
Sabina Davisc6329342019-03-01 20:44:42 -0800392 grab_piece_ = false;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700393 AOS_LOG(INFO, "releasing due to other thing\n");
Austin Schuh1a17e132019-02-17 15:05:06 -0800394 }
Sabina Davis91b23602019-01-21 00:06:01 -0800395
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800396 if (data.IsPressed(kRocketBackwardUnpressed)) {
397 elevator_wrist_pos_ = kStowPos;
398 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700399 mutable_superstructure_goal->mutable_intake()->mutate_unsafe_goal(-1.2);
400 mutable_superstructure_goal->mutate_roller_voltage(0.0);
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800401
James Kuszmaul13738862019-04-14 10:48:00 -0700402 const bool kDoBallIntake =
403 (!climbed_ && release_mode_ == ReleaseButtonMode::kBallIntake &&
404 data.IsPressed(kRelease)) ||
405 data.GetAxis(kBallIntake) > 0.9;
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800406 const bool kDoBallOutake = data.GetAxis(kBallOutake) > 0.9;
407
408 if (data.IsPressed(kPanelSwitch)) {
409 switch_ball_ = false;
410 } else if (data.IsPressed(kCargoSwitch)) {
411 switch_ball_ = true;
412 }
413
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800414 if (switch_ball_) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700415 if (superstructure_status_fetcher_->has_piece()) {
416 mutable_superstructure_goal->mutable_wrist()
417 ->mutable_profile_params()
418 ->mutate_max_acceleration(20);
Austin Schuh1a17e132019-02-17 15:05:06 -0800419 }
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800420
421 // Go to intake position and apply vacuum
422 if (data.IsPressed(kBallHPIntakeForward)) {
Sabina Davisc6329342019-03-01 20:44:42 -0800423 grab_piece_ = true;
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800424 elevator_wrist_pos_ = kBallHPIntakeForwardPos;
425 } else if (data.IsPressed(kBallHPIntakeBackward)) {
Sabina Davisc6329342019-03-01 20:44:42 -0800426 grab_piece_ = true;
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800427 elevator_wrist_pos_ = kBallHPIntakeBackwardPos;
428 }
429
430 // Go to elevator/wrist position. Overrides intake position if pressed so
431 // we can re-grab the ball.
432 if (data.IsPressed(kRocketForwardLower)) {
433 elevator_wrist_pos_ = kBallForwardLowerPos;
434 } else if (data.IsPressed(kRocketBackwardLower)) {
435 elevator_wrist_pos_ = kBallBackwardLowerPos;
436 } else if (data.IsPressed(kRocketForwardMiddle)) {
437 elevator_wrist_pos_ = kBallForwardMiddlePos;
438 } else if (data.IsPressed(kRocketBackwardMiddle)) {
439 elevator_wrist_pos_ = kBallBackwardMiddlePos;
440 } else if (data.IsPressed(kRocketForwardUpper)) {
441 elevator_wrist_pos_ = kBallForwardUpperPos;
442 } else if (data.IsPressed(kRocketBackwardUpper)) {
443 elevator_wrist_pos_ = kBallBackwardUpperPos;
444 } else if (data.IsPressed(kCargoForward)) {
445 elevator_wrist_pos_ = kBallCargoForwardPos;
446 } else if (data.IsPressed(kCargoBackward)) {
447 elevator_wrist_pos_ = kBallCargoBackwardPos;
448 }
Austin Schuh1a17e132019-02-17 15:05:06 -0800449 } else {
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800450 if (data.IsPressed(kPanelHPIntakeForward)) {
Sabina Davisc6329342019-03-01 20:44:42 -0800451 grab_piece_ = true;
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800452 elevator_wrist_pos_ = kPanelHPIntakeForwrdPos;
453 } else if (data.IsPressed(kPanelHPIntakeBackward)) {
Sabina Davisc6329342019-03-01 20:44:42 -0800454 grab_piece_ = true;
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800455 elevator_wrist_pos_ = kPanelHPIntakeBackwardPos;
456 }
457
458 // Go to elevator/wrist position. Overrides intake position if pressed so
459 // we can re-grab the panel.
460 if (data.IsPressed(kRocketForwardLower)) {
461 elevator_wrist_pos_ = kPanelForwardLowerPos;
462 } else if (data.IsPressed(kRocketBackwardLower)) {
463 elevator_wrist_pos_ = kPanelBackwardLowerPos;
464 } else if (data.IsPressed(kRocketForwardMiddle)) {
465 elevator_wrist_pos_ = kPanelForwardMiddlePos;
466 } else if (data.IsPressed(kRocketBackwardMiddle)) {
467 elevator_wrist_pos_ = kPanelBackwardMiddlePos;
468 } else if (data.IsPressed(kRocketForwardUpper)) {
469 elevator_wrist_pos_ = kPanelForwardUpperPos;
470 } else if (data.IsPressed(kRocketBackwardUpper)) {
471 elevator_wrist_pos_ = kPanelBackwardUpperPos;
Sabina Davise6fe6c52019-03-03 15:48:51 -0800472 } else if (data.IsPressed(kCargoForward)) {
473 elevator_wrist_pos_ = kPanelCargoForwardPos;
474 } else if (data.IsPressed(kCargoBackward)) {
475 elevator_wrist_pos_ = kPanelCargoBackwardPos;
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800476 }
Austin Schuh1a17e132019-02-17 15:05:06 -0800477 }
478
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800479 if (switch_ball_) {
480 if (kDoBallOutake ||
Austin Schuhaab7e162019-03-13 22:44:58 -0700481 (kDoBallIntake &&
Austin Schuh20dc0502019-03-30 07:24:07 -0700482 monotonic_now < last_not_has_piece_ + chrono::milliseconds(200))) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700483 mutable_superstructure_goal->mutable_intake()->mutate_unsafe_goal(0.83);
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800484 }
Austin Schuh23a51632019-02-19 16:50:36 -0800485
Alex Perrycb7da4b2019-08-28 19:35:56 -0700486 if (kDoBallIntake && !superstructure_status_fetcher_->has_piece()) {
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800487 elevator_wrist_pos_ = kBallIntakePos;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700488 mutable_superstructure_goal->mutate_roller_voltage(9.0);
Sabina Davisc6329342019-03-01 20:44:42 -0800489 grab_piece_ = true;
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800490 } else {
491 if (kDoBallOutake) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700492 mutable_superstructure_goal->mutate_roller_voltage(-6.0);
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800493 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700494 mutable_superstructure_goal->mutate_roller_voltage(0.0);
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800495 }
496 }
Austin Schuh23a51632019-02-19 16:50:36 -0800497 }
Austin Schuh1a17e132019-02-17 15:05:06 -0800498
Austin Schuh457bde32019-03-17 18:16:41 -0700499 constexpr double kDeployStiltPosition = 0.5;
500
Austin Schuh457bde32019-03-17 18:16:41 -0700501 if (data.IsPressed(kFallOver)) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700502 mutable_superstructure_goal->mutable_stilts()->mutate_unsafe_goal(0.71);
503 mutable_superstructure_goal->mutable_stilts()
504 ->mutable_profile_params()
505 ->mutate_max_velocity(0.65);
506 mutable_superstructure_goal->mutable_stilts()
507 ->mutable_profile_params()
508 ->mutate_max_acceleration(1.25);
Austin Schuhe2f22482019-04-13 23:05:43 -0700509 } else if (data.IsPressed(kHalfStilt)) {
510 was_above_ = false;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700511 mutable_superstructure_goal->mutable_stilts()->mutate_unsafe_goal ( 0.345);
512 mutable_superstructure_goal->mutable_stilts()->mutable_profile_params()->mutate_max_velocity ( 0.65);
Austin Schuhe2f22482019-04-13 23:05:43 -0700513 } else if (data.IsPressed(kDeployStilt) || was_above_) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700514 mutable_superstructure_goal->mutable_stilts()->mutate_unsafe_goal(
515 kDeployStiltPosition);
516 mutable_superstructure_goal->mutable_stilts()
517 ->mutable_profile_params()
518 ->mutate_max_velocity(0.65);
519 mutable_superstructure_goal->mutable_stilts()
520 ->mutable_profile_params()
521 ->mutate_max_acceleration(2.0);
Austin Schuh457bde32019-03-17 18:16:41 -0700522 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700523 mutable_superstructure_goal->mutable_stilts()->mutate_unsafe_goal(0.005);
524 mutable_superstructure_goal->mutable_stilts()
525 ->mutable_profile_params()
526 ->mutate_max_velocity(0.65);
527 mutable_superstructure_goal->mutable_stilts()
528 ->mutable_profile_params()
529 ->mutate_max_acceleration(2.0);
Austin Schuh457bde32019-03-17 18:16:41 -0700530 }
531
Alex Perrycb7da4b2019-08-28 19:35:56 -0700532 if (superstructure_status_fetcher_->stilts()->position() > 0.1) {
Austin Schuh02be8b92019-03-24 16:04:14 -0700533 elevator_wrist_pos_ = kClimbPos;
James Kuszmaul13738862019-04-14 10:48:00 -0700534 climbed_ = true;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700535 mutable_superstructure_goal->mutable_wrist()
536 ->mutable_profile_params()
537 ->mutate_max_acceleration(10);
538 mutable_superstructure_goal->mutable_elevator()
539 ->mutable_profile_params()
540 ->mutate_max_acceleration(6);
Austin Schuh02be8b92019-03-24 16:04:14 -0700541 }
542
Austin Schuhe2f22482019-04-13 23:05:43 -0700543 // If we've been asked to go above deploy and made it up that high, latch
544 // was_above.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700545 if (mutable_superstructure_goal->stilts()->unsafe_goal() >
546 kDeployStiltPosition &&
547 superstructure_status_fetcher_->stilts()->position() >=
Austin Schuh170f4952019-06-29 18:58:30 -0700548 kDeployStiltPosition) {
Austin Schuhe2f22482019-04-13 23:05:43 -0700549 was_above_ = true;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700550 } else if ((superstructure_position_fetcher_->platform_left_detect() &&
551 superstructure_position_fetcher_->platform_right_detect()) &&
Austin Schuhe2f22482019-04-13 23:05:43 -0700552 !data.IsPressed(kDeployStilt) && !data.IsPressed(kFallOver)) {
Austin Schuhe2f22482019-04-13 23:05:43 -0700553 was_above_ = false;
554 }
555
Alex Perrycb7da4b2019-08-28 19:35:56 -0700556 if (superstructure_status_fetcher_->stilts()->position() >
Austin Schuh170f4952019-06-29 18:58:30 -0700557 kDeployStiltPosition &&
Alex Perrycb7da4b2019-08-28 19:35:56 -0700558 mutable_superstructure_goal->stilts()->unsafe_goal() ==
559 kDeployStiltPosition) {
560 mutable_superstructure_goal->mutable_stilts()
561 ->mutable_profile_params()
562 ->mutate_max_velocity(0.30);
563 mutable_superstructure_goal->mutable_stilts()
564 ->mutable_profile_params()
565 ->mutate_max_acceleration(0.75);
Austin Schuh457bde32019-03-17 18:16:41 -0700566 }
567
James Kuszmaul13738862019-04-14 10:48:00 -0700568 if ((release_mode_ == ReleaseButtonMode::kRelease &&
569 data.IsPressed(kRelease)) ||
570 data.IsPressed(kReleaseButtonBoard)) {
Sabina Davisc6329342019-03-01 20:44:42 -0800571 grab_piece_ = false;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700572 AOS_LOG(INFO, "Releasing due to button\n");
Austin Schuh1a17e132019-02-17 15:05:06 -0800573 }
574
Sabina Davisc6329342019-03-01 20:44:42 -0800575 if (switch_ball_) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700576 mutable_superstructure_goal->mutable_suction()->mutate_gamepiece_mode(0);
Sabina Davisc6329342019-03-01 20:44:42 -0800577 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700578 mutable_superstructure_goal->mutable_suction()->mutate_gamepiece_mode(1);
Sabina Davisc6329342019-03-01 20:44:42 -0800579 }
580
Tyler Chatowe0241452019-03-08 21:07:50 -0800581 vision_control_.set_flip_image(elevator_wrist_pos_.wrist < 0);
582
Alex Perrycb7da4b2019-08-28 19:35:56 -0700583 mutable_superstructure_goal->mutable_suction()->mutate_grab_piece(
584 grab_piece_);
Sabina Davis91b23602019-01-21 00:06:01 -0800585
Alex Perrycb7da4b2019-08-28 19:35:56 -0700586 mutable_superstructure_goal->mutable_elevator()->mutate_unsafe_goal(
587 elevator_wrist_pos_.elevator);
588 mutable_superstructure_goal->mutable_wrist()->mutate_unsafe_goal(
589 elevator_wrist_pos_.wrist);
Sabina Davis91b23602019-01-21 00:06:01 -0800590
Alex Perrycb7da4b2019-08-28 19:35:56 -0700591 if (!main_superstructure_goal_builder.Send(superstructure_goal_offset)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700592 AOS_LOG(ERROR, "Sending superstructure goal failed.\n");
Sabina Davis91b23602019-01-21 00:06:01 -0800593 }
Tyler Chatowe0241452019-03-08 21:07:50 -0800594
Austin Schuhaab7e162019-03-13 22:44:58 -0700595 if (monotonic_now >
596 last_vision_control_ + ::std::chrono::milliseconds(50)) {
Tyler Chatow4fedeea2019-03-10 15:33:36 -0700597 video_tx_->Send(vision_control_);
Austin Schuhaab7e162019-03-13 22:44:58 -0700598 last_vision_control_ = monotonic_now;
Tyler Chatow4fedeea2019-03-10 15:33:36 -0700599 }
Austin Schuh4e2629d2019-03-28 14:44:37 -0700600
601 {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700602 auto builder = camera_log_sender_.MakeBuilder();
603 builder.Send(CreateCameraLog(*builder.fbb(), data.IsPressed(kCameraLog)));
Austin Schuh4e2629d2019-03-28 14:44:37 -0700604 }
Sabina Davis91b23602019-01-21 00:06:01 -0800605 }
606
607 private:
Austin Schuhc087b102019-05-12 15:33:12 -0700608 ::aos::Sender<::y2019::control_loops::drivetrain::TargetSelectorHint>
609 target_selector_hint_sender_;
610
Alex Perrycb7da4b2019-08-28 19:35:56 -0700611 ::aos::Sender<LocalizerControl> localizer_control_sender_;
Austin Schuheb99d072019-05-12 21:03:38 -0700612
Austin Schuh5671a8c2019-05-19 17:01:04 -0700613 ::aos::Sender<::y2019::CameraLog> camera_log_sender_;
614
Alex Perrycb7da4b2019-08-28 19:35:56 -0700615 ::aos::Fetcher<superstructure::Goal> superstructure_goal_fetcher_;
Austin Schuh170f4952019-06-29 18:58:30 -0700616
Alex Perrycb7da4b2019-08-28 19:35:56 -0700617 ::aos::Sender<superstructure::Goal> superstructure_goal_sender_;
Austin Schuh170f4952019-06-29 18:58:30 -0700618
Alex Perrycb7da4b2019-08-28 19:35:56 -0700619 ::aos::Fetcher<superstructure::Position> superstructure_position_fetcher_;
620 ::aos::Fetcher<superstructure::Status> superstructure_status_fetcher_;
Austin Schuh170f4952019-06-29 18:58:30 -0700621
Austin Schuhe2f22482019-04-13 23:05:43 -0700622 // Bool to track if we've been above the deploy position. Once this bool is
623 // set, don't let the stilts retract until we see the platform.
624 bool was_above_ = false;
625
Sabina Davis91b23602019-01-21 00:06:01 -0800626 // Current goals here.
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800627 ElevatorWristPosition elevator_wrist_pos_ = kStowPos;
Sabina Davisc6329342019-03-01 20:44:42 -0800628 bool grab_piece_ = false;
Tyler Chatow7bcb52f2019-02-24 00:16:54 -0800629
630 bool switch_ball_ = false;
Tyler Chatowe0241452019-03-08 21:07:50 -0800631
James Kuszmaul13738862019-04-14 10:48:00 -0700632 bool climbed_ = false;
633
634 enum class ReleaseButtonMode {
635 kBallIntake,
636 kRelease,
637 };
638
639 ReleaseButtonMode release_mode_ = ReleaseButtonMode::kRelease;
640 aos::monotonic_clock::time_point last_release_button_press_ =
641 aos::monotonic_clock::min_time;
642
Tyler Chatowe0241452019-03-08 21:07:50 -0800643 VisionControl vision_control_;
644 ::std::unique_ptr<ProtoTXUdpSocket<VisionControl>> video_tx_;
Tyler Chatow4fedeea2019-03-10 15:33:36 -0700645 ::aos::monotonic_clock::time_point last_vision_control_ =
646 ::aos::monotonic_clock::time_point::min();
Austin Schuhaab7e162019-03-13 22:44:58 -0700647
648 // Time at which we last did not have a game piece.
649 ::aos::monotonic_clock::time_point last_not_has_piece_ =
650 ::aos::monotonic_clock::time_point::min();
Sabina Davis91b23602019-01-21 00:06:01 -0800651};
652
653} // namespace joysticks
654} // namespace input
655} // namespace y2019
656
657int main() {
Austin Schuh9fe68f72019-08-10 19:32:03 -0700658 ::aos::InitNRT(true);
659
Alex Perrycb7da4b2019-08-28 19:35:56 -0700660 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
661 aos::configuration::ReadConfig("config.json");
662
663 ::aos::ShmEventLoop event_loop(&config.message());
Sabina Davis91b23602019-01-21 00:06:01 -0800664 ::y2019::input::joysticks::Reader reader(&event_loop);
Austin Schuh9fe68f72019-08-10 19:32:03 -0700665
666 event_loop.Run();
667
Sabina Davis91b23602019-01-21 00:06:01 -0800668 ::aos::Cleanup();
669}