@@ -20,28 +20,36 @@ export class TransportNode implements Types.Transport {
20
20
Types . DeviceStatusEnum . DeviceDisconnected ;
21
21
22
22
private closingByUser = false ;
23
+ private errored = false ;
23
24
24
25
/**
25
26
* Creates and connects a new TransportNode instance.
26
27
* @param hostname - The IP address or hostname of the Meshtastic device.
27
28
* @param port - The port number for the TCP connection (defaults to 4403).
29
+ * @param timeout - TCP socket timeout in milliseconds (defaults to 60000).
28
30
* @returns A promise that resolves with a connected TransportNode instance.
29
31
*/
30
- public static create ( hostname : string , port = 4403 ) : Promise < TransportNode > {
32
+ public static create (
33
+ hostname : string ,
34
+ port = 4403 ,
35
+ timeout = 60000 ,
36
+ ) : Promise < TransportNode > {
31
37
return new Promise ( ( resolve , reject ) => {
32
38
const socket = new Socket ( ) ;
33
39
34
40
const onError = ( err : Error ) => {
35
41
socket . destroy ( ) ;
42
+ socket . removeAllListeners ( ) ;
36
43
reject ( err ) ;
37
44
} ;
38
45
39
46
socket . once ( "error" , onError ) ;
40
-
41
- socket . connect ( port , hostname , ( ) => {
47
+ socket . once ( "ready" , ( ) => {
42
48
socket . removeListener ( "error" , onError ) ;
43
49
resolve ( new TransportNode ( socket ) ) ;
44
50
} ) ;
51
+ socket . setTimeout ( timeout ) ;
52
+ socket . connect ( port , hostname ) ;
45
53
} ) ;
46
54
}
47
55
@@ -52,8 +60,10 @@ export class TransportNode implements Types.Transport {
52
60
constructor ( connection : Socket ) {
53
61
this . socket = connection ;
54
62
55
- this . socket . on ( "error" , ( err ) => {
56
- console . error ( "Socket connection error:" , err ) ;
63
+ this . socket . on ( "error" , ( ) => {
64
+ this . errored = true ;
65
+ this . socket ?. removeAllListeners ( ) ;
66
+ this . socket ?. destroy ( ) ;
57
67
if ( ! this . closingByUser ) {
58
68
this . emitStatus (
59
69
Types . DeviceStatusEnum . DeviceDisconnected ,
@@ -62,6 +72,24 @@ export class TransportNode implements Types.Transport {
62
72
}
63
73
} ) ;
64
74
75
+ this . socket . on ( "end" , ( ) => {
76
+ if ( this . closingByUser ) {
77
+ return ; // suppress close-derived disconnect in user flow
78
+ }
79
+ this . emitStatus ( Types . DeviceStatusEnum . DeviceDisconnected , "socket-end" ) ;
80
+ this . socket ?. removeAllListeners ( ) ;
81
+ this . socket ?. destroy ( ) ;
82
+ } ) ;
83
+
84
+ this . socket . on ( "timeout" , ( ) => {
85
+ this . emitStatus (
86
+ Types . DeviceStatusEnum . DeviceDisconnected ,
87
+ "socket-timeout" ,
88
+ ) ;
89
+ this . socket ?. removeAllListeners ( ) ;
90
+ this . socket ?. destroy ( ) ;
91
+ } ) ;
92
+
65
93
this . socket . on ( "close" , ( ) => {
66
94
if ( this . closingByUser ) {
67
95
return ; // suppress close-derived disconnect in user flow
@@ -98,7 +126,7 @@ export class TransportNode implements Types.Transport {
98
126
}
99
127
ctrl . close ( ) ;
100
128
} catch ( error ) {
101
- if ( this . closingByUser ) {
129
+ if ( this . closingByUser || this . errored ) {
102
130
ctrl . close ( ) ;
103
131
} else {
104
132
this . emitStatus (
@@ -120,18 +148,17 @@ export class TransportNode implements Types.Transport {
120
148
} ) ;
121
149
122
150
// Stream for data going FROM the application TO the Meshtastic device.
123
- const toDeviceTransform = Utils . toDeviceStream ;
151
+ const toDeviceTransform = Utils . toDeviceStream ( ) ;
124
152
this . _toDevice = toDeviceTransform . writable ;
125
153
126
154
this . pipePromise = toDeviceTransform . readable
127
155
. pipeTo ( Writable . toWeb ( connection ) as WritableStream < Uint8Array > , {
128
156
signal : abortController . signal ,
129
157
} )
130
158
. catch ( ( err ) => {
131
- if ( abortController . signal . aborted ) {
159
+ if ( abortController . signal . aborted || this . socket ?. destroyed ) {
132
160
return ;
133
161
}
134
- console . error ( "Error piping data to socket:" , err ) ;
135
162
const error = err instanceof Error ? err : new Error ( String ( err ) ) ;
136
163
this . socket ?. destroy ( error ) ;
137
164
} ) ;
@@ -160,11 +187,11 @@ export class TransportNode implements Types.Transport {
160
187
if ( this . pipePromise ) {
161
188
await this . pipePromise ;
162
189
}
163
-
164
190
this . socket ?. destroy ( ) ;
165
191
} finally {
166
192
this . socket = undefined ;
167
193
this . closingByUser = false ;
194
+ this . errored = false ;
168
195
}
169
196
}
170
197
@@ -173,9 +200,13 @@ export class TransportNode implements Types.Transport {
173
200
return ;
174
201
}
175
202
this . lastStatus = next ;
176
- this . fromDeviceController ?. enqueue ( {
177
- type : "status" ,
178
- data : { status : next , reason } ,
179
- } ) ;
203
+ try {
204
+ this . fromDeviceController ?. enqueue ( {
205
+ type : "status" ,
206
+ data : { status : next , reason } ,
207
+ } ) ;
208
+ } catch ( e ) {
209
+ console . error ( "Enqueue fail" , e ) ;
210
+ }
180
211
}
181
212
}
0 commit comments