blob: aef97f7b79b0f8ee3a84575d30b10fb50f3fd484 [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;
61 }
62 | {
Filip Kujawa0ef334c2023-02-20 19:42:45 -080063 type: 'pickupObjectAction';
64 timestamp?: number;
65 objectType: ObjectType;
66 auto?: boolean;
67 }
68 | {
69 type: 'placeObjectAction';
70 timestamp?: number;
71 objectType?: ObjectType;
72 scoreLevel: ScoreLevel;
73 auto?: boolean;
74 }
75 | {
76 type: 'robotDeathAction';
77 timestamp?: number;
78 robotOn: boolean;
79 }
80 | {
81 type: 'endMatchAction';
82 docked: boolean;
83 engaged: boolean;
84 timestamp?: number;
85 }
86 | {
87 // This is not a action that is submitted,
88 // It is used for undoing purposes.
89 type: 'endAutoPhase';
90 timestamp?: number;
91 };
emilym38d08ba2022-10-22 15:25:01 -070092
Philipp Schrader23993e82022-03-18 18:54:00 -070093@Component({
94 selector: 'app-entry',
95 templateUrl: './entry.ng.html',
Philipp Schrader175a93c2023-02-19 13:13:40 -080096 styleUrls: ['../app/common.css', './entry.component.css'],
Philipp Schrader23993e82022-03-18 18:54:00 -070097})
98export class EntryComponent {
Philipp Schrader36df73a2022-03-17 23:27:24 -070099 // Re-export the type here so that we can use it in the `[value]` attribute
100 // of radio buttons.
Philipp Schrader8aeb14f2022-04-08 21:23:18 -0700101 readonly COMP_LEVELS = COMP_LEVELS;
102 readonly COMP_LEVEL_LABELS = COMP_LEVEL_LABELS;
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800103 readonly ObjectType = ObjectType;
104 readonly ScoreLevel = ScoreLevel;
Philipp Schrader36df73a2022-03-17 23:27:24 -0700105
Ravago Jones2813c032022-03-16 23:44:11 -0700106 section: Section = 'Team Selection';
107 @Output() switchTabsEvent = new EventEmitter<string>();
108 @Input() matchNumber: number = 1;
109 @Input() teamNumber: number = 1;
Philipp Schrader30b4a682022-04-16 14:36:17 -0700110 @Input() setNumber: number = 1;
Philipp Schrader8aeb14f2022-04-08 21:23:18 -0700111 @Input() compLevel: CompLevel = 'qm';
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800112
113 actionList: ActionT[] = [];
Ravago Jones2813c032022-03-16 23:44:11 -0700114 errorMessage: string = '';
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800115 autoPhase: boolean = true;
116 lastObject: ObjectType = null;
117
118 matchStartTimestamp: number = 0;
119
120 addAction(action: ActionT): void {
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800121 if (action.type == 'startMatchAction') {
122 // Unix nanosecond timestamp.
123 this.matchStartTimestamp = Date.now() * 1e6;
124 action.timestamp = 0;
125 } else {
126 // Unix nanosecond timestamp relative to match start.
127 action.timestamp = Date.now() * 1e6 - this.matchStartTimestamp;
128 }
129
130 if (
131 action.type == 'pickupObjectAction' ||
132 action.type == 'placeObjectAction'
133 ) {
134 action.auto = this.autoPhase;
135 if (action.type == 'pickupObjectAction') {
136 this.lastObject = action.objectType;
137 } else if (action.type == 'placeObjectAction') {
138 action.objectType = this.lastObject;
139 }
140 }
141 this.actionList.push(action);
142 }
143
144 undoLastAction() {
145 if (this.actionList.length > 0) {
146 let lastAction = this.actionList.pop();
147 switch (lastAction?.type) {
148 case 'endAutoPhase':
149 this.autoPhase = true;
150 case 'pickupObjectAction':
151 this.section = 'Pickup';
152 break;
153 case 'placeObjectAction':
154 this.section = 'Place';
155 break;
156 case 'endMatchAction':
157 this.section = 'Pickup';
158 break;
Filip Kujawa9f56d0e2023-03-03 19:44:43 -0800159 case 'robotDeathAction':
160 // TODO(FILIP): Return user to the screen they
161 // clicked dead robot on. Pickup is fine for now but
162 // might cause confusion.
163 this.section = 'Pickup';
164 break;
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800165 default:
166 break;
167 }
168 }
169 }
170
171 changeSectionTo(target: Section) {
172 this.section = target;
173 }
Philipp Schrader80587432022-03-05 15:41:22 -0800174
Ravago Jones2813c032022-03-16 23:44:11 -0700175 @ViewChild('header') header: ElementRef;
Philipp Schrader6b2e9502022-03-15 23:42:56 -0700176
Ravago Jones2813c032022-03-16 23:44:11 -0700177 private scrollToTop() {
178 this.header.nativeElement.scrollIntoView();
179 }
180
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800181 async submitActions() {
James Kuszmauldac091f2022-03-22 09:35:06 -0700182 const builder = new Builder();
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800183 const actionOffsets: number[] = [];
184
185 for (const action of this.actionList) {
186 let actionOffset: number | undefined;
187 console.log(action.type);
188
189 switch (action.type) {
190 case 'startMatchAction':
191 const startMatchActionOffset =
192 StartMatchAction.createStartMatchAction(builder, action.position);
193 actionOffset = Action.createAction(
194 builder,
Philipp Schrader8c878a22023-03-20 22:36:38 -0700195 BigInt(action.timestamp || 0),
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800196 ActionType.StartMatchAction,
197 startMatchActionOffset
198 );
199 break;
200
201 case 'pickupObjectAction':
202 const pickupObjectActionOffset =
203 PickupObjectAction.createPickupObjectAction(
204 builder,
205 action.objectType,
206 action.auto || false
207 );
208 actionOffset = Action.createAction(
209 builder,
Philipp Schrader8c878a22023-03-20 22:36:38 -0700210 BigInt(action.timestamp || 0),
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800211 ActionType.PickupObjectAction,
212 pickupObjectActionOffset
213 );
214 break;
215
Filip Kujawa4c286442023-03-03 10:41:22 -0800216 case 'autoBalanceAction':
217 const autoBalanceActionOffset =
218 AutoBalanceAction.createAutoBalanceAction(
219 builder,
220 action.docked,
221 action.engaged
222 );
223 actionOffset = Action.createAction(
224 builder,
Philipp Schrader8c878a22023-03-20 22:36:38 -0700225 BigInt(action.timestamp || 0),
Filip Kujawa4c286442023-03-03 10:41:22 -0800226 ActionType.AutoBalanceAction,
227 autoBalanceActionOffset
228 );
229 break;
230
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800231 case 'placeObjectAction':
232 const placeObjectActionOffset =
233 PlaceObjectAction.createPlaceObjectAction(
234 builder,
235 action.objectType,
236 action.scoreLevel,
237 action.auto || false
238 );
239 actionOffset = Action.createAction(
240 builder,
Philipp Schrader8c878a22023-03-20 22:36:38 -0700241 BigInt(action.timestamp || 0),
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800242 ActionType.PlaceObjectAction,
243 placeObjectActionOffset
244 );
245 break;
246
247 case 'robotDeathAction':
248 const robotDeathActionOffset =
249 RobotDeathAction.createRobotDeathAction(builder, action.robotOn);
250 actionOffset = Action.createAction(
251 builder,
Philipp Schrader8c878a22023-03-20 22:36:38 -0700252 BigInt(action.timestamp || 0),
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800253 ActionType.RobotDeathAction,
254 robotDeathActionOffset
255 );
256 break;
257
258 case 'endMatchAction':
259 const endMatchActionOffset = EndMatchAction.createEndMatchAction(
260 builder,
261 action.docked,
262 action.engaged
263 );
264 actionOffset = Action.createAction(
265 builder,
Philipp Schrader8c878a22023-03-20 22:36:38 -0700266 BigInt(action.timestamp || 0),
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800267 ActionType.EndMatchAction,
268 endMatchActionOffset
269 );
270 break;
271
272 case 'endAutoPhase':
273 // Not important action.
274 break;
275
276 default:
277 throw new Error(`Unknown action type`);
278 }
279
280 if (actionOffset !== undefined) {
281 actionOffsets.push(actionOffset);
282 }
283 }
284
285 const actionsVector = SubmitActions.createActionsListVector(
Philipp Schrader817cce32022-03-26 15:00:00 -0700286 builder,
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800287 actionOffsets
Philipp Schrader817cce32022-03-26 15:00:00 -0700288 );
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800289 SubmitActions.startSubmitActions(builder);
290 SubmitActions.addActionsList(builder, actionsVector);
291 builder.finish(SubmitActions.endSubmitActions(builder));
Ravago Jones2813c032022-03-16 23:44:11 -0700292
293 const buffer = builder.asUint8Array();
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800294 const res = await fetch('/requests/submit/actions', {
Philipp Schrader817cce32022-03-26 15:00:00 -0700295 method: 'POST',
296 body: buffer,
297 });
Ravago Jones2813c032022-03-16 23:44:11 -0700298
299 if (res.ok) {
300 // We successfully submitted the data. Report success.
301 this.section = 'Success';
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800302 this.actionList = [];
Ravago Jones2813c032022-03-16 23:44:11 -0700303 } else {
304 const resBuffer = await res.arrayBuffer();
305 const fbBuffer = new ByteBuffer(new Uint8Array(resBuffer));
James Kuszmauldac091f2022-03-22 09:35:06 -0700306 const parsedResponse = ErrorResponse.getRootAsErrorResponse(fbBuffer);
Ravago Jones2813c032022-03-16 23:44:11 -0700307
308 const errorMessage = parsedResponse.errorMessage();
Philipp Schrader817cce32022-03-26 15:00:00 -0700309 this.errorMessage = `Received ${res.status} ${res.statusText}: "${errorMessage}"`;
Alex Perrybb3d2062022-03-05 18:14:33 -0800310 }
Ravago Jones2813c032022-03-16 23:44:11 -0700311 }
Philipp Schrader80587432022-03-05 15:41:22 -0800312}