Skip to content

Commit

Permalink
feat: copy over inflated chickens types and ws
Browse files Browse the repository at this point in the history
  • Loading branch information
azaleacolburn committed Jan 20, 2025
1 parent 4bc0f8e commit 811b9d5
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 42 deletions.
Binary file modified bun.lockb
Binary file not shown.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@
"@tanstack/table-core": "^8.20.5",
"echarts": "^5.5.1",
"echarts-stat": "^1.2.0",
"lucide-svelte": "^0.469.0"
"lucide-svelte": "^0.469.0",
"socket.io": "^4.8.1",
"socket.io-client": "^4.8.1"
},
"prisma": {
"seed": "bun ./prisma/seed.ts"
Expand Down
81 changes: 81 additions & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// For DB
export type Match = {
match_key: string
event_key: string
}

export type User = {
id: string
name: string
is_admin: boolean
slack_token: string
}

// DB Tables

/// Auto actions table stored and retreived from the backend
export type AutoActionsTM = ActionsTM & {
coral_l1: number
coral_l2: number
coral_l3: number
coral_l4: number
algae_l2: number
algae_l3: number
// TODE
actions: AutoActionData[]
}

export type TeleActionsTM = ActionsTM & {
actions: TeleActionData[]
}

export type ActionsTM = {
id: number
// TODO
}

/// TeamMatch data that gets sent to the backend
export type TeamMatch = {
id: number
scout_id: string
match_key: string
team_key: string
leave: boolean
skill: number
notes: string
broke: boolean
died: boolean
auto_actions: AutoActionData[]
tele_actions: TeleActionData[]
}

export type AutoActionData = {
action: AutoAction
success: boolean
ok: boolean
}

export type TeleActionData = {
action: TeleAction
success: boolean
ok: boolean
}

// Action Types
// Naming Convention: action_type + game_piece + where
// TODO
export type TeleAction = ""

export type PreAction = "" | ""
export type AutoAction = TeleAction | PreAction

export type TeleHeldItems = {
// TODO
}
export type AutoHeldItems = TeleHeldItems

// For state machine
// TODO Decide in intake semantics
export type ItemInputState = "Intake" | "Score" | "Eject" | "None"
export type TeleInputState = TeleAction | ItemInputState
export type AutoInputState = TeleInputState
88 changes: 48 additions & 40 deletions src/routes/admin/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,71 +1,79 @@
<script lang="ts">
let next_match = $state("")
let next_red_robots = $state(["1678", "254", "1341"])
let next_blue_robot = $state(["394", "1930", "1540"])
let curr_red_robots = $state(["1678", "256", "1341"])
let curr_blue_robot = $state(["394", "1930", "1540"])
let bug_title = $state("")
let bug_description = $state("")
let bug_reports: { title: string; description: string }[] = $state([])
let new_users: string[] = $state(["Azalea", "Zach", "Autumn"])
let scout_queue: string[] = $state([])
</script>

<div class="grid grid-flow-row grid-cols-3 grid-rows-2 gap-4">
<div class="grid grid-cols-1 grid-rows-3 gap-2 rounded p-2 outline">
<button class="rounded p-2 outline">Queue Next Match</button>
<div class="ml-2 mr-2 mt-2 grid grid-flow-col grid-cols-4 grid-rows-2 gap-4">
<div
class="col-span-2 grid grid-cols-1 grid-rows-3 gap-2 rounded p-2 outline"
>
<div class="grid grid-cols-3 grid-rows-1 gap-2">
<input
bind:value={next_match}
placeholder="Next Match"
class="rounded p-2 outline"
/>
<button class="rounded p-2 outline">Auto Load</button>
<button class="rounded p-2 outline">Queue Match</button>
</div>
<div class="grid grid-cols-3 grid-rows-1 gap-2">
{#each next_red_robots as robot}
<button class="rounded bg-red-500 p-2 outline">{robot}</button>
{#each next_red_robots as _robot, i}
<input
bind:value={next_red_robots[i]}
class="rounded bg-red-500 p-2 outline"
/>
{/each}
</div>
<div class="grid grid-cols-3 grid-rows-1 gap-2">
{#each next_blue_robot as robot}
<button class="rounded bg-blue-500 p-2 outline">{robot}</button>
{#each next_blue_robot as _robot, i}
<input
bind:value={next_blue_robot[i]}
class="rounded bg-blue-500 p-2 outline"
/>
{/each}
</div>
</div>
<div
class="col-span-2 grid grid-cols-1 grid-rows-3 gap-2 rounded p-2 outline"
>
<div class="grid grid-cols-1 grid-rows-3 gap-2 rounded p-2 outline">
<button class="rounded p-2 outline">Currently Scouting</button>
<div class="grid grid-cols-3 grid-rows-1 gap-2">
{#each curr_red_robots as robot}
<button class="rounded bg-red-500 p-2 outline">{robot}</button>
{#each curr_red_robots as _robot, i}
<input
bind:value={curr_red_robots[i]}
class="rounded bg-red-500 p-2 outline"
/>
{/each}
</div>
<div class="grid grid-cols-3 grid-rows-1 gap-2">
{#each curr_blue_robot as robot}
<button class="rounded bg-blue-500 p-2 outline">{robot}</button>
{#each curr_blue_robot as _robot, i}
<input
bind:value={curr_blue_robot[i]}
class="rounded bg-blue-500 p-2 outline"
/>
{/each}
</div>
</div>
<div class="text-align grid gap-2 rounded p-2 outline">
<button class="grid outline">Send Bug</button>
<input
bind:value={bug_title}
class="grid outline"
placeholder="Bug Title"
/>
<input
bind:value={bug_description}
class="grid outline"
placeholder="Bug Description"
/>
</div>
<div class="grid outline">
<div>Bug Reports</div>
<div>
{#each bug_reports as bug_report}
<div class="grid">
<button class="grid outline">{bug_report.title}</button>
<button class="grid outline">FW</button>
</div>
{/each}
</div>
<div
class="text-align row-span-2 grid gap-2 overflow-y-scroll rounded p-2 outline"
>
{#each new_users as user}
<div class="grid grid-cols-2 text-center">
{user}
<button class="rounded outline">Approve</button>
</div>
{/each}
</div>
<div class="row-span-2 grid outline">
<div class="row-span-2 grid rounded p-2 outline">
<div>Queue</div>
{#each scout_queue as scout}
<div class="grid outline">{scout}</div>
Expand Down
8 changes: 7 additions & 1 deletion vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { sveltekit } from "@sveltejs/kit/vite"
import { defineConfig } from "vite"
import webSocketServer from "./ws.js"

export default defineConfig({
plugins: [sveltekit()],
plugins: [sveltekit(), webSocketServer],
server: {
fs: {
allow: ["./"],
},
},
})
166 changes: 166 additions & 0 deletions ws.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import type { TeamMatch } from "$lib/types"
import { Server } from "socket.io"
import { type ViteDevServer } from "vite"
const info = (s: string) => console.log(`\x1b[32m ${s} \x1b[0m`)

const sid_to_username: Map<string, string> = new Map()
let robot_queue: [string, "red" | "blue"][] = []
let curr_match_key: string = ""

const webSocketServer = {
name: "webSocketServer",
configureServer(server: ViteDevServer) {
if (!server.httpServer) return
const io = new Server(server.httpServer)

io.use((socket, next) => {
const username = socket.handshake.auth.username
if (!username) {
return next(new Error("invalid username"))
}

// erroring in prod
let old_entries = Object.entries(sid_to_username).find(
([_key, value]) => value === username
)
if (old_entries) {
old_entries
.map(([key, _value]) => key)
.forEach(key => sid_to_username.delete(key))
}

sid_to_username.set(socket.id, username)

next()
})

io.on("connect", socket => {
if (socket.handshake.auth.token === "celary") {
info("Admin Aquired")
socket.join("admin_room")
}

socket.on("join_queue", () => {
const username = sid_to_username.get(socket.id)

const team_data = robot_queue.pop()
if (!team_data) {
io.to("admin_room").emit("scout_joined_queue", username)
socket.join("scout_queue")
return
}
io.to("admin_room").emit("robot_left_queue", team_data)
socket.emit("time_to_scout", [curr_match_key, ...team_data])
})

socket.on("leave_scout_queue", (scout_id: string) => {
const scout_sid = Object.entries(sid_to_username)
.filter(([_sid, scout]) => scout === scout_id)
.map(([sid, _]) => sid)[0]
// This event exist in the cast that the scout removed itself from the queue
io.emit("scout_left_queue", scout_id)
// This event exists in the case that the admin removed the scout from the queue
// io.to(scout_sid).emit('you_left_queue');
io.sockets.sockets.get(scout_sid)?.leave("scout_queue")
})

socket.on(
"leave_robot_queue",
(robot: [string, "red" | "blue"]) => {
const robotsEqual = (
robot1: [string, "red" | "blue"],
robot2: [string, "red" | "blue"]
): boolean => {
return (
robot1[0] === robot2[0] && robot1[1] === robot2[1]
)
}
const index = robot_queue.findIndex(robot_t =>
robotsEqual(robot_t, robot)
)
if (index === -1) return

robot_queue.splice(index, 1)
}
)

socket.on(
"send_match",
async ([match_key, teams]: [
string,
[string, "red" | "blue"][],
]) => {
if (!socket.rooms.has("admin_room")) return

// TODO: New TeamMatch in DB request here

info(`${match_key}: ${teams}`)
robot_queue = []

const scout_queue = (
await io.in("scout_queue").fetchSockets()
).reverse()
for (const socket of scout_queue) {
const team_data = teams.pop()
if (!team_data) break

const username = sid_to_username.get(socket.id)
if (!username) {
console.error("Scout in queue not in map")
continue
}

socket.leave("scout_queue")
socket.emit("time_to_scout", [match_key, ...team_data])
io.to("admin_room").emit("scout_left_queue", username)
}

io.to("admin_room").emit("robot_joined_queue", teams)
robot_queue.push(...teams)

// Update all connected sockets with new match info (for cosmetic purposes)
io.emit("new_match", match_key)
curr_match_key = match_key
}
)

// Event-listener sockets that were offline to sync back up with the current match key
socket.on("get_curr_match", callback => {
callback({
curr_match_key,
})
})

socket.on("get_robot_queue", callback => {
callback({
robots: robot_queue ?? [],
})
})

socket.on("get_scout_queue", async callback => {
callback({
scouts: (
(await io.in("scout_queue").fetchSockets()) ?? []
).reverse(),
})
})

// For these two, the team match has already been sent or removed by the client sending a request to the server

socket.on("submit_team_match", (team_match: TeamMatch) => {
io.to("admin_room").emit("new_team_match", team_match)
})

socket.on("failed_submit_team_match", (team_match: TeamMatch) => {
io.to("admin_room").emit("failed_team_match", team_match)
})

socket.on("disconnect", _reason => {
const scout_id = sid_to_username.get(socket.id)
io.to("admin_room").emit("scout_left_queue", scout_id)
})
})
},
}

export default webSocketServer

0 comments on commit 811b9d5

Please sign in to comment.