finish database, udp pool
This commit is contained in:
11
server/common.ts
Normal file
11
server/common.ts
Normal file
@@ -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`)
|
||||
}
|
||||
}
|
||||
|
||||
|
159
server/equipment.ts
Normal file
159
server/equipment.ts
Normal file
@@ -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) => {
|
||||
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
|
5
server/test.ts
Normal file
5
server/test.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { allTables } from "./database";
|
||||
|
||||
|
||||
const tables = allTables();
|
||||
console.log(tables);
|
58
server/udp.ts
Normal file
58
server/udp.ts
Normal file
@@ -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
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user