finish database, udp pool
This commit is contained in:
parent
53eeac5272
commit
12caccd2ca
|
@ -29,3 +29,6 @@ coverage
|
|||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
||||
|
||||
# Generated Files
|
||||
*.sqlite
|
||||
|
|
6
bun.lock
6
bun.lock
|
@ -6,12 +6,14 @@
|
|||
"dependencies": {
|
||||
"@trpc/client": "^10.45.2",
|
||||
"@trpc/server": "^10.45.2",
|
||||
"@types/lodash": "^4.17.16",
|
||||
"log-symbols": "^7.0.0",
|
||||
"pinia": "^3.0.1",
|
||||
"trpc-bun-adapter": "^1.2.2",
|
||||
"ts-log": "^2.2.7",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "4",
|
||||
"zod": "^3.24.2",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.0.12",
|
||||
|
@ -243,6 +245,8 @@
|
|||
|
||||
"@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="],
|
||||
|
||||
"@types/lodash": ["@types/lodash@4.17.16", "", {}, "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g=="],
|
||||
|
||||
"@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
|
||||
|
@ -557,6 +561,8 @@
|
|||
|
||||
"yoctocolors": ["yoctocolors@2.1.1", "", {}, "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ=="],
|
||||
|
||||
"zod": ["zod@3.24.2", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="],
|
||||
|
||||
"@vue/devtools-core/nanoid": ["nanoid@5.1.3", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-zAbEOEr7u2CbxwoMRlz/pNSpRP0FdAU4pRaYunCdEezWohXFs+a0Xw7RfkKaezMsmSM1vttcLthJtwRnVtOfHQ=="],
|
||||
|
||||
"cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
|
|
@ -15,6 +15,12 @@
|
|||
default = pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
bun
|
||||
sqlite
|
||||
sqls
|
||||
dbeaver-bin
|
||||
|
||||
# LSP
|
||||
typescript-language-server
|
||||
];
|
||||
shellHook = ''
|
||||
export PATH=$PATH:$HOME/.bun/bin
|
||||
|
|
|
@ -15,12 +15,14 @@
|
|||
"dependencies": {
|
||||
"@trpc/client": "^10.45.2",
|
||||
"@trpc/server": "^10.45.2",
|
||||
"@types/lodash": "^4.17.16",
|
||||
"log-symbols": "^7.0.0",
|
||||
"pinia": "^3.0.1",
|
||||
"trpc-bun-adapter": "^1.2.2",
|
||||
"ts-log": "^2.2.7",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "4"
|
||||
"vue-router": "4",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.0.12",
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
export function numberToBytes(num: number, bytesLength: number): Uint8Array {
|
||||
var array = new Uint8Array(bytesLength)
|
||||
|
||||
var i;
|
||||
for (i = 0; i < bytesLength; i++) {
|
||||
array[i] = num & (0xFF << (i << 3))
|
||||
}
|
||||
|
||||
return array
|
||||
}
|
||||
|
|
@ -1,7 +1,131 @@
|
|||
import { Database } from "bun:sqlite";
|
||||
import { isNumber, isString } from "lodash";
|
||||
import { z } from "zod";
|
||||
|
||||
const db = new Database("lab.sqlite", { strict: true })
|
||||
initDB();
|
||||
|
||||
export function addUser(name: string, password: string) {
|
||||
const query = db.query()
|
||||
|
||||
const boardSchema = z.object({
|
||||
id: z.number(),
|
||||
name: z.string(),
|
||||
room: z.string(),
|
||||
ipv4: z.string().ip({ version: "v4" }),
|
||||
ipv6: z.string().ip({ version: "v6" }),
|
||||
port: z.number().nonnegative(),
|
||||
cmdID: z.number().nonnegative()
|
||||
}).partial({
|
||||
ipv6: true,
|
||||
cmdID: true
|
||||
})
|
||||
|
||||
export type Board = z.infer<typeof boardSchema>
|
||||
|
||||
export function isBoard(obj: any): obj is Board {
|
||||
return boardSchema.safeParse(obj).success
|
||||
}
|
||||
|
||||
const userSchema = z.object({
|
||||
id: z.number(),
|
||||
name: z.string(),
|
||||
password: z.string(),
|
||||
boardID: z.number(),
|
||||
}).partial({
|
||||
boardID: true,
|
||||
})
|
||||
|
||||
export type User = z.infer<typeof userSchema>
|
||||
|
||||
export function isUser(obj: any): obj is User {
|
||||
return userSchema.safeParse(obj).success
|
||||
}
|
||||
|
||||
|
||||
function initDB() {
|
||||
const tables = allTables()
|
||||
|
||||
if (!tables.includes("Users")) {
|
||||
db.query(`
|
||||
CREATE TABLE Users(
|
||||
id INT PRIMARY KEY NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
password TEXT NOT NULL
|
||||
);
|
||||
`).run();
|
||||
}
|
||||
|
||||
if (!tables.includes("Boards"))
|
||||
db.query(`
|
||||
CREATE TABLE Boards(
|
||||
id INT PRIMARY KEY NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
room TEXT NOT NULL,
|
||||
ipv4 CHAR(16) NOT NULL,
|
||||
ipv6 CHAR(46) ,
|
||||
port INT NOT NULL
|
||||
)
|
||||
`).run();
|
||||
}
|
||||
|
||||
export function allTables(): Array<string> {
|
||||
const query = db.query(`SELECT name FROM sqlite_master WHERE type='table'`)
|
||||
var tables = new Array()
|
||||
// Flaten array
|
||||
for (const item of query.values()) {
|
||||
tables.push(item[0])
|
||||
}
|
||||
|
||||
query.finalize()
|
||||
return tables
|
||||
}
|
||||
|
||||
export function tableColumnName(table: string): Array<string> {
|
||||
const query = db.query(`PRAGMA table_info(${table})`)
|
||||
|
||||
var columnName = new Array()
|
||||
for (const column of query.values()) {
|
||||
columnName.push(column[1])
|
||||
}
|
||||
|
||||
return columnName
|
||||
}
|
||||
|
||||
export function allBoards() {
|
||||
const query = db.query(`SELECT * FROM Boards`)
|
||||
const boards = query.all()
|
||||
|
||||
query.finalize()
|
||||
return boards
|
||||
}
|
||||
|
||||
export function findBoard(id?: number | string): Board {
|
||||
let condition: string
|
||||
if (isNumber(id)) {
|
||||
condition = `id=${id}`
|
||||
} else if (isString(id)) {
|
||||
condition = `name='${id}'`
|
||||
} else {
|
||||
throw new Error("Failure: Wrong type when find board")
|
||||
}
|
||||
|
||||
const query = db.query(`SELECT * FROM Boards WHERE ${condition}`)
|
||||
|
||||
const spRet = boardSchema.safeParse(query.get())
|
||||
if (spRet.success) {
|
||||
return spRet.data
|
||||
} else {
|
||||
throw new Error(`Not Found ${id} FPGA Board`)
|
||||
}
|
||||
}
|
||||
|
||||
export function findUser(name: string): User {
|
||||
const query = db.query(`SELECT * FROM Users WHERE name='${name}'`)
|
||||
|
||||
const spRet = userSchema.safeParse(query.get())
|
||||
if (spRet.success) {
|
||||
return spRet.data
|
||||
} else {
|
||||
throw new Error(`Failure: Not found ${name} User`)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
import type { Board } from "./database";
|
||||
import { boardSchema, findBoard } from "./database";
|
||||
import { numberToBytes } from "./common";
|
||||
import { z } from "zod";
|
||||
import _ from "lodash";
|
||||
import { udpSocketPool } from "./udp";
|
||||
|
||||
export namespace EquipmentPackage {
|
||||
const HEADER_LENGTH = 8
|
||||
const BYTES_RETURN_ACK = 0b0001_0000
|
||||
const BYTES_TRANSFORM_TYPE = 0b0000_1000
|
||||
const BYTES_READ_WRITE_TYPE = 0b0000_0100
|
||||
|
||||
const HeaderOptionsSchema = z.object({
|
||||
returnAck: z.boolean(),
|
||||
transformType: z.enum(["Fixed", "Extend"]),
|
||||
readWriteType: z.enum(["r", "w"])
|
||||
}).partial()
|
||||
|
||||
export type HeaderOptions = z.infer<typeof HeaderOptionsSchema>
|
||||
|
||||
export function isHeaderOptions(obj: any): obj is HeaderOptions {
|
||||
return HeaderOptionsSchema.safeParse(obj).success
|
||||
}
|
||||
|
||||
export class Header {
|
||||
private commandType: number = 0
|
||||
private bytesLength: number = 0
|
||||
private commmandID: number = 0
|
||||
private _reserved: number = 0
|
||||
private address: number = 0
|
||||
|
||||
constructor(options: HeaderOptions) {
|
||||
this.setCommandType(options)
|
||||
}
|
||||
|
||||
setCommandType(options: HeaderOptions) {
|
||||
const validOptions = HeaderOptionsSchema.parse(options)
|
||||
this.commandType =
|
||||
(validOptions.returnAck === true ? BYTES_RETURN_ACK : 0) |
|
||||
(validOptions.transformType === "Extend" ? BYTES_TRANSFORM_TYPE : 0) |
|
||||
(validOptions.readWriteType === "w" ? BYTES_READ_WRITE_TYPE : 0)
|
||||
}
|
||||
|
||||
setBytesLength(length: number) {
|
||||
this.bytesLength = length
|
||||
}
|
||||
|
||||
setAddress(address: number) {
|
||||
this.address = address
|
||||
}
|
||||
|
||||
getCommandType(): HeaderOptions {
|
||||
return {
|
||||
returnAck: ((this.commandType & BYTES_RETURN_ACK) === BYTES_RETURN_ACK ? true : false),
|
||||
transformType: ((this.commandType & BYTES_TRANSFORM_TYPE) === BYTES_TRANSFORM_TYPE ? "Extend" : "Fixed"),
|
||||
readWriteType: ((this.commandType & BYTES_READ_WRITE_TYPE) === BYTES_READ_WRITE_TYPE ? "w" : "r")
|
||||
}
|
||||
}
|
||||
|
||||
incCommandID() {
|
||||
this.commmandID++
|
||||
}
|
||||
|
||||
clearCommandID() {
|
||||
this.commmandID = 0
|
||||
}
|
||||
|
||||
toUint8Array(): Uint8Array {
|
||||
var array = new Uint8Array(HEADER_LENGTH)
|
||||
|
||||
array[0] = this.commandType
|
||||
array[1] = this.bytesLength
|
||||
array[2] = this.commmandID
|
||||
array[3] = this._reserved
|
||||
array[4] = this._reserved
|
||||
|
||||
let addressBytes = numberToBytes(this.address, 3)
|
||||
array[5] = addressBytes[0]
|
||||
array[6] = addressBytes[1]
|
||||
array[7] = addressBytes[2]
|
||||
|
||||
return array
|
||||
}
|
||||
}
|
||||
|
||||
export class Package {
|
||||
|
||||
private header: Header
|
||||
private body: Uint8Array
|
||||
|
||||
|
||||
constructor(header: Header | HeaderOptions, body?: Uint8Array) {
|
||||
if (header instanceof Header) {
|
||||
this.header = header
|
||||
} else if (isHeaderOptions(header)) {
|
||||
this.header = new Header(header)
|
||||
} else {
|
||||
throw Error("Create EquipmentSocket Failure!")
|
||||
}
|
||||
|
||||
this.body = (body === undefined) ? new Uint8Array(0) : _.cloneDeep(body)
|
||||
}
|
||||
|
||||
toUint8Array(): Uint8Array {
|
||||
const header = this.header.toUint8Array()
|
||||
const bodyLength = this.body.length
|
||||
|
||||
let total = new Uint8Array(header.length + bodyLength)
|
||||
total.set(header)
|
||||
if (bodyLength > 0)
|
||||
total.set(this.body, header.length)
|
||||
|
||||
return total
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class Equipment {
|
||||
board: Board
|
||||
|
||||
constructor(name?: string)
|
||||
constructor(board?: Board)
|
||||
constructor(arg1: any) {
|
||||
if (boardSchema.safeParse(arg1).success) {
|
||||
this.board = arg1
|
||||
|
||||
} else if (typeof arg1 === "string") {
|
||||
try {
|
||||
const board = findBoard(arg1)
|
||||
this.board = boardSchema.parse(board)
|
||||
} catch (error) {
|
||||
throw new Error("Equipment Construction Failure")
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new Error("Equipment Construction Failure")
|
||||
}
|
||||
}
|
||||
|
||||
send(header: EquipmentPackage.HeaderOptions) {
|
||||
const pack = new EquipmentPackage.Package(header)
|
||||
|
||||
return udpSocketPool.send(pack.toUint8Array(), this.board.port, this.board.ipv4)
|
||||
}
|
||||
|
||||
// TODO: add params file
|
||||
uploadBitStream() {
|
||||
const header: EquipmentPackage.HeaderOptions = {
|
||||
returnAck: true,
|
||||
transformType: "Extend",
|
||||
readWriteType: "w"
|
||||
}
|
||||
|
||||
return this.send(header)
|
||||
}
|
||||
}
|
|
@ -1,8 +1,12 @@
|
|||
import { router, publicProcedure } from "./trpc.ts"
|
||||
// import { addUser } from "./database.ts";
|
||||
|
||||
export const appRouter = router({
|
||||
api: router({
|
||||
status: publicProcedure.query(() => "OK"),
|
||||
signUp: publicProcedure.query((opts) => {
|
||||
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import { allTables } from "./database";
|
||||
|
||||
|
||||
const tables = allTables();
|
||||
console.log(tables);
|
|
@ -0,0 +1,58 @@
|
|||
import type { udp } from "bun"
|
||||
|
||||
const udpServer = await Bun.udpSocket({
|
||||
port: 33000,
|
||||
socket: {
|
||||
data(_socket, _buf, _port, _addr) {
|
||||
// todo : Handle Recieved Data
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export const udpSocketPool = await createUDPSocketPool(5)
|
||||
|
||||
type bunUDPSocket = udp.Socket<"buffer">
|
||||
|
||||
export function getUDPServerPort() {
|
||||
return udpServer.port
|
||||
}
|
||||
|
||||
export class UDPSocketPool {
|
||||
freeSockets: Set<bunUDPSocket> = new Set()
|
||||
busySockets: Set<bunUDPSocket> = new Set()
|
||||
|
||||
getFreeSocket(): bunUDPSocket {
|
||||
const socket = this.freeSockets.values().next().value
|
||||
if (socket !== undefined) {
|
||||
this.busySockets.add(socket)
|
||||
this.freeSockets.delete(socket)
|
||||
} else {
|
||||
throw Error("Failure: Create udp socket failed")
|
||||
}
|
||||
return socket
|
||||
}
|
||||
|
||||
releaseSocket(socket: any) {
|
||||
this.freeSockets.add(socket)
|
||||
this.busySockets.delete(socket)
|
||||
}
|
||||
|
||||
send(data: udp.Data, port: number, hostname: string) {
|
||||
const socket = this.getFreeSocket()
|
||||
socket.send(data, port, hostname)
|
||||
this.releaseSocket(socket)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export async function createUDPSocketPool(depth: number): Promise<UDPSocketPool> {
|
||||
const pool = new UDPSocketPool()
|
||||
for (var i = 0; i < depth; i++) {
|
||||
const socket = await Bun.udpSocket({})
|
||||
pool.freeSockets.add(socket)
|
||||
}
|
||||
|
||||
return pool
|
||||
}
|
||||
|
||||
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { client } from '@/client';
|
||||
import { TRPCClientError } from '@trpc/client';
|
||||
|
||||
var bitstream = null;
|
||||
|
||||
|
@ -41,9 +42,20 @@ function handleFileChange(event: Event): void {
|
|||
}
|
||||
|
||||
async function uploadBitStream() {
|
||||
const serverStatus = await client.api.status.query();
|
||||
try {
|
||||
const serverStatus = await client.api.status.query();
|
||||
if (serverStatus != "OK") {
|
||||
throw new Error("Server Busy...")
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error instanceof TRPCClientError) {
|
||||
console.error("Can't connect to Server!")
|
||||
} else {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
console.log(serverStatus)
|
||||
}
|
||||
|
||||
function checkFileType(file: File) {
|
||||
|
|
Loading…
Reference in New Issue