blob: d2c922e63bc89ef57d7470ab7e3135697d316408 [file] [log] [blame]
Philipp Schrader817cce32022-03-26 15:00:00 -07001import {
2 Component,
3 ElementRef,
4 EventEmitter,
5 Input,
6 OnInit,
7 Output,
8 ViewChild,
9} from '@angular/core';
Ravago Jones2813c032022-03-16 23:44:11 -070010import {FormsModule} from '@angular/forms';
James Kuszmauldac091f2022-03-22 09:35:06 -070011import {Builder, ByteBuffer} from 'flatbuffers';
Philipp Schradere5d13942024-03-17 15:44:35 -070012import {ErrorResponse} from '@org_frc971/scouting/webserver/requests/messages/error_response_generated';
Philipp Schrader817cce32022-03-26 15:00:00 -070013import {
Filip Kujawa0ef334c2023-02-20 19:42:45 -080014 StartMatchAction,
Emily Markovadcadcb62024-02-03 13:07:17 -080015 ScoreType,
16 StageType,
17 Submit2024Actions,
Filip Kujawa0b4b1e52023-04-15 14:05:40 -070018 MobilityAction,
Emily Markovadcadcb62024-02-03 13:07:17 -080019 PenaltyAction,
20 PickupNoteAction,
21 PlaceNoteAction,
Filip Kujawa0ef334c2023-02-20 19:42:45 -080022 RobotDeathAction,
23 EndMatchAction,
24 ActionType,
25 Action,
Philipp Schradere5d13942024-03-17 15:44:35 -070026} from '@org_frc971/scouting/webserver/requests/messages/submit_2024_actions_generated';
27import {Match} from '@org_frc971/scouting/webserver/requests/messages/request_all_matches_response_generated';
28import {MatchListRequestor} from '@org_frc971/scouting/www/rpc';
Philipp Schradere2e27ff2024-02-25 22:08:55 -080029import * as pako from 'pako';
Philipp Schrader8b8ed672022-03-05 18:08:50 -080030
Philipp Schrader817cce32022-03-26 15:00:00 -070031type Section =
32 | 'Team Selection'
Filip Kujawa0ef334c2023-02-20 19:42:45 -080033 | 'Init'
34 | 'Pickup'
35 | 'Place'
36 | 'Endgame'
37 | 'Dead'
Philipp Schrader817cce32022-03-26 15:00:00 -070038 | 'Review and Submit'
Philipp Schradere2e27ff2024-02-25 22:08:55 -080039 | 'QR Code'
Philipp Schrader817cce32022-03-26 15:00:00 -070040 | 'Success';
Philipp Schrader80587432022-03-05 15:41:22 -080041
Philipp Schrader8aeb14f2022-04-08 21:23:18 -070042// TODO(phil): Deduplicate with match_list.component.ts.
43const COMP_LEVELS = ['qm', 'ef', 'qf', 'sf', 'f'] as const;
Philipp Schraderba315da2024-03-17 16:16:50 -070044export type CompLevel = typeof COMP_LEVELS[number];
Philipp Schrader8aeb14f2022-04-08 21:23:18 -070045
46// TODO(phil): Deduplicate with match_list.component.ts.
47const COMP_LEVEL_LABELS: Record<CompLevel, string> = {
48 qm: 'Qualifications',
49 ef: 'Eighth Finals',
50 qf: 'Quarter Finals',
51 sf: 'Semi Finals',
52 f: 'Finals',
53};
54
Philipp Schradere2e27ff2024-02-25 22:08:55 -080055// The maximum number of bytes per QR code. The user can adjust this value to
56// make the QR code contain less information, but easier to scan.
57const QR_CODE_PIECE_SIZES = [150, 300, 450, 600, 750, 900];
58
59// The default index into QR_CODE_PIECE_SIZES.
60const DEFAULT_QR_CODE_PIECE_SIZE_INDEX = QR_CODE_PIECE_SIZES.indexOf(750);
61
Filip Kujawa0ef334c2023-02-20 19:42:45 -080062type ActionT =
63 | {
64 type: 'startMatchAction';
65 timestamp?: number;
66 position: number;
67 }
68 | {
Filip Kujawa0b4b1e52023-04-15 14:05:40 -070069 type: 'mobilityAction';
70 timestamp?: number;
71 mobility: boolean;
72 }
73 | {
Emily Markovadcadcb62024-02-03 13:07:17 -080074 type: 'pickupNoteAction';
Filip Kujawa4413a592023-03-01 10:54:34 -080075 timestamp?: number;
Filip Kujawa0ef334c2023-02-20 19:42:45 -080076 auto?: boolean;
77 }
78 | {
Emily Markovadcadcb62024-02-03 13:07:17 -080079 type: 'placeNoteAction';
Filip Kujawa0ef334c2023-02-20 19:42:45 -080080 timestamp?: number;
Emily Markovadcadcb62024-02-03 13:07:17 -080081 scoreType: ScoreType;
Filip Kujawa0ef334c2023-02-20 19:42:45 -080082 auto?: boolean;
83 }
84 | {
85 type: 'robotDeathAction';
86 timestamp?: number;
Emily Markova040123c2024-02-27 09:48:37 -080087 robotDead: boolean;
Filip Kujawa0ef334c2023-02-20 19:42:45 -080088 }
89 | {
Emily Markovadcadcb62024-02-03 13:07:17 -080090 type: 'penaltyAction';
91 timestamp?: number;
92 penalties: number;
93 }
94 | {
Filip Kujawa0ef334c2023-02-20 19:42:45 -080095 type: 'endMatchAction';
Emily Markovadcadcb62024-02-03 13:07:17 -080096 stageType: StageType;
97 trapNote: boolean;
Emily Markova6079e2f2024-02-17 13:17:24 -080098 spotlight: boolean;
Filip Kujawa0ef334c2023-02-20 19:42:45 -080099 timestamp?: number;
100 }
101 | {
102 // This is not a action that is submitted,
103 // It is used for undoing purposes.
104 type: 'endAutoPhase';
105 timestamp?: number;
Emily Markovadcadcb62024-02-03 13:07:17 -0800106 }
107 | {
108 // This is not a action that is submitted,
109 // It is used for undoing purposes.
110 type: 'endTeleopPhase';
111 timestamp?: number;
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800112 };
emilym38d08ba2022-10-22 15:25:01 -0700113
Philipp Schrader23993e82022-03-18 18:54:00 -0700114@Component({
115 selector: 'app-entry',
116 templateUrl: './entry.ng.html',
Philipp Schrader175a93c2023-02-19 13:13:40 -0800117 styleUrls: ['../app/common.css', './entry.component.css'],
Philipp Schrader23993e82022-03-18 18:54:00 -0700118})
Philipp Schrader75021f52023-04-09 21:14:13 -0700119export class EntryComponent implements OnInit {
Philipp Schrader36df73a2022-03-17 23:27:24 -0700120 // Re-export the type here so that we can use it in the `[value]` attribute
121 // of radio buttons.
Philipp Schrader8aeb14f2022-04-08 21:23:18 -0700122 readonly COMP_LEVELS = COMP_LEVELS;
123 readonly COMP_LEVEL_LABELS = COMP_LEVEL_LABELS;
Philipp Schradere2e27ff2024-02-25 22:08:55 -0800124 readonly QR_CODE_PIECE_SIZES = QR_CODE_PIECE_SIZES;
Emily Markovadcadcb62024-02-03 13:07:17 -0800125 readonly ScoreType = ScoreType;
Emily Markova6079e2f2024-02-17 13:17:24 -0800126 readonly StageType = StageType;
Philipp Schrader36df73a2022-03-17 23:27:24 -0700127
Ravago Jones2813c032022-03-16 23:44:11 -0700128 section: Section = 'Team Selection';
Ravago Jones2813c032022-03-16 23:44:11 -0700129 @Input() matchNumber: number = 1;
Emily Markovae68b7632023-12-30 14:17:55 -0800130 @Input() teamNumber: string = '1';
Philipp Schrader30b4a682022-04-16 14:36:17 -0700131 @Input() setNumber: number = 1;
Philipp Schrader8aeb14f2022-04-08 21:23:18 -0700132 @Input() compLevel: CompLevel = 'qm';
Philipp Schrader75021f52023-04-09 21:14:13 -0700133 @Input() skipTeamSelection = false;
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800134
Philipp Schrader63198402024-03-16 14:19:02 -0700135 @ViewChild('header') header: ElementRef;
136
Philipp Schrader8702b782023-04-15 17:33:37 -0700137 matchList: Match[] = [];
138
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800139 actionList: ActionT[] = [];
Philipp Schrader8702b782023-04-15 17:33:37 -0700140 progressMessage: string = '';
Ravago Jones2813c032022-03-16 23:44:11 -0700141 errorMessage: string = '';
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800142 autoPhase: boolean = true;
Filip Kujawab73e94c2023-04-19 09:33:14 -0700143 mobilityCompleted: boolean = false;
Philipp Schraderba315da2024-03-17 16:16:50 -0700144 // TODO(phil): Come up with a better name here.
Evelyn Yangc8036b12023-10-11 21:14:46 -0700145 selectedValue = 0;
Philipp Schraderba315da2024-03-17 16:16:50 -0700146 endGameAction: StageType = StageType.kMISSING;
147 noteIsTrapped: boolean = false;
148 endGameSpotlight: boolean = false;
149
Evelyn Yangc8036b12023-10-11 21:14:46 -0700150 nextTeamNumber = '';
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800151
Philipp Schradere1498852023-04-15 18:06:45 -0700152 preScouting: boolean = false;
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800153 matchStartTimestamp: number = 0;
Emily Markovadcadcb62024-02-03 13:07:17 -0800154 penalties: number = 0;
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800155
Philipp Schrader8702b782023-04-15 17:33:37 -0700156 teamSelectionIsValid = false;
157
Philipp Schradere2e27ff2024-02-25 22:08:55 -0800158 // When the user chooses to generate QR codes, we convert the flatbuffer into
159 // a long string. Since we frequently have more data than we can display in a
160 // single QR code, we break the data into multiple QR codes. The data for
161 // each QR code ("pieces") is stored in the `qrCodeValuePieces` list below.
162 // The `qrCodeValueIndex` keeps track of which QR code we're currently
163 // displaying.
164 qrCodeValuePieceSize = QR_CODE_PIECE_SIZES[DEFAULT_QR_CODE_PIECE_SIZE_INDEX];
165 qrCodeValuePieces: string[] = [];
166 qrCodeValueIndex: number = 0;
167
Philipp Schrader8702b782023-04-15 17:33:37 -0700168 constructor(private readonly matchListRequestor: MatchListRequestor) {}
169
Philipp Schrader75021f52023-04-09 21:14:13 -0700170 ngOnInit() {
171 // When the user navigated from the match list, we can skip the team
172 // selection. I.e. we trust that the user clicked the correct button.
173 this.section = this.skipTeamSelection ? 'Init' : 'Team Selection';
Evelyn Yangc8036b12023-10-11 21:14:46 -0700174 this.fetchMatchList();
175 }
Philipp Schrader8702b782023-04-15 17:33:37 -0700176
Evelyn Yangc8036b12023-10-11 21:14:46 -0700177 goToNextTeam() {
178 this.ngOnInit();
179 this.teamNumber = this.nextTeamNumber;
180 this.nextTeamNumber = '';
Philipp Schrader8702b782023-04-15 17:33:37 -0700181 }
182
183 async fetchMatchList() {
184 this.progressMessage = 'Fetching match list. Please be patient.';
185 this.errorMessage = '';
186
187 try {
188 this.matchList = await this.matchListRequestor.fetchMatchList();
189 this.progressMessage = 'Successfully fetched match list.';
190 } catch (e) {
191 this.errorMessage = e;
192 this.progressMessage = '';
193 }
194 }
195
196 // This gets called when the user changes something on the Init screen.
197 // It makes sure that the user can't click "Next" until the information is
Philipp Schradere1498852023-04-15 18:06:45 -0700198 // valid, or this is for pre-scouting.
Philipp Schrader8702b782023-04-15 17:33:37 -0700199 updateTeamSelectionValidity(): void {
Philipp Schradere1498852023-04-15 18:06:45 -0700200 this.teamSelectionIsValid = this.preScouting || this.matchIsInMatchList();
Philipp Schrader8702b782023-04-15 17:33:37 -0700201 }
202
203 matchIsInMatchList(): boolean {
204 // If the user deletes the content of the teamNumber field, the value here
205 // is undefined. Guard against that.
206 if (this.teamNumber == null) {
207 return false;
208 }
Philipp Schrader8702b782023-04-15 17:33:37 -0700209
210 for (const match of this.matchList) {
211 if (
212 this.matchNumber == match.matchNumber() &&
213 this.setNumber == match.setNumber() &&
214 this.compLevel == match.compLevel() &&
Evelyn Yangc8036b12023-10-11 21:14:46 -0700215 (this.teamNumber === match.r1() ||
216 this.teamNumber === match.r2() ||
217 this.teamNumber === match.r3() ||
218 this.teamNumber === match.b1() ||
219 this.teamNumber === match.b2() ||
220 this.teamNumber === match.b3())
Philipp Schrader8702b782023-04-15 17:33:37 -0700221 ) {
222 return true;
223 }
224 }
225 return false;
Philipp Schrader75021f52023-04-09 21:14:13 -0700226 }
227
Emily Markovadcadcb62024-02-03 13:07:17 -0800228 addPenalty(): void {
229 this.penalties += 1;
230 }
231
232 removePenalty(): void {
233 if (this.penalties > 0) {
234 this.penalties -= 1;
235 }
236 }
237
238 addPenalties(): void {
239 this.addAction({type: 'penaltyAction', penalties: this.penalties});
240 }
241
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800242 addAction(action: ActionT): void {
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800243 if (action.type == 'startMatchAction') {
244 // Unix nanosecond timestamp.
245 this.matchStartTimestamp = Date.now() * 1e6;
246 action.timestamp = 0;
247 } else {
248 // Unix nanosecond timestamp relative to match start.
249 action.timestamp = Date.now() * 1e6 - this.matchStartTimestamp;
250 }
251
Emily Markovadcadcb62024-02-03 13:07:17 -0800252 if (action.type == 'endMatchAction') {
Philipp Schradere2e27ff2024-02-25 22:08:55 -0800253 // endMatchAction occurs at the same time as penaltyAction so add to its
254 // timestamp to make it unique.
Emily Markovadcadcb62024-02-03 13:07:17 -0800255 action.timestamp += 1;
256 }
257
Filip Kujawab73e94c2023-04-19 09:33:14 -0700258 if (action.type == 'mobilityAction') {
259 this.mobilityCompleted = true;
260 }
261
Emily Markovadcadcb62024-02-03 13:07:17 -0800262 if (action.type == 'pickupNoteAction' || action.type == 'placeNoteAction') {
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800263 action.auto = this.autoPhase;
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800264 }
265 this.actionList.push(action);
266 }
267
268 undoLastAction() {
269 if (this.actionList.length > 0) {
270 let lastAction = this.actionList.pop();
271 switch (lastAction?.type) {
272 case 'endAutoPhase':
273 this.autoPhase = true;
Emily Markovadcadcb62024-02-03 13:07:17 -0800274 this.section = 'Pickup';
275 case 'pickupNoteAction':
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800276 this.section = 'Pickup';
277 break;
Emily Markovadcadcb62024-02-03 13:07:17 -0800278 case 'endTeleopPhase':
279 this.section = 'Pickup';
280 break;
281 case 'placeNoteAction':
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800282 this.section = 'Place';
283 break;
284 case 'endMatchAction':
Emily Markovadcadcb62024-02-03 13:07:17 -0800285 this.section = 'Endgame';
286 case 'mobilityAction':
287 this.mobilityCompleted = false;
288 break;
289 case 'startMatchAction':
290 this.section = 'Init';
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800291 break;
Filip Kujawa9f56d0e2023-03-03 19:44:43 -0800292 case 'robotDeathAction':
293 // TODO(FILIP): Return user to the screen they
294 // clicked dead robot on. Pickup is fine for now but
295 // might cause confusion.
296 this.section = 'Pickup';
297 break;
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800298 default:
299 break;
300 }
301 }
302 }
303
Emily Markovadcadcb62024-02-03 13:07:17 -0800304 stringifyScoreType(scoreType: ScoreType): String {
305 return ScoreType[scoreType];
Emily Markovaf4b06a22023-05-10 17:44:09 -0700306 }
307
Emily Markovadcadcb62024-02-03 13:07:17 -0800308 stringifyStageType(stageType: StageType): String {
309 return StageType[stageType];
Emily Markovaf4b06a22023-05-10 17:44:09 -0700310 }
311
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800312 changeSectionTo(target: Section) {
Philipp Schrader8702b782023-04-15 17:33:37 -0700313 // Clear the messages since they won't be relevant in the next section.
314 this.errorMessage = '';
315 this.progressMessage = '';
316
Philipp Schradere2e27ff2024-02-25 22:08:55 -0800317 // For the QR code screen, we need to make the value to encode available.
318 if (target == 'QR Code') {
319 this.updateQrCodeValuePieceSize();
320 }
321
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800322 this.section = target;
323 }
Philipp Schrader80587432022-03-05 15:41:22 -0800324
Ravago Jones2813c032022-03-16 23:44:11 -0700325 private scrollToTop() {
326 this.header.nativeElement.scrollIntoView();
327 }
328
Philipp Schradere2e27ff2024-02-25 22:08:55 -0800329 createActionsBuffer() {
James Kuszmauldac091f2022-03-22 09:35:06 -0700330 const builder = new Builder();
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800331 const actionOffsets: number[] = [];
332
333 for (const action of this.actionList) {
334 let actionOffset: number | undefined;
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800335
336 switch (action.type) {
337 case 'startMatchAction':
338 const startMatchActionOffset =
339 StartMatchAction.createStartMatchAction(builder, action.position);
340 actionOffset = Action.createAction(
341 builder,
Philipp Schrader8c878a22023-03-20 22:36:38 -0700342 BigInt(action.timestamp || 0),
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800343 ActionType.StartMatchAction,
344 startMatchActionOffset
345 );
346 break;
Filip Kujawa0b4b1e52023-04-15 14:05:40 -0700347 case 'mobilityAction':
348 const mobilityActionOffset = MobilityAction.createMobilityAction(
349 builder,
350 action.mobility
351 );
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800352 actionOffset = Action.createAction(
353 builder,
Philipp Schrader8c878a22023-03-20 22:36:38 -0700354 BigInt(action.timestamp || 0),
Filip Kujawa0b4b1e52023-04-15 14:05:40 -0700355 ActionType.MobilityAction,
356 mobilityActionOffset
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800357 );
358 break;
Emily Markovadcadcb62024-02-03 13:07:17 -0800359 case 'penaltyAction':
360 const penaltyActionOffset = PenaltyAction.createPenaltyAction(
361 builder,
362 action.penalties
363 );
Filip Kujawa4c286442023-03-03 10:41:22 -0800364 actionOffset = Action.createAction(
365 builder,
Philipp Schrader8c878a22023-03-20 22:36:38 -0700366 BigInt(action.timestamp || 0),
Emily Markovadcadcb62024-02-03 13:07:17 -0800367 ActionType.PenaltyAction,
368 penaltyActionOffset
Filip Kujawa4c286442023-03-03 10:41:22 -0800369 );
370 break;
Emily Markovadcadcb62024-02-03 13:07:17 -0800371 case 'pickupNoteAction':
372 const pickupNoteActionOffset =
373 PickupNoteAction.createPickupNoteAction(
Filip Kujawa0b4b1e52023-04-15 14:05:40 -0700374 builder,
Filip Kujawa0b4b1e52023-04-15 14:05:40 -0700375 action.auto || false
376 );
377 actionOffset = Action.createAction(
378 builder,
379 BigInt(action.timestamp || 0),
Emily Markovadcadcb62024-02-03 13:07:17 -0800380 ActionType.PickupNoteAction,
381 pickupNoteActionOffset
Filip Kujawa0b4b1e52023-04-15 14:05:40 -0700382 );
383 break;
Emily Markovadcadcb62024-02-03 13:07:17 -0800384 case 'placeNoteAction':
385 const placeNoteActionOffset = PlaceNoteAction.createPlaceNoteAction(
386 builder,
387 action.scoreType,
388 action.auto || false
389 );
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800390 actionOffset = Action.createAction(
391 builder,
Philipp Schrader8c878a22023-03-20 22:36:38 -0700392 BigInt(action.timestamp || 0),
Emily Markovadcadcb62024-02-03 13:07:17 -0800393 ActionType.PlaceNoteAction,
394 placeNoteActionOffset
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800395 );
396 break;
397
398 case 'robotDeathAction':
399 const robotDeathActionOffset =
Emily Markova040123c2024-02-27 09:48:37 -0800400 RobotDeathAction.createRobotDeathAction(builder, action.robotDead);
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800401 actionOffset = Action.createAction(
402 builder,
Philipp Schrader8c878a22023-03-20 22:36:38 -0700403 BigInt(action.timestamp || 0),
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800404 ActionType.RobotDeathAction,
405 robotDeathActionOffset
406 );
407 break;
408
409 case 'endMatchAction':
410 const endMatchActionOffset = EndMatchAction.createEndMatchAction(
411 builder,
Emily Markovadcadcb62024-02-03 13:07:17 -0800412 action.stageType,
Emily Markova6079e2f2024-02-17 13:17:24 -0800413 action.trapNote,
414 action.spotlight
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800415 );
416 actionOffset = Action.createAction(
417 builder,
Philipp Schrader8c878a22023-03-20 22:36:38 -0700418 BigInt(action.timestamp || 0),
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800419 ActionType.EndMatchAction,
420 endMatchActionOffset
421 );
422 break;
423
424 case 'endAutoPhase':
425 // Not important action.
426 break;
427
Emily Markovadcadcb62024-02-03 13:07:17 -0800428 case 'endTeleopPhase':
429 // Not important action.
430 break;
431
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800432 default:
433 throw new Error(`Unknown action type`);
434 }
435
436 if (actionOffset !== undefined) {
437 actionOffsets.push(actionOffset);
438 }
439 }
Emily Markovae68b7632023-12-30 14:17:55 -0800440 const teamNumberFb = builder.createString(this.teamNumber);
Philipp Schradere859e6e2023-03-22 19:59:51 -0700441 const compLevelFb = builder.createString(this.compLevel);
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800442
Emily Markovadcadcb62024-02-03 13:07:17 -0800443 const actionsVector = Submit2024Actions.createActionsListVector(
Philipp Schrader817cce32022-03-26 15:00:00 -0700444 builder,
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800445 actionOffsets
Philipp Schrader817cce32022-03-26 15:00:00 -0700446 );
Emily Markovadcadcb62024-02-03 13:07:17 -0800447 Submit2024Actions.startSubmit2024Actions(builder);
448 Submit2024Actions.addTeamNumber(builder, teamNumberFb);
449 Submit2024Actions.addMatchNumber(builder, this.matchNumber);
450 Submit2024Actions.addSetNumber(builder, this.setNumber);
451 Submit2024Actions.addCompLevel(builder, compLevelFb);
452 Submit2024Actions.addActionsList(builder, actionsVector);
453 Submit2024Actions.addPreScouting(builder, this.preScouting);
454 builder.finish(Submit2024Actions.endSubmit2024Actions(builder));
Ravago Jones2813c032022-03-16 23:44:11 -0700455
Philipp Schradere2e27ff2024-02-25 22:08:55 -0800456 return builder.asUint8Array();
457 }
458
459 // Same as createActionsBuffer, but encoded as Base64. It's also split into
460 // a number of pieces so that each piece is roughly limited to
461 // `qrCodeValuePieceSize` bytes.
462 createBase64ActionsBuffers(): string[] {
463 const originalBuffer = this.createActionsBuffer();
464 const deflatedData = pako.deflate(originalBuffer, {level: 9});
465
466 const pieceSize = this.qrCodeValuePieceSize;
467 const fullValue = btoa(String.fromCharCode(...deflatedData));
468 const numPieces = Math.ceil(fullValue.length / pieceSize);
469
470 let splitData: string[] = [];
471 for (let i = 0; i < numPieces; i++) {
472 const splitPiece = fullValue.slice(i * pieceSize, (i + 1) * pieceSize);
473 splitData.push(`${i}_${numPieces}_${pieceSize}_${splitPiece}`);
474 }
475 return splitData;
476 }
477
478 setQrCodeValueIndex(index: number) {
479 this.qrCodeValueIndex = Math.max(
480 0,
481 Math.min(index, this.qrCodeValuePieces.length - 1)
482 );
483 }
484
485 updateQrCodeValuePieceSize() {
486 this.qrCodeValuePieces = this.createBase64ActionsBuffers();
487 this.qrCodeValueIndex = 0;
488 }
489
490 async submit2024Actions() {
Emily Markovadcadcb62024-02-03 13:07:17 -0800491 const res = await fetch('/requests/submit/submit_2024_actions', {
Philipp Schrader817cce32022-03-26 15:00:00 -0700492 method: 'POST',
Philipp Schradere2e27ff2024-02-25 22:08:55 -0800493 body: this.createActionsBuffer(),
Philipp Schrader817cce32022-03-26 15:00:00 -0700494 });
Ravago Jones2813c032022-03-16 23:44:11 -0700495
496 if (res.ok) {
497 // We successfully submitted the data. Report success.
498 this.section = 'Success';
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800499 this.actionList = [];
Evelyn Yangc8036b12023-10-11 21:14:46 -0700500
Philipp Schrader63198402024-03-16 14:19:02 -0700501 // Keep track of the position of the last robot, use to figure out what
502 // the next robot in the same position is.
Evelyn Yangc8036b12023-10-11 21:14:46 -0700503 let lastTeamPos = '0';
504 for (const match of this.matchList) {
505 if (
506 this.matchNumber === match.matchNumber() &&
507 this.setNumber === match.setNumber() &&
508 this.compLevel === match.compLevel()
509 ) {
510 this.teamNumber = this.teamNumber;
511 if (this.teamNumber == match.r1()) {
512 lastTeamPos = 'r1';
513 } else if (this.teamNumber == match.r2()) {
514 lastTeamPos = 'r2';
515 } else if (this.teamNumber == match.r3()) {
516 lastTeamPos = 'r3';
517 } else if (this.teamNumber == match.b1()) {
518 lastTeamPos = 'b1';
519 } else if (this.teamNumber == match.b2()) {
520 lastTeamPos = 'b2';
521 } else if (this.teamNumber == match.b3()) {
522 lastTeamPos = 'b3';
523 } else {
524 console.log('Position of scouted team not found.');
525 }
526 break;
527 }
528 }
529 if (lastTeamPos != '0') {
530 this.matchNumber += 1;
531 for (const match of this.matchList) {
532 if (
533 this.matchNumber == match.matchNumber() &&
534 this.setNumber == match.setNumber() &&
535 this.compLevel == match.compLevel()
536 ) {
537 if (lastTeamPos == 'r1') {
538 this.nextTeamNumber = match.r1();
539 } else if (lastTeamPos == 'r2') {
540 this.nextTeamNumber = match.r2();
541 } else if (lastTeamPos == 'r3') {
542 this.nextTeamNumber = match.r3();
543 } else if (lastTeamPos == 'b1') {
544 this.nextTeamNumber = match.b1();
545 } else if (lastTeamPos == 'b2') {
546 this.nextTeamNumber = match.b2();
547 } else if (lastTeamPos == 'b3') {
548 this.nextTeamNumber = match.b3();
549 } else {
550 console.log('Position of last team not found.');
551 }
552 break;
553 }
554 }
555 } else {
556 console.log('Last team position not found.');
557 }
558 this.matchList = [];
559 this.progressMessage = '';
560 this.errorMessage = '';
561 this.autoPhase = true;
562 this.actionList = [];
563 this.mobilityCompleted = false;
564 this.preScouting = false;
565 this.matchStartTimestamp = 0;
566 this.selectedValue = 0;
Ravago Jones2813c032022-03-16 23:44:11 -0700567 } else {
568 const resBuffer = await res.arrayBuffer();
569 const fbBuffer = new ByteBuffer(new Uint8Array(resBuffer));
James Kuszmauldac091f2022-03-22 09:35:06 -0700570 const parsedResponse = ErrorResponse.getRootAsErrorResponse(fbBuffer);
Ravago Jones2813c032022-03-16 23:44:11 -0700571
572 const errorMessage = parsedResponse.errorMessage();
Philipp Schrader817cce32022-03-26 15:00:00 -0700573 this.errorMessage = `Received ${res.status} ${res.statusText}: "${errorMessage}"`;
Alex Perrybb3d2062022-03-05 18:14:33 -0800574 }
Ravago Jones2813c032022-03-16 23:44:11 -0700575 }
Philipp Schrader80587432022-03-05 15:41:22 -0800576}