diff --git a/index.html b/index.html index 05ec84a..1af9fde 100644 --- a/index.html +++ b/index.html @@ -128,6 +128,15 @@ + + + @@ -672,6 +681,30 @@ alert("failed to set manual add contacts"); } }, + async getStatsCore() { + try { + const stats = await this.connection.getStatsCore(); + console.log(stats); + } catch(e) { + console.log(e); + } + }, + async getStatsRadio() { + try { + const stats = await this.connection.getStatsRadio(); + console.log(stats); + } catch(e) { + console.log(e); + } + }, + async getStatsPackets() { + try { + const stats = await this.connection.getStatsPackets(); + console.log(stats); + } catch(e) { + console.log(e); + } + }, showCommandLine(contact) { // hide cli if clicked same contact diff --git a/src/connection/connection.js b/src/connection/connection.js index e6c008a..af6f6d6 100644 --- a/src/connection/connection.js +++ b/src/connection/connection.js @@ -268,6 +268,13 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + async sendCommandGetStats(statsType) { + const data = new BufferWriter(); + data.writeByte(Constants.CommandCodes.GetStats); + data.writeByte(statsType); + await this.sendToRadioFrame(data.toBytes()); + } + async sendCommandSendChannelData(channelIdx, pathLen, path, dataType, payload) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SendChannelData); @@ -377,6 +384,8 @@ class Connection extends EventEmitter { this.onSignStartResponse(bufferReader); } else if(responseCode === Constants.ResponseCodes.Signature){ this.onSignatureResponse(bufferReader); + } else if(responseCode === Constants.ResponseCodes.Stats){ + this.onStatsResponse(bufferReader); } else if(responseCode === Constants.ResponseCodes.ChannelDataRecv){ this.onChannelDataRecvResponse(bufferReader); } else if(responseCode === Constants.PushCodes.Advert){ @@ -625,6 +634,47 @@ class Connection extends EventEmitter { }); } + onStatsResponse(bufferReader) { + + const type = bufferReader.readUInt8(); + const raw = bufferReader.readRemainingBytes(); + const rawBufferReader = new BufferReader(raw); + + var data = {}; + if(type === Constants.StatsTypes.Core){ + data = { + batteryMilliVolts: rawBufferReader.readUInt16LE(), + uptimeSecs: rawBufferReader.readUInt32LE(), + queueLen: rawBufferReader.readUInt8(), + }; + } else if(type === Constants.StatsTypes.Radio){ + data = { + noiseFloor: rawBufferReader.readInt16LE(), + lastRssi: rawBufferReader.readInt8(), + lastSnr: rawBufferReader.readInt8() / 4, + txAirSecs: rawBufferReader.readUInt32LE(), + rxAirSecs: rawBufferReader.readUInt32LE(), + }; + } else if(type === Constants.StatsTypes.Packets){ + data = { + recv: rawBufferReader.readUInt32LE(), + sent: rawBufferReader.readUInt32LE(), + nSentFlood: rawBufferReader.readUInt32LE(), + nSentDirect: rawBufferReader.readUInt32LE(), + nRecvFlood: rawBufferReader.readUInt32LE(), + nRecvDirect: rawBufferReader.readUInt32LE(), + nRecvErrors: rawBufferReader.getRemainingBytesCount() >= 4 ? rawBufferReader.readUInt32LE() : null, + }; + } + + this.emit(Constants.ResponseCodes.Stats, { + type: type, + raw: raw, + data: data, + }); + + } + onChannelDataRecvResponse(bufferReader) { const snr = bufferReader.readInt8() / 4; const reserved1 = bufferReader.readByte(); @@ -1856,6 +1906,56 @@ class Connection extends EventEmitter { }); } + getStats(statsType) { + return new Promise(async (resolve, reject) => { + try { + + // resolve promise when we receive stats + const onStats = (response) => { + + // make sure stats response is for this stats request + if(response.type !== statsType){ + return; + } + + this.off(Constants.ResponseCodes.Stats, onStats); + this.off(Constants.ResponseCodes.Err, onErr); + resolve(response); + + } + + // reject promise when we receive err + const onErr = () => { + this.off(Constants.ResponseCodes.Stats, onStats); + this.off(Constants.ResponseCodes.Err, onErr); + reject(); + } + + // listen for events + this.once(Constants.ResponseCodes.Stats, onStats); + this.once(Constants.ResponseCodes.Err, onErr); + + // send get stats + await this.sendCommandGetStats(statsType); + + } catch(e) { + reject(e); + } + }); + } + + getStatsCore() { + return this.getStats(Constants.StatsTypes.Core); + } + + getStatsRadio() { + return this.getStats(Constants.StatsTypes.Radio); + } + + getStatsPackets() { + return this.getStats(Constants.StatsTypes.Packets); + } + sendChannelData(channelIdx, pathLen, path, dataType, payload) { return new Promise(async (resolve, reject) => { try { diff --git a/src/constants.js b/src/constants.js index c4c4236..4ba7e78 100644 --- a/src/constants.js +++ b/src/constants.js @@ -17,6 +17,12 @@ class Constants { Dev: 0xFFFF, // developer namespace for experimenting with group/channel datagrams and building apps } + static StatsTypes = { + Core: 0, + Radio: 1, + Packets: 2, + } + static CommandCodes = { AppStart: 1, SendTxtMsg: 2, @@ -59,6 +65,8 @@ class Constants { SetFloodScope: 54, + GetStats: 56, + SendChannelData: 62, } @@ -82,6 +90,7 @@ class Constants { ChannelInfo: 18, SignStart: 19, Signature: 20, + Stats: 24, ChannelDataRecv: 27, }