blob: 71c8de24b1292981d7b145f73586eae29cf6476d [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 Schraderd7efa2b2023-02-17 21:15:13 -080012import {ErrorResponse} from '../../webserver/requests/messages/error_response_generated';
Philipp Schrader817cce32022-03-26 15:00:00 -070013import {
Filip Kujawa0ef334c2023-02-20 19:42:45 -080014 ObjectType,
15 ScoreLevel,
16 SubmitActions,
17 StartMatchAction,
Filip Kujawa4413a592023-03-01 10:54:34 -080018 AutoBalanceAction,
Filip Kujawa0ef334c2023-02-20 19:42:45 -080019 PickupObjectAction,
20 PlaceObjectAction,
21 RobotDeathAction,
22 EndMatchAction,
23 ActionType,
24 Action,
25} from '../../webserver/requests/messages/submit_actions_generated';
Philipp Schrader8b8ed672022-03-05 18:08:50 -080026
Philipp Schrader817cce32022-03-26 15:00:00 -070027type Section =
28 | 'Team Selection'
Filip Kujawa0ef334c2023-02-20 19:42:45 -080029 | 'Init'
30 | 'Pickup'
31 | 'Place'
32 | 'Endgame'
33 | 'Dead'
Philipp Schrader817cce32022-03-26 15:00:00 -070034 | 'Review and Submit'
35 | 'Success';
Philipp Schrader80587432022-03-05 15:41:22 -080036
Philipp Schrader8aeb14f2022-04-08 21:23:18 -070037// TODO(phil): Deduplicate with match_list.component.ts.
38const COMP_LEVELS = ['qm', 'ef', 'qf', 'sf', 'f'] as const;
39type CompLevel = typeof COMP_LEVELS[number];
40
41// TODO(phil): Deduplicate with match_list.component.ts.
42const COMP_LEVEL_LABELS: Record<CompLevel, string> = {
43 qm: 'Qualifications',
44 ef: 'Eighth Finals',
45 qf: 'Quarter Finals',
46 sf: 'Semi Finals',
47 f: 'Finals',
48};
49
Filip Kujawa0ef334c2023-02-20 19:42:45 -080050type ActionT =
51 | {
52 type: 'startMatchAction';
53 timestamp?: number;
54 position: number;
55 }
56 | {
Filip Kujawa4413a592023-03-01 10:54:34 -080057 type: 'autoBalanceAction';
58 timestamp?: number;
59 docked: boolean;
60 engaged: boolean;
Emily Markova63c63f62023-03-29 20:57:35 -070061 balanceAttempt: boolean;
Filip Kujawa4413a592023-03-01 10:54:34 -080062 }
63 | {
Filip Kujawa0ef334c2023-02-20 19:42:45 -080064 type: 'pickupObjectAction';
65 timestamp?: number;
66 objectType: ObjectType;
67 auto?: boolean;
68 }
69 | {
70 type: 'placeObjectAction';
71 timestamp?: number;
72 objectType?: ObjectType;
73 scoreLevel: ScoreLevel;
74 auto?: boolean;
75 }
76 | {
77 type: 'robotDeathAction';
78 timestamp?: number;
79 robotOn: boolean;
80 }
81 | {
82 type: 'endMatchAction';
83 docked: boolean;
84 engaged: boolean;
Emily Markova63c63f62023-03-29 20:57:35 -070085 balanceAttempt: boolean;
Filip Kujawa0ef334c2023-02-20 19:42:45 -080086 timestamp?: number;
87 }
88 | {
89 // This is not a action that is submitted,
90 // It is used for undoing purposes.
91 type: 'endAutoPhase';
92 timestamp?: number;
93 };
emilym38d08ba2022-10-22 15:25:01 -070094
Philipp Schrader23993e82022-03-18 18:54:00 -070095@Component({
96 selector: 'app-entry',
97 templateUrl: './entry.ng.html',
Philipp Schrader175a93c2023-02-19 13:13:40 -080098 styleUrls: ['../app/common.css', './entry.component.css'],
Philipp Schrader23993e82022-03-18 18:54:00 -070099})
100export class EntryComponent {
Philipp Schrader36df73a2022-03-17 23:27:24 -0700101 // Re-export the type here so that we can use it in the `[value]` attribute
102 // of radio buttons.
Philipp Schrader8aeb14f2022-04-08 21:23:18 -0700103 readonly COMP_LEVELS = COMP_LEVELS;
104 readonly COMP_LEVEL_LABELS = COMP_LEVEL_LABELS;
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800105 readonly ObjectType = ObjectType;
106 readonly ScoreLevel = ScoreLevel;
Philipp Schrader36df73a2022-03-17 23:27:24 -0700107
Ravago Jones2813c032022-03-16 23:44:11 -0700108 section: Section = 'Team Selection';
109 @Output() switchTabsEvent = new EventEmitter<string>();
110 @Input() matchNumber: number = 1;
111 @Input() teamNumber: number = 1;
Philipp Schrader30b4a682022-04-16 14:36:17 -0700112 @Input() setNumber: number = 1;
Philipp Schrader8aeb14f2022-04-08 21:23:18 -0700113 @Input() compLevel: CompLevel = 'qm';
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800114
115 actionList: ActionT[] = [];
Ravago Jones2813c032022-03-16 23:44:11 -0700116 errorMessage: string = '';
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800117 autoPhase: boolean = true;
118 lastObject: ObjectType = null;
119
120 matchStartTimestamp: number = 0;
121
122 addAction(action: ActionT): void {
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800123 if (action.type == 'startMatchAction') {
124 // Unix nanosecond timestamp.
125 this.matchStartTimestamp = Date.now() * 1e6;
126 action.timestamp = 0;
127 } else {
128 // Unix nanosecond timestamp relative to match start.
129 action.timestamp = Date.now() * 1e6 - this.matchStartTimestamp;
130 }
131
132 if (
133 action.type == 'pickupObjectAction' ||
134 action.type == 'placeObjectAction'
135 ) {
136 action.auto = this.autoPhase;
137 if (action.type == 'pickupObjectAction') {
138 this.lastObject = action.objectType;
139 } else if (action.type == 'placeObjectAction') {
140 action.objectType = this.lastObject;
141 }
142 }
143 this.actionList.push(action);
144 }
145
146 undoLastAction() {
147 if (this.actionList.length > 0) {
148 let lastAction = this.actionList.pop();
149 switch (lastAction?.type) {
150 case 'endAutoPhase':
151 this.autoPhase = true;
152 case 'pickupObjectAction':
153 this.section = 'Pickup';
154 break;
155 case 'placeObjectAction':
156 this.section = 'Place';
157 break;
158 case 'endMatchAction':
159 this.section = 'Pickup';
160 break;
Filip Kujawa9f56d0e2023-03-03 19:44:43 -0800161 case 'robotDeathAction':
162 // TODO(FILIP): Return user to the screen they
163 // clicked dead robot on. Pickup is fine for now but
164 // might cause confusion.
165 this.section = 'Pickup';
166 break;
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800167 default:
168 break;
169 }
170 }
171 }
172
173 changeSectionTo(target: Section) {
174 this.section = target;
175 }
Philipp Schrader80587432022-03-05 15:41:22 -0800176
Ravago Jones2813c032022-03-16 23:44:11 -0700177 @ViewChild('header') header: ElementRef;
Philipp Schrader6b2e9502022-03-15 23:42:56 -0700178
Ravago Jones2813c032022-03-16 23:44:11 -0700179 private scrollToTop() {
180 this.header.nativeElement.scrollIntoView();
181 }
182
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800183 async submitActions() {
James Kuszmauldac091f2022-03-22 09:35:06 -0700184 const builder = new Builder();
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800185 const actionOffsets: number[] = [];
186
187 for (const action of this.actionList) {
188 let actionOffset: number | undefined;
189 console.log(action.type);
190
191 switch (action.type) {
192 case 'startMatchAction':
193 const startMatchActionOffset =
194 StartMatchAction.createStartMatchAction(builder, action.position);
195 actionOffset = Action.createAction(
196 builder,
Philipp Schrader8c878a22023-03-20 22:36:38 -0700197 BigInt(action.timestamp || 0),
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800198 ActionType.StartMatchAction,
199 startMatchActionOffset
200 );
201 break;
202
203 case 'pickupObjectAction':
204 const pickupObjectActionOffset =
205 PickupObjectAction.createPickupObjectAction(
206 builder,
207 action.objectType,
208 action.auto || false
209 );
210 actionOffset = Action.createAction(
211 builder,
Philipp Schrader8c878a22023-03-20 22:36:38 -0700212 BigInt(action.timestamp || 0),
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800213 ActionType.PickupObjectAction,
214 pickupObjectActionOffset
215 );
216 break;
217
Filip Kujawa4c286442023-03-03 10:41:22 -0800218 case 'autoBalanceAction':
219 const autoBalanceActionOffset =
220 AutoBalanceAction.createAutoBalanceAction(
221 builder,
222 action.docked,
Emily Markova63c63f62023-03-29 20:57:35 -0700223 action.engaged,
224 action.balanceAttempt
Filip Kujawa4c286442023-03-03 10:41:22 -0800225 );
226 actionOffset = Action.createAction(
227 builder,
Philipp Schrader8c878a22023-03-20 22:36:38 -0700228 BigInt(action.timestamp || 0),
Filip Kujawa4c286442023-03-03 10:41:22 -0800229 ActionType.AutoBalanceAction,
230 autoBalanceActionOffset
231 );
232 break;
233
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800234 case 'placeObjectAction':
235 const placeObjectActionOffset =
236 PlaceObjectAction.createPlaceObjectAction(
237 builder,
238 action.objectType,
239 action.scoreLevel,
240 action.auto || false
241 );
242 actionOffset = Action.createAction(
243 builder,
Philipp Schrader8c878a22023-03-20 22:36:38 -0700244 BigInt(action.timestamp || 0),
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800245 ActionType.PlaceObjectAction,
246 placeObjectActionOffset
247 );
248 break;
249
250 case 'robotDeathAction':
251 const robotDeathActionOffset =
252 RobotDeathAction.createRobotDeathAction(builder, action.robotOn);
253 actionOffset = Action.createAction(
254 builder,
Philipp Schrader8c878a22023-03-20 22:36:38 -0700255 BigInt(action.timestamp || 0),
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800256 ActionType.RobotDeathAction,
257 robotDeathActionOffset
258 );
259 break;
260
261 case 'endMatchAction':
262 const endMatchActionOffset = EndMatchAction.createEndMatchAction(
263 builder,
264 action.docked,
Emily Markova63c63f62023-03-29 20:57:35 -0700265 action.engaged,
266 action.balanceAttempt
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800267 );
268 actionOffset = Action.createAction(
269 builder,
Philipp Schrader8c878a22023-03-20 22:36:38 -0700270 BigInt(action.timestamp || 0),
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800271 ActionType.EndMatchAction,
272 endMatchActionOffset
273 );
274 break;
275
276 case 'endAutoPhase':
277 // Not important action.
278 break;
279
280 default:
281 throw new Error(`Unknown action type`);
282 }
283
284 if (actionOffset !== undefined) {
285 actionOffsets.push(actionOffset);
286 }
287 }
Philipp Schradere859e6e2023-03-22 19:59:51 -0700288 const teamNumberFb = builder.createString(this.teamNumber.toString());
289 const compLevelFb = builder.createString(this.compLevel);
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800290
291 const actionsVector = SubmitActions.createActionsListVector(
Philipp Schrader817cce32022-03-26 15:00:00 -0700292 builder,
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800293 actionOffsets
Philipp Schrader817cce32022-03-26 15:00:00 -0700294 );
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800295 SubmitActions.startSubmitActions(builder);
Philipp Schradere859e6e2023-03-22 19:59:51 -0700296 SubmitActions.addTeamNumber(builder, teamNumberFb);
297 SubmitActions.addMatchNumber(builder, this.matchNumber);
298 SubmitActions.addSetNumber(builder, this.setNumber);
299 SubmitActions.addCompLevel(builder, compLevelFb);
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800300 SubmitActions.addActionsList(builder, actionsVector);
301 builder.finish(SubmitActions.endSubmitActions(builder));
Ravago Jones2813c032022-03-16 23:44:11 -0700302
303 const buffer = builder.asUint8Array();
Sabina Leaver9b4eb312023-02-20 19:58:17 -0800304 const res = await fetch('/requests/submit/submit_actions', {
Philipp Schrader817cce32022-03-26 15:00:00 -0700305 method: 'POST',
306 body: buffer,
307 });
Ravago Jones2813c032022-03-16 23:44:11 -0700308
309 if (res.ok) {
310 // We successfully submitted the data. Report success.
311 this.section = 'Success';
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800312 this.actionList = [];
Ravago Jones2813c032022-03-16 23:44:11 -0700313 } else {
314 const resBuffer = await res.arrayBuffer();
315 const fbBuffer = new ByteBuffer(new Uint8Array(resBuffer));
James Kuszmauldac091f2022-03-22 09:35:06 -0700316 const parsedResponse = ErrorResponse.getRootAsErrorResponse(fbBuffer);
Ravago Jones2813c032022-03-16 23:44:11 -0700317
318 const errorMessage = parsedResponse.errorMessage();
Philipp Schrader817cce32022-03-26 15:00:00 -0700319 this.errorMessage = `Received ${res.status} ${res.statusText}: "${errorMessage}"`;
Alex Perrybb3d2062022-03-05 18:14:33 -0800320 }
Ravago Jones2813c032022-03-16 23:44:11 -0700321 }
Philipp Schrader80587432022-03-05 15:41:22 -0800322}