This repository has been archived on 2024-09-16. You can view files and clone it, but cannot push or open issues or pull requests.

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;
}
}