160 lines
4.1 KiB
TypeScript
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)
|
|
}
|
|
}
|