Transport disconnect method (#753)

* Prevent reads from piling up

* Implement disconnect() method for all transports
This commit is contained in:
Henri Bergius
2025-08-05 22:46:23 -04:00
committed by GitHub
parent 313c4dbb71
commit 0a7b653ec8
7 changed files with 62 additions and 6 deletions
+1
View File
@@ -818,6 +818,7 @@ export class MeshDevice {
this.complete();
await this.transport.toDevice.close();
await this.transport.disconnect();
}
/**
+1
View File
@@ -15,6 +15,7 @@ export type DeviceOutput = Packet | DebugLog;
export interface Transport {
toDevice: WritableStream<Uint8Array>;
fromDevice: ReadableStream<DeviceOutput>;
disconnect(): Promise<void>;
}
export interface QueueItem {
+10 -2
View File
@@ -4,6 +4,7 @@ import { Utils } from "@meshtastic/core";
export class TransportDeno implements Types.Transport {
private _toDevice: WritableStream<Uint8Array>;
private _fromDevice: ReadableStream<Types.DeviceOutput>;
private connection: Deno.Conn | undefined;
public static async create(hostname: string): Promise<TransportDeno> {
const connection = await Deno.connect({
@@ -14,10 +15,11 @@ export class TransportDeno implements Types.Transport {
}
constructor(connection: Deno.Conn) {
Utils.toDeviceStream.readable.pipeTo(connection.writable);
this.connection = connection;
Utils.toDeviceStream.readable.pipeTo(this.connection.writable);
this._toDevice = Utils.toDeviceStream.writable;
this._fromDevice = connection.readable.pipeThrough(
this._fromDevice = this.connection.readable.pipeThrough(
Utils.fromDeviceStream(),
);
}
@@ -29,4 +31,10 @@ export class TransportDeno implements Types.Transport {
get fromDevice(): ReadableStream<Types.DeviceOutput> {
return this._fromDevice;
}
disconnect(): Promise<void> {
this.connection.close();
this.connection = undefined;
return Promise.resolve();
}
}
+24 -2
View File
@@ -6,6 +6,8 @@ export class TransportHTTP implements Types.Transport {
private url: string;
private receiveBatchRequests: boolean;
private fetchInterval: number;
private fetching: boolean;
private interval: number | undefined;
public static async create(
address: string,
@@ -23,6 +25,7 @@ export class TransportHTTP implements Types.Transport {
this.url = url;
this.receiveBatchRequests = false;
this.fetchInterval = 3000;
this.fetching = false;
this._toDevice = new WritableStream<Uint8Array>({
write: async (chunk) => {
@@ -38,8 +41,18 @@ export class TransportHTTP implements Types.Transport {
},
});
setInterval(async () => {
await this.readFromRadio(controller);
this.interval = setInterval(async () => {
if (this.fetching) {
// We still have the previous request open
return;
}
this.fetching = true;
try {
await this.readFromRadio(controller);
} catch (e) {
// TODO: Emit disconnection events for certain types of errors
}
this.fetching = false;
}, this.fetchInterval);
}
@@ -88,4 +101,13 @@ export class TransportHTTP implements Types.Transport {
get fromDevice(): ReadableStream<Types.DeviceOutput> {
return this._fromDevice;
}
disconnect() : Promise<void> {
this.fetching = false;
if (this.interval) {
clearInterval(this.interval);
}
this.interval = undefined;
return Promise.resolve();
}
}
+10 -2
View File
@@ -6,6 +6,7 @@ import { Utils } from "@meshtastic/core";
export class TransportNode implements Types.Transport {
private readonly _toDevice: WritableStream<Uint8Array>;
private readonly _fromDevice: ReadableStream<Types.DeviceOutput>;
private socket: Socket | undefined;
/**
* Creates and connects a new TransportNode instance.
@@ -36,7 +37,8 @@ export class TransportNode implements Types.Transport {
* @param connection - An active Node.js net.Socket connection.
*/
constructor(connection: Socket) {
connection.on("error", (err) => {
this.socket = connection;
this.socket.on("error", (err) => {
console.error("Socket connection error:", err);
});
@@ -56,7 +58,7 @@ export class TransportNode implements Types.Transport {
.pipeTo(Writable.toWeb(connection) as WritableStream<Uint8Array>)
.catch((err) => {
console.error("Error piping data to socket:", err);
connection.destroy(err as Error);
this.socket.destroy(err as Error);
});
}
@@ -73,4 +75,10 @@ export class TransportNode implements Types.Transport {
public get fromDevice(): ReadableStream<Types.DeviceOutput> {
return this._fromDevice;
}
disconnect() {
this.socket.destroy();
this.socket = undefined;
return Promise.resolve();
}
}
@@ -9,6 +9,7 @@ export class TransportWebBluetooth implements Types.Transport {
private toRadioCharacteristic: BluetoothRemoteGATTCharacteristic;
private fromRadioCharacteristic: BluetoothRemoteGATTCharacteristic;
private fromNumCharacteristic: BluetoothRemoteGATTCharacteristic;
private gattServer: BluetoothRemoteGATTServer;
static ToRadioUuid = "f75c76d2-129e-4dad-a1dd-7866124401e7";
static FromRadioUuid = "2c55e69e-4993-11ed-b878-0242ac120002";
@@ -65,6 +66,7 @@ export class TransportWebBluetooth implements Types.Transport {
toRadioCharacteristic,
fromRadioCharacteristic,
fromNumCharacteristic,
gattServer,
);
}
@@ -72,10 +74,12 @@ export class TransportWebBluetooth implements Types.Transport {
toRadioCharacteristic: BluetoothRemoteGATTCharacteristic,
fromRadioCharacteristic: BluetoothRemoteGATTCharacteristic,
fromNumCharacteristic: BluetoothRemoteGATTCharacteristic,
gattServer: BluetoothRemoteGATTServer,
) {
this.toRadioCharacteristic = toRadioCharacteristic;
this.fromRadioCharacteristic = fromRadioCharacteristic;
this.fromNumCharacteristic = fromNumCharacteristic;
this.gattServer = gattServer;
this._fromDevice = new ReadableStream({
start: (ctrl) => {
@@ -133,4 +137,9 @@ export class TransportWebBluetooth implements Types.Transport {
});
}
}
disconnect() : Promise<void> {
this.gattServer.disconnect();
return Promise.resolve();
}
}
@@ -4,6 +4,7 @@ import { Utils } from "@meshtastic/core";
export class TransportWebSerial implements Types.Transport {
private _toDevice: WritableStream<Uint8Array>;
private _fromDevice: ReadableStream<Types.DeviceOutput>;
private connection: SerialPort;
public static async create(baudRate?: number): Promise<TransportWebSerial> {
const port = await navigator.serial.requestPort();
@@ -24,6 +25,8 @@ export class TransportWebSerial implements Types.Transport {
throw new Error("Stream not accessible");
}
this.connection = connection;
Utils.toDeviceStream.readable.pipeTo(connection.writable);
this._toDevice = Utils.toDeviceStream.writable;
@@ -39,4 +42,8 @@ export class TransportWebSerial implements Types.Transport {
get fromDevice(): ReadableStream<Types.DeviceOutput> {
return this._fromDevice;
}
disconnect() {
return this.connection.close();
}
}