blob: 3c8aba968ec5b3fc1571e925ce1cfd69f52ff53c [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,
Philipp Schrader3d7dedc2024-03-16 16:27:25 -070015 StartMatchActionT,
Emily Markovaf17f2812024-04-03 20:55:12 -070016 NoShowActionT,
17 NoShowAction,
Emily Markovadcadcb62024-02-03 13:07:17 -080018 ScoreType,
19 StageType,
20 Submit2024Actions,
Filip Kujawa0b4b1e52023-04-15 14:05:40 -070021 MobilityAction,
Philipp Schrader3d7dedc2024-03-16 16:27:25 -070022 MobilityActionT,
Emily Markovadcadcb62024-02-03 13:07:17 -080023 PenaltyAction,
Philipp Schrader3d7dedc2024-03-16 16:27:25 -070024 PenaltyActionT,
Emily Markovadcadcb62024-02-03 13:07:17 -080025 PickupNoteAction,
Philipp Schrader3d7dedc2024-03-16 16:27:25 -070026 PickupNoteActionT,
Emily Markovadcadcb62024-02-03 13:07:17 -080027 PlaceNoteAction,
Philipp Schrader3d7dedc2024-03-16 16:27:25 -070028 PlaceNoteActionT,
Filip Kujawa0ef334c2023-02-20 19:42:45 -080029 RobotDeathAction,
Philipp Schrader3d7dedc2024-03-16 16:27:25 -070030 RobotDeathActionT,
Filip Kujawa0ef334c2023-02-20 19:42:45 -080031 EndMatchAction,
Philipp Schrader3d7dedc2024-03-16 16:27:25 -070032 EndMatchActionT,
Filip Kujawa0ef334c2023-02-20 19:42:45 -080033 ActionType,
34 Action,
Philipp Schrader3d7dedc2024-03-16 16:27:25 -070035 ActionT,
Philipp Schradere5d13942024-03-17 15:44:35 -070036} from '@org_frc971/scouting/webserver/requests/messages/submit_2024_actions_generated';
37import {Match} from '@org_frc971/scouting/webserver/requests/messages/request_all_matches_response_generated';
Philipp Schraderad2a6fb2024-03-20 20:51:36 -070038import {
39 MatchListRequestor,
40 ActionsSubmitter,
41} from '@org_frc971/scouting/www/rpc';
Emily Markova521725a2024-03-21 18:46:04 -070042import {RequestCurrentScouting} from '@org_frc971/scouting/webserver/requests/messages/request_current_scouting_generated';
43import {RequestCurrentScoutingResponse} from '@org_frc971/scouting/webserver/requests/messages/request_current_scouting_response_generated';
Philipp Schrader3d7dedc2024-03-16 16:27:25 -070044import {ActionHelper, ConcreteAction} from './action_helper';
Philipp Schradere2e27ff2024-02-25 22:08:55 -080045import * as pako from 'pako';
Philipp Schrader8b8ed672022-03-05 18:08:50 -080046
Philipp Schrader817cce32022-03-26 15:00:00 -070047type Section =
48 | 'Team Selection'
Filip Kujawa0ef334c2023-02-20 19:42:45 -080049 | 'Init'
50 | 'Pickup'
51 | 'Place'
52 | 'Endgame'
53 | 'Dead'
Philipp Schrader817cce32022-03-26 15:00:00 -070054 | 'Review and Submit'
Philipp Schradere2e27ff2024-02-25 22:08:55 -080055 | 'QR Code'
Philipp Schrader817cce32022-03-26 15:00:00 -070056 | 'Success';
Philipp Schrader80587432022-03-05 15:41:22 -080057
Emily Markova9c18e9c2024-04-03 20:06:27 -070058type CompType = 'PreScouting' | 'Practice' | 'Regular';
59
Philipp Schrader8aeb14f2022-04-08 21:23:18 -070060// TODO(phil): Deduplicate with match_list.component.ts.
61const COMP_LEVELS = ['qm', 'ef', 'qf', 'sf', 'f'] as const;
Philipp Schraderba315da2024-03-17 16:16:50 -070062export type CompLevel = typeof COMP_LEVELS[number];
Philipp Schrader8aeb14f2022-04-08 21:23:18 -070063
64// TODO(phil): Deduplicate with match_list.component.ts.
65const COMP_LEVEL_LABELS: Record<CompLevel, string> = {
66 qm: 'Qualifications',
67 ef: 'Eighth Finals',
68 qf: 'Quarter Finals',
69 sf: 'Semi Finals',
70 f: 'Finals',
71};
72
Philipp Schradere2e27ff2024-02-25 22:08:55 -080073// The maximum number of bytes per QR code. The user can adjust this value to
74// make the QR code contain less information, but easier to scan.
75const QR_CODE_PIECE_SIZES = [150, 300, 450, 600, 750, 900];
76
77// The default index into QR_CODE_PIECE_SIZES.
Philipp Schrader329b55a2024-04-18 09:42:59 -070078const DEFAULT_QR_CODE_PIECE_SIZE_INDEX = QR_CODE_PIECE_SIZES.indexOf(450);
Philipp Schradere2e27ff2024-02-25 22:08:55 -080079
Philipp Schrader3d7dedc2024-03-16 16:27:25 -070080// The actions that are purely used for tracking state. They don't actually
81// have any permanent meaning and will not be saved in the database.
82const STATE_ACTIONS: ActionType[] = [
83 ActionType.EndAutoPhaseAction,
84 ActionType.EndTeleopPhaseAction,
85];
emilym38d08ba2022-10-22 15:25:01 -070086
Philipp Schrader23993e82022-03-18 18:54:00 -070087@Component({
88 selector: 'app-entry',
89 templateUrl: './entry.ng.html',
Philipp Schrader175a93c2023-02-19 13:13:40 -080090 styleUrls: ['../app/common.css', './entry.component.css'],
Philipp Schrader23993e82022-03-18 18:54:00 -070091})
Philipp Schrader75021f52023-04-09 21:14:13 -070092export class EntryComponent implements OnInit {
Philipp Schrader36df73a2022-03-17 23:27:24 -070093 // Re-export the type here so that we can use it in the `[value]` attribute
94 // of radio buttons.
Philipp Schrader8aeb14f2022-04-08 21:23:18 -070095 readonly COMP_LEVELS = COMP_LEVELS;
96 readonly COMP_LEVEL_LABELS = COMP_LEVEL_LABELS;
Philipp Schradere2e27ff2024-02-25 22:08:55 -080097 readonly QR_CODE_PIECE_SIZES = QR_CODE_PIECE_SIZES;
Emily Markovadcadcb62024-02-03 13:07:17 -080098 readonly ScoreType = ScoreType;
Emily Markova6079e2f2024-02-17 13:17:24 -080099 readonly StageType = StageType;
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700100 readonly ActionT = ActionT;
101 readonly ActionType = ActionType;
Emily Markovaf17f2812024-04-03 20:55:12 -0700102 readonly NoShowActionT = NoShowActionT;
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700103 readonly StartMatchActionT = StartMatchActionT;
104 readonly MobilityActionT = MobilityActionT;
105 readonly PickupNoteActionT = PickupNoteActionT;
106 readonly PlaceNoteActionT = PlaceNoteActionT;
107 readonly RobotDeathActionT = RobotDeathActionT;
108 readonly PenaltyActionT = PenaltyActionT;
109 readonly EndMatchActionT = EndMatchActionT;
Philipp Schrader36df73a2022-03-17 23:27:24 -0700110
Ravago Jones2813c032022-03-16 23:44:11 -0700111 section: Section = 'Team Selection';
Ravago Jones2813c032022-03-16 23:44:11 -0700112 @Input() matchNumber: number = 1;
Emily Markovae68b7632023-12-30 14:17:55 -0800113 @Input() teamNumber: string = '1';
Philipp Schrader30b4a682022-04-16 14:36:17 -0700114 @Input() setNumber: number = 1;
Philipp Schrader8aeb14f2022-04-08 21:23:18 -0700115 @Input() compLevel: CompLevel = 'qm';
Emily Markova9c18e9c2024-04-03 20:06:27 -0700116 @Input() compType: CompType = 'Regular';
Philipp Schrader75021f52023-04-09 21:14:13 -0700117 @Input() skipTeamSelection = false;
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800118
Philipp Schrader63198402024-03-16 14:19:02 -0700119 @ViewChild('header') header: ElementRef;
120
Philipp Schrader8702b782023-04-15 17:33:37 -0700121 matchList: Match[] = [];
122
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700123 actionHelper: ActionHelper;
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800124 actionList: ActionT[] = [];
Philipp Schrader8702b782023-04-15 17:33:37 -0700125 progressMessage: string = '';
Ravago Jones2813c032022-03-16 23:44:11 -0700126 errorMessage: string = '';
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800127 autoPhase: boolean = true;
Filip Kujawab73e94c2023-04-19 09:33:14 -0700128 mobilityCompleted: boolean = false;
Philipp Schraderba315da2024-03-17 16:16:50 -0700129 // TODO(phil): Come up with a better name here.
Evelyn Yangc8036b12023-10-11 21:14:46 -0700130 selectedValue = 0;
Philipp Schraderba315da2024-03-17 16:16:50 -0700131 endGameAction: StageType = StageType.kMISSING;
132 noteIsTrapped: boolean = false;
133 endGameSpotlight: boolean = false;
134
Evelyn Yangc8036b12023-10-11 21:14:46 -0700135 nextTeamNumber = '';
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800136
137 matchStartTimestamp: number = 0;
Emily Markovadcadcb62024-02-03 13:07:17 -0800138 penalties: number = 0;
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800139
Philipp Schrader8702b782023-04-15 17:33:37 -0700140 teamSelectionIsValid = false;
Emily Markova521725a2024-03-21 18:46:04 -0700141 searchForDuplicateScouting: boolean = false;
142 duplicateSearchTimer: ReturnType<typeof setTimeout> | null = null;
Philipp Schrader8702b782023-04-15 17:33:37 -0700143
Philipp Schradere2e27ff2024-02-25 22:08:55 -0800144 // When the user chooses to generate QR codes, we convert the flatbuffer into
145 // a long string. Since we frequently have more data than we can display in a
146 // single QR code, we break the data into multiple QR codes. The data for
147 // each QR code ("pieces") is stored in the `qrCodeValuePieces` list below.
148 // The `qrCodeValueIndex` keeps track of which QR code we're currently
149 // displaying.
150 qrCodeValuePieceSize = QR_CODE_PIECE_SIZES[DEFAULT_QR_CODE_PIECE_SIZE_INDEX];
151 qrCodeValuePieces: string[] = [];
152 qrCodeValueIndex: number = 0;
153
Philipp Schraderad2a6fb2024-03-20 20:51:36 -0700154 constructor(
155 private readonly matchListRequestor: MatchListRequestor,
156 private readonly actionsSubmitter: ActionsSubmitter
157 ) {}
Philipp Schrader8702b782023-04-15 17:33:37 -0700158
Philipp Schrader75021f52023-04-09 21:14:13 -0700159 ngOnInit() {
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700160 this.actionHelper = new ActionHelper(
161 (actionType: ActionType, action: ConcreteAction) => {
162 this.addAction(actionType, action);
163 }
164 );
165
Philipp Schrader75021f52023-04-09 21:14:13 -0700166 // When the user navigated from the match list, we can skip the team
167 // selection. I.e. we trust that the user clicked the correct button.
168 this.section = this.skipTeamSelection ? 'Init' : 'Team Selection';
Evelyn Yangc8036b12023-10-11 21:14:46 -0700169 this.fetchMatchList();
170 }
Philipp Schrader8702b782023-04-15 17:33:37 -0700171
Emily Markova521725a2024-03-21 18:46:04 -0700172 ngOnDestroy() {
173 clearInterval(this.duplicateSearchTimer);
174 }
175
Evelyn Yangc8036b12023-10-11 21:14:46 -0700176 goToNextTeam() {
177 this.ngOnInit();
Emily Markova521725a2024-03-21 18:46:04 -0700178 this.ngOnDestroy();
Evelyn Yangc8036b12023-10-11 21:14:46 -0700179 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
Emily Markova521725a2024-03-21 18:46:04 -0700196 async findOthersScoutingThisRobot() {
197 const builder = new Builder();
198 const teamNumber = builder.createString(this.teamNumber);
199
200 builder.finish(
201 RequestCurrentScouting.createRequestCurrentScouting(builder, teamNumber)
202 );
203
204 const buffer = builder.asUint8Array();
205 const res = await fetch('/requests/request/current_scouting', {
206 method: 'POST',
207 body: buffer,
208 });
209
210 if (!res.ok) {
211 const resBuffer = await res.arrayBuffer();
212 const fbBuffer = new ByteBuffer(new Uint8Array(resBuffer));
213 const parsedResponse = ErrorResponse.getRootAsErrorResponse(fbBuffer);
214 const errorMessage = parsedResponse.errorMessage();
215 this.errorMessage = `Received ${res.status} ${res.statusText}: "${errorMessage}"`;
216 } else {
217 const resBuffer = await res.arrayBuffer();
218 const fbBuffer = new ByteBuffer(new Uint8Array(resBuffer));
219 const parsedResponse =
220 RequestCurrentScoutingResponse.getRootAsRequestCurrentScoutingResponse(
221 fbBuffer
222 );
223 const collectedBy = [];
224 for (let i = 0; i < parsedResponse.collectedByLength(); i++) {
225 collectedBy.push(parsedResponse.collectedBy(i).name());
226 }
227 this.duplicateSearchTimer = setInterval(() => {
228 if (this.searchForDuplicateScouting && collectedBy.length != 0) {
229 if (
230 confirm(
231 'This team is currently being scouted by ' +
232 collectedBy +
233 '. Would you like to receive more alerts?'
234 ) != true
235 ) {
236 this.searchForDuplicateScouting = false;
237 }
238 }
239 }, 10000);
240 }
241 }
242
Philipp Schrader8702b782023-04-15 17:33:37 -0700243 // This gets called when the user changes something on the Init screen.
244 // It makes sure that the user can't click "Next" until the information is
Emily Markova9c18e9c2024-04-03 20:06:27 -0700245 // valid, or this is for pre-scouting or practice matches.
Philipp Schrader8702b782023-04-15 17:33:37 -0700246 updateTeamSelectionValidity(): void {
Emily Markova9c18e9c2024-04-03 20:06:27 -0700247 this.teamSelectionIsValid =
248 this.compType != 'Regular' || this.matchIsInMatchList();
Philipp Schrader8702b782023-04-15 17:33:37 -0700249 }
250
251 matchIsInMatchList(): boolean {
252 // If the user deletes the content of the teamNumber field, the value here
253 // is undefined. Guard against that.
254 if (this.teamNumber == null) {
255 return false;
256 }
Philipp Schrader8702b782023-04-15 17:33:37 -0700257
258 for (const match of this.matchList) {
259 if (
260 this.matchNumber == match.matchNumber() &&
261 this.setNumber == match.setNumber() &&
262 this.compLevel == match.compLevel() &&
Evelyn Yangc8036b12023-10-11 21:14:46 -0700263 (this.teamNumber === match.r1() ||
264 this.teamNumber === match.r2() ||
265 this.teamNumber === match.r3() ||
266 this.teamNumber === match.b1() ||
267 this.teamNumber === match.b2() ||
268 this.teamNumber === match.b3())
Philipp Schrader8702b782023-04-15 17:33:37 -0700269 ) {
270 return true;
271 }
272 }
273 return false;
Philipp Schrader75021f52023-04-09 21:14:13 -0700274 }
275
Emily Markovadcadcb62024-02-03 13:07:17 -0800276 addPenalty(): void {
277 this.penalties += 1;
278 }
279
280 removePenalty(): void {
281 if (this.penalties > 0) {
282 this.penalties -= 1;
283 }
284 }
285
286 addPenalties(): void {
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700287 this.actionHelper.addPenaltyAction({penalties: this.penalties});
Emily Markovadcadcb62024-02-03 13:07:17 -0800288 }
289
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700290 addAction(actionType: ActionType, action: ConcreteAction): void {
291 let timestamp: number = 0;
292
293 if (actionType == ActionType.StartMatchAction) {
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800294 // Unix nanosecond timestamp.
295 this.matchStartTimestamp = Date.now() * 1e6;
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800296 } else {
297 // Unix nanosecond timestamp relative to match start.
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700298 timestamp = Date.now() * 1e6 - this.matchStartTimestamp;
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800299 }
300
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700301 if (actionType == ActionType.EndMatchAction) {
Philipp Schradere2e27ff2024-02-25 22:08:55 -0800302 // endMatchAction occurs at the same time as penaltyAction so add to its
303 // timestamp to make it unique.
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700304 timestamp += 1;
Emily Markovadcadcb62024-02-03 13:07:17 -0800305 }
306
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700307 if (actionType == ActionType.MobilityAction) {
Filip Kujawab73e94c2023-04-19 09:33:14 -0700308 this.mobilityCompleted = true;
309 }
310
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700311 this.actionList.push(new ActionT(BigInt(timestamp), actionType, action));
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800312 }
313
314 undoLastAction() {
315 if (this.actionList.length > 0) {
316 let lastAction = this.actionList.pop();
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700317 switch (lastAction?.actionTakenType) {
318 case ActionType.EndAutoPhaseAction:
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800319 this.autoPhase = true;
Emily Markovadcadcb62024-02-03 13:07:17 -0800320 this.section = 'Pickup';
Philipp Schrader652a4c92024-04-18 10:53:39 -0700321 break;
Emily Markovaf17f2812024-04-03 20:55:12 -0700322 case ActionType.NoShowAction:
323 this.autoPhase = true;
324 this.section = 'Init';
325 break;
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700326 case ActionType.PickupNoteAction:
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800327 this.section = 'Pickup';
328 break;
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700329 case ActionType.EndTeleopPhaseAction:
Emily Markovadcadcb62024-02-03 13:07:17 -0800330 this.section = 'Pickup';
331 break;
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700332 case ActionType.PlaceNoteAction:
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800333 this.section = 'Place';
334 break;
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700335 case ActionType.EndMatchAction:
Emily Markovadcadcb62024-02-03 13:07:17 -0800336 this.section = 'Endgame';
Emily Markovaf2743e82024-09-21 14:14:04 -0700337 // Also delete the penalty action.
338 this.undoLastAction();
339 break;
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700340 case ActionType.MobilityAction:
Emily Markovadcadcb62024-02-03 13:07:17 -0800341 this.mobilityCompleted = false;
342 break;
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700343 case ActionType.StartMatchAction:
Emily Markovadcadcb62024-02-03 13:07:17 -0800344 this.section = 'Init';
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800345 break;
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700346 case ActionType.RobotDeathAction:
Filip Kujawa9f56d0e2023-03-03 19:44:43 -0800347 // TODO(FILIP): Return user to the screen they
348 // clicked dead robot on. Pickup is fine for now but
349 // might cause confusion.
350 this.section = 'Pickup';
351 break;
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800352 default:
353 break;
354 }
355 }
356 }
357
Emily Markovadcadcb62024-02-03 13:07:17 -0800358 stringifyScoreType(scoreType: ScoreType): String {
359 return ScoreType[scoreType];
Emily Markovaf4b06a22023-05-10 17:44:09 -0700360 }
361
Emily Markovadcadcb62024-02-03 13:07:17 -0800362 stringifyStageType(stageType: StageType): String {
363 return StageType[stageType];
Emily Markovaf4b06a22023-05-10 17:44:09 -0700364 }
365
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800366 changeSectionTo(target: Section) {
Philipp Schrader8702b782023-04-15 17:33:37 -0700367 // Clear the messages since they won't be relevant in the next section.
368 this.errorMessage = '';
369 this.progressMessage = '';
370
Philipp Schradere2e27ff2024-02-25 22:08:55 -0800371 // For the QR code screen, we need to make the value to encode available.
372 if (target == 'QR Code') {
373 this.updateQrCodeValuePieceSize();
374 }
375
Emily Markova521725a2024-03-21 18:46:04 -0700376 if (target == 'Init') {
377 this.searchForDuplicateScouting = true;
378 this.findOthersScoutingThisRobot();
379 }
380
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800381 this.section = target;
382 }
Philipp Schrader80587432022-03-05 15:41:22 -0800383
Ravago Jones2813c032022-03-16 23:44:11 -0700384 private scrollToTop() {
385 this.header.nativeElement.scrollIntoView();
386 }
387
Philipp Schradere2e27ff2024-02-25 22:08:55 -0800388 createActionsBuffer() {
James Kuszmauldac091f2022-03-22 09:35:06 -0700389 const builder = new Builder();
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800390 const actionOffsets: number[] = [];
391
392 for (const action of this.actionList) {
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700393 if (STATE_ACTIONS.includes(action.actionTakenType)) {
394 // Actions only used for undo purposes are not submitted.
395 continue;
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800396 }
Philipp Schrader3d7dedc2024-03-16 16:27:25 -0700397 actionOffsets.push(action.pack(builder));
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800398 }
Emily Markovae68b7632023-12-30 14:17:55 -0800399 const teamNumberFb = builder.createString(this.teamNumber);
Philipp Schradere859e6e2023-03-22 19:59:51 -0700400 const compLevelFb = builder.createString(this.compLevel);
Emily Markova9c18e9c2024-04-03 20:06:27 -0700401 const compTypeFb = builder.createString(this.compType);
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800402
Emily Markovadcadcb62024-02-03 13:07:17 -0800403 const actionsVector = Submit2024Actions.createActionsListVector(
Philipp Schrader817cce32022-03-26 15:00:00 -0700404 builder,
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800405 actionOffsets
Philipp Schrader817cce32022-03-26 15:00:00 -0700406 );
Emily Markovadcadcb62024-02-03 13:07:17 -0800407 Submit2024Actions.startSubmit2024Actions(builder);
408 Submit2024Actions.addTeamNumber(builder, teamNumberFb);
409 Submit2024Actions.addMatchNumber(builder, this.matchNumber);
410 Submit2024Actions.addSetNumber(builder, this.setNumber);
411 Submit2024Actions.addCompLevel(builder, compLevelFb);
412 Submit2024Actions.addActionsList(builder, actionsVector);
Emily Markova9c18e9c2024-04-03 20:06:27 -0700413 Submit2024Actions.addCompType(builder, compTypeFb);
Emily Markovadcadcb62024-02-03 13:07:17 -0800414 builder.finish(Submit2024Actions.endSubmit2024Actions(builder));
Ravago Jones2813c032022-03-16 23:44:11 -0700415
Philipp Schradere2e27ff2024-02-25 22:08:55 -0800416 return builder.asUint8Array();
417 }
418
419 // Same as createActionsBuffer, but encoded as Base64. It's also split into
420 // a number of pieces so that each piece is roughly limited to
421 // `qrCodeValuePieceSize` bytes.
422 createBase64ActionsBuffers(): string[] {
423 const originalBuffer = this.createActionsBuffer();
424 const deflatedData = pako.deflate(originalBuffer, {level: 9});
425
426 const pieceSize = this.qrCodeValuePieceSize;
427 const fullValue = btoa(String.fromCharCode(...deflatedData));
428 const numPieces = Math.ceil(fullValue.length / pieceSize);
429
430 let splitData: string[] = [];
431 for (let i = 0; i < numPieces; i++) {
432 const splitPiece = fullValue.slice(i * pieceSize, (i + 1) * pieceSize);
433 splitData.push(`${i}_${numPieces}_${pieceSize}_${splitPiece}`);
434 }
435 return splitData;
436 }
437
438 setQrCodeValueIndex(index: number) {
439 this.qrCodeValueIndex = Math.max(
440 0,
441 Math.min(index, this.qrCodeValuePieces.length - 1)
442 );
443 }
444
445 updateQrCodeValuePieceSize() {
446 this.qrCodeValuePieces = this.createBase64ActionsBuffers();
447 this.qrCodeValueIndex = 0;
448 }
449
450 async submit2024Actions() {
Philipp Schraderad2a6fb2024-03-20 20:51:36 -0700451 const res = await this.actionsSubmitter.submit(this.createActionsBuffer());
Ravago Jones2813c032022-03-16 23:44:11 -0700452
453 if (res.ok) {
454 // We successfully submitted the data. Report success.
455 this.section = 'Success';
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800456 this.actionList = [];
Evelyn Yangc8036b12023-10-11 21:14:46 -0700457
Philipp Schrader63198402024-03-16 14:19:02 -0700458 // Keep track of the position of the last robot, use to figure out what
459 // the next robot in the same position is.
Evelyn Yangc8036b12023-10-11 21:14:46 -0700460 let lastTeamPos = '0';
461 for (const match of this.matchList) {
462 if (
463 this.matchNumber === match.matchNumber() &&
464 this.setNumber === match.setNumber() &&
465 this.compLevel === match.compLevel()
466 ) {
467 this.teamNumber = this.teamNumber;
468 if (this.teamNumber == match.r1()) {
469 lastTeamPos = 'r1';
470 } else if (this.teamNumber == match.r2()) {
471 lastTeamPos = 'r2';
472 } else if (this.teamNumber == match.r3()) {
473 lastTeamPos = 'r3';
474 } else if (this.teamNumber == match.b1()) {
475 lastTeamPos = 'b1';
476 } else if (this.teamNumber == match.b2()) {
477 lastTeamPos = 'b2';
478 } else if (this.teamNumber == match.b3()) {
479 lastTeamPos = 'b3';
480 } else {
481 console.log('Position of scouted team not found.');
482 }
483 break;
484 }
485 }
486 if (lastTeamPos != '0') {
487 this.matchNumber += 1;
488 for (const match of this.matchList) {
489 if (
490 this.matchNumber == match.matchNumber() &&
491 this.setNumber == match.setNumber() &&
492 this.compLevel == match.compLevel()
493 ) {
494 if (lastTeamPos == 'r1') {
495 this.nextTeamNumber = match.r1();
496 } else if (lastTeamPos == 'r2') {
497 this.nextTeamNumber = match.r2();
498 } else if (lastTeamPos == 'r3') {
499 this.nextTeamNumber = match.r3();
500 } else if (lastTeamPos == 'b1') {
501 this.nextTeamNumber = match.b1();
502 } else if (lastTeamPos == 'b2') {
503 this.nextTeamNumber = match.b2();
504 } else if (lastTeamPos == 'b3') {
505 this.nextTeamNumber = match.b3();
506 } else {
507 console.log('Position of last team not found.');
508 }
509 break;
510 }
511 }
512 } else {
513 console.log('Last team position not found.');
514 }
515 this.matchList = [];
516 this.progressMessage = '';
517 this.errorMessage = '';
518 this.autoPhase = true;
519 this.actionList = [];
520 this.mobilityCompleted = false;
Emily Markova9c18e9c2024-04-03 20:06:27 -0700521 this.compType = 'Regular';
Evelyn Yangc8036b12023-10-11 21:14:46 -0700522 this.matchStartTimestamp = 0;
523 this.selectedValue = 0;
Emily Markova521725a2024-03-21 18:46:04 -0700524 this.searchForDuplicateScouting = false;
Ravago Jones2813c032022-03-16 23:44:11 -0700525 } else {
526 const resBuffer = await res.arrayBuffer();
527 const fbBuffer = new ByteBuffer(new Uint8Array(resBuffer));
James Kuszmauldac091f2022-03-22 09:35:06 -0700528 const parsedResponse = ErrorResponse.getRootAsErrorResponse(fbBuffer);
Ravago Jones2813c032022-03-16 23:44:11 -0700529
530 const errorMessage = parsedResponse.errorMessage();
Philipp Schrader817cce32022-03-26 15:00:00 -0700531 this.errorMessage = `Received ${res.status} ${res.statusText}: "${errorMessage}"`;
Alex Perrybb3d2062022-03-05 18:14:33 -0800532 }
Ravago Jones2813c032022-03-16 23:44:11 -0700533 }
Philipp Schrader80587432022-03-05 15:41:22 -0800534}