rewrite udp and its protocol, also add some common function and type

This commit is contained in:
SikongJueluo 2025-03-27 18:00:08 +08:00
parent 27b69c5d0d
commit 3d462f88aa
No known key found for this signature in database
6 changed files with 258 additions and 165 deletions

View File

@ -1,30 +1,73 @@
import _ from "lodash" import _ from "lodash"
import { Option, Some, None } from "ts-results-es"; import { Option, Some, None, Result, Ok, Err } from "ts-results-es";
import { z } from "zod"; import { z } from "zod";
export const uint8 = z.number().nonnegative().lt(Math.pow(2, 8))
export const uint16 = z.number().nonnegative().lt(Math.pow(2, 16))
export const uint32 = z.number().nonnegative().lt(Math.pow(2, 32))
export const int8 = z.number().lt(Math.pow(2, 7)).gte(-Math.pow(2, 8))
export const int16 = z.number().lt(Math.pow(2, 15)).gte(-Math.pow(2, 16))
export const int32 = z.number().lt(Math.pow(2, 31)).gte(-Math.pow(2, 32))
export function UNUSED(_: unknown): void { } export function UNUSED(_: unknown): void { }
export namespace fun { export namespace type {
export function numberToBytes(num: number, bytesLength: number): Uint8Array {
export const Integer = z.number().int()
export const UInteger = z.number().int().nonnegative()
export const UInt8 = z.number().int().nonnegative().lt(Math.pow(2, 8))
export const UInt16 = z.number().int().nonnegative().lt(Math.pow(2, 16))
export const UInt32 = z.number().int().nonnegative().lt(Math.pow(2, 32))
export const Int8 = z.number().int().lt(Math.pow(2, 7)).gte(-Math.pow(2, 8))
export const Int16 = z.number().int().lt(Math.pow(2, 15)).gte(-Math.pow(2, 16))
export const Int32 = z.number().int().lt(Math.pow(2, 31)).gte(-Math.pow(2, 32))
export function numberToBytes(num: number, bytesLength: number): Result<Uint8Array, "Not Integer"> {
// Check Integer
if (!(Integer.safeParse(num).success && Integer.safeParse(bytesLength).success)) {
return new Err("Not Integer")
}
var array = new Uint8Array(bytesLength) var array = new Uint8Array(bytesLength)
for (let i = 0; i < bytesLength; i++) { for (let i = 0; i < bytesLength; i++) {
array[i] = num & (0xFF << (i << 3)) array[i] = num & (0xFF << (i << 3))
} }
return array return new Ok(array)
} }
export function randomFromArray(array: Array<any>) {
return array[_.random(0, array.length - 1, false)] export function numberMatch(
srcNum: number,
destNum: number
): boolean;
export function numberMatch<T>(
srcNum: number,
destNum: number,
True: T,
False: T
): T;
export function numberMatch<T>(
srcNum: number,
destNum: number,
True: T = true as T,
False: T = false as T
): T {
const ret = (srcNum & destNum) === destNum;
return ret ? True : False;
}
export function numberMatchEnum() {
}
export function numberSetBit(num: number, loc: number): number {
return num | (1 << loc)
}
export function numberUnsetBit(num: number, loc: number): number {
return num | (~1 << loc)
}
export function numberHighBits(num: number) {
} }
export function isStringArray(obj: any): obj is Array<string> { export function isStringArray(obj: any): obj is Array<string> {
@ -34,6 +77,13 @@ export namespace fun {
export function isNumberArray(obj: any): obj is Array<number> { export function isNumberArray(obj: any): obj is Array<number> {
return z.number().array().safeParse(obj).success return z.number().array().safeParse(obj).success
} }
}
export namespace fun {
export function randomFromArray(array: Array<any>) {
return array[_.random(0, array.length - 1, false)]
}
export function sqlConditionFromArray( export function sqlConditionFromArray(
columnName: string, columnName: string,

View File

@ -1,11 +1,35 @@
import { resolve } from "path"; import { resolve } from "path";
import { type udp } from "bun";
import Tinypool from "tinypool"; import Tinypool from "tinypool";
type UDPDataType = {
address: string,
data: udp.Data,
port: number,
date?: string,
}
const udpClientsPool = new Tinypool({ const udpClientsPool = new Tinypool({
filename: resolve(__dirname, "./udpClient.ts"), filename: resolve(__dirname, "./udpClient.ts"),
workerData: {} workerData: {}
}) })
export function send() { const udpServer = new Tinypool({
udpClientsPool.run({}, { name: "send" }) filename: resolve(__dirname, "./udpServer.ts"),
workerData: {},
maxThreads: 1,
minThreads: 1,
})
async function udpServerPort(): Promise<number> {
return udpServer.run(null, { name: "port" })
} }
async function uploadBitStream(): Promise<boolean> {
let ret = await udpClientsPool.run({} as UDPDataType, { name: "send" })
return ret
}
export { udpServerPort, uploadBitStream }
export type { UDPDataType }

View File

@ -1,30 +1,8 @@
import { type udp } from "bun"; import type { UDPDataType } from "./udp"
import { MsgProtocol } from "./msgProtocol";
import { parentPort } from "worker_threads";
declare var self: Worker
const udpClient = await Bun.udpSocket({}) const udpClient = await Bun.udpSocket({})
parentPort?.on("message", (msg: MsgProtocol.MessageQuery) => { export function send(data: UDPDataType): boolean {
if (MsgProtocol.isMessageQuery(msg)) { return udpClient.send(data.data, data.port, data.address)
switch (msg.command) { }
case "send": {
const args = msg.data.args as {
data: udp.Data,
port: number,
address: string,
}
udpClient.send(args.data, args.port, args.address)
postMessage(MsgProtocol.genMessageResult("Send Successfully", msg))
break
}
default: {
postMessage(MsgProtocol.genMessageError("No Such Command", msg))
break
}
}
} else {
postMessage(MsgProtocol.genMessageError("No Currenct Destination", msg))
}
})

View File

@ -1,116 +0,0 @@
import { fun } from "./common";
import { z } from "zod";
import _ from "lodash";
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 = fun.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
}
}
}

93
server/udpProtocol.ts Normal file
View File

@ -0,0 +1,93 @@
import { type } from "./common";
import { z } from "zod";
import _ from "lodash";
export namespace SendProtocol {
const CMD_BURST_TYPE = 0b1100_0000
const CMD_TASK_ID = 0b0011_0000
const CMD_READ_WRITE_TYPE = 0b0000_0001
const AddrPackageOptionsSchema = z.object({
burstType: z.enum(["Fixed", "Extend"]),
taskID: z.number().nonnegative().lt(4),
readWriteType: z.enum(["r", "w"])
})
export type AddrPackageOptions = z.infer<typeof AddrPackageOptionsSchema>
export function isAddrPackageOptions(obj: any): obj is AddrPackageOptions {
return AddrPackageOptionsSchema.safeParse(obj).success
}
export class AddrPackage {
private ID: number = 0x00
private commandType: number = 0
private burstLength: number = 0
private _reserved: number = 0
private address: number = 0
constructor(options: AddrPackageOptions) {
this.setCommandType(options)
}
setCommandType(options: AddrPackageOptions) {
const validOptions = AddrPackageOptionsSchema.parse(options)
this.commandType =
}
setBurstLength(len: number) {
this.burstLength = len
}
setAddress(addr: number) {
this.address = addr
}
options(): AddrPackageOptions {
return {
burstType: type.numberMatch(this.commandType, CMD_BURST_TYPE, "Extend", "Fixed"),
readWriteType: type.numberMatch(this.commandType, CMD_READ_WRITE_TYPE, "w", "r")
}
}
toUint8Array(): Uint8Array {
var array = new Uint8Array(8)
array[0] = this.ID
array[1] = this.commandType
array[2] = this.burstLength
array[3] = this._reserved
let addressBytes = type.numberToBytes(this.address, 4)
array[4] = addressBytes[0]
array[5] = addressBytes[1]
array[6] = addressBytes[2]
array[7] = addressBytes[3]
return array
}
}
export class DataPackage {
private ID: number = 0xFF
private _reserved: number = 0
private body: Uint8Array
constructor(body: Uint8Array) {
this.body = body
}
toUint8Array(): Uint8Array {
var array = new Uint8Array(4 + this.body.length)
array[0] = this.ID
array.fill(this._reserved, 1, 4)
array.set(this.body, 4)
return array
}
}
}
export namespace ReceiveProcotol { }

View File

@ -1,5 +1,8 @@
import _ from "lodash" import _ from "lodash"
import { type udp } from "bun" import { type udp } from "bun"
import type { UDPDataType } from "./udp";
import { None, Some, type Option } from "ts-results-es";
import { z } from "zod";
declare var self: Worker declare var self: Worker
export type UDPServerMsgType = "port" export type UDPServerMsgType = "port"
@ -8,12 +11,14 @@ interface BinaryTypeList {
arraybuffer: ArrayBuffer; arraybuffer: ArrayBuffer;
buffer: Buffer; buffer: Buffer;
uint8array: Uint8Array; uint8array: Uint8Array;
// TODO: DataView
// dataview: DataView;
} }
type BinaryType = keyof BinaryTypeList; type BinaryType = keyof BinaryTypeList;
const receivedData: Map<string, Array<{
data: udp.Data,
port: number,
date: string
}>> = new Map()
const udpServer = await Bun.udpSocket({ const udpServer = await Bun.udpSocket({
port: 33000, port: 33000,
@ -24,12 +29,71 @@ const udpServer = await Bun.udpSocket({
port: number, port: number,
address: string, address: string,
) { ) {
// todo : Handle Recieved Data // Add Received Data
let arrayData = receivedData.get(address)
if (_.isUndefined(arrayData)) {
} else {
receivedData.set(address, [])
arrayData.push({
data: data,
port: port,
date: new Date().toUTCString()
})
}
} }
} }
}) })
function port(): number {
export function udpServerPort(): number {
return udpServer.port return udpServer.port
} }
function lastestData(address: string): Option<UDPDataType> {
if (!z.string().ip().safeParse(address).success) {
return None
}
const arrayData = receivedData.get(address)
if (_.isUndefined(arrayData)) {
return None
}
const data = arrayData.pop()
if (_.isUndefined(data)) {
return None
}
return Some({
address: address,
data: data.data,
port: data.port,
date: data.date,
})
}
function oldestData(address: string): Option<UDPDataType> {
if (!z.string().ip().safeParse(address).success) {
return None
}
const arrayData = receivedData.get(address)
if (_.isUndefined(arrayData)) {
return None
}
const data = arrayData.shift()
if (_.isUndefined(data)) {
return None
}
return Some({
address: address,
data: data.data,
port: data.port,
date: data.date,
})
}
export { port, lastestData, oldestData }