Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Capture device debug content and log it #117

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/adapters/serialConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class SerialConnection extends MeshDevice {
* through a transform stream (https://stackoverflow.com/questions/71262432) */
private pipePromise?: Promise<void>;

/* Reference for the heartbeat ping interval so it can be canceled on disconnect. */
/* Reference for the heartbeat ping interval so it can be canceled on disconnect. */
private heartbeatInterval?: ReturnType<typeof setInterval> | undefined;

/**
Expand Down Expand Up @@ -161,9 +161,9 @@ export class SerialConnection extends MeshDevice {
// The firmware requires at least one ping per 15 minutes, so this should be more than enough.
this.heartbeatInterval = setInterval(() => {
this.heartbeat().catch((err) => {
console.error('Heartbeat error', err);
console.error("Heartbeat error", err);
});
}, 60*1000);
}, 60 * 1000);
} else {
console.log("not readable or writable");
}
Expand Down Expand Up @@ -193,11 +193,11 @@ export class SerialConnection extends MeshDevice {
if (this.port?.readable) {
await this.port?.close();
}

// stop the interval when disconnecting.
if (this.heartbeatInterval) {
clearInterval(this.heartbeatInterval);
this.heartbeatInterval = undefined;
this.heartbeatInterval = undefined;
}
// -------
this.updateDeviceStatus(Types.DeviceStatusEnum.DeviceDisconnected);
Expand Down
44 changes: 40 additions & 4 deletions src/utils/transformHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,83 @@ import type { Logger } from "tslog";
import * as Protobuf from "@meshtastic/protobufs";
import * as Types from "../types.ts";

// This function takes the raw binary stream from the radio
// and converts it into usable "packets" that are returned to the
// adapter for handling
export const transformHandler = (
log: Logger<unknown>,
onReleaseEvent: SimpleEventDispatcher<boolean>,
onDeviceDebugLog: SimpleEventDispatcher<Uint8Array>,
concurrentLogOutput: boolean,
) => {
// byteBuffer contains the data to be processed
let byteBuffer = new Uint8Array([]);

// return the actual transformer that will be called for each
// new chunk of data...
return new TransformStream<Uint8Array, Uint8Array>({
transform(chunk: Uint8Array, controller): void {
log = log.getSubLogger({ name: "streamTransformer" });
onReleaseEvent.subscribe(() => {
controller.terminate();
});

// add the latest chunk of data into the array
byteBuffer = new Uint8Array([...byteBuffer, ...chunk]);

// This loop looks for Meshtastic packets in the stream based on the
// protocol definition. byteBuffer may contain 0 or more packets at
// any time.
let processingExhausted = false;
while (byteBuffer.length !== 0 && !processingExhausted) {
// Look for the magic byte that indicates a packet is starting
const framingIndex = byteBuffer.findIndex((byte) => byte === 0x94);
// Check the second confirmation byte
const framingByte2 = byteBuffer[framingIndex + 1];
if (framingByte2 === 0xc3) {
// Check to see if there is content in the buffer before the packet starts
// Per the protocol spec, data that is outside of the packet
// is likely to be ascii debugging information from the radio
// This includes formatting escape codes.
if (byteBuffer.subarray(0, framingIndex).length) {
if (concurrentLogOutput) {
// dispatch the raw data as an event
// the consumer will have to translate the bytes into ascii
onDeviceDebugLog.dispatch(byteBuffer.subarray(0, framingIndex));
} else {
log.warn(
// This takes the bytes, translates them into ascii, and logs them
const ascii_debug = Array.from(
byteBuffer.subarray(0, framingIndex),
)
.map((code) => String.fromCharCode(code))
.join("");
log.trace(
Types.EmitterScope.SerialConnection,
Types.Emitter.Connect,
`⚠️ Found unneccesary message padding, removing: ${byteBuffer
.subarray(0, framingIndex)
.toString()}`,
`Debug from radio:\n ${ascii_debug}`,
);
}

// Remove everything before the magic byte
byteBuffer = byteBuffer.subarray(framingIndex);
}

// the next two bytes define the length of the packet
const msb = byteBuffer[2];
const lsb = byteBuffer[3];

// If we have a valid length, and the byteBuffer is long enough,
// then we should have a full packet. Let's process it...
if (
msb !== undefined &&
lsb !== undefined &&
byteBuffer.length >= 4 + (msb << 8) + lsb
) {
// extract just the right amount of bytes
const packet = byteBuffer.subarray(4, 4 + (msb << 8) + lsb);

// check to make sure these bytes don't include a new packet start
// this would indicate a malformed packet...
const malformedDetectorIndex = packet.findIndex(
(byte) => byte === 0x94,
);
Expand All @@ -64,9 +96,13 @@ export const transformHandler = (
Protobuf.Mesh.LogRecord_Level.WARNING,
);

// prune out the malformed packet
byteBuffer = byteBuffer.subarray(malformedDetectorIndex);
} else {
// since we have a valid packet, we can remove those bytes...
byteBuffer = byteBuffer.subarray(3 + (msb << 8) + lsb + 1);

// and return the packet to the pipe...
controller.enqueue(packet);
}
} else {
Expand Down
Loading