finish basic udp protocol

This commit is contained in:
SikongJueluo 2025-03-28 19:02:11 +08:00
parent 04aacbcbc1
commit 35b6fa4a16
No known key found for this signature in database
5 changed files with 150 additions and 32 deletions

View File

@ -36,6 +36,13 @@ test("Test Number Processor Function", () => {
expect(type.numberToBytes(0x12345678, 4).unwrap()).toEqual(new Uint8Array([0x78, 0x56, 0x34, 0x12]))
expect(type.numberToBytes(0x12345678, 4, true).unwrap()).toEqual(new Uint8Array([0x12, 0x34, 0x56, 0x78]))
// Convert Uint8Array to Number
expect(type.bytesToNumber(new Uint8Array([0xFF]))).toBe(255)
expect(type.bytesToNumber(new Uint8Array([0xAA, 0xAA]))).toEqual(0xAAAA)
expect(type.bytesToNumber(new Uint8Array([0x78, 0x56, 0x34, 0x12]))).toEqual(0x12345678)
expect(type.bytesToNumber(new Uint8Array([0x12, 0x34, 0x56, 0x78]), true)).toEqual(0x12345678)
// Number Match
for (let i = 0; i < CYCLES; i++) {
const num1 = _.random(CYCLES / 2, false)

View File

@ -28,7 +28,7 @@ export namespace type {
export const Int16 = Integer.lt(Math.pow(2, 15)).gte(-Math.pow(2, 16))
export const Int32 = Integer.lt(Math.pow(2, 31)).gte(-Math.pow(2, 32))
export function numberToBytes(num: number, bytesLength: number, reverse: boolean = false)
export function numberToBytes(num: number, bytesLength: number, isRightHigh: boolean = false)
: Result<Uint8Array, ErrorType> {
// Check Integer
if (!Int32.safeParse(num).success && !UInt32.lte(32).safeParse(bytesLength).success) {
@ -39,7 +39,7 @@ export namespace type {
var array = new Uint8Array(bytesLength)
if (reverse) {
if (isRightHigh) {
for (let i = 0; i < bytesLength; i++) {
array[bytesLength - 1 - i] = ((num >> (i << 3)) & 0xFF)
}
@ -52,6 +52,21 @@ export namespace type {
return new Ok(array)
}
export function bytesToNumber(bytes: Uint8Array, isRightHigh: boolean = false): number {
let num = 0
const len = bytes.length
if (isRightHigh) {
for (let i = 0; i < len; i++) {
num += bytes[len - 1 - i] << (i << 3)
}
} else {
for (let i = 0; i < len; i++) {
num += bytes[i] << (i << 3)
}
}
return num
}
export function numberMatch(
srcNum: number,

View File

@ -1,10 +1,15 @@
import { resolve } from "path";
import { type udp } from "bun";
import Tinypool from "tinypool";
import type { UDPReceiveProcotol, UDPSendProtocol } from "./udpProtocol";
type UDPDataType = {
address: string,
data: udp.Data,
data: (
UDPSendProtocol.CmdPackage |
UDPSendProtocol.DataPackage |
UDPReceiveProcotol.ReadPackage |
UDPReceiveProcotol.WritePackage
),
port: number,
date?: string,
}

View File

@ -3,6 +3,6 @@ import type { UDPDataType } from "./udp"
const udpClient = await Bun.udpSocket({})
export function send(data: UDPDataType): boolean {
return udpClient.send(data.data, data.port, data.address)
return udpClient.send(data.data.toUint8Array(), data.port, data.address)
}

View File

@ -1,52 +1,86 @@
import { type } from "./common";
import { z } from "zod";
import _ from "lodash";
import { Err, Ok, type Result } from "ts-results-es";
import { isUint8Array } from "util/types";
export namespace SendProtocol {
const CMD_BURST_TYPE = 0b1100_0000
const CMD_TASK_ID = 0b0011_0000
const CMD_READ_WRITE_TYPE = 0b0000_0001
const PKG_SIGN_ADDR = 0x00
const PKG_SIGN_DATA = 0xFF
const PKG_SIGN_READ = 0x0F
const PKG_SIGN_WRITE = 0xF0
const AddrPackageOptionsSchema = z.object({
export namespace UDPSendProtocol {
const CMDLOC_BURST_TYPE = 5
const CMDLOC_TASK_ID = 3
const CMDLOC_READ_WRITE_TYPE = 0
const CmdPackageOptionsSchema = 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 type CmdPackageOptions = z.infer<typeof CmdPackageOptionsSchema>
export function isAddrPackageOptions(obj: any): obj is AddrPackageOptions {
return AddrPackageOptionsSchema.safeParse(obj).success
export function isCmdPackageOptions(obj: any): obj is CmdPackageOptions {
return CmdPackageOptionsSchema.safeParse(obj).success
}
export class AddrPackage {
private ID: number = 0x00
export class CmdPackage {
private ID: number = PKG_SIGN_ADDR
private commandType: number = 0
private burstLength: number = 0
private _reserved: number = 0
private address: number = 0
constructor(options: AddrPackageOptions) {
this.setCommandType(options)
constructor(array: Uint8Array)
constructor(options: CmdPackageOptions)
constructor(arg: any) {
if (isCmdPackageOptions(arg)) {
this.setCommandType(arg)
} else if (isUint8Array(arg)) {
this.ID = arg[0]
this.commandType = arg[1]
this.burstLength = arg[2]
this._reserved = arg[3]
this.address = type.bytesToNumber(arg.slice(4))
} else {
throw new Err("Wrong Type")
}
}
setCommandType(options: AddrPackageOptions) {
const validOptions = AddrPackageOptionsSchema.parse(options)
this.commandType =
setCommandType(options: CmdPackageOptions) {
const validOptions = CmdPackageOptionsSchema.parse(options)
this.commandType = (
((validOptions.burstType === "Fixed") ? 1 << CMDLOC_BURST_TYPE : 0) |
(validOptions.taskID << CMDLOC_TASK_ID) |
((validOptions.readWriteType === "r") ? 1 << CMDLOC_READ_WRITE_TYPE : 0)
)
}
setBurstLength(len: number) {
setBurstLength(len: number): Result<null, "Not 8Bits Unsigned Integer"> {
if (!type.UInt8.safeParse(len).success) {
return new Err("Not 8Bits Unsigned Integer")
}
this.burstLength = len
return new Ok(null)
}
setAddress(addr: number) {
setAddress(addr: number): Result<null, "Not 32Bits Unsigned Integer"> {
if (!type.UInt32.safeParse(addr).success) {
return new Err("Not 32Bits Unsigned Integer")
}
this.address = addr
return new Ok(null)
}
options(): AddrPackageOptions {
options(): CmdPackageOptions {
return {
burstType: type.numberMatch(this.commandType, CMD_BURST_TYPE, "Extend", "Fixed"),
readWriteType: type.numberMatch(this.commandType, CMD_READ_WRITE_TYPE, "w", "r")
burstType: type.numberMatch(this.commandType, CMDLOC_BURST_TYPE, "Extend", "Fixed"),
taskID: (this.commandType >> CMDLOC_TASK_ID) & 0b0011,
readWriteType: type.numberMatch(this.commandType, CMDLOC_READ_WRITE_TYPE, "w", "r")
}
}
@ -58,18 +92,16 @@ export namespace SendProtocol {
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]
// Already check address length at the begining of set address
let addressBytes = type.numberToBytes(this.address, 4).unwrap()
array.set(addressBytes, 4)
return array
}
}
export class DataPackage {
private ID: number = 0xFF
private ID: number = PKG_SIGN_DATA
private _reserved: number = 0
private body: Uint8Array
@ -90,4 +122,63 @@ export namespace SendProtocol {
}
export namespace ReceiveProcotol { }
export namespace UDPReceiveProcotol {
export class ReadPackage {
private ID: number = PKG_SIGN_READ
private taskID: number = 0
private resp: number = 0
private _reserved: number = 0
body: Uint8Array
constructor(array: Uint8Array) {
if (array.length < 5) {
throw new Err("Not Long Enough")
}
this.ID = array[0]
this.taskID = array[1]
this.resp = array[2]
this._reserved = array[3]
this.body = new Uint8Array(array.slice(4))
}
toUint8Array(): Uint8Array {
let array = new Uint8Array(4 + this.body.length)
array[0] = this.ID
array[1] = this.taskID
array[2] = this.resp
array[3] = this._reserved
array.set(this.body, 4)
return array
}
}
export class WritePackage {
private ID: number = PKG_SIGN_WRITE
private taskID: number = 0
private resp: number = 0
private _reserved: number = 0
constructor(array: Uint8Array) {
if (array.length < 4) {
throw new Err("Not Long Enough")
}
this.ID = array[0]
this.taskID = array[1]
this.resp = array[2]
this._reserved = array[3]
}
toUint8Array(): Uint8Array {
return new Uint8Array([
this.ID,
this.taskID,
this.resp,
this._reserved
])
}
}
}