144 lines
3.7 KiB
TypeScript
144 lines
3.7 KiB
TypeScript
import { Validators } from "../utils";
|
|
import { RequestCommandId, ResponseCommandId } from ".";
|
|
|
|
export class NiimbotPacket {
|
|
public static readonly HEAD = new Uint8Array([0x55, 0x55]);
|
|
public static readonly TAIL = new Uint8Array([0xaa, 0xaa]);
|
|
|
|
private _command: RequestCommandId;
|
|
private _data: Uint8Array;
|
|
private _validResponseIds: ResponseCommandId[];
|
|
|
|
/** There can be no response after this request. */
|
|
private _oneWay: boolean;
|
|
|
|
constructor(command: number, data: Uint8Array | number[], validResponseIds: ResponseCommandId[] = []) {
|
|
this._command = command;
|
|
this._data = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
this._validResponseIds = validResponseIds;
|
|
this._oneWay = false;
|
|
}
|
|
|
|
/** Data length (header, command, dataLen, checksum, tail are excluded). */
|
|
public get dataLength(): number {
|
|
return this._data.length;
|
|
}
|
|
public get length(): number {
|
|
return (
|
|
NiimbotPacket.HEAD.length + // head
|
|
1 + // cmd
|
|
1 + // dataLength
|
|
this.dataLength +
|
|
1 + // checksum
|
|
NiimbotPacket.TAIL.length
|
|
);
|
|
}
|
|
|
|
public set oneWay(value: boolean) {
|
|
this._oneWay = value;
|
|
}
|
|
|
|
public get oneWay(): boolean {
|
|
return this._oneWay;
|
|
}
|
|
|
|
public get validResponseIds(): ResponseCommandId[] {
|
|
return this._validResponseIds;
|
|
}
|
|
|
|
public get command(): number {
|
|
return this._command;
|
|
}
|
|
|
|
public get data(): Uint8Array {
|
|
return this._data;
|
|
}
|
|
|
|
public get checksum(): number {
|
|
let checksum = 0;
|
|
checksum ^= this._command;
|
|
checksum ^= this._data.length;
|
|
this._data.forEach((i: number) => (checksum ^= i));
|
|
return checksum;
|
|
}
|
|
|
|
// [0x55, 0x55, CMD, DATA_LEN, DA =//= TA, CHECKSUM, 0xAA, 0xAA]
|
|
public toBytes(): Uint8Array {
|
|
const buf = new ArrayBuffer(
|
|
NiimbotPacket.HEAD.length + // head
|
|
1 + // cmd
|
|
1 + // dataLength
|
|
this._data.length +
|
|
1 + // checksum
|
|
NiimbotPacket.TAIL.length
|
|
);
|
|
|
|
const arr = new Uint8Array(buf);
|
|
|
|
let pos = 0;
|
|
|
|
arr.set(NiimbotPacket.HEAD, pos);
|
|
pos += NiimbotPacket.HEAD.length;
|
|
|
|
arr[pos] = this._command;
|
|
pos += 1;
|
|
|
|
arr[pos] = this._data.length;
|
|
pos += 1;
|
|
|
|
arr.set(this._data, pos);
|
|
pos += this._data.length;
|
|
|
|
arr[pos] = this.checksum;
|
|
pos += 1;
|
|
|
|
arr.set(NiimbotPacket.TAIL, pos);
|
|
|
|
if (this._command === RequestCommandId.Connect) {
|
|
// const newArr = new Uint8Array(arr.length + 1);
|
|
// newArr[0] = 3;
|
|
// newArr.set(arr, 1);
|
|
return new Uint8Array([3, ...arr]);
|
|
}
|
|
|
|
return arr;
|
|
}
|
|
|
|
public static fromBytes(buf: DataView): NiimbotPacket {
|
|
const arrBuf: ArrayBuffer = buf.buffer;
|
|
const head = new Uint8Array(arrBuf.slice(0, 2));
|
|
const tail = new Uint8Array(arrBuf.slice(arrBuf.byteLength - 2));
|
|
const minPacketSize =
|
|
NiimbotPacket.HEAD.length + // head
|
|
1 + // cmd
|
|
1 + // dataLength
|
|
1 + // checksum
|
|
NiimbotPacket.TAIL.length;
|
|
|
|
if (arrBuf.byteLength < minPacketSize) {
|
|
throw new Error(`Packet is too small (${arrBuf.byteLength} < ${minPacketSize})`);
|
|
}
|
|
|
|
Validators.u8ArraysEqual(head, NiimbotPacket.HEAD, "Invalid packet head");
|
|
|
|
Validators.u8ArraysEqual(tail, NiimbotPacket.TAIL, "Invalid packet tail");
|
|
|
|
const cmd: number = buf.getUint8(2);
|
|
const dataLen: number = buf.getUint8(3);
|
|
|
|
if (arrBuf.byteLength !== minPacketSize + dataLen) {
|
|
throw new Error(`Invalid packet size (${arrBuf.byteLength} < ${minPacketSize + dataLen})`);
|
|
}
|
|
|
|
const data: Uint8Array = new Uint8Array(arrBuf.slice(4, 4 + dataLen));
|
|
const checksum: number = buf.getUint8(4 + dataLen);
|
|
const packet = new NiimbotPacket(cmd, data);
|
|
|
|
if (packet.checksum !== checksum) {
|
|
throw new Error("Invalid packet checksum");
|
|
}
|
|
|
|
return packet;
|
|
}
|
|
}
|