added support for get stats commands

This commit is contained in:
liamcottle 2026-03-26 10:04:15 +13:00
parent ff135fe0b4
commit 6f571c90dd
3 changed files with 142 additions and 0 deletions

View file

@ -128,6 +128,15 @@
<button @click="setManualAddContacts" class="border border-gray-500 px-2 bg-gray-100 hover:bg-gray-200 rounded">
setManualAddContacts
</button>
<button @click="getStatsCore" class="border border-gray-500 px-2 bg-gray-100 hover:bg-gray-200 rounded">
getStatsCore
</button>
<button @click="getStatsRadio" class="border border-gray-500 px-2 bg-gray-100 hover:bg-gray-200 rounded">
getStatsRadio
</button>
<button @click="getStatsPackets" class="border border-gray-500 px-2 bg-gray-100 hover:bg-gray-200 rounded">
getStatsPackets
</button>
</div>
</div>
@ -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

View file

@ -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 {

View file

@ -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,
}