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 { Option, Some, None } from "ts-results-es";
|
||||
import { Option, Some, None, Result, Ok, Err } from "ts-results-es";
|
||||
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 namespace fun {
|
||||
export function numberToBytes(num: number, bytesLength: number): Uint8Array {
|
||||
export namespace type {
|
||||
|
||||
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)
|
||||
|
||||
for (let i = 0; i < bytesLength; i++) {
|
||||
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> {
|
||||
|
@ -34,6 +77,13 @@ export namespace fun {
|
|||
export function isNumberArray(obj: any): obj is Array<number> {
|
||||
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(
|
||||
columnName: string,
|
||||
|
|
|
@ -1,11 +1,35 @@
|
|||
import { resolve } from "path";
|
||||
import { type udp } from "bun";
|
||||
import Tinypool from "tinypool";
|
||||
|
||||
type UDPDataType = {
|
||||
address: string,
|
||||
data: udp.Data,
|
||||
port: number,
|
||||
date?: string,
|
||||
}
|
||||
|
||||
const udpClientsPool = new Tinypool({
|
||||
filename: resolve(__dirname, "./udpClient.ts"),
|
||||
workerData: {}
|
||||
})
|
||||
|
||||
export function send() {
|
||||
udpClientsPool.run({}, { name: "send" })
|
||||
const udpServer = new Tinypool({
|
||||
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 { MsgProtocol } from "./msgProtocol";
|
||||
import { parentPort } from "worker_threads";
|
||||
|
||||
declare var self: Worker
|
||||
import type { UDPDataType } from "./udp"
|
||||
|
||||
const udpClient = await Bun.udpSocket({})
|
||||
|
||||
parentPort?.on("message", (msg: MsgProtocol.MessageQuery) => {
|
||||
if (MsgProtocol.isMessageQuery(msg)) {
|
||||
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))
|
||||
}
|
||||
})
|
||||
export function send(data: UDPDataType): boolean {
|
||||
return udpClient.send(data.data, data.port, data.address)
|
||||
}
|
||||
|
||||
|
|
|
@ -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 { 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
|
||||
export type UDPServerMsgType = "port"
|
||||
|
@ -8,12 +11,14 @@ interface BinaryTypeList {
|
|||
arraybuffer: ArrayBuffer;
|
||||
buffer: Buffer;
|
||||
uint8array: Uint8Array;
|
||||
// TODO: DataView
|
||||
// dataview: DataView;
|
||||
}
|
||||
type BinaryType = keyof BinaryTypeList;
|
||||
|
||||
|
||||
const receivedData: Map<string, Array<{
|
||||
data: udp.Data,
|
||||
port: number,
|
||||
date: string
|
||||
}>> = new Map()
|
||||
|
||||
const udpServer = await Bun.udpSocket({
|
||||
port: 33000,
|
||||
|
@ -24,12 +29,71 @@ const udpServer = await Bun.udpSocket({
|
|||
port: number,
|
||||
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()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
export function udpServerPort(): number {
|
||||
function port(): number {
|
||||
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