FPGA_WebLab/server/equipment.ts

160 lines
4.1 KiB
TypeScript

import type { Board } from "./database";
import { findBoard, isBoard } 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 (isBoard(arg1)) {
this.board = arg1
} else if (typeof arg1 === "string") {
try {
const board = findBoard(arg1)
this.board = 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)
}
}