mirror of
https://github.com/meshcore-dev/meshcore.js.git
synced 2026-04-20 22:13:49 +00:00
chore: enforce prettier configuration
In order to avoid reformatting of the code this PR add a prettier configuration as well as scripts to automatically format apply the rules
This commit is contained in:
parent
f6fea65063
commit
3f899c8445
19 changed files with 3477 additions and 3474 deletions
7
.prettierrc
Normal file
7
.prettierrc
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"arrowParens": "always",
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
19
package-lock.json
generated
19
package-lock.json
generated
|
|
@ -11,6 +11,9 @@
|
|||
"dependencies": {
|
||||
"@noble/curves": "^1.8.1",
|
||||
"serialport": "^13.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^3.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/curves": {
|
||||
|
|
@ -266,6 +269,22 @@
|
|||
"node-gyp-build-test": "build-test.js"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
|
||||
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/serialport": {
|
||||
"version": "13.0.0",
|
||||
"resolved": "https://registry.npmjs.org/serialport/-/serialport-13.0.0.tgz",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
"main": "src/index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"prettier": "prettier --check src",
|
||||
"prettier-write": "prettier --write src",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "Liam Cottle <liam@liamcottle.com>",
|
||||
|
|
@ -12,5 +14,8 @@
|
|||
"dependencies": {
|
||||
"@noble/curves": "^1.8.1",
|
||||
"serialport": "^13.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^3.6.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
174
src/advert.js
174
src/advert.js
|
|
@ -1,101 +1,97 @@
|
|||
import BufferReader from "./buffer_reader.js";
|
||||
import BufferWriter from "./buffer_writer.js";
|
||||
import BufferReader from './buffer_reader.js';
|
||||
import BufferWriter from './buffer_writer.js';
|
||||
|
||||
class Advert {
|
||||
static ADV_TYPE_NONE = 0;
|
||||
static ADV_TYPE_CHAT = 1;
|
||||
static ADV_TYPE_REPEATER = 2;
|
||||
static ADV_TYPE_ROOM = 3;
|
||||
|
||||
static ADV_TYPE_NONE = 0;
|
||||
static ADV_TYPE_CHAT = 1;
|
||||
static ADV_TYPE_REPEATER = 2;
|
||||
static ADV_TYPE_ROOM = 3;
|
||||
static ADV_LATLON_MASK = 0x10;
|
||||
static ADV_BATTERY_MASK = 0x20;
|
||||
static ADV_TEMPERATURE_MASK = 0x40;
|
||||
static ADV_NAME_MASK = 0x80;
|
||||
|
||||
static ADV_LATLON_MASK = 0x10;
|
||||
static ADV_BATTERY_MASK = 0x20;
|
||||
static ADV_TEMPERATURE_MASK = 0x40;
|
||||
static ADV_NAME_MASK = 0x80;
|
||||
constructor(publicKey, timestamp, signature, appData) {
|
||||
this.publicKey = publicKey;
|
||||
this.timestamp = timestamp;
|
||||
this.signature = signature;
|
||||
this.appData = appData;
|
||||
this.parsed = this.parseAppData();
|
||||
}
|
||||
|
||||
constructor(publicKey, timestamp, signature, appData) {
|
||||
this.publicKey = publicKey;
|
||||
this.timestamp = timestamp;
|
||||
this.signature = signature;
|
||||
this.appData = appData;
|
||||
this.parsed = this.parseAppData();
|
||||
static fromBytes(bytes) {
|
||||
// read bytes
|
||||
const bufferReader = new BufferReader(bytes);
|
||||
const publicKey = bufferReader.readBytes(32);
|
||||
const timestamp = bufferReader.readUInt32LE();
|
||||
const signature = bufferReader.readBytes(64);
|
||||
const appData = bufferReader.readRemainingBytes();
|
||||
|
||||
return new Advert(publicKey, timestamp, signature, appData);
|
||||
}
|
||||
|
||||
getFlags() {
|
||||
return this.appData[0];
|
||||
}
|
||||
|
||||
getType() {
|
||||
const flags = this.getFlags();
|
||||
return flags & 0x0f;
|
||||
}
|
||||
|
||||
getTypeString() {
|
||||
const type = this.getType();
|
||||
if (type === Advert.ADV_TYPE_NONE) return 'NONE';
|
||||
if (type === Advert.ADV_TYPE_CHAT) return 'CHAT';
|
||||
if (type === Advert.ADV_TYPE_REPEATER) return 'REPEATER';
|
||||
if (type === Advert.ADV_TYPE_ROOM) return 'ROOM';
|
||||
return null;
|
||||
}
|
||||
|
||||
async isVerified() {
|
||||
const { ed25519 } = await import('@noble/curves/ed25519');
|
||||
|
||||
// build signed data
|
||||
const bufferWriter = new BufferWriter();
|
||||
bufferWriter.writeBytes(this.publicKey);
|
||||
bufferWriter.writeUInt32LE(this.timestamp);
|
||||
bufferWriter.writeBytes(this.appData);
|
||||
|
||||
// verify signature
|
||||
return ed25519.verify(
|
||||
this.signature,
|
||||
bufferWriter.toBytes(),
|
||||
this.publicKey,
|
||||
);
|
||||
}
|
||||
|
||||
parseAppData() {
|
||||
// read app data
|
||||
const bufferReader = new BufferReader(this.appData);
|
||||
const flags = bufferReader.readByte();
|
||||
|
||||
// parse lat lon
|
||||
var lat = null;
|
||||
var lon = null;
|
||||
if (flags & Advert.ADV_LATLON_MASK) {
|
||||
lat = bufferReader.readInt32LE();
|
||||
lon = bufferReader.readInt32LE();
|
||||
}
|
||||
|
||||
static fromBytes(bytes) {
|
||||
|
||||
// read bytes
|
||||
const bufferReader = new BufferReader(bytes);
|
||||
const publicKey = bufferReader.readBytes(32);
|
||||
const timestamp = bufferReader.readUInt32LE();
|
||||
const signature = bufferReader.readBytes(64);
|
||||
const appData = bufferReader.readRemainingBytes();
|
||||
|
||||
return new Advert(publicKey, timestamp, signature, appData);
|
||||
|
||||
}
|
||||
|
||||
getFlags() {
|
||||
return this.appData[0];
|
||||
}
|
||||
|
||||
getType() {
|
||||
const flags = this.getFlags();
|
||||
return flags & 0x0F;
|
||||
}
|
||||
|
||||
getTypeString() {
|
||||
const type = this.getType();
|
||||
if(type === Advert.ADV_TYPE_NONE) return "NONE";
|
||||
if(type === Advert.ADV_TYPE_CHAT) return "CHAT";
|
||||
if(type === Advert.ADV_TYPE_REPEATER) return "REPEATER";
|
||||
if(type === Advert.ADV_TYPE_ROOM) return "ROOM";
|
||||
return null;
|
||||
}
|
||||
|
||||
async isVerified() {
|
||||
|
||||
const { ed25519 } = await import("@noble/curves/ed25519");
|
||||
|
||||
// build signed data
|
||||
const bufferWriter = new BufferWriter();
|
||||
bufferWriter.writeBytes(this.publicKey);
|
||||
bufferWriter.writeUInt32LE(this.timestamp);
|
||||
bufferWriter.writeBytes(this.appData);
|
||||
|
||||
// verify signature
|
||||
return ed25519.verify(this.signature, bufferWriter.toBytes(), this.publicKey);
|
||||
|
||||
}
|
||||
|
||||
parseAppData() {
|
||||
|
||||
// read app data
|
||||
const bufferReader = new BufferReader(this.appData);
|
||||
const flags = bufferReader.readByte();
|
||||
|
||||
// parse lat lon
|
||||
var lat = null;
|
||||
var lon = null;
|
||||
if(flags & Advert.ADV_LATLON_MASK){
|
||||
lat = bufferReader.readInt32LE();
|
||||
lon = bufferReader.readInt32LE();
|
||||
}
|
||||
|
||||
// parse name (remainder of app data)
|
||||
var name = null;
|
||||
if(flags & Advert.ADV_NAME_MASK){
|
||||
name = bufferReader.readString();
|
||||
}
|
||||
|
||||
return {
|
||||
type: this.getTypeString(),
|
||||
lat: lat,
|
||||
lon: lon,
|
||||
name: name,
|
||||
};
|
||||
|
||||
// parse name (remainder of app data)
|
||||
var name = null;
|
||||
if (flags & Advert.ADV_NAME_MASK) {
|
||||
name = bufferReader.readString();
|
||||
}
|
||||
|
||||
return {
|
||||
type: this.getTypeString(),
|
||||
lat: lat,
|
||||
lon: lon,
|
||||
name: name,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default Advert;
|
||||
|
|
|
|||
|
|
@ -1,118 +1,113 @@
|
|||
class BufferReader {
|
||||
constructor(data) {
|
||||
this.pointer = 0;
|
||||
this.buffer = new Uint8Array(data);
|
||||
}
|
||||
|
||||
constructor(data) {
|
||||
this.pointer = 0;
|
||||
this.buffer = new Uint8Array(data);
|
||||
}
|
||||
|
||||
getRemainingBytesCount() {
|
||||
return this.buffer.length - this.pointer;
|
||||
}
|
||||
|
||||
readByte() {
|
||||
return this.readBytes(1)[0];
|
||||
}
|
||||
|
||||
readBytes(count) {
|
||||
const data = this.buffer.slice(this.pointer, this.pointer + count);
|
||||
this.pointer += count;
|
||||
return data;
|
||||
}
|
||||
|
||||
readRemainingBytes() {
|
||||
return this.readBytes(this.getRemainingBytesCount());
|
||||
}
|
||||
|
||||
readString() {
|
||||
return new TextDecoder().decode(this.readRemainingBytes());
|
||||
}
|
||||
|
||||
readCString(maxLength) {
|
||||
const value = [];
|
||||
const bytes = this.readBytes(maxLength);
|
||||
for(const byte of bytes){
|
||||
|
||||
// if we find a null terminator character, we have reached the end of the cstring
|
||||
if(byte === 0){
|
||||
return new TextDecoder().decode(new Uint8Array(value));
|
||||
}
|
||||
|
||||
value.push(byte);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
readInt8() {
|
||||
const bytes = this.readBytes(1);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getInt8(0);
|
||||
}
|
||||
|
||||
readUInt8() {
|
||||
const bytes = this.readBytes(1);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint8(0);
|
||||
}
|
||||
|
||||
readUInt16LE() {
|
||||
const bytes = this.readBytes(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint16(0, true);
|
||||
}
|
||||
|
||||
readUInt16BE() {
|
||||
const bytes = this.readBytes(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint16(0, false);
|
||||
}
|
||||
|
||||
readUInt32LE() {
|
||||
const bytes = this.readBytes(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint32(0, true);
|
||||
}
|
||||
|
||||
readUInt32BE() {
|
||||
const bytes = this.readBytes(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint32(0, false);
|
||||
}
|
||||
|
||||
readInt16LE() {
|
||||
const bytes = this.readBytes(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getInt16(0, true);
|
||||
}
|
||||
|
||||
readInt16BE() {
|
||||
const bytes = this.readBytes(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getInt16(0, false);
|
||||
}
|
||||
|
||||
readInt32LE() {
|
||||
const bytes = this.readBytes(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getInt32(0, true);
|
||||
}
|
||||
|
||||
readInt24BE() {
|
||||
|
||||
// read 24-bit (3 bytes) big endian integer
|
||||
var value = (this.readByte() << 16) | (this.readByte() << 8) | this.readByte();
|
||||
|
||||
// convert 24-bit signed integer to 32-bit signed integer
|
||||
// 0x800000 is the sign bit for a 24-bit value
|
||||
// if it's set, value is negative in 24-bit two's complement
|
||||
// so we subtract 0x1000000 (which is 2^24) to get the correct negative value as a Dart integer
|
||||
if((value & 0x800000) !== 0){
|
||||
value -= 0x1000000;
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
getRemainingBytesCount() {
|
||||
return this.buffer.length - this.pointer;
|
||||
}
|
||||
|
||||
readByte() {
|
||||
return this.readBytes(1)[0];
|
||||
}
|
||||
|
||||
readBytes(count) {
|
||||
const data = this.buffer.slice(this.pointer, this.pointer + count);
|
||||
this.pointer += count;
|
||||
return data;
|
||||
}
|
||||
|
||||
readRemainingBytes() {
|
||||
return this.readBytes(this.getRemainingBytesCount());
|
||||
}
|
||||
|
||||
readString() {
|
||||
return new TextDecoder().decode(this.readRemainingBytes());
|
||||
}
|
||||
|
||||
readCString(maxLength) {
|
||||
const value = [];
|
||||
const bytes = this.readBytes(maxLength);
|
||||
for (const byte of bytes) {
|
||||
// if we find a null terminator character, we have reached the end of the cstring
|
||||
if (byte === 0) {
|
||||
return new TextDecoder().decode(new Uint8Array(value));
|
||||
}
|
||||
|
||||
value.push(byte);
|
||||
}
|
||||
}
|
||||
|
||||
readInt8() {
|
||||
const bytes = this.readBytes(1);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getInt8(0);
|
||||
}
|
||||
|
||||
readUInt8() {
|
||||
const bytes = this.readBytes(1);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint8(0);
|
||||
}
|
||||
|
||||
readUInt16LE() {
|
||||
const bytes = this.readBytes(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint16(0, true);
|
||||
}
|
||||
|
||||
readUInt16BE() {
|
||||
const bytes = this.readBytes(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint16(0, false);
|
||||
}
|
||||
|
||||
readUInt32LE() {
|
||||
const bytes = this.readBytes(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint32(0, true);
|
||||
}
|
||||
|
||||
readUInt32BE() {
|
||||
const bytes = this.readBytes(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint32(0, false);
|
||||
}
|
||||
|
||||
readInt16LE() {
|
||||
const bytes = this.readBytes(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getInt16(0, true);
|
||||
}
|
||||
|
||||
readInt16BE() {
|
||||
const bytes = this.readBytes(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getInt16(0, false);
|
||||
}
|
||||
|
||||
readInt32LE() {
|
||||
const bytes = this.readBytes(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getInt32(0, true);
|
||||
}
|
||||
|
||||
readInt24BE() {
|
||||
// read 24-bit (3 bytes) big endian integer
|
||||
var value =
|
||||
(this.readByte() << 16) | (this.readByte() << 8) | this.readByte();
|
||||
|
||||
// convert 24-bit signed integer to 32-bit signed integer
|
||||
// 0x800000 is the sign bit for a 24-bit value
|
||||
// if it's set, value is negative in 24-bit two's complement
|
||||
// so we subtract 0x1000000 (which is 2^24) to get the correct negative value as a Dart integer
|
||||
if ((value & 0x800000) !== 0) {
|
||||
value -= 0x1000000;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
export default BufferReader;
|
||||
|
|
|
|||
|
|
@ -1,40 +1,40 @@
|
|||
class BufferUtils {
|
||||
static bytesToHex(uint8Array) {
|
||||
return Array.from(uint8Array)
|
||||
.map((byte) => {
|
||||
return byte.toString(16).padStart(2, '0');
|
||||
})
|
||||
.join('');
|
||||
}
|
||||
|
||||
static bytesToHex(uint8Array) {
|
||||
return Array.from(uint8Array).map(byte => {
|
||||
return byte.toString(16).padStart(2, '0');
|
||||
}).join('');
|
||||
static hexToBytes(hex) {
|
||||
return Uint8Array.from(
|
||||
hex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)),
|
||||
);
|
||||
}
|
||||
|
||||
static base64ToBytes(base64) {
|
||||
return Uint8Array.from(atob(base64), (c) => {
|
||||
return c.charCodeAt(0);
|
||||
});
|
||||
}
|
||||
|
||||
static areBuffersEqual(byteArray1, byteArray2) {
|
||||
// ensure length is the same
|
||||
if (byteArray1.length !== byteArray2.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static hexToBytes(hex) {
|
||||
return Uint8Array.from(hex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
|
||||
}
|
||||
|
||||
static base64ToBytes(base64) {
|
||||
return Uint8Array.from(atob(base64), (c) => {
|
||||
return c.charCodeAt(0);
|
||||
});
|
||||
}
|
||||
|
||||
static areBuffersEqual(byteArray1, byteArray2) {
|
||||
|
||||
// ensure length is the same
|
||||
if(byteArray1.length !== byteArray2.length){
|
||||
return false;
|
||||
}
|
||||
|
||||
// ensure each item is the same
|
||||
for(let i = 0; i < byteArray1.length; i++){
|
||||
if(byteArray1[i] !== byteArray2[i]){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// arrays are the same
|
||||
return true;
|
||||
|
||||
// ensure each item is the same
|
||||
for (let i = 0; i < byteArray1.length; i++) {
|
||||
if (byteArray1[i] !== byteArray2[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// arrays are the same
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export default BufferUtils;
|
||||
|
|
|
|||
|
|
@ -1,72 +1,63 @@
|
|||
class BufferWriter {
|
||||
constructor() {
|
||||
this.buffer = [];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.buffer = [];
|
||||
toBytes() {
|
||||
return new Uint8Array(this.buffer);
|
||||
}
|
||||
|
||||
writeBytes(bytes) {
|
||||
this.buffer = [...this.buffer, ...bytes];
|
||||
}
|
||||
|
||||
writeByte(byte) {
|
||||
this.writeBytes([byte]);
|
||||
}
|
||||
|
||||
writeUInt16LE(num) {
|
||||
const bytes = new Uint8Array(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
view.setUint16(0, num, true);
|
||||
this.writeBytes(bytes);
|
||||
}
|
||||
|
||||
writeUInt32LE(num) {
|
||||
const bytes = new Uint8Array(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
view.setUint32(0, num, true);
|
||||
this.writeBytes(bytes);
|
||||
}
|
||||
|
||||
writeInt32LE(num) {
|
||||
const bytes = new Uint8Array(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
view.setInt32(0, num, true);
|
||||
this.writeBytes(bytes);
|
||||
}
|
||||
|
||||
writeString(string) {
|
||||
this.writeBytes(new TextEncoder().encode(string));
|
||||
}
|
||||
|
||||
writeCString(string, maxLength) {
|
||||
// create buffer of max length
|
||||
const bytes = new Uint8Array(new ArrayBuffer(maxLength));
|
||||
|
||||
// encode string to bytes
|
||||
const encodedString = new TextEncoder().encode(string);
|
||||
|
||||
// copy in string until we hit the max length, or we run out of string bytes
|
||||
for (var i = 0; i < maxLength && i < encodedString.length; i++) {
|
||||
bytes[i] = encodedString[i];
|
||||
}
|
||||
|
||||
toBytes() {
|
||||
return new Uint8Array(this.buffer);
|
||||
}
|
||||
|
||||
writeBytes(bytes) {
|
||||
this.buffer = [
|
||||
...this.buffer,
|
||||
...bytes,
|
||||
];
|
||||
}
|
||||
|
||||
writeByte(byte) {
|
||||
this.writeBytes([
|
||||
byte,
|
||||
]);
|
||||
}
|
||||
|
||||
writeUInt16LE(num) {
|
||||
const bytes = new Uint8Array(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
view.setUint16(0, num, true);
|
||||
this.writeBytes(bytes);
|
||||
}
|
||||
|
||||
writeUInt32LE(num) {
|
||||
const bytes = new Uint8Array(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
view.setUint32(0, num, true);
|
||||
this.writeBytes(bytes);
|
||||
}
|
||||
|
||||
writeInt32LE(num) {
|
||||
const bytes = new Uint8Array(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
view.setInt32(0, num, true);
|
||||
this.writeBytes(bytes);
|
||||
}
|
||||
|
||||
writeString(string) {
|
||||
this.writeBytes(new TextEncoder().encode(string));
|
||||
}
|
||||
|
||||
writeCString(string, maxLength) {
|
||||
|
||||
// create buffer of max length
|
||||
const bytes = new Uint8Array(new ArrayBuffer(maxLength));
|
||||
|
||||
// encode string to bytes
|
||||
const encodedString = new TextEncoder().encode(string);
|
||||
|
||||
// copy in string until we hit the max length, or we run out of string bytes
|
||||
for(var i = 0; i < maxLength && i < encodedString.length; i++){
|
||||
bytes[i] = encodedString[i];
|
||||
}
|
||||
|
||||
// ensure the last byte is always a null terminator
|
||||
bytes[bytes.length - 1] = 0;
|
||||
|
||||
// write to buffer
|
||||
this.writeBytes(bytes);
|
||||
|
||||
}
|
||||
// ensure the last byte is always a null terminator
|
||||
bytes[bytes.length - 1] = 0;
|
||||
|
||||
// write to buffer
|
||||
this.writeBytes(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
export default BufferWriter;
|
||||
|
|
|
|||
|
|
@ -1,196 +1,192 @@
|
|||
import BufferReader from "./buffer_reader.js";
|
||||
import BufferReader from './buffer_reader.js';
|
||||
|
||||
class CayenneLpp {
|
||||
static LPP_DIGITAL_INPUT = 0; // 1 byte
|
||||
static LPP_DIGITAL_OUTPUT = 1; // 1 byte
|
||||
static LPP_ANALOG_INPUT = 2; // 2 bytes, 0.01 signed
|
||||
static LPP_ANALOG_OUTPUT = 3; // 2 bytes, 0.01 signed
|
||||
static LPP_GENERIC_SENSOR = 100; // 4 bytes, unsigned
|
||||
static LPP_LUMINOSITY = 101; // 2 bytes, 1 lux unsigned
|
||||
static LPP_PRESENCE = 102; // 1 byte, bool
|
||||
static LPP_TEMPERATURE = 103; // 2 bytes, 0.1°C signed
|
||||
static LPP_RELATIVE_HUMIDITY = 104; // 1 byte, 0.5% unsigned
|
||||
static LPP_ACCELEROMETER = 113; // 2 bytes per axis, 0.001G
|
||||
static LPP_BAROMETRIC_PRESSURE = 115; // 2 bytes 0.1hPa unsigned
|
||||
static LPP_VOLTAGE = 116; // 2 bytes 0.01V unsigned
|
||||
static LPP_CURRENT = 117; // 2 bytes 0.001A unsigned
|
||||
static LPP_FREQUENCY = 118; // 4 bytes 1Hz unsigned
|
||||
static LPP_PERCENTAGE = 120; // 1 byte 1-100% unsigned
|
||||
static LPP_ALTITUDE = 121; // 2 byte 1m signed
|
||||
static LPP_CONCENTRATION = 125; // 2 bytes, 1 ppm unsigned
|
||||
static LPP_POWER = 128; // 2 byte, 1W, unsigned
|
||||
static LPP_DISTANCE = 130; // 4 byte, 0.001m, unsigned
|
||||
static LPP_ENERGY = 131; // 4 byte, 0.001kWh, unsigned
|
||||
static LPP_DIRECTION = 132; // 2 bytes, 1deg, unsigned
|
||||
static LPP_UNIXTIME = 133; // 4 bytes, unsigned
|
||||
static LPP_GYROMETER = 134; // 2 bytes per axis, 0.01 °/s
|
||||
static LPP_COLOUR = 135; // 1 byte per RGB Color
|
||||
static LPP_GPS = 136; // 3 byte lon/lat 0.0001 °, 3 bytes alt 0.01 meter
|
||||
static LPP_SWITCH = 142; // 1 byte, 0/1
|
||||
static LPP_POLYLINE = 240; // 1 byte size, 1 byte delta factor, 3 byte lon/lat 0.0001° * factor, n (size-8) bytes deltas
|
||||
|
||||
static LPP_DIGITAL_INPUT = 0; // 1 byte
|
||||
static LPP_DIGITAL_OUTPUT = 1; // 1 byte
|
||||
static LPP_ANALOG_INPUT = 2; // 2 bytes, 0.01 signed
|
||||
static LPP_ANALOG_OUTPUT = 3; // 2 bytes, 0.01 signed
|
||||
static LPP_GENERIC_SENSOR = 100; // 4 bytes, unsigned
|
||||
static LPP_LUMINOSITY = 101; // 2 bytes, 1 lux unsigned
|
||||
static LPP_PRESENCE = 102; // 1 byte, bool
|
||||
static LPP_TEMPERATURE = 103; // 2 bytes, 0.1°C signed
|
||||
static LPP_RELATIVE_HUMIDITY = 104; // 1 byte, 0.5% unsigned
|
||||
static LPP_ACCELEROMETER = 113; // 2 bytes per axis, 0.001G
|
||||
static LPP_BAROMETRIC_PRESSURE = 115; // 2 bytes 0.1hPa unsigned
|
||||
static LPP_VOLTAGE = 116; // 2 bytes 0.01V unsigned
|
||||
static LPP_CURRENT = 117; // 2 bytes 0.001A unsigned
|
||||
static LPP_FREQUENCY = 118; // 4 bytes 1Hz unsigned
|
||||
static LPP_PERCENTAGE = 120; // 1 byte 1-100% unsigned
|
||||
static LPP_ALTITUDE = 121; // 2 byte 1m signed
|
||||
static LPP_CONCENTRATION = 125; // 2 bytes, 1 ppm unsigned
|
||||
static LPP_POWER = 128; // 2 byte, 1W, unsigned
|
||||
static LPP_DISTANCE = 130; // 4 byte, 0.001m, unsigned
|
||||
static LPP_ENERGY = 131; // 4 byte, 0.001kWh, unsigned
|
||||
static LPP_DIRECTION = 132; // 2 bytes, 1deg, unsigned
|
||||
static LPP_UNIXTIME = 133; // 4 bytes, unsigned
|
||||
static LPP_GYROMETER = 134; // 2 bytes per axis, 0.01 °/s
|
||||
static LPP_COLOUR = 135; // 1 byte per RGB Color
|
||||
static LPP_GPS = 136; // 3 byte lon/lat 0.0001 °, 3 bytes alt 0.01 meter
|
||||
static LPP_SWITCH = 142; // 1 byte, 0/1
|
||||
static LPP_POLYLINE = 240; // 1 byte size, 1 byte delta factor, 3 byte lon/lat 0.0001° * factor, n (size-8) bytes deltas
|
||||
static parse(bytes) {
|
||||
const buffer = new BufferReader(bytes);
|
||||
const telemetry = [];
|
||||
|
||||
static parse(bytes) {
|
||||
while (buffer.getRemainingBytesCount() >= 2) {
|
||||
// need at least 2 more bytes to get channel and type
|
||||
|
||||
const buffer = new BufferReader(bytes);
|
||||
const telemetry = [];
|
||||
const channel = buffer.readUInt8();
|
||||
const type = buffer.readUInt8();
|
||||
|
||||
while(buffer.getRemainingBytesCount() >= 2){ // need at least 2 more bytes to get channel and type
|
||||
|
||||
const channel = buffer.readUInt8();
|
||||
const type = buffer.readUInt8();
|
||||
|
||||
// stop parsing if channel and type are zero, as there seems to be garbage bytes???
|
||||
if(channel === 0 && type === 0){
|
||||
break;
|
||||
}
|
||||
|
||||
switch(type){
|
||||
case this.LPP_GENERIC_SENSOR: {
|
||||
const value = buffer.readUInt32BE();
|
||||
// console.log(`[CayenneLpp] parsed LPP_GENERIC_SENSOR=${value}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
"value": value,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_LUMINOSITY: {
|
||||
const lux = buffer.readInt16BE();
|
||||
// console.log(`[CayenneLpp] parsed LPP_LUMINOSITY=${lux}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
"value": lux,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_PRESENCE: {
|
||||
const presence = buffer.readUInt8();
|
||||
// console.log(`[CayenneLpp] parsed LPP_PRESENCE=${presence}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
"value": presence,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_TEMPERATURE: {
|
||||
const temperature = buffer.readInt16BE() / 10;
|
||||
// console.log(`[CayenneLpp] parsed LPP_TEMPERATURE=${temperature}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
"value": temperature,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_RELATIVE_HUMIDITY: {
|
||||
const relativeHumidity = buffer.readUInt8() / 2;
|
||||
// console.log(`[CayenneLpp] parsed LPP_RELATIVE_HUMIDITY=${relativeHumidity}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
"value": relativeHumidity,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_BAROMETRIC_PRESSURE: {
|
||||
const barometricPressure = buffer.readUInt16BE() / 10;
|
||||
// console.log(`[CayenneLpp] parsed LPP_BAROMETRIC_PRESSURE=${barometricPressure}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
"value": barometricPressure,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_VOLTAGE: {
|
||||
// uint16: 0v to 655.35v
|
||||
// int16: -327.67v to +327.67v
|
||||
// should be readUInt16BE, but I'm using readInt16BE to allow for negative voltage
|
||||
const voltage = buffer.readInt16BE() / 100;
|
||||
// console.log(`[CayenneLpp] parsed LPP_VOLTAGE=${voltage}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
"value": voltage,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_CURRENT: {
|
||||
// uint16: 0A to 655.35A
|
||||
// int16: -327.67A to +327.67A
|
||||
// should be readUInt16BE, but I'm using readInt16BE to allow for negative current
|
||||
const current = buffer.readInt16BE() / 1000;
|
||||
// console.log(`[CayenneLpp] parsed LPP_CURRENT=${current}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
"value": current,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_PERCENTAGE: {
|
||||
const percentage = buffer.readUInt8();
|
||||
// console.log(`[CayenneLpp] parsed LPP_PERCENTAGE=${percentage}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
"value": percentage,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_CONCENTRATION: {
|
||||
const concentration = buffer.readUInt16BE();
|
||||
// console.log(`[CayenneLpp] parsed LPP_CONCENTRATION=${concentration}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
"value": concentration,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_POWER: {
|
||||
const power = buffer.readUInt16BE();
|
||||
// console.log(`[CayenneLpp] parsed LPP_POWER=${power}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
"value": power,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_GPS: {
|
||||
const latitude = buffer.readInt24BE() / 10000;
|
||||
const longitude = buffer.readInt24BE() / 10000;
|
||||
const altitude = buffer.readInt24BE() / 100;
|
||||
// console.log(`[CayenneLpp] parsed LPP_GPS=${latitude},${longitude},${altitude}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
"value": {
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
altitude: altitude,
|
||||
},
|
||||
});
|
||||
break;
|
||||
}
|
||||
// todo support all telemetry types, otherwise if an unknown is given, we can't read other telemetry after it
|
||||
default: {
|
||||
// console.log(`[CayenneLpp] unsupported type: ${type}`);
|
||||
return telemetry;
|
||||
}
|
||||
}
|
||||
// stop parsing if channel and type are zero, as there seems to be garbage bytes???
|
||||
if (channel === 0 && type === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case this.LPP_GENERIC_SENSOR: {
|
||||
const value = buffer.readUInt32BE();
|
||||
// console.log(`[CayenneLpp] parsed LPP_GENERIC_SENSOR=${value}`);
|
||||
telemetry.push({
|
||||
channel: channel,
|
||||
type: type,
|
||||
value: value,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
return telemetry;
|
||||
|
||||
case this.LPP_LUMINOSITY: {
|
||||
const lux = buffer.readInt16BE();
|
||||
// console.log(`[CayenneLpp] parsed LPP_LUMINOSITY=${lux}`);
|
||||
telemetry.push({
|
||||
channel: channel,
|
||||
type: type,
|
||||
value: lux,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_PRESENCE: {
|
||||
const presence = buffer.readUInt8();
|
||||
// console.log(`[CayenneLpp] parsed LPP_PRESENCE=${presence}`);
|
||||
telemetry.push({
|
||||
channel: channel,
|
||||
type: type,
|
||||
value: presence,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_TEMPERATURE: {
|
||||
const temperature = buffer.readInt16BE() / 10;
|
||||
// console.log(`[CayenneLpp] parsed LPP_TEMPERATURE=${temperature}`);
|
||||
telemetry.push({
|
||||
channel: channel,
|
||||
type: type,
|
||||
value: temperature,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_RELATIVE_HUMIDITY: {
|
||||
const relativeHumidity = buffer.readUInt8() / 2;
|
||||
// console.log(`[CayenneLpp] parsed LPP_RELATIVE_HUMIDITY=${relativeHumidity}`);
|
||||
telemetry.push({
|
||||
channel: channel,
|
||||
type: type,
|
||||
value: relativeHumidity,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_BAROMETRIC_PRESSURE: {
|
||||
const barometricPressure = buffer.readUInt16BE() / 10;
|
||||
// console.log(`[CayenneLpp] parsed LPP_BAROMETRIC_PRESSURE=${barometricPressure}`);
|
||||
telemetry.push({
|
||||
channel: channel,
|
||||
type: type,
|
||||
value: barometricPressure,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_VOLTAGE: {
|
||||
// uint16: 0v to 655.35v
|
||||
// int16: -327.67v to +327.67v
|
||||
// should be readUInt16BE, but I'm using readInt16BE to allow for negative voltage
|
||||
const voltage = buffer.readInt16BE() / 100;
|
||||
// console.log(`[CayenneLpp] parsed LPP_VOLTAGE=${voltage}`);
|
||||
telemetry.push({
|
||||
channel: channel,
|
||||
type: type,
|
||||
value: voltage,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_CURRENT: {
|
||||
// uint16: 0A to 655.35A
|
||||
// int16: -327.67A to +327.67A
|
||||
// should be readUInt16BE, but I'm using readInt16BE to allow for negative current
|
||||
const current = buffer.readInt16BE() / 1000;
|
||||
// console.log(`[CayenneLpp] parsed LPP_CURRENT=${current}`);
|
||||
telemetry.push({
|
||||
channel: channel,
|
||||
type: type,
|
||||
value: current,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_PERCENTAGE: {
|
||||
const percentage = buffer.readUInt8();
|
||||
// console.log(`[CayenneLpp] parsed LPP_PERCENTAGE=${percentage}`);
|
||||
telemetry.push({
|
||||
channel: channel,
|
||||
type: type,
|
||||
value: percentage,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_CONCENTRATION: {
|
||||
const concentration = buffer.readUInt16BE();
|
||||
// console.log(`[CayenneLpp] parsed LPP_CONCENTRATION=${concentration}`);
|
||||
telemetry.push({
|
||||
channel: channel,
|
||||
type: type,
|
||||
value: concentration,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_POWER: {
|
||||
const power = buffer.readUInt16BE();
|
||||
// console.log(`[CayenneLpp] parsed LPP_POWER=${power}`);
|
||||
telemetry.push({
|
||||
channel: channel,
|
||||
type: type,
|
||||
value: power,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case this.LPP_GPS: {
|
||||
const latitude = buffer.readInt24BE() / 10000;
|
||||
const longitude = buffer.readInt24BE() / 10000;
|
||||
const altitude = buffer.readInt24BE() / 100;
|
||||
// console.log(`[CayenneLpp] parsed LPP_GPS=${latitude},${longitude},${altitude}`);
|
||||
telemetry.push({
|
||||
channel: channel,
|
||||
type: type,
|
||||
value: {
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
altitude: altitude,
|
||||
},
|
||||
});
|
||||
break;
|
||||
}
|
||||
// todo support all telemetry types, otherwise if an unknown is given, we can't read other telemetry after it
|
||||
default: {
|
||||
// console.log(`[CayenneLpp] unsupported type: ${type}`);
|
||||
return telemetry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return telemetry;
|
||||
}
|
||||
}
|
||||
|
||||
export default CayenneLpp;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,60 +1,56 @@
|
|||
import SerialConnection from "./serial_connection.js";
|
||||
import SerialConnection from './serial_connection.js';
|
||||
|
||||
class NodeJSSerialConnection extends SerialConnection {
|
||||
/**
|
||||
* @param path serial port to connect to, e.g: "/dev/ttyACM0" or "/dev/cu.usbmodem14401"
|
||||
*/
|
||||
constructor(path) {
|
||||
super();
|
||||
this.serialPortPath = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param path serial port to connect to, e.g: "/dev/ttyACM0" or "/dev/cu.usbmodem14401"
|
||||
*/
|
||||
constructor(path) {
|
||||
super();
|
||||
this.serialPortPath = path;
|
||||
}
|
||||
|
||||
async connect() {
|
||||
|
||||
// note: serialport module is only available in NodeJS, you shouldn't use NodeJSSerialConnection from a web browser
|
||||
const { SerialPort } = await import('serialport');
|
||||
|
||||
// create new serial port
|
||||
this.serialPort = new SerialPort({
|
||||
autoOpen: false, // don't auto open, we want to control this manually
|
||||
path: this.serialPortPath, // e.g: "/dev/ttyACM0" or "/dev/cu.usbmodem14401"
|
||||
baudRate: 115200,
|
||||
});
|
||||
|
||||
this.serialPort.on("open", async () => {
|
||||
await this.onConnected();
|
||||
});
|
||||
|
||||
this.serialPort.on("close", () => {
|
||||
this.onDisconnected();
|
||||
});
|
||||
|
||||
this.serialPort.on("error", function(err) {
|
||||
console.log("SerialPort Error: ", err.message)
|
||||
});
|
||||
|
||||
this.serialPort.on("data", async (data) => {
|
||||
await this.onDataReceived(data);
|
||||
});
|
||||
|
||||
// open serial connection
|
||||
this.serialPort.open();
|
||||
|
||||
}
|
||||
|
||||
async close() {
|
||||
try {
|
||||
await this.serialPort.close();
|
||||
} catch(e) {
|
||||
console.log("failed to close serial port, ignoring...", e);
|
||||
}
|
||||
}
|
||||
|
||||
/* override */ async write(bytes) {
|
||||
this.serialPort.write(bytes);
|
||||
async connect() {
|
||||
// note: serialport module is only available in NodeJS, you shouldn't use NodeJSSerialConnection from a web browser
|
||||
const { SerialPort } = await import('serialport');
|
||||
|
||||
// create new serial port
|
||||
this.serialPort = new SerialPort({
|
||||
autoOpen: false, // don't auto open, we want to control this manually
|
||||
path: this.serialPortPath, // e.g: "/dev/ttyACM0" or "/dev/cu.usbmodem14401"
|
||||
baudRate: 115200,
|
||||
});
|
||||
|
||||
this.serialPort.on('open', async () => {
|
||||
await this.onConnected();
|
||||
});
|
||||
|
||||
this.serialPort.on('close', () => {
|
||||
this.onDisconnected();
|
||||
});
|
||||
|
||||
this.serialPort.on('error', function (err) {
|
||||
console.log('SerialPort Error: ', err.message);
|
||||
});
|
||||
|
||||
this.serialPort.on('data', async (data) => {
|
||||
await this.onDataReceived(data);
|
||||
});
|
||||
|
||||
// open serial connection
|
||||
this.serialPort.open();
|
||||
}
|
||||
|
||||
async close() {
|
||||
try {
|
||||
await this.serialPort.close();
|
||||
} catch (e) {
|
||||
console.log('failed to close serial port, ignoring...', e);
|
||||
}
|
||||
}
|
||||
|
||||
/* override */ async write(bytes) {
|
||||
this.serialPort.write(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
export default NodeJSSerialConnection;
|
||||
|
|
|
|||
|
|
@ -1,99 +1,100 @@
|
|||
import BufferWriter from "../buffer_writer.js";
|
||||
import BufferReader from "../buffer_reader.js";
|
||||
import Constants from "../constants.js";
|
||||
import Connection from "./connection.js";
|
||||
import BufferWriter from '../buffer_writer.js';
|
||||
import BufferReader from '../buffer_reader.js';
|
||||
import Constants from '../constants.js';
|
||||
import Connection from './connection.js';
|
||||
|
||||
class SerialConnection extends Connection {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.readBuffer = [];
|
||||
if(this.constructor === SerialConnection){
|
||||
throw new Error("SerialConnection is an abstract class and can't be instantiated.");
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
this.readBuffer = [];
|
||||
if (this.constructor === SerialConnection) {
|
||||
throw new Error(
|
||||
"SerialConnection is an abstract class and can't be instantiated.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async write(bytes) {
|
||||
throw new Error("Not Implemented: write must be implemented by SerialConnection sub class.");
|
||||
}
|
||||
async write(bytes) {
|
||||
throw new Error(
|
||||
'Not Implemented: write must be implemented by SerialConnection sub class.',
|
||||
);
|
||||
}
|
||||
|
||||
async writeFrame(frameType, frameData) {
|
||||
async writeFrame(frameType, frameData) {
|
||||
// create frame
|
||||
const frame = new BufferWriter();
|
||||
|
||||
// create frame
|
||||
const frame = new BufferWriter();
|
||||
// add frame header
|
||||
frame.writeByte(frameType);
|
||||
frame.writeUInt16LE(frameData.length);
|
||||
|
||||
// add frame header
|
||||
frame.writeByte(frameType);
|
||||
frame.writeUInt16LE(frameData.length);
|
||||
// add frame data
|
||||
frame.writeBytes(frameData);
|
||||
|
||||
// add frame data
|
||||
frame.writeBytes(frameData);
|
||||
// write frame to device
|
||||
await this.write(frame.toBytes());
|
||||
}
|
||||
|
||||
// write frame to device
|
||||
await this.write(frame.toBytes());
|
||||
async sendToRadioFrame(data) {
|
||||
// write "app to radio" frame 0x3c "<"
|
||||
this.emit('tx', data);
|
||||
await this.writeFrame(0x3c, data);
|
||||
}
|
||||
|
||||
}
|
||||
async onDataReceived(value) {
|
||||
// append received bytes to read buffer
|
||||
this.readBuffer = [...this.readBuffer, ...value];
|
||||
|
||||
async sendToRadioFrame(data) {
|
||||
// write "app to radio" frame 0x3c "<"
|
||||
this.emit("tx", data);
|
||||
await this.writeFrame(0x3c, data);
|
||||
}
|
||||
// process read buffer while there is enough bytes for a frame header
|
||||
// 3 bytes frame header = (1 byte frame type) + (2 bytes frame length as unsigned 16-bit little endian)
|
||||
const frameHeaderLength = 3;
|
||||
while (this.readBuffer.length >= frameHeaderLength) {
|
||||
try {
|
||||
// extract frame header
|
||||
const frameHeader = new BufferReader(
|
||||
this.readBuffer.slice(0, frameHeaderLength),
|
||||
);
|
||||
|
||||
async onDataReceived(value) {
|
||||
|
||||
// append received bytes to read buffer
|
||||
this.readBuffer = [
|
||||
...this.readBuffer,
|
||||
...value,
|
||||
];
|
||||
|
||||
// process read buffer while there is enough bytes for a frame header
|
||||
// 3 bytes frame header = (1 byte frame type) + (2 bytes frame length as unsigned 16-bit little endian)
|
||||
const frameHeaderLength = 3;
|
||||
while(this.readBuffer.length >= frameHeaderLength){
|
||||
try {
|
||||
|
||||
// extract frame header
|
||||
const frameHeader = new BufferReader(this.readBuffer.slice(0, frameHeaderLength));
|
||||
|
||||
// ensure frame type supported
|
||||
const frameType = frameHeader.readByte();
|
||||
if(frameType !== Constants.SerialFrameTypes.Incoming && frameType !== Constants.SerialFrameTypes.Outgoing){
|
||||
// unexpected byte, lets skip it and try again
|
||||
this.readBuffer = this.readBuffer.slice(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// ensure frame length valid
|
||||
const frameLength = frameHeader.readUInt16LE();
|
||||
if(!frameLength){
|
||||
// unexpected byte, lets skip it and try again
|
||||
this.readBuffer = this.readBuffer.slice(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if we have received enough bytes for this frame, otherwise wait until more bytes received
|
||||
const requiredLength = frameHeaderLength + frameLength;
|
||||
if(this.readBuffer.length < requiredLength){
|
||||
break;
|
||||
}
|
||||
|
||||
// get frame data, and remove it and its frame header from the read buffer
|
||||
const frameData = this.readBuffer.slice(frameHeaderLength, requiredLength);
|
||||
this.readBuffer = this.readBuffer.slice(requiredLength);
|
||||
|
||||
// handle received frame
|
||||
this.onFrameReceived(frameData);
|
||||
|
||||
} catch(e) {
|
||||
console.error("Failed to process frame", e);
|
||||
break;
|
||||
}
|
||||
// ensure frame type supported
|
||||
const frameType = frameHeader.readByte();
|
||||
if (
|
||||
frameType !== Constants.SerialFrameTypes.Incoming &&
|
||||
frameType !== Constants.SerialFrameTypes.Outgoing
|
||||
) {
|
||||
// unexpected byte, lets skip it and try again
|
||||
this.readBuffer = this.readBuffer.slice(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
// ensure frame length valid
|
||||
const frameLength = frameHeader.readUInt16LE();
|
||||
if (!frameLength) {
|
||||
// unexpected byte, lets skip it and try again
|
||||
this.readBuffer = this.readBuffer.slice(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if we have received enough bytes for this frame, otherwise wait until more bytes received
|
||||
const requiredLength = frameHeaderLength + frameLength;
|
||||
if (this.readBuffer.length < requiredLength) {
|
||||
break;
|
||||
}
|
||||
|
||||
// get frame data, and remove it and its frame header from the read buffer
|
||||
const frameData = this.readBuffer.slice(
|
||||
frameHeaderLength,
|
||||
requiredLength,
|
||||
);
|
||||
this.readBuffer = this.readBuffer.slice(requiredLength);
|
||||
|
||||
// handle received frame
|
||||
this.onFrameReceived(frameData);
|
||||
} catch (e) {
|
||||
console.error('Failed to process frame', e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default SerialConnection;
|
||||
|
|
|
|||
|
|
@ -1,136 +1,131 @@
|
|||
import BufferWriter from "../buffer_writer.js";
|
||||
import BufferReader from "../buffer_reader.js";
|
||||
import Constants from "../constants.js";
|
||||
import Connection from "./connection.js";
|
||||
import BufferWriter from '../buffer_writer.js';
|
||||
import BufferReader from '../buffer_reader.js';
|
||||
import Constants from '../constants.js';
|
||||
import Connection from './connection.js';
|
||||
|
||||
class TCPConnection extends Connection {
|
||||
constructor(host, port) {
|
||||
super();
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.readBuffer = [];
|
||||
}
|
||||
|
||||
constructor(host, port) {
|
||||
super();
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.readBuffer = [];
|
||||
}
|
||||
async connect() {
|
||||
// note: net module is only available in NodeJS, you shouldn't use TCPConnection from a web browser
|
||||
const { Socket } = await import('net');
|
||||
|
||||
async connect() {
|
||||
// create new socket
|
||||
this.socket = new Socket();
|
||||
|
||||
// note: net module is only available in NodeJS, you shouldn't use TCPConnection from a web browser
|
||||
const { Socket } = await import("net");
|
||||
// handle received data
|
||||
this.socket.on('data', (data) => {
|
||||
this.onSocketDataReceived(data);
|
||||
});
|
||||
|
||||
// create new socket
|
||||
this.socket = new Socket();
|
||||
// handle errors
|
||||
this.socket.on('error', (error) => {
|
||||
console.error('Connection Error', error);
|
||||
});
|
||||
|
||||
// handle received data
|
||||
this.socket.on('data', (data) => {
|
||||
this.onSocketDataReceived(data);
|
||||
});
|
||||
// handle socket close
|
||||
this.socket.on('close', (error) => {
|
||||
this.onDisconnected();
|
||||
});
|
||||
|
||||
// handle errors
|
||||
this.socket.on('error', (error) => {
|
||||
console.error('Connection Error', error);
|
||||
});
|
||||
// connect to server
|
||||
this.socket.connect(this.port, this.host, async () => {
|
||||
await this.onConnected();
|
||||
});
|
||||
}
|
||||
|
||||
// handle socket close
|
||||
this.socket.on('close', (error) => {
|
||||
this.onDisconnected();
|
||||
});
|
||||
onSocketDataReceived(data) {
|
||||
// append received bytes to read buffer
|
||||
this.readBuffer = [...this.readBuffer, ...data];
|
||||
|
||||
// connect to server
|
||||
this.socket.connect(this.port, this.host, async () => {
|
||||
await this.onConnected();
|
||||
});
|
||||
// process read buffer while there is enough bytes for a frame header
|
||||
// 3 bytes frame header = (1 byte frame type) + (2 bytes frame length as unsigned 16-bit little endian)
|
||||
const frameHeaderLength = 3;
|
||||
while (this.readBuffer.length >= frameHeaderLength) {
|
||||
try {
|
||||
// extract frame header
|
||||
const frameHeader = new BufferReader(
|
||||
this.readBuffer.slice(0, frameHeaderLength),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
onSocketDataReceived(data) {
|
||||
|
||||
// append received bytes to read buffer
|
||||
this.readBuffer = [
|
||||
...this.readBuffer,
|
||||
...data,
|
||||
];
|
||||
|
||||
// process read buffer while there is enough bytes for a frame header
|
||||
// 3 bytes frame header = (1 byte frame type) + (2 bytes frame length as unsigned 16-bit little endian)
|
||||
const frameHeaderLength = 3;
|
||||
while(this.readBuffer.length >= frameHeaderLength){
|
||||
try {
|
||||
|
||||
// extract frame header
|
||||
const frameHeader = new BufferReader(this.readBuffer.slice(0, frameHeaderLength));
|
||||
|
||||
// ensure frame type supported
|
||||
const frameType = frameHeader.readByte();
|
||||
if(frameType !== Constants.SerialFrameTypes.Incoming && frameType !== Constants.SerialFrameTypes.Outgoing){
|
||||
// unexpected byte, lets skip it and try again
|
||||
this.readBuffer = this.readBuffer.slice(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// ensure frame length valid
|
||||
const frameLength = frameHeader.readUInt16LE();
|
||||
if(!frameLength){
|
||||
// unexpected byte, lets skip it and try again
|
||||
this.readBuffer = this.readBuffer.slice(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if we have received enough bytes for this frame, otherwise wait until more bytes received
|
||||
const requiredLength = frameHeaderLength + frameLength;
|
||||
if(this.readBuffer.length < requiredLength){
|
||||
break;
|
||||
}
|
||||
|
||||
// get frame data, and remove it and its frame header from the read buffer
|
||||
const frameData = this.readBuffer.slice(frameHeaderLength, requiredLength);
|
||||
this.readBuffer = this.readBuffer.slice(requiredLength);
|
||||
|
||||
// handle received frame
|
||||
this.onFrameReceived(frameData);
|
||||
|
||||
} catch(e) {
|
||||
console.error("Failed to process frame", e);
|
||||
break;
|
||||
}
|
||||
// ensure frame type supported
|
||||
const frameType = frameHeader.readByte();
|
||||
if (
|
||||
frameType !== Constants.SerialFrameTypes.Incoming &&
|
||||
frameType !== Constants.SerialFrameTypes.Outgoing
|
||||
) {
|
||||
// unexpected byte, lets skip it and try again
|
||||
this.readBuffer = this.readBuffer.slice(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
close() {
|
||||
try {
|
||||
this.socket.destroy();
|
||||
} catch(e) {
|
||||
// console.log("failed to release lock on serial port readable, ignoring...", e);
|
||||
// ensure frame length valid
|
||||
const frameLength = frameHeader.readUInt16LE();
|
||||
if (!frameLength) {
|
||||
// unexpected byte, lets skip it and try again
|
||||
this.readBuffer = this.readBuffer.slice(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if we have received enough bytes for this frame, otherwise wait until more bytes received
|
||||
const requiredLength = frameHeaderLength + frameLength;
|
||||
if (this.readBuffer.length < requiredLength) {
|
||||
break;
|
||||
}
|
||||
|
||||
// get frame data, and remove it and its frame header from the read buffer
|
||||
const frameData = this.readBuffer.slice(
|
||||
frameHeaderLength,
|
||||
requiredLength,
|
||||
);
|
||||
this.readBuffer = this.readBuffer.slice(requiredLength);
|
||||
|
||||
// handle received frame
|
||||
this.onFrameReceived(frameData);
|
||||
} catch (e) {
|
||||
console.error('Failed to process frame', e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async write(bytes) {
|
||||
this.socket.write(new Uint8Array(bytes));
|
||||
close() {
|
||||
try {
|
||||
this.socket.destroy();
|
||||
} catch (e) {
|
||||
// console.log("failed to release lock on serial port readable, ignoring...", e);
|
||||
}
|
||||
}
|
||||
|
||||
async writeFrame(frameType, frameData) {
|
||||
async write(bytes) {
|
||||
this.socket.write(new Uint8Array(bytes));
|
||||
}
|
||||
|
||||
// create frame
|
||||
const frame = new BufferWriter();
|
||||
async writeFrame(frameType, frameData) {
|
||||
// create frame
|
||||
const frame = new BufferWriter();
|
||||
|
||||
// add frame header
|
||||
frame.writeByte(frameType);
|
||||
frame.writeUInt16LE(frameData.length);
|
||||
// add frame header
|
||||
frame.writeByte(frameType);
|
||||
frame.writeUInt16LE(frameData.length);
|
||||
|
||||
// add frame data
|
||||
frame.writeBytes(frameData);
|
||||
// add frame data
|
||||
frame.writeBytes(frameData);
|
||||
|
||||
// write frame to device
|
||||
await this.write(frame.toBytes());
|
||||
|
||||
}
|
||||
|
||||
async sendToRadioFrame(data) {
|
||||
// write "app to radio" frame 0x3c "<"
|
||||
this.emit("tx", data);
|
||||
await this.writeFrame(0x3c, data);
|
||||
}
|
||||
// write frame to device
|
||||
await this.write(frame.toBytes());
|
||||
}
|
||||
|
||||
async sendToRadioFrame(data) {
|
||||
// write "app to radio" frame 0x3c "<"
|
||||
this.emit('tx', data);
|
||||
await this.writeFrame(0x3c, data);
|
||||
}
|
||||
}
|
||||
|
||||
export default TCPConnection;
|
||||
|
|
|
|||
|
|
@ -1,106 +1,109 @@
|
|||
import Constants from "../constants.js";
|
||||
import Connection from "./connection.js";
|
||||
import Constants from '../constants.js';
|
||||
import Connection from './connection.js';
|
||||
|
||||
class WebBleConnection extends Connection {
|
||||
constructor(bleDevice) {
|
||||
super();
|
||||
this.bleDevice = bleDevice;
|
||||
this.gattServer = null;
|
||||
this.rxCharacteristic = null;
|
||||
this.txCharacteristic = null;
|
||||
this.init();
|
||||
}
|
||||
|
||||
constructor(bleDevice) {
|
||||
super();
|
||||
this.bleDevice = bleDevice;
|
||||
this.gattServer = null;
|
||||
this.rxCharacteristic = null;
|
||||
this.txCharacteristic = null;
|
||||
this.init();
|
||||
static async open() {
|
||||
// ensure browser supports web bluetooth
|
||||
if (!navigator.bluetooth) {
|
||||
alert('Web Bluetooth is not supported in this browser');
|
||||
return;
|
||||
}
|
||||
|
||||
static async open() {
|
||||
|
||||
// ensure browser supports web bluetooth
|
||||
if(!navigator.bluetooth){
|
||||
alert("Web Bluetooth is not supported in this browser");
|
||||
return;
|
||||
}
|
||||
|
||||
// ask user to select device
|
||||
const device = await navigator.bluetooth.requestDevice({
|
||||
filters: [
|
||||
{
|
||||
services: [
|
||||
Constants.Ble.ServiceUuid.toLowerCase(),
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// make sure user selected a device
|
||||
if(!device){
|
||||
return null;
|
||||
}
|
||||
|
||||
return new WebBleConnection(device);
|
||||
// ask user to select device
|
||||
const device = await navigator.bluetooth.requestDevice({
|
||||
filters: [
|
||||
{
|
||||
services: [Constants.Ble.ServiceUuid.toLowerCase()],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// make sure user selected a device
|
||||
if (!device) {
|
||||
return null;
|
||||
}
|
||||
|
||||
async init() {
|
||||
return new WebBleConnection(device);
|
||||
}
|
||||
|
||||
// listen for ble disconnect
|
||||
this.bleDevice.addEventListener("gattserverdisconnected", () => {
|
||||
this.onDisconnected();
|
||||
});
|
||||
async init() {
|
||||
// listen for ble disconnect
|
||||
this.bleDevice.addEventListener('gattserverdisconnected', () => {
|
||||
this.onDisconnected();
|
||||
});
|
||||
|
||||
// connect to gatt server
|
||||
this.gattServer = await this.bleDevice.gatt.connect();
|
||||
// connect to gatt server
|
||||
this.gattServer = await this.bleDevice.gatt.connect();
|
||||
|
||||
// find service
|
||||
const service = await this.gattServer.getPrimaryService(Constants.Ble.ServiceUuid.toLowerCase());
|
||||
const characteristics = await service.getCharacteristics();
|
||||
// find service
|
||||
const service = await this.gattServer.getPrimaryService(
|
||||
Constants.Ble.ServiceUuid.toLowerCase(),
|
||||
);
|
||||
const characteristics = await service.getCharacteristics();
|
||||
|
||||
// find rx characteristic (we write to this one, it's where the radio reads from)
|
||||
this.rxCharacteristic = characteristics.find((characteristic) => {
|
||||
return characteristic.uuid.toLowerCase() === Constants.Ble.CharacteristicUuidRx.toLowerCase();
|
||||
});
|
||||
// find rx characteristic (we write to this one, it's where the radio reads from)
|
||||
this.rxCharacteristic = characteristics.find((characteristic) => {
|
||||
return (
|
||||
characteristic.uuid.toLowerCase() ===
|
||||
Constants.Ble.CharacteristicUuidRx.toLowerCase()
|
||||
);
|
||||
});
|
||||
|
||||
// find tx characteristic (we read this one, it's where the radio writes to)
|
||||
this.txCharacteristic = characteristics.find((characteristic) => {
|
||||
return characteristic.uuid.toLowerCase() === Constants.Ble.CharacteristicUuidTx.toLowerCase();
|
||||
});
|
||||
// find tx characteristic (we read this one, it's where the radio writes to)
|
||||
this.txCharacteristic = characteristics.find((characteristic) => {
|
||||
return (
|
||||
characteristic.uuid.toLowerCase() ===
|
||||
Constants.Ble.CharacteristicUuidTx.toLowerCase()
|
||||
);
|
||||
});
|
||||
|
||||
// listen for frames from transmitted to us from the ble device
|
||||
await this.txCharacteristic.startNotifications();
|
||||
this.txCharacteristic.addEventListener('characteristicvaluechanged', (event) => {
|
||||
const frame = new Uint8Array(event.target.value.buffer);
|
||||
this.onFrameReceived(frame);
|
||||
});
|
||||
// listen for frames from transmitted to us from the ble device
|
||||
await this.txCharacteristic.startNotifications();
|
||||
this.txCharacteristic.addEventListener(
|
||||
'characteristicvaluechanged',
|
||||
(event) => {
|
||||
const frame = new Uint8Array(event.target.value.buffer);
|
||||
this.onFrameReceived(frame);
|
||||
},
|
||||
);
|
||||
|
||||
// fire connected event
|
||||
await this.onConnected();
|
||||
// fire connected event
|
||||
await this.onConnected();
|
||||
}
|
||||
|
||||
async close() {
|
||||
try {
|
||||
this.gattServer?.disconnect();
|
||||
this.gattServer = null;
|
||||
} catch (e) {
|
||||
// ignore error when disconnecting
|
||||
}
|
||||
}
|
||||
|
||||
async close() {
|
||||
try {
|
||||
this.gattServer?.disconnect();
|
||||
this.gattServer = null;
|
||||
} catch(e) {
|
||||
// ignore error when disconnecting
|
||||
}
|
||||
}
|
||||
|
||||
async write(bytes) {
|
||||
try {
|
||||
// fixme: NetworkError: GATT operation already in progress.
|
||||
// todo: implement mutex to prevent multiple writes when another write is in progress
|
||||
// we write to the rx characteristic, as that's where the radio reads from
|
||||
await this.rxCharacteristic.writeValue(bytes);
|
||||
} catch(e) {
|
||||
console.log("failed to write to ble device", e);
|
||||
}
|
||||
}
|
||||
|
||||
async sendToRadioFrame(frame) {
|
||||
this.emit("tx", frame);
|
||||
await this.write(frame);
|
||||
async write(bytes) {
|
||||
try {
|
||||
// fixme: NetworkError: GATT operation already in progress.
|
||||
// todo: implement mutex to prevent multiple writes when another write is in progress
|
||||
// we write to the rx characteristic, as that's where the radio reads from
|
||||
await this.rxCharacteristic.writeValue(bytes);
|
||||
} catch (e) {
|
||||
console.log('failed to write to ble device', e);
|
||||
}
|
||||
}
|
||||
|
||||
async sendToRadioFrame(frame) {
|
||||
this.emit('tx', frame);
|
||||
await this.write(frame);
|
||||
}
|
||||
}
|
||||
|
||||
export default WebBleConnection;
|
||||
|
|
|
|||
|
|
@ -1,105 +1,93 @@
|
|||
import SerialConnection from "./serial_connection.js";
|
||||
import SerialConnection from './serial_connection.js';
|
||||
|
||||
class WebSerialConnection extends SerialConnection {
|
||||
constructor(serialPort) {
|
||||
super();
|
||||
|
||||
constructor(serialPort) {
|
||||
this.serialPort = serialPort;
|
||||
this.reader = serialPort.readable.getReader();
|
||||
this.writable = serialPort.writable;
|
||||
this.readLoop();
|
||||
|
||||
super();
|
||||
// listen for disconnect
|
||||
this.serialPort.addEventListener('disconnect', () => {
|
||||
this.onDisconnected();
|
||||
});
|
||||
|
||||
this.serialPort = serialPort;
|
||||
this.reader = serialPort.readable.getReader();
|
||||
this.writable = serialPort.writable;
|
||||
this.readLoop();
|
||||
|
||||
// listen for disconnect
|
||||
this.serialPort.addEventListener("disconnect", () => {
|
||||
this.onDisconnected();
|
||||
});
|
||||
|
||||
// fire connected callback after constructor has returned
|
||||
setTimeout(async () => {
|
||||
await this.onConnected();
|
||||
}, 0);
|
||||
// fire connected callback after constructor has returned
|
||||
setTimeout(async () => {
|
||||
await this.onConnected();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
static async open() {
|
||||
// ensure browser supports web serial
|
||||
if (!navigator.serial) {
|
||||
alert('Web Serial is not supported in this browser');
|
||||
return null;
|
||||
}
|
||||
|
||||
static async open() {
|
||||
// ask user to select device
|
||||
const serialPort = await navigator.serial.requestPort({
|
||||
filters: [],
|
||||
});
|
||||
|
||||
// ensure browser supports web serial
|
||||
if(!navigator.serial){
|
||||
alert("Web Serial is not supported in this browser");
|
||||
return null;
|
||||
}
|
||||
// open port
|
||||
await serialPort.open({
|
||||
baudRate: 115200,
|
||||
});
|
||||
|
||||
// ask user to select device
|
||||
const serialPort = await navigator.serial.requestPort({
|
||||
filters: [],
|
||||
});
|
||||
|
||||
// open port
|
||||
await serialPort.open({
|
||||
baudRate: 115200,
|
||||
});
|
||||
|
||||
return new WebSerialConnection(serialPort);
|
||||
return new WebSerialConnection(serialPort);
|
||||
}
|
||||
|
||||
async close() {
|
||||
// release reader lock
|
||||
try {
|
||||
this.reader.releaseLock();
|
||||
} catch (e) {
|
||||
// console.log("failed to release lock on serial port readable, ignoring...", e);
|
||||
}
|
||||
|
||||
async close() {
|
||||
|
||||
// release reader lock
|
||||
try {
|
||||
this.reader.releaseLock();
|
||||
} catch(e) {
|
||||
// console.log("failed to release lock on serial port readable, ignoring...", e);
|
||||
}
|
||||
|
||||
// close serial port
|
||||
try {
|
||||
await this.serialPort.close();
|
||||
} catch(e) {
|
||||
// console.log("failed to close serial port, ignoring...", e);
|
||||
}
|
||||
|
||||
// close serial port
|
||||
try {
|
||||
await this.serialPort.close();
|
||||
} catch (e) {
|
||||
// console.log("failed to close serial port, ignoring...", e);
|
||||
}
|
||||
}
|
||||
|
||||
/* override */ async write(bytes) {
|
||||
const writer = this.writable.getWriter();
|
||||
try {
|
||||
await writer.write(new Uint8Array(bytes));
|
||||
} finally {
|
||||
writer.releaseLock();
|
||||
}
|
||||
/* override */ async write(bytes) {
|
||||
const writer = this.writable.getWriter();
|
||||
try {
|
||||
await writer.write(new Uint8Array(bytes));
|
||||
} finally {
|
||||
writer.releaseLock();
|
||||
}
|
||||
}
|
||||
|
||||
async readLoop() {
|
||||
try {
|
||||
while(true){
|
||||
|
||||
// read bytes until reader indicates it's done
|
||||
const { value, done } = await this.reader.read();
|
||||
if(done){
|
||||
break;
|
||||
}
|
||||
|
||||
// pass to super class handler
|
||||
await this.onDataReceived(value);
|
||||
|
||||
}
|
||||
} catch(error) {
|
||||
|
||||
// ignore error if reader was released
|
||||
if(error instanceof TypeError){
|
||||
return;
|
||||
}
|
||||
|
||||
console.error('Error reading from serial port: ', error);
|
||||
|
||||
} finally {
|
||||
this.reader.releaseLock();
|
||||
async readLoop() {
|
||||
try {
|
||||
while (true) {
|
||||
// read bytes until reader indicates it's done
|
||||
const { value, done } = await this.reader.read();
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// pass to super class handler
|
||||
await this.onDataReceived(value);
|
||||
}
|
||||
} catch (error) {
|
||||
// ignore error if reader was released
|
||||
if (error instanceof TypeError) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.error('Error reading from serial port: ', error);
|
||||
} finally {
|
||||
this.reader.releaseLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default WebSerialConnection;
|
||||
|
|
|
|||
230
src/constants.js
230
src/constants.js
|
|
@ -1,131 +1,129 @@
|
|||
class Constants {
|
||||
static SupportedCompanionProtocolVersion = 1;
|
||||
|
||||
static SupportedCompanionProtocolVersion = 1;
|
||||
static SerialFrameTypes = {
|
||||
Incoming: 0x3e, // ">"
|
||||
Outgoing: 0x3c, // "<"
|
||||
};
|
||||
|
||||
static SerialFrameTypes = {
|
||||
Incoming: 0x3e, // ">"
|
||||
Outgoing: 0x3c, // "<"
|
||||
}
|
||||
static Ble = {
|
||||
ServiceUuid: '6E400001-B5A3-F393-E0A9-E50E24DCCA9E',
|
||||
CharacteristicUuidRx: '6E400002-B5A3-F393-E0A9-E50E24DCCA9E',
|
||||
CharacteristicUuidTx: '6E400003-B5A3-F393-E0A9-E50E24DCCA9E',
|
||||
};
|
||||
|
||||
static Ble = {
|
||||
ServiceUuid: "6E400001-B5A3-F393-E0A9-E50E24DCCA9E",
|
||||
CharacteristicUuidRx: "6E400002-B5A3-F393-E0A9-E50E24DCCA9E",
|
||||
CharacteristicUuidTx: "6E400003-B5A3-F393-E0A9-E50E24DCCA9E",
|
||||
}
|
||||
static CommandCodes = {
|
||||
AppStart: 1,
|
||||
SendTxtMsg: 2,
|
||||
SendChannelTxtMsg: 3,
|
||||
GetContacts: 4,
|
||||
GetDeviceTime: 5,
|
||||
SetDeviceTime: 6,
|
||||
SendSelfAdvert: 7,
|
||||
SetAdvertName: 8,
|
||||
AddUpdateContact: 9,
|
||||
SyncNextMessage: 10,
|
||||
SetRadioParams: 11,
|
||||
SetTxPower: 12,
|
||||
ResetPath: 13,
|
||||
SetAdvertLatLon: 14,
|
||||
RemoveContact: 15,
|
||||
ShareContact: 16,
|
||||
ExportContact: 17,
|
||||
ImportContact: 18,
|
||||
Reboot: 19,
|
||||
GetBatteryVoltage: 20,
|
||||
SetTuningParams: 21, // todo
|
||||
DeviceQuery: 22,
|
||||
ExportPrivateKey: 23,
|
||||
ImportPrivateKey: 24,
|
||||
SendRawData: 25,
|
||||
SendLogin: 26, // todo
|
||||
SendStatusReq: 27, // todo
|
||||
GetChannel: 31,
|
||||
SetChannel: 32,
|
||||
SignStart: 33,
|
||||
SignData: 34,
|
||||
SignFinish: 35,
|
||||
SendTracePath: 36,
|
||||
// todo set device pin command
|
||||
SetOtherParams: 38,
|
||||
SendTelemetryReq: 39,
|
||||
|
||||
static CommandCodes = {
|
||||
AppStart: 1,
|
||||
SendTxtMsg: 2,
|
||||
SendChannelTxtMsg: 3,
|
||||
GetContacts: 4,
|
||||
GetDeviceTime: 5,
|
||||
SetDeviceTime: 6,
|
||||
SendSelfAdvert: 7,
|
||||
SetAdvertName: 8,
|
||||
AddUpdateContact: 9,
|
||||
SyncNextMessage: 10,
|
||||
SetRadioParams: 11,
|
||||
SetTxPower: 12,
|
||||
ResetPath: 13,
|
||||
SetAdvertLatLon: 14,
|
||||
RemoveContact: 15,
|
||||
ShareContact: 16,
|
||||
ExportContact: 17,
|
||||
ImportContact: 18,
|
||||
Reboot: 19,
|
||||
GetBatteryVoltage: 20,
|
||||
SetTuningParams: 21, // todo
|
||||
DeviceQuery: 22,
|
||||
ExportPrivateKey: 23,
|
||||
ImportPrivateKey: 24,
|
||||
SendRawData: 25,
|
||||
SendLogin: 26, // todo
|
||||
SendStatusReq: 27, // todo
|
||||
GetChannel: 31,
|
||||
SetChannel: 32,
|
||||
SignStart: 33,
|
||||
SignData: 34,
|
||||
SignFinish: 35,
|
||||
SendTracePath: 36,
|
||||
// todo set device pin command
|
||||
SetOtherParams: 38,
|
||||
SendTelemetryReq: 39,
|
||||
SendBinaryReq: 50,
|
||||
};
|
||||
|
||||
SendBinaryReq: 50,
|
||||
}
|
||||
static ResponseCodes = {
|
||||
Ok: 0, // todo
|
||||
Err: 1, // todo
|
||||
ContactsStart: 2,
|
||||
Contact: 3,
|
||||
EndOfContacts: 4,
|
||||
SelfInfo: 5,
|
||||
Sent: 6,
|
||||
ContactMsgRecv: 7,
|
||||
ChannelMsgRecv: 8,
|
||||
CurrTime: 9,
|
||||
NoMoreMessages: 10,
|
||||
ExportContact: 11,
|
||||
BatteryVoltage: 12,
|
||||
DeviceInfo: 13,
|
||||
PrivateKey: 14,
|
||||
Disabled: 15,
|
||||
ChannelInfo: 18,
|
||||
SignStart: 19,
|
||||
Signature: 20,
|
||||
};
|
||||
|
||||
static ResponseCodes = {
|
||||
Ok: 0, // todo
|
||||
Err: 1, // todo
|
||||
ContactsStart: 2,
|
||||
Contact: 3,
|
||||
EndOfContacts: 4,
|
||||
SelfInfo: 5,
|
||||
Sent: 6,
|
||||
ContactMsgRecv: 7,
|
||||
ChannelMsgRecv: 8,
|
||||
CurrTime: 9,
|
||||
NoMoreMessages: 10,
|
||||
ExportContact: 11,
|
||||
BatteryVoltage: 12,
|
||||
DeviceInfo: 13,
|
||||
PrivateKey: 14,
|
||||
Disabled: 15,
|
||||
ChannelInfo: 18,
|
||||
SignStart: 19,
|
||||
Signature: 20,
|
||||
}
|
||||
static PushCodes = {
|
||||
Advert: 0x80, // when companion is set to auto add contacts
|
||||
PathUpdated: 0x81,
|
||||
SendConfirmed: 0x82,
|
||||
MsgWaiting: 0x83,
|
||||
RawData: 0x84,
|
||||
LoginSuccess: 0x85,
|
||||
LoginFail: 0x86, // not usable yet
|
||||
StatusResponse: 0x87,
|
||||
LogRxData: 0x88,
|
||||
TraceData: 0x89,
|
||||
NewAdvert: 0x8a, // when companion is set to manually add contacts
|
||||
TelemetryResponse: 0x8b,
|
||||
BinaryResponse: 0x8c,
|
||||
};
|
||||
|
||||
static PushCodes = {
|
||||
Advert: 0x80, // when companion is set to auto add contacts
|
||||
PathUpdated: 0x81,
|
||||
SendConfirmed: 0x82,
|
||||
MsgWaiting: 0x83,
|
||||
RawData: 0x84,
|
||||
LoginSuccess: 0x85,
|
||||
LoginFail: 0x86, // not usable yet
|
||||
StatusResponse: 0x87,
|
||||
LogRxData: 0x88,
|
||||
TraceData: 0x89,
|
||||
NewAdvert: 0x8A, // when companion is set to manually add contacts
|
||||
TelemetryResponse: 0x8B,
|
||||
BinaryResponse: 0x8C,
|
||||
}
|
||||
static ErrorCodes = {
|
||||
UnsupportedCmd: 1,
|
||||
NotFound: 2,
|
||||
TableFull: 3,
|
||||
BadState: 4,
|
||||
FileIoError: 5,
|
||||
IllegalArg: 6,
|
||||
};
|
||||
|
||||
static ErrorCodes = {
|
||||
UnsupportedCmd: 1,
|
||||
NotFound: 2,
|
||||
TableFull: 3,
|
||||
BadState: 4,
|
||||
FileIoError: 5,
|
||||
IllegalArg: 6,
|
||||
}
|
||||
static AdvType = {
|
||||
None: 0,
|
||||
Chat: 1,
|
||||
Repeater: 2,
|
||||
Room: 3,
|
||||
};
|
||||
|
||||
static AdvType = {
|
||||
None: 0,
|
||||
Chat: 1,
|
||||
Repeater: 2,
|
||||
Room: 3,
|
||||
}
|
||||
static SelfAdvertTypes = {
|
||||
ZeroHop: 0,
|
||||
Flood: 1,
|
||||
};
|
||||
|
||||
static SelfAdvertTypes = {
|
||||
ZeroHop: 0,
|
||||
Flood: 1,
|
||||
}
|
||||
|
||||
static TxtTypes = {
|
||||
Plain: 0,
|
||||
CliData: 1,
|
||||
SignedPlain: 2,
|
||||
}
|
||||
|
||||
static BinaryRequestTypes = {
|
||||
GetTelemetryData: 0x03, // #define REQ_TYPE_GET_TELEMETRY_DATA 0x03
|
||||
GetAvgMinMax: 0x04, // #define REQ_TYPE_GET_AVG_MIN_MAX 0x04
|
||||
GetAccessList: 0x05, // #define REQ_TYPE_GET_ACCESS_LIST 0x05
|
||||
GetNeighbours: 0x06, // #define REQ_TYPE_GET_NEIGHBOURS 0x06
|
||||
}
|
||||
static TxtTypes = {
|
||||
Plain: 0,
|
||||
CliData: 1,
|
||||
SignedPlain: 2,
|
||||
};
|
||||
|
||||
static BinaryRequestTypes = {
|
||||
GetTelemetryData: 0x03, // #define REQ_TYPE_GET_TELEMETRY_DATA 0x03
|
||||
GetAvgMinMax: 0x04, // #define REQ_TYPE_GET_AVG_MIN_MAX 0x04
|
||||
GetAccessList: 0x05, // #define REQ_TYPE_GET_ACCESS_LIST 0x05
|
||||
GetNeighbours: 0x06, // #define REQ_TYPE_GET_NEIGHBOURS 0x06
|
||||
};
|
||||
}
|
||||
|
||||
export default Constants;
|
||||
|
|
|
|||
|
|
@ -1,60 +1,50 @@
|
|||
class EventEmitter {
|
||||
constructor() {
|
||||
this.eventListenersMap = new Map();
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.eventListenersMap = new Map();
|
||||
on(event, callback) {
|
||||
// create list of listeners for event if it doesn't exist
|
||||
if (!this.eventListenersMap.has(event)) {
|
||||
this.eventListenersMap.set(event, []);
|
||||
}
|
||||
|
||||
on(event, callback) {
|
||||
|
||||
// create list of listeners for event if it doesn't exist
|
||||
if(!this.eventListenersMap.has(event)){
|
||||
this.eventListenersMap.set(event, []);
|
||||
}
|
||||
|
||||
// add listener for event
|
||||
this.eventListenersMap.get(event).push(callback);
|
||||
// add listener for event
|
||||
this.eventListenersMap.get(event).push(callback);
|
||||
}
|
||||
|
||||
off(event, callback) {
|
||||
// remove callback from listeners for this event
|
||||
if (this.eventListenersMap.has(event)) {
|
||||
const callbacks = this.eventListenersMap
|
||||
.get(event)
|
||||
.filter((cb) => cb !== callback);
|
||||
this.eventListenersMap.set(event, callbacks);
|
||||
}
|
||||
}
|
||||
|
||||
off(event, callback) {
|
||||
once(event, callback) {
|
||||
// internal callback to handle the event
|
||||
const internalCallback = (...data) => {
|
||||
// we received an event, so lets remove the event listener
|
||||
this.off(event, internalCallback);
|
||||
|
||||
// remove callback from listeners for this event
|
||||
if(this.eventListenersMap.has(event)){
|
||||
const callbacks = this.eventListenersMap.get(event).filter(cb => cb !== callback);
|
||||
this.eventListenersMap.set(event, callbacks);
|
||||
}
|
||||
// fire the original callback provided by the user
|
||||
setTimeout(() => callback(...data), 0);
|
||||
};
|
||||
|
||||
// listen to this event
|
||||
this.on(event, internalCallback);
|
||||
}
|
||||
|
||||
emit(event, ...data) {
|
||||
// invoke each listener for this event
|
||||
if (this.eventListenersMap.has(event)) {
|
||||
for (const eventListener of this.eventListenersMap.get(event)) {
|
||||
setTimeout(() => eventListener(...data), 0);
|
||||
}
|
||||
}
|
||||
|
||||
once(event, callback) {
|
||||
|
||||
// internal callback to handle the event
|
||||
const internalCallback = (...data) => {
|
||||
|
||||
// we received an event, so lets remove the event listener
|
||||
this.off(event, internalCallback);
|
||||
|
||||
// fire the original callback provided by the user
|
||||
setTimeout(() => callback(...data), 0);
|
||||
|
||||
};
|
||||
|
||||
// listen to this event
|
||||
this.on(event, internalCallback);
|
||||
|
||||
}
|
||||
|
||||
emit(event, ...data) {
|
||||
|
||||
// invoke each listener for this event
|
||||
if(this.eventListenersMap.has(event)){
|
||||
for(const eventListener of this.eventListenersMap.get(event)){
|
||||
setTimeout(() => eventListener(...data), 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default EventEmitter;
|
||||
|
|
|
|||
44
src/index.js
44
src/index.js
|
|
@ -1,25 +1,25 @@
|
|||
import Connection from "./connection/connection.js";
|
||||
import WebBleConnection from "./connection/web_ble_connection.js";
|
||||
import SerialConnection from "./connection/serial_connection.js";
|
||||
import NodeJSSerialConnection from "./connection/nodejs_serial_connection.js";
|
||||
import WebSerialConnection from "./connection/web_serial_connection.js";
|
||||
import TCPConnection from "./connection/tcp_connection.js";
|
||||
import Constants from "./constants.js";
|
||||
import Advert from "./advert.js";
|
||||
import Packet from "./packet.js";
|
||||
import BufferUtils from "./buffer_utils.js";
|
||||
import CayenneLpp from "./cayenne_lpp.js";
|
||||
import Connection from './connection/connection.js';
|
||||
import WebBleConnection from './connection/web_ble_connection.js';
|
||||
import SerialConnection from './connection/serial_connection.js';
|
||||
import NodeJSSerialConnection from './connection/nodejs_serial_connection.js';
|
||||
import WebSerialConnection from './connection/web_serial_connection.js';
|
||||
import TCPConnection from './connection/tcp_connection.js';
|
||||
import Constants from './constants.js';
|
||||
import Advert from './advert.js';
|
||||
import Packet from './packet.js';
|
||||
import BufferUtils from './buffer_utils.js';
|
||||
import CayenneLpp from './cayenne_lpp.js';
|
||||
|
||||
export {
|
||||
Connection,
|
||||
WebBleConnection,
|
||||
SerialConnection,
|
||||
NodeJSSerialConnection,
|
||||
WebSerialConnection,
|
||||
TCPConnection,
|
||||
Constants,
|
||||
Advert,
|
||||
Packet,
|
||||
BufferUtils,
|
||||
CayenneLpp,
|
||||
Connection,
|
||||
WebBleConnection,
|
||||
SerialConnection,
|
||||
NodeJSSerialConnection,
|
||||
WebSerialConnection,
|
||||
TCPConnection,
|
||||
Constants,
|
||||
Advert,
|
||||
Packet,
|
||||
BufferUtils,
|
||||
CayenneLpp,
|
||||
};
|
||||
|
|
|
|||
380
src/packet.js
380
src/packet.js
|
|
@ -1,215 +1,223 @@
|
|||
import BufferReader from "./buffer_reader.js";
|
||||
import Advert from "./advert.js";
|
||||
import BufferReader from './buffer_reader.js';
|
||||
import Advert from './advert.js';
|
||||
|
||||
class Packet {
|
||||
// Packet::header values
|
||||
static PH_ROUTE_MASK = 0x03; // 2-bits
|
||||
static PH_TYPE_SHIFT = 2;
|
||||
static PH_TYPE_MASK = 0x0f; // 4-bits
|
||||
static PH_VER_SHIFT = 6;
|
||||
static PH_VER_MASK = 0x03; // 2-bits
|
||||
|
||||
// Packet::header values
|
||||
static PH_ROUTE_MASK = 0x03; // 2-bits
|
||||
static PH_TYPE_SHIFT = 2;
|
||||
static PH_TYPE_MASK = 0x0F; // 4-bits
|
||||
static PH_VER_SHIFT = 6;
|
||||
static PH_VER_MASK = 0x03; // 2-bits
|
||||
static ROUTE_TYPE_RESERVED1 = 0x00; // FUTURE
|
||||
static ROUTE_TYPE_FLOOD = 0x01; // flood mode, needs 'path' to be built up (max 64 bytes)
|
||||
static ROUTE_TYPE_DIRECT = 0x02; // direct route, 'path' is supplied
|
||||
static ROUTE_TYPE_RESERVED2 = 0x03; // FUTURE
|
||||
|
||||
static ROUTE_TYPE_RESERVED1 = 0x00; // FUTURE
|
||||
static ROUTE_TYPE_FLOOD = 0x01; // flood mode, needs 'path' to be built up (max 64 bytes)
|
||||
static ROUTE_TYPE_DIRECT = 0x02; // direct route, 'path' is supplied
|
||||
static ROUTE_TYPE_RESERVED2 = 0x03; // FUTURE
|
||||
static PAYLOAD_TYPE_REQ = 0x00; // request (prefixed with dest/src hashes, MAC) (enc data: timestamp, blob)
|
||||
static PAYLOAD_TYPE_RESPONSE = 0x01; // response to REQ or ANON_REQ (prefixed with dest/src hashes, MAC) (enc data: timestamp, blob)
|
||||
static PAYLOAD_TYPE_TXT_MSG = 0x02; // a plain text message (prefixed with dest/src hashes, MAC) (enc data: timestamp, text)
|
||||
static PAYLOAD_TYPE_ACK = 0x03; // a simple ack
|
||||
static PAYLOAD_TYPE_ADVERT = 0x04; // a node advertising its Identity
|
||||
static PAYLOAD_TYPE_GRP_TXT = 0x05; // an (unverified) group text message (prefixed with channel hash, MAC) (enc data: timestamp, "name: msg")
|
||||
static PAYLOAD_TYPE_GRP_DATA = 0x06; // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: timestamp, blob)
|
||||
static PAYLOAD_TYPE_ANON_REQ = 0x07; // generic request (prefixed with dest_hash, ephemeral pub_key, MAC) (enc data: ...)
|
||||
static PAYLOAD_TYPE_PATH = 0x08; // returned path (prefixed with dest/src hashes, MAC) (enc data: path, extra)
|
||||
static PAYLOAD_TYPE_TRACE = 0x09; // trace a path, collecting SNR for each hop
|
||||
static PAYLOAD_TYPE_RAW_CUSTOM = 0x0f; // custom packet as raw bytes, for applications with custom encryption, payloads, etc
|
||||
|
||||
static PAYLOAD_TYPE_REQ = 0x00; // request (prefixed with dest/src hashes, MAC) (enc data: timestamp, blob)
|
||||
static PAYLOAD_TYPE_RESPONSE = 0x01; // response to REQ or ANON_REQ (prefixed with dest/src hashes, MAC) (enc data: timestamp, blob)
|
||||
static PAYLOAD_TYPE_TXT_MSG = 0x02; // a plain text message (prefixed with dest/src hashes, MAC) (enc data: timestamp, text)
|
||||
static PAYLOAD_TYPE_ACK = 0x03; // a simple ack
|
||||
static PAYLOAD_TYPE_ADVERT = 0x04; // a node advertising its Identity
|
||||
static PAYLOAD_TYPE_GRP_TXT = 0x05; // an (unverified) group text message (prefixed with channel hash, MAC) (enc data: timestamp, "name: msg")
|
||||
static PAYLOAD_TYPE_GRP_DATA = 0x06; // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: timestamp, blob)
|
||||
static PAYLOAD_TYPE_ANON_REQ = 0x07; // generic request (prefixed with dest_hash, ephemeral pub_key, MAC) (enc data: ...)
|
||||
static PAYLOAD_TYPE_PATH = 0x08; // returned path (prefixed with dest/src hashes, MAC) (enc data: path, extra)
|
||||
static PAYLOAD_TYPE_TRACE = 0x09; // trace a path, collecting SNR for each hop
|
||||
static PAYLOAD_TYPE_RAW_CUSTOM = 0x0F; // custom packet as raw bytes, for applications with custom encryption, payloads, etc
|
||||
constructor(header, path, payload) {
|
||||
this.header = header;
|
||||
this.path = path;
|
||||
this.payload = payload;
|
||||
|
||||
constructor(header, path, payload) {
|
||||
// parsed info
|
||||
this.route_type = this.getRouteType();
|
||||
this.route_type_string = this.getRouteTypeString();
|
||||
this.payload_type = this.getPayloadType();
|
||||
this.payload_type_string = this.getPayloadTypeString();
|
||||
this.payload_version = this.getPayloadVer();
|
||||
this.is_marked_do_not_retransmit = this.isMarkedDoNotRetransmit();
|
||||
}
|
||||
|
||||
static fromBytes(bytes) {
|
||||
const bufferReader = new BufferReader(bytes);
|
||||
const header = bufferReader.readByte();
|
||||
const pathLen = bufferReader.readInt8();
|
||||
const path = bufferReader.readBytes(pathLen);
|
||||
const payload = bufferReader.readRemainingBytes();
|
||||
return new Packet(header, path, payload);
|
||||
}
|
||||
|
||||
this.header = header;
|
||||
this.path = path;
|
||||
this.payload = payload;
|
||||
|
||||
// parsed info
|
||||
this.route_type = this.getRouteType();
|
||||
this.route_type_string = this.getRouteTypeString();
|
||||
this.payload_type = this.getPayloadType();
|
||||
this.payload_type_string = this.getPayloadTypeString();
|
||||
this.payload_version = this.getPayloadVer();
|
||||
this.is_marked_do_not_retransmit = this.isMarkedDoNotRetransmit();
|
||||
getRouteType() {
|
||||
return this.header & Packet.PH_ROUTE_MASK;
|
||||
}
|
||||
|
||||
getRouteTypeString() {
|
||||
switch (this.getRouteType()) {
|
||||
case Packet.ROUTE_TYPE_FLOOD:
|
||||
return 'FLOOD';
|
||||
case Packet.ROUTE_TYPE_DIRECT:
|
||||
return 'DIRECT';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static fromBytes(bytes) {
|
||||
const bufferReader = new BufferReader(bytes);
|
||||
const header = bufferReader.readByte();
|
||||
const pathLen = bufferReader.readInt8();
|
||||
const path = bufferReader.readBytes(pathLen);
|
||||
const payload = bufferReader.readRemainingBytes();
|
||||
return new Packet(header, path, payload);
|
||||
isRouteFlood() {
|
||||
return this.getRouteType() === Packet.ROUTE_TYPE_FLOOD;
|
||||
}
|
||||
|
||||
isRouteDirect() {
|
||||
return this.getRouteType() === Packet.ROUTE_TYPE_DIRECT;
|
||||
}
|
||||
|
||||
getPayloadType() {
|
||||
return (this.header >> Packet.PH_TYPE_SHIFT) & Packet.PH_TYPE_MASK;
|
||||
}
|
||||
|
||||
getPayloadTypeString() {
|
||||
switch (this.getPayloadType()) {
|
||||
case Packet.PAYLOAD_TYPE_REQ:
|
||||
return 'REQ';
|
||||
case Packet.PAYLOAD_TYPE_RESPONSE:
|
||||
return 'RESPONSE';
|
||||
case Packet.PAYLOAD_TYPE_TXT_MSG:
|
||||
return 'TXT_MSG';
|
||||
case Packet.PAYLOAD_TYPE_ACK:
|
||||
return 'ACK';
|
||||
case Packet.PAYLOAD_TYPE_ADVERT:
|
||||
return 'ADVERT';
|
||||
case Packet.PAYLOAD_TYPE_GRP_TXT:
|
||||
return 'GRP_TXT';
|
||||
case Packet.PAYLOAD_TYPE_GRP_DATA:
|
||||
return 'GRP_DATA';
|
||||
case Packet.PAYLOAD_TYPE_ANON_REQ:
|
||||
return 'ANON_REQ';
|
||||
case Packet.PAYLOAD_TYPE_PATH:
|
||||
return 'PATH';
|
||||
case Packet.PAYLOAD_TYPE_TRACE:
|
||||
return 'TRACE';
|
||||
case Packet.PAYLOAD_TYPE_RAW_CUSTOM:
|
||||
return 'RAW_CUSTOM';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
getRouteType() {
|
||||
return this.header & Packet.PH_ROUTE_MASK;
|
||||
getPayloadVer() {
|
||||
return (this.header >> Packet.PH_VER_SHIFT) & Packet.PH_VER_MASK;
|
||||
}
|
||||
|
||||
markDoNotRetransmit() {
|
||||
this.header = 0xff;
|
||||
}
|
||||
|
||||
isMarkedDoNotRetransmit() {
|
||||
return this.header === 0xff;
|
||||
}
|
||||
|
||||
parsePayload() {
|
||||
switch (this.getPayloadType()) {
|
||||
case Packet.PAYLOAD_TYPE_PATH:
|
||||
return this.parsePayloadTypePath();
|
||||
case Packet.PAYLOAD_TYPE_REQ:
|
||||
return this.parsePayloadTypeReq();
|
||||
case Packet.PAYLOAD_TYPE_RESPONSE:
|
||||
return this.parsePayloadTypeResponse();
|
||||
case Packet.PAYLOAD_TYPE_TXT_MSG:
|
||||
return this.parsePayloadTypeTxtMsg();
|
||||
case Packet.PAYLOAD_TYPE_ACK:
|
||||
return this.parsePayloadTypeAck();
|
||||
case Packet.PAYLOAD_TYPE_ADVERT:
|
||||
return this.parsePayloadTypeAdvert();
|
||||
case Packet.PAYLOAD_TYPE_ANON_REQ:
|
||||
return this.parsePayloadTypeAnonReq();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
getRouteTypeString() {
|
||||
switch(this.getRouteType()){
|
||||
case Packet.ROUTE_TYPE_FLOOD: return "FLOOD";
|
||||
case Packet.ROUTE_TYPE_DIRECT: return "DIRECT";
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
parsePayloadTypePath() {
|
||||
// parse bytes
|
||||
const bufferReader = new BufferReader(this.payload);
|
||||
const dest = bufferReader.readByte();
|
||||
const src = bufferReader.readByte();
|
||||
// todo other fields
|
||||
|
||||
isRouteFlood() {
|
||||
return this.getRouteType() === Packet.ROUTE_TYPE_FLOOD;
|
||||
}
|
||||
return {
|
||||
src: src,
|
||||
dest: dest,
|
||||
};
|
||||
}
|
||||
|
||||
isRouteDirect() {
|
||||
return this.getRouteType() === Packet.ROUTE_TYPE_DIRECT;
|
||||
}
|
||||
parsePayloadTypeReq() {
|
||||
// parse bytes
|
||||
const bufferReader = new BufferReader(this.payload);
|
||||
const dest = bufferReader.readByte();
|
||||
const src = bufferReader.readByte();
|
||||
const encrypted = bufferReader.readRemainingBytes();
|
||||
|
||||
getPayloadType() {
|
||||
return (this.header >> Packet.PH_TYPE_SHIFT) & Packet.PH_TYPE_MASK;
|
||||
}
|
||||
return {
|
||||
src: src,
|
||||
dest: dest,
|
||||
encrypted: encrypted,
|
||||
};
|
||||
}
|
||||
|
||||
getPayloadTypeString() {
|
||||
switch(this.getPayloadType()){
|
||||
case Packet.PAYLOAD_TYPE_REQ: return "REQ";
|
||||
case Packet.PAYLOAD_TYPE_RESPONSE: return "RESPONSE";
|
||||
case Packet.PAYLOAD_TYPE_TXT_MSG: return "TXT_MSG";
|
||||
case Packet.PAYLOAD_TYPE_ACK: return "ACK";
|
||||
case Packet.PAYLOAD_TYPE_ADVERT: return "ADVERT";
|
||||
case Packet.PAYLOAD_TYPE_GRP_TXT: return "GRP_TXT";
|
||||
case Packet.PAYLOAD_TYPE_GRP_DATA: return "GRP_DATA";
|
||||
case Packet.PAYLOAD_TYPE_ANON_REQ: return "ANON_REQ";
|
||||
case Packet.PAYLOAD_TYPE_PATH: return "PATH";
|
||||
case Packet.PAYLOAD_TYPE_TRACE: return "TRACE";
|
||||
case Packet.PAYLOAD_TYPE_RAW_CUSTOM: return "RAW_CUSTOM";
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
parsePayloadTypeResponse() {
|
||||
// parse bytes
|
||||
const bufferReader = new BufferReader(this.payload);
|
||||
const dest = bufferReader.readByte();
|
||||
const src = bufferReader.readByte();
|
||||
// todo other fields
|
||||
|
||||
getPayloadVer() {
|
||||
return (this.header >> Packet.PH_VER_SHIFT) & Packet.PH_VER_MASK;
|
||||
}
|
||||
return {
|
||||
src: src,
|
||||
dest: dest,
|
||||
};
|
||||
}
|
||||
|
||||
markDoNotRetransmit() {
|
||||
this.header = 0xFF;
|
||||
}
|
||||
parsePayloadTypeTxtMsg() {
|
||||
// parse bytes
|
||||
const bufferReader = new BufferReader(this.payload);
|
||||
const dest = bufferReader.readByte();
|
||||
const src = bufferReader.readByte();
|
||||
// todo other fields
|
||||
|
||||
isMarkedDoNotRetransmit() {
|
||||
return this.header === 0xFF;
|
||||
}
|
||||
return {
|
||||
src: src,
|
||||
dest: dest,
|
||||
};
|
||||
}
|
||||
|
||||
parsePayload() {
|
||||
switch(this.getPayloadType()){
|
||||
case Packet.PAYLOAD_TYPE_PATH: return this.parsePayloadTypePath();
|
||||
case Packet.PAYLOAD_TYPE_REQ: return this.parsePayloadTypeReq();
|
||||
case Packet.PAYLOAD_TYPE_RESPONSE: return this.parsePayloadTypeResponse();
|
||||
case Packet.PAYLOAD_TYPE_TXT_MSG: return this.parsePayloadTypeTxtMsg();
|
||||
case Packet.PAYLOAD_TYPE_ACK: return this.parsePayloadTypeAck();
|
||||
case Packet.PAYLOAD_TYPE_ADVERT: return this.parsePayloadTypeAdvert();
|
||||
case Packet.PAYLOAD_TYPE_ANON_REQ: return this.parsePayloadTypeAnonReq();
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
parsePayloadTypeAck() {
|
||||
return {
|
||||
ack_code: this.payload,
|
||||
};
|
||||
}
|
||||
|
||||
parsePayloadTypePath() {
|
||||
parsePayloadTypeAdvert() {
|
||||
const advert = Advert.fromBytes(this.payload);
|
||||
return {
|
||||
public_key: advert.publicKey,
|
||||
timestamp: advert.timestamp,
|
||||
app_data: advert.parseAppData(),
|
||||
};
|
||||
}
|
||||
|
||||
// parse bytes
|
||||
const bufferReader = new BufferReader(this.payload);
|
||||
const dest = bufferReader.readByte();
|
||||
const src = bufferReader.readByte();
|
||||
// todo other fields
|
||||
|
||||
return {
|
||||
src: src,
|
||||
dest: dest,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
parsePayloadTypeReq() {
|
||||
|
||||
// parse bytes
|
||||
const bufferReader = new BufferReader(this.payload);
|
||||
const dest = bufferReader.readByte();
|
||||
const src = bufferReader.readByte();
|
||||
const encrypted = bufferReader.readRemainingBytes();
|
||||
|
||||
return {
|
||||
src: src,
|
||||
dest: dest,
|
||||
encrypted: encrypted,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
parsePayloadTypeResponse() {
|
||||
|
||||
// parse bytes
|
||||
const bufferReader = new BufferReader(this.payload);
|
||||
const dest = bufferReader.readByte();
|
||||
const src = bufferReader.readByte();
|
||||
// todo other fields
|
||||
|
||||
return {
|
||||
src: src,
|
||||
dest: dest,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
parsePayloadTypeTxtMsg() {
|
||||
|
||||
// parse bytes
|
||||
const bufferReader = new BufferReader(this.payload);
|
||||
const dest = bufferReader.readByte();
|
||||
const src = bufferReader.readByte();
|
||||
// todo other fields
|
||||
|
||||
return {
|
||||
src: src,
|
||||
dest: dest,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
parsePayloadTypeAck() {
|
||||
return {
|
||||
ack_code: this.payload,
|
||||
};
|
||||
}
|
||||
|
||||
parsePayloadTypeAdvert() {
|
||||
const advert = Advert.fromBytes(this.payload);
|
||||
return {
|
||||
public_key: advert.publicKey,
|
||||
timestamp: advert.timestamp,
|
||||
app_data: advert.parseAppData(),
|
||||
};
|
||||
}
|
||||
|
||||
parsePayloadTypeAnonReq() {
|
||||
|
||||
// parse bytes
|
||||
const bufferReader = new BufferReader(this.payload);
|
||||
const dest = bufferReader.readByte();
|
||||
const srcPublicKey = bufferReader.readBytes(32);
|
||||
// todo other fields
|
||||
|
||||
return {
|
||||
src: srcPublicKey,
|
||||
dest: dest,
|
||||
};
|
||||
|
||||
}
|
||||
parsePayloadTypeAnonReq() {
|
||||
// parse bytes
|
||||
const bufferReader = new BufferReader(this.payload);
|
||||
const dest = bufferReader.readByte();
|
||||
const srcPublicKey = bufferReader.readBytes(32);
|
||||
// todo other fields
|
||||
|
||||
return {
|
||||
src: srcPublicKey,
|
||||
dest: dest,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default Packet;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
class RandomUtils {
|
||||
|
||||
static getRandomInt(min, max) {
|
||||
min = Math.ceil(min);
|
||||
max = Math.floor(max);
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
static getRandomInt(min, max) {
|
||||
min = Math.ceil(min);
|
||||
max = Math.floor(max);
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
}
|
||||
|
||||
export default RandomUtils;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue