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).unwrap()).toEqual(new Uint8Array([0x78, 0x56, 0x34, 0x12]))
|
||||||
expect(type.numberToBytes(0x12345678, 4, true).unwrap()).toEqual(new Uint8Array([0x12, 0x34, 0x56, 0x78]))
|
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
|
// Number Match
|
||||||
for (let i = 0; i < CYCLES; i++) {
|
for (let i = 0; i < CYCLES; i++) {
|
||||||
const num1 = _.random(CYCLES / 2, false)
|
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 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 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> {
|
: Result<Uint8Array, ErrorType> {
|
||||||
// Check Integer
|
// Check Integer
|
||||||
if (!Int32.safeParse(num).success && !UInt32.lte(32).safeParse(bytesLength).success) {
|
if (!Int32.safeParse(num).success && !UInt32.lte(32).safeParse(bytesLength).success) {
|
||||||
|
@ -39,7 +39,7 @@ export namespace type {
|
||||||
|
|
||||||
var array = new Uint8Array(bytesLength)
|
var array = new Uint8Array(bytesLength)
|
||||||
|
|
||||||
if (reverse) {
|
if (isRightHigh) {
|
||||||
for (let i = 0; i < bytesLength; i++) {
|
for (let i = 0; i < bytesLength; i++) {
|
||||||
array[bytesLength - 1 - i] = ((num >> (i << 3)) & 0xFF)
|
array[bytesLength - 1 - i] = ((num >> (i << 3)) & 0xFF)
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,21 @@ export namespace type {
|
||||||
return new Ok(array)
|
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(
|
export function numberMatch(
|
||||||
srcNum: number,
|
srcNum: number,
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
import { resolve } from "path";
|
import { resolve } from "path";
|
||||||
import { type udp } from "bun";
|
|
||||||
import Tinypool from "tinypool";
|
import Tinypool from "tinypool";
|
||||||
|
import type { UDPReceiveProcotol, UDPSendProtocol } from "./udpProtocol";
|
||||||
|
|
||||||
type UDPDataType = {
|
type UDPDataType = {
|
||||||
address: string,
|
address: string,
|
||||||
data: udp.Data,
|
data: (
|
||||||
|
UDPSendProtocol.CmdPackage |
|
||||||
|
UDPSendProtocol.DataPackage |
|
||||||
|
UDPReceiveProcotol.ReadPackage |
|
||||||
|
UDPReceiveProcotol.WritePackage
|
||||||
|
),
|
||||||
port: number,
|
port: number,
|
||||||
date?: string,
|
date?: string,
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,6 @@ import type { UDPDataType } from "./udp"
|
||||||
const udpClient = await Bun.udpSocket({})
|
const udpClient = await Bun.udpSocket({})
|
||||||
|
|
||||||
export function send(data: UDPDataType): boolean {
|
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 { type } from "./common";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import { Err, Ok, type Result } from "ts-results-es";
|
||||||
|
import { isUint8Array } from "util/types";
|
||||||
|
|
||||||
export namespace SendProtocol {
|
const PKG_SIGN_ADDR = 0x00
|
||||||
const CMD_BURST_TYPE = 0b1100_0000
|
const PKG_SIGN_DATA = 0xFF
|
||||||
const CMD_TASK_ID = 0b0011_0000
|
const PKG_SIGN_READ = 0x0F
|
||||||
const CMD_READ_WRITE_TYPE = 0b0000_0001
|
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"]),
|
burstType: z.enum(["Fixed", "Extend"]),
|
||||||
taskID: z.number().nonnegative().lt(4),
|
taskID: z.number().nonnegative().lt(4),
|
||||||
readWriteType: z.enum(["r", "w"])
|
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 {
|
export function isCmdPackageOptions(obj: any): obj is CmdPackageOptions {
|
||||||
return AddrPackageOptionsSchema.safeParse(obj).success
|
return CmdPackageOptionsSchema.safeParse(obj).success
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AddrPackage {
|
export class CmdPackage {
|
||||||
private ID: number = 0x00
|
private ID: number = PKG_SIGN_ADDR
|
||||||
private commandType: number = 0
|
private commandType: number = 0
|
||||||
private burstLength: number = 0
|
private burstLength: number = 0
|
||||||
private _reserved: number = 0
|
private _reserved: number = 0
|
||||||
private address: 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) {
|
setCommandType(options: CmdPackageOptions) {
|
||||||
const validOptions = AddrPackageOptionsSchema.parse(options)
|
const validOptions = CmdPackageOptionsSchema.parse(options)
|
||||||
this.commandType =
|
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
|
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
|
this.address = addr
|
||||||
|
return new Ok(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
options(): AddrPackageOptions {
|
options(): CmdPackageOptions {
|
||||||
return {
|
return {
|
||||||
burstType: type.numberMatch(this.commandType, CMD_BURST_TYPE, "Extend", "Fixed"),
|
burstType: type.numberMatch(this.commandType, CMDLOC_BURST_TYPE, "Extend", "Fixed"),
|
||||||
readWriteType: type.numberMatch(this.commandType, CMD_READ_WRITE_TYPE, "w", "r")
|
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[2] = this.burstLength
|
||||||
array[3] = this._reserved
|
array[3] = this._reserved
|
||||||
|
|
||||||
let addressBytes = type.numberToBytes(this.address, 4)
|
// Already check address length at the begining of set address
|
||||||
array[4] = addressBytes[0]
|
let addressBytes = type.numberToBytes(this.address, 4).unwrap()
|
||||||
array[5] = addressBytes[1]
|
array.set(addressBytes, 4)
|
||||||
array[6] = addressBytes[2]
|
|
||||||
array[7] = addressBytes[3]
|
|
||||||
|
|
||||||
return array
|
return array
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DataPackage {
|
export class DataPackage {
|
||||||
private ID: number = 0xFF
|
private ID: number = PKG_SIGN_DATA
|
||||||
private _reserved: number = 0
|
private _reserved: number = 0
|
||||||
private body: Uint8Array
|
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