rewrite udp and its protocol, also add some common function and type
This commit is contained in:
parent
27b69c5d0d
commit
3d462f88aa
|
@ -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,
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
|
@ -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))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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 { }
|
|
@ -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 }
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue