Skip to content

Commit

Permalink
refactor: action input state machine into component
Browse files Browse the repository at this point in the history
  • Loading branch information
Azalea Colburn committed Nov 14, 2024
1 parent 01b0333 commit 4be6e7b
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 71 deletions.
9 changes: 5 additions & 4 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export type TeamMatch = {
scout_id: string;
match_key: string;
team_key: string;
skills: number;
skill: number;
notes: string;
broke: boolean;
died: boolean;
Expand All @@ -68,17 +68,18 @@ export type TeleActionData = {
};

// Action Types
export type AutoAction = TeleAction | BunnyAction;
export type BunnyAction = 'IntakeBunny' | 'ScoreBunnyTote' | 'ScoreBunnyLow';
export type TeleAction =
| 'IntakeTote'
| 'IntakeBalloon'
| 'TakeBalloonFromCoral'
| 'IntakeCoral'
| 'ScoreYourHeldTote'
| 'ScoreOtherHeldTote'
| 'ScoreExternalTote'
| 'ScoreBalloonLow';

export type BunnyAction = 'IntakeBunny' | 'ScoreBunnyTote' | 'ScoreBunnyLow';
export type AutoAction = TeleAction | BunnyAction;

// For state machine
export type ItemInputState = 'Intake' | 'Score' | 'None';
export type TeleInputState = TeleAction | ItemInputState;
Expand Down
93 changes: 26 additions & 67 deletions src/routes/scout/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,82 +1,41 @@
<script lang="ts">
import type {
AutoInputState,
TeamMatch,
TeleActionData,
TeleAction,
ItemInputState
} from '$lib/types';
import SuccessFail from '$lib/components/SuccessFail.svelte';
import { goto } from '$app/navigation';
import { type TeamMatch, type AutoActionData } from '$lib/types';
import Timeline from '$lib/components/Timeline.svelte';
let actionState: AutoInputState = $state('None');
import ActionInputs from './ActionInputs.svelte';
const intake_piece = () => (actionState = actionState === 'None' ? 'Intake' : actionState);
const score_piece = () => (actionState = actionState === 'None' ? 'Score' : actionState);
const score_tote = (type: 'YourHeld' | 'OtherHeld' | 'External') =>
(actionState = `Score${type}Tote`);
const score_low = (type: 'Balloon' | 'Bunny') => (actionState = `Score${type}Low`);
const complete = (success: boolean) => {
let action: TeleActionData = {
action: actionState as TeleAction,
const {
match_key,
team_key = '1540',
scout_id
}: { match_key: string; team_key: string; scout_id: string } = $props();
success: success
};
actionState = 'None';
};
let actions: AutoActionData[] = $state([]);
let timelineExtended = $state(false);
let latestActions: AutoActionData[] = $state([]);
let { match_key, team_key }: { match_key: string; team_key: string } = $props();
team_key = '1540';
let match: TeamMatch = $state({
const match: TeamMatch = $state({
id: 0,
scout_id,
team_key,
match_key,
skill: 3,
notes: '',
broke: false,
died: false,
auto_actions: [],
tele_actions: []
});
$effect(() => console.log(actionState));
const is_input_state = $derived(actionState instanceof ItemInputState);
let actions: ActionData[] = $state([]);
let timelineExtended = $state(false);
let latestActions: ActionData[] = $state([]);
</script>

<main class="text-zinc-50 flex flex-col p-2 h-svh">
{#if actionState != 'None'}
<SuccessFail {complete} cancel={() => (actionState = 'None')} />
{:else if is_input_state}
<span class="text-center font-bold pb-2">team {team_key}</span>
<div class="grid gap-2 grid-cols-2 flex-grow">
<button class="bg-zinc-500 p-2 rounded" onclick={() => score_low('Balloon')}>
Score
</button>
<button class="bg-zinc-500 p-2 rounded" onclick={() => (actionState = 'Intake')}
>Intake</button
>
<button class="bg-zinc-500 p-2 rounded col-span-2" onclick={() => goto('/scout')}>
Timeline
</button>
</div>
{/if}
<span class="text-center font-bold pb-2">team {team_key}</span>
<div class="grid gap-2 grid-cols-2 flex-grow">
<button class="bg-zinc-500 p-2 rounded" onclick={() => score_low('Balloon')}>
Score
</button>
<button class="bg-zinc-500 p-2 rounded" onclick={() => (actionState = 'Intake')}
>Intake</button
>
<button class="bg-zinc-500 p-2 rounded col-span-2"> Timeline </button>
<div class="text-zinc-50 p-2 h-svh">
<h1 class="text-center font-bold pb-2 h-5">Team {team_key}</h1>
{#if timelineExtended}
<Timeline bind:actions={latestActions} bind:displaying={timelineExtended} />
{:else}
<ActionInputs bind:actions />
<button
class="bg-btn_grey w-80 p-1 rounded border-2 border-outline_gray static"
onclick={() => console.log('todo')}>Add Action</button
>
<button
class="bg-btn_grey w-80 p-1 rounded border-2 border-outline_gray static"
class="bg-btn_grey h-10 w-80 p-1 rounded border-2 border-outline_gray static"
onclick={() => (timelineExtended = !timelineExtended)}>Show Timeline</button
>
</div>
<Timeline bind:actions={latestActions} bind:displaying={timelineExtended} />
</main>
{/if}
</div>
88 changes: 88 additions & 0 deletions src/routes/scout/ActionInputs.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<script lang="ts">
import SuccessFail from '$lib/components/SuccessFail.svelte';
import type { AutoAction, AutoInputState, AutoActionData } from '$lib/types';
const { actions = $bindable() }: { actions: AutoActionData[] } = $props();
let actionState: AutoInputState = $state('None') as AutoInputState;
let held_pieces: number = $state(0);
function intake_piece() {
actionState = actionState === 'None' ? 'Intake' : actionState;
}
function score_piece() {
if (held_pieces < 1) return;
actionState = actionState === 'None' ? 'Score' : actionState;
}
function complete(success: boolean) {
// Assume that the robot ejects even if they fail to score
if (actionState.substring(0, 5) === 'Score') held_pieces--;
else if (actionState.substring(0, 6) === 'Intake' && success) held_pieces++;
const action: AutoActionData = {
action: actionState as AutoAction,
success: success
};
actions.push(action);
actionState = 'None';
}
const is_none_state = $derived(actionState === 'None');
const is_intake_state = $derived(actionState === 'Intake');
const is_score_state = $derived(actionState === 'Score');
</script>

<div class="grid gap-2 grid-cols-1 grid-rows-2 flex-grow">
<p>Number of pieces currently held: {held_pieces}</p>
{#if is_none_state}
<div class="grid gap-2 grid-cols-2 flex-grow">
<button class="bg-zinc-500 p-2 rounded" onclick={intake_piece}>Intake</button>
<button class="bg-zinc-500 p-2 rounded" onclick={score_piece}>Score</button>
</div>
{:else if is_intake_state}
<div class="grid gap-2 grid-cols-2 grid-rows-2 flex-grow">
<button class="bg-zinc-500 p-2 rounded" onclick={() => (actionState = 'IntakeBunny')}
>Intake Bunny</button
>
<button class="bg-zinc-500 p-2 rounded" onclick={() => (actionState = 'IntakeTote')}
>Intake Tote</button
>
<button class="bg-zinc-500 p-2 rounded" onclick={() => (actionState = 'IntakeBalloon')}
>Intake Balloon From Ground</button
>
<button class="bg-zinc-500 p-2 rounded" onclick={() => (actionState = 'IntakeCoral')}
>Intake Ballon From Coral</button
>
</div>
{:else if is_score_state}
<div class="grid gap-2 grid-cols-2 grid-rows-3 flex-grow">
<button class="bg-zinc-500 p-2 rounded" onclick={() => (actionState = 'ScoreBunnyLow')}
>Score Bunny in Low Zone</button
>
<button class="bg-zinc-500 p-2 rounded" onclick={() => (actionState = 'ScoreBunnyTote')}
>Score Bunny in Tote</button
>
<button
class="bg-zinc-500 p-2 rounded"
onclick={() => (actionState = 'ScoreBalloonLow')}>Score Ballon in Low Zone</button
>
<button
class="bg-zinc-500 p-2 rounded"
onclick={() => (actionState = 'ScoreExternalTote')}
>Score Bunny in Uncontrolled Tote</button
>
<button
class="bg-zinc-500 p-2 rounded"
onclick={() => (actionState = 'ScoreYourHeldTote')}
>Score Bunny in Your Held Tote</button
>
<button
class="bg-zinc-500 p-2 rounded"
onclick={() => (actionState = 'ScoreOtherHeldTote')}
>Score Bunny in Tote Held by Other Robot</button
>
</div>
{:else}
<SuccessFail {complete} cancel={() => (actionState = 'None')} />
{/if}
</div>

0 comments on commit 4be6e7b

Please sign in to comment.