finish basic udp protocol
This commit is contained in:
parent
04aacbcbc1
commit
35b6fa4a16
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue