blob: 9aae0d1ee68119f74ae6ee373e7167db7f72d0d6 [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,
18 PickupObjectAction,
19 PlaceObjectAction,
20 RobotDeathAction,
21 EndMatchAction,
22 ActionType,
23 Action,
24} from '../../webserver/requests/messages/submit_actions_generated';
Philipp Schrader8b8ed672022-03-05 18:08:50 -080025
Philipp Schrader817cce32022-03-26 15:00:00 -070026type Section =
27 | 'Team Selection'
Filip Kujawa0ef334c2023-02-20 19:42:45 -080028 | 'Init'
29 | 'Pickup'
30 | 'Place'
31 | 'Endgame'
32 | 'Dead'
Philipp Schrader817cce32022-03-26 15:00:00 -070033 | 'Review and Submit'
34 | 'Success';
Philipp Schrader80587432022-03-05 15:41:22 -080035
Philipp Schrader8aeb14f2022-04-08 21:23:18 -070036// TODO(phil): Deduplicate with match_list.component.ts.
37const COMP_LEVELS = ['qm', 'ef', 'qf', 'sf', 'f'] as const;
38type CompLevel = typeof COMP_LEVELS[number];
39
40// TODO(phil): Deduplicate with match_list.component.ts.
41const COMP_LEVEL_LABELS: Record<CompLevel, string> = {
42 qm: 'Qualifications',
43 ef: 'Eighth Finals',
44 qf: 'Quarter Finals',
45 sf: 'Semi Finals',
46 f: 'Finals',
47};
48
Filip Kujawa0ef334c2023-02-20 19:42:45 -080049type ActionT =
50 | {
51 type: 'startMatchAction';
52 timestamp?: number;
53 position: number;
54 }
55 | {
56 type: 'pickupObjectAction';
57 timestamp?: number;
58 objectType: ObjectType;
59 auto?: boolean;
60 }
61 | {
62 type: 'placeObjectAction';
63 timestamp?: number;
64 objectType?: ObjectType;
65 scoreLevel: ScoreLevel;
66 auto?: boolean;
67 }
68 | {
69 type: 'robotDeathAction';
70 timestamp?: number;
71 robotOn: boolean;
72 }
73 | {
74 type: 'endMatchAction';
75 docked: boolean;
76 engaged: boolean;
77 timestamp?: number;
78 }
79 | {
80 // This is not a action that is submitted,
81 // It is used for undoing purposes.
82 type: 'endAutoPhase';
83 timestamp?: number;
84 };
emilym38d08ba2022-10-22 15:25:01 -070085
Philipp Schrader23993e82022-03-18 18:54:00 -070086@Component({
87 selector: 'app-entry',
88 templateUrl: './entry.ng.html',
Philipp Schrader175a93c2023-02-19 13:13:40 -080089 styleUrls: ['../app/common.css', './entry.component.css'],
Philipp Schrader23993e82022-03-18 18:54:00 -070090})
91export class EntryComponent {
Philipp Schrader36df73a2022-03-17 23:27:24 -070092 // Re-export the type here so that we can use it in the `[value]` attribute
93 // of radio buttons.
Philipp Schrader8aeb14f2022-04-08 21:23:18 -070094 readonly COMP_LEVELS = COMP_LEVELS;
95 readonly COMP_LEVEL_LABELS = COMP_LEVEL_LABELS;
Filip Kujawa0ef334c2023-02-20 19:42:45 -080096 readonly ObjectType = ObjectType;
97 readonly ScoreLevel = ScoreLevel;
Philipp Schrader36df73a2022-03-17 23:27:24 -070098
Ravago Jones2813c032022-03-16 23:44:11 -070099 section: Section = 'Team Selection';
100 @Output() switchTabsEvent = new EventEmitter<string>();
101 @Input() matchNumber: number = 1;
102 @Input() teamNumber: number = 1;
Philipp Schrader30b4a682022-04-16 14:36:17 -0700103 @Input() setNumber: number = 1;
Philipp Schrader8aeb14f2022-04-08 21:23:18 -0700104 @Input() compLevel: CompLevel = 'qm';
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800105
106 actionList: ActionT[] = [];
Ravago Jones2813c032022-03-16 23:44:11 -0700107 errorMessage: string = '';
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800108 autoPhase: boolean = true;
109 lastObject: ObjectType = null;
110
111 matchStartTimestamp: number = 0;
112
113 addAction(action: ActionT): void {
114 action.timestamp = Math.floor(Date.now() / 1000);
115 if (action.type == 'startMatchAction') {
116 // Unix nanosecond timestamp.
117 this.matchStartTimestamp = Date.now() * 1e6;
118 action.timestamp = 0;
119 } else {
120 // Unix nanosecond timestamp relative to match start.
121 action.timestamp = Date.now() * 1e6 - this.matchStartTimestamp;
122 }
123
124 if (
125 action.type == 'pickupObjectAction' ||
126 action.type == 'placeObjectAction'
127 ) {
128 action.auto = this.autoPhase;
129 if (action.type == 'pickupObjectAction') {
130 this.lastObject = action.objectType;
131 } else if (action.type == 'placeObjectAction') {
132 action.objectType = this.lastObject;
133 }
134 }
135 this.actionList.push(action);
136 }
137
138 undoLastAction() {
139 if (this.actionList.length > 0) {
140 let lastAction = this.actionList.pop();
141 switch (lastAction?.type) {
142 case 'endAutoPhase':
143 this.autoPhase = true;
144 case 'pickupObjectAction':
145 this.section = 'Pickup';
146 break;
147 case 'placeObjectAction':
148 this.section = 'Place';
149 break;
150 case 'endMatchAction':
151 this.section = 'Pickup';
152 break;
153 default:
154 break;
155 }
156 }
157 }
158
159 changeSectionTo(target: Section) {
160 this.section = target;
161 }
Philipp Schrader80587432022-03-05 15:41:22 -0800162
Ravago Jones2813c032022-03-16 23:44:11 -0700163 @ViewChild('header') header: ElementRef;
Philipp Schrader6b2e9502022-03-15 23:42:56 -0700164
Ravago Jones2813c032022-03-16 23:44:11 -0700165 private scrollToTop() {
166 this.header.nativeElement.scrollIntoView();
167 }
168
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800169 async submitActions() {
James Kuszmauldac091f2022-03-22 09:35:06 -0700170 const builder = new Builder();
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800171 const actionOffsets: number[] = [];
172
173 for (const action of this.actionList) {
174 let actionOffset: number | undefined;
175 console.log(action.type);
176
177 switch (action.type) {
178 case 'startMatchAction':
179 const startMatchActionOffset =
180 StartMatchAction.createStartMatchAction(builder, action.position);
181 actionOffset = Action.createAction(
182 builder,
183 action.timestamp || 0,
184 ActionType.StartMatchAction,
185 startMatchActionOffset
186 );
187 break;
188
189 case 'pickupObjectAction':
190 const pickupObjectActionOffset =
191 PickupObjectAction.createPickupObjectAction(
192 builder,
193 action.objectType,
194 action.auto || false
195 );
196 actionOffset = Action.createAction(
197 builder,
198 action.timestamp || 0,
199 ActionType.PickupObjectAction,
200 pickupObjectActionOffset
201 );
202 break;
203
204 case 'placeObjectAction':
205 const placeObjectActionOffset =
206 PlaceObjectAction.createPlaceObjectAction(
207 builder,
208 action.objectType,
209 action.scoreLevel,
210 action.auto || false
211 );
212 actionOffset = Action.createAction(
213 builder,
214 action.timestamp || 0,
215 ActionType.PlaceObjectAction,
216 placeObjectActionOffset
217 );
218 break;
219
220 case 'robotDeathAction':
221 const robotDeathActionOffset =
222 RobotDeathAction.createRobotDeathAction(builder, action.robotOn);
223 actionOffset = Action.createAction(
224 builder,
225 action.timestamp || 0,
226 ActionType.RobotDeathAction,
227 robotDeathActionOffset
228 );
229 break;
230
231 case 'endMatchAction':
232 const endMatchActionOffset = EndMatchAction.createEndMatchAction(
233 builder,
234 action.docked,
235 action.engaged
236 );
237 actionOffset = Action.createAction(
238 builder,
239 action.timestamp || 0,
240 ActionType.EndMatchAction,
241 endMatchActionOffset
242 );
243 break;
244
245 case 'endAutoPhase':
246 // Not important action.
247 break;
248
249 default:
250 throw new Error(`Unknown action type`);
251 }
252
253 if (actionOffset !== undefined) {
254 actionOffsets.push(actionOffset);
255 }
256 }
257
258 const actionsVector = SubmitActions.createActionsListVector(
Philipp Schrader817cce32022-03-26 15:00:00 -0700259 builder,
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800260 actionOffsets
Philipp Schrader817cce32022-03-26 15:00:00 -0700261 );
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800262 SubmitActions.startSubmitActions(builder);
263 SubmitActions.addActionsList(builder, actionsVector);
264 builder.finish(SubmitActions.endSubmitActions(builder));
Ravago Jones2813c032022-03-16 23:44:11 -0700265
266 const buffer = builder.asUint8Array();
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800267 const res = await fetch('/requests/submit/actions', {
Philipp Schrader817cce32022-03-26 15:00:00 -0700268 method: 'POST',
269 body: buffer,
270 });
Ravago Jones2813c032022-03-16 23:44:11 -0700271
272 if (res.ok) {
273 // We successfully submitted the data. Report success.
274 this.section = 'Success';
Filip Kujawa0ef334c2023-02-20 19:42:45 -0800275 this.actionList = [];
Ravago Jones2813c032022-03-16 23:44:11 -0700276 } else {
277 const resBuffer = await res.arrayBuffer();
278 const fbBuffer = new ByteBuffer(new Uint8Array(resBuffer));
James Kuszmauldac091f2022-03-22 09:35:06 -0700279 const parsedResponse = ErrorResponse.getRootAsErrorResponse(fbBuffer);
Ravago Jones2813c032022-03-16 23:44:11 -0700280
281 const errorMessage = parsedResponse.errorMessage();
Philipp Schrader817cce32022-03-26 15:00:00 -0700282 this.errorMessage = `Received ${res.status} ${res.statusText}: "${errorMessage}"`;
Alex Perrybb3d2062022-03-05 18:14:33 -0800283 }
Ravago Jones2813c032022-03-16 23:44:11 -0700284 }
Philipp Schrader80587432022-03-05 15:41:22 -0800285}