@neurodevs/node-lsl 23.1.0 → 23.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/__tests__/AbstractPackageTest.js +0 -1
- package/build/__tests__/AbstractPackageTest.js.map +1 -1
- package/build/__tests__/impl/BleGattController.test.d.ts +4 -0
- package/build/__tests__/impl/BleGattController.test.js +27 -5
- package/build/__tests__/impl/BleGattController.test.js.map +1 -1
- package/build/__tests__/impl/LslStreamInlet.test.d.ts +4 -5
- package/build/__tests__/impl/LslStreamInlet.test.js +37 -58
- package/build/__tests__/impl/LslStreamInlet.test.js.map +1 -1
- package/build/impl/LslStreamInfo.d.ts +3 -3
- package/build/impl/LslStreamInfo.js.map +1 -1
- package/build/impl/LslStreamInlet.d.ts +9 -39
- package/build/impl/LslStreamInlet.js +23 -151
- package/build/impl/LslStreamInlet.js.map +1 -1
- package/build/impl/LslStreamOutlet.d.ts +3 -3
- package/build/impl/LslStreamOutlet.js.map +1 -1
- package/build/impl/clocks/WindowedClockRegressor.d.ts +2 -2
- package/build/impl/clocks/WindowedClockRegressor.js.map +1 -1
- package/build/impl/controllers/BleGattController.d.ts +21 -12
- package/build/impl/controllers/BleGattController.js +22 -30
- package/build/impl/controllers/BleGattController.js.map +1 -1
- package/build/testDoubles/BleGatt/FakeBleGatt.d.ts +3 -2
- package/build/testDoubles/BleGatt/FakeBleGatt.js +6 -4
- package/build/testDoubles/BleGatt/FakeBleGatt.js.map +1 -1
- package/build/testDoubles/BleGatt/SpyBleGatt.d.ts +1 -1
- package/build/testDoubles/BleGatt/SpyBleGatt.js +2 -2
- package/build/testDoubles/BleGatt/SpyBleGatt.js.map +1 -1
- package/build/testDoubles/LslOutlet/FakeLslOutlet.d.ts +1 -1
- package/package.json +3 -4
- package/src/__tests__/AbstractPackageTest.ts +0 -2
- package/src/__tests__/impl/BleGattController.test.ts +48 -6
- package/src/__tests__/impl/LslStreamInlet.test.ts +45 -86
- package/src/impl/LslStreamInfo.ts +3 -3
- package/src/impl/LslStreamInlet.ts +38 -218
- package/src/impl/LslStreamOutlet.ts +3 -3
- package/src/impl/clocks/WindowedClockRegressor.ts +1 -1
- package/src/impl/controllers/BleGattController.ts +35 -37
- package/src/testDoubles/BleGatt/FakeBleGatt.ts +9 -6
- package/src/testDoubles/BleGatt/SpyBleGatt.ts +2 -2
|
@@ -1,24 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
handleLslError,
|
|
3
|
-
InfoHandle,
|
|
4
|
-
InletHandle,
|
|
5
|
-
LiblslAdapter,
|
|
6
|
-
} from '@neurodevs/ndx-native'
|
|
7
|
-
import {
|
|
8
|
-
JsExternal,
|
|
9
|
-
unwrapPointer,
|
|
10
|
-
createPointer,
|
|
11
|
-
DataType,
|
|
12
|
-
freePointer,
|
|
13
|
-
PointerType,
|
|
14
|
-
} from 'ffi-rs'
|
|
1
|
+
import { InfoHandle, InletHandle, LiblslAdapter } from '@neurodevs/ndx-native'
|
|
15
2
|
|
|
16
3
|
export default class LslStreamInlet implements LslInlet {
|
|
17
4
|
public static Class?: LslInletConstructor
|
|
18
5
|
public static waitAfterOpenStreamMs = 100
|
|
19
6
|
public static lsl = LiblslAdapter.getInstance()
|
|
20
|
-
public static handleLslError = handleLslError
|
|
21
|
-
public static freePointer = freePointer
|
|
22
7
|
|
|
23
8
|
public isRunning = false
|
|
24
9
|
|
|
@@ -32,37 +17,12 @@ export default class LslStreamInlet implements LslInlet {
|
|
|
32
17
|
private onData: OnDataCallback
|
|
33
18
|
|
|
34
19
|
private infoHandle!: InfoHandle
|
|
35
|
-
private inletHandle
|
|
36
|
-
private
|
|
37
|
-
|
|
38
|
-
private pullMethod!: () => {
|
|
39
|
-
samples: number[] | undefined
|
|
40
|
-
timestamps: number[] | undefined
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
private sampleBuffer!: Buffer<ArrayBuffer>
|
|
44
|
-
private sampleBufferRef!: JsExternal[]
|
|
45
|
-
private sampleBufferPtr!: JsExternal
|
|
46
|
-
|
|
47
|
-
private timestampBuffer!: Buffer<ArrayBuffer>
|
|
48
|
-
private timestampBufferRef!: JsExternal[]
|
|
49
|
-
private timestampBufferPtr!: JsExternal
|
|
50
|
-
|
|
51
|
-
private pullErrorBuffer!: Buffer<ArrayBuffer>
|
|
52
|
-
private pullErrorBufferRef!: JsExternal[]
|
|
53
|
-
private pullErrorBufferPtr!: JsExternal
|
|
54
|
-
|
|
55
|
-
private openStreamErrorBuffer!: Buffer<ArrayBuffer>
|
|
56
|
-
private openStreamErrorBufferRef!: JsExternal[]
|
|
57
|
-
private openStreamErrorBufferPtr!: JsExternal
|
|
20
|
+
private inletHandle?: InletHandle
|
|
21
|
+
private pullMethod: () => PulledData | undefined
|
|
58
22
|
|
|
59
23
|
private readonly sixMinutesInMs = 360 * 1000
|
|
60
24
|
private readonly aboutOneYearInMs = 32000000 * 1000
|
|
61
25
|
|
|
62
|
-
private readonly bytesPerFloat = 4
|
|
63
|
-
private readonly bytesPerDouble = 8
|
|
64
|
-
private readonly bytesPerI32 = 4
|
|
65
|
-
|
|
66
26
|
protected constructor(options: LslInletOptions, onData: OnDataCallback) {
|
|
67
27
|
const {
|
|
68
28
|
sourceId,
|
|
@@ -82,6 +42,10 @@ export default class LslStreamInlet implements LslInlet {
|
|
|
82
42
|
this.waitBetweenPullsMs = waitBetweenPullsMs ?? 1
|
|
83
43
|
this.flushInletOnStop = flushInletOnStop ?? true
|
|
84
44
|
this.onData = onData
|
|
45
|
+
|
|
46
|
+
this.pullMethod = (
|
|
47
|
+
chunkSize === 1 ? this.pullSample : this.pullChunk
|
|
48
|
+
).bind(this)
|
|
85
49
|
}
|
|
86
50
|
|
|
87
51
|
public static async Create(
|
|
@@ -106,10 +70,6 @@ export default class LslStreamInlet implements LslInlet {
|
|
|
106
70
|
private async createInlet() {
|
|
107
71
|
this.infoHandle = this.resolveInfoHandle()
|
|
108
72
|
this.inletHandle = this.doCreateInlet()
|
|
109
|
-
this.channelCount = this.getChannelCount()
|
|
110
|
-
|
|
111
|
-
this.allocateWritableBuffers()
|
|
112
|
-
this.setPullMethod()
|
|
113
73
|
|
|
114
74
|
this.openStream()
|
|
115
75
|
}
|
|
@@ -146,144 +106,14 @@ export default class LslStreamInlet implements LslInlet {
|
|
|
146
106
|
return this.lsl.createInlet({
|
|
147
107
|
infoHandle: this.infoHandle,
|
|
148
108
|
maxBufferedMs: this.maxBufferedMs,
|
|
109
|
+
chunkSize: this.chunkSize,
|
|
149
110
|
})
|
|
150
111
|
}
|
|
151
112
|
|
|
152
|
-
private getChannelCount() {
|
|
153
|
-
return this.lsl.getChannelCount({ infoHandle: this.infoHandle })
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
private allocateWritableBuffers() {
|
|
157
|
-
this.allocateDataBuffer()
|
|
158
|
-
this.allocateTimestampBuffer()
|
|
159
|
-
this.allocatePullErrorBuffer()
|
|
160
|
-
this.allocateOpenStreamErrorBuffer()
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
private allocateDataBuffer() {
|
|
164
|
-
this.sampleBuffer = Buffer.alloc(
|
|
165
|
-
this.channelCount * this.chunkSize * this.bytesPerFloat
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
this.sampleBufferRef = createPointer({
|
|
169
|
-
paramsType: [DataType.U8Array],
|
|
170
|
-
paramsValue: [this.sampleBuffer],
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
this.sampleBufferPtr = unwrapPointer(this.sampleBufferRef)[0]
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
private allocateTimestampBuffer() {
|
|
177
|
-
this.timestampBuffer = Buffer.alloc(
|
|
178
|
-
this.chunkSize * this.bytesPerDouble
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
this.timestampBufferRef = createPointer({
|
|
182
|
-
paramsType: [DataType.U8Array],
|
|
183
|
-
paramsValue: [this.timestampBuffer],
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
this.timestampBufferPtr = unwrapPointer(this.timestampBufferRef)[0]
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
private allocatePullErrorBuffer() {
|
|
190
|
-
this.pullErrorBuffer = Buffer.alloc(this.bytesPerI32)
|
|
191
|
-
|
|
192
|
-
this.pullErrorBufferRef = createPointer({
|
|
193
|
-
paramsType: [DataType.U8Array],
|
|
194
|
-
paramsValue: [this.pullErrorBuffer],
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
this.pullErrorBufferPtr = unwrapPointer(this.pullErrorBufferRef)[0]
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
private allocateOpenStreamErrorBuffer() {
|
|
201
|
-
this.openStreamErrorBuffer = Buffer.alloc(this.bytesPerI32)
|
|
202
|
-
|
|
203
|
-
this.openStreamErrorBufferRef = createPointer({
|
|
204
|
-
paramsType: [DataType.U8Array],
|
|
205
|
-
paramsValue: [this.openStreamErrorBuffer],
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
this.openStreamErrorBufferPtr = unwrapPointer(
|
|
209
|
-
this.openStreamErrorBufferRef
|
|
210
|
-
)[0]
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
private setPullMethod() {
|
|
214
|
-
this.pullMethod =
|
|
215
|
-
this.chunkSize === 1 ? this.pullSample : this.pullChunk
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
private pullSample = () => {
|
|
219
|
-
const timestampSec = this.doPullsample()
|
|
220
|
-
|
|
221
|
-
if (timestampSec > 0) {
|
|
222
|
-
return {
|
|
223
|
-
samples: this.readSamplesFromBuffer(),
|
|
224
|
-
timestamps: [timestampSec],
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
return { samples: undefined, timestamps: undefined }
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
private doPullsample() {
|
|
231
|
-
return this.lsl.pullSample({
|
|
232
|
-
inletHandle: this.inletHandle,
|
|
233
|
-
sampleBufferPtr: this.sampleBufferPtr,
|
|
234
|
-
sampleBufferElements: this.channelCount,
|
|
235
|
-
timeoutMs: this.pullTimeoutMs,
|
|
236
|
-
errorCodePtr: this.pullErrorBufferPtr,
|
|
237
|
-
})
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
private readSamplesFromBuffer() {
|
|
241
|
-
const floats = new Float32Array(
|
|
242
|
-
this.sampleBuffer.buffer,
|
|
243
|
-
this.sampleBuffer.byteOffset,
|
|
244
|
-
this.chunkSize * this.channelCount
|
|
245
|
-
)
|
|
246
|
-
return Array.from(floats)
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
private pullChunk = () => {
|
|
250
|
-
const firstTimestampSec = this.doPullChunk()
|
|
251
|
-
|
|
252
|
-
if (firstTimestampSec > 0) {
|
|
253
|
-
return {
|
|
254
|
-
samples: this.readSamplesFromBuffer(),
|
|
255
|
-
timestamps: this.readTimestampsFromBuffer(),
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
return { samples: undefined, timestamps: undefined }
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
private doPullChunk() {
|
|
262
|
-
return this.lsl.pullChunk({
|
|
263
|
-
inletHandle: this.inletHandle,
|
|
264
|
-
sampleBufferPtr: this.sampleBufferPtr,
|
|
265
|
-
sampleBufferElements: this.chunkSize * this.channelCount,
|
|
266
|
-
timestampBufferPtr: this.timestampBufferPtr,
|
|
267
|
-
timestampBufferElements: this.chunkSize,
|
|
268
|
-
timeoutMs: this.pullTimeoutMs,
|
|
269
|
-
errorCodePtr: this.pullErrorBufferPtr,
|
|
270
|
-
})
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
private readTimestampsFromBuffer() {
|
|
274
|
-
const doubles = new Float64Array(
|
|
275
|
-
this.timestampBuffer.buffer,
|
|
276
|
-
this.timestampBuffer.byteOffset,
|
|
277
|
-
this.chunkSize
|
|
278
|
-
)
|
|
279
|
-
return Array.from(doubles)
|
|
280
|
-
}
|
|
281
|
-
|
|
282
113
|
private openStream() {
|
|
283
114
|
this.lsl.openStream({
|
|
284
|
-
inletHandle: this.inletHandle
|
|
115
|
+
inletHandle: this.inletHandle!,
|
|
285
116
|
timeoutMs: this.openStreamTimeoutMs,
|
|
286
|
-
errorCodePtr: this.openStreamErrorBufferPtr,
|
|
287
117
|
})
|
|
288
118
|
}
|
|
289
119
|
|
|
@@ -295,17 +125,26 @@ export default class LslStreamInlet implements LslInlet {
|
|
|
295
125
|
}
|
|
296
126
|
|
|
297
127
|
private async pullDataOnce() {
|
|
298
|
-
const
|
|
299
|
-
this.handleLslErrorIfPresent()
|
|
128
|
+
const pulled = this.pullMethod()
|
|
300
129
|
|
|
301
|
-
if (
|
|
302
|
-
this.onData(samples, timestamps)
|
|
130
|
+
if (pulled) {
|
|
131
|
+
this.onData(pulled.samples, pulled.timestamps)
|
|
303
132
|
}
|
|
304
133
|
}
|
|
305
134
|
|
|
306
|
-
private
|
|
307
|
-
|
|
308
|
-
|
|
135
|
+
private pullSample() {
|
|
136
|
+
return this.lsl.pullSample(this.pullOptions)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private pullChunk() {
|
|
140
|
+
return this.lsl.pullChunk(this.pullOptions)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private get pullOptions() {
|
|
144
|
+
return {
|
|
145
|
+
inletHandle: this.inletHandle!,
|
|
146
|
+
timeoutMs: this.pullTimeoutMs,
|
|
147
|
+
}
|
|
309
148
|
}
|
|
310
149
|
|
|
311
150
|
private async waitBetweenPulls() {
|
|
@@ -313,7 +152,7 @@ export default class LslStreamInlet implements LslInlet {
|
|
|
313
152
|
}
|
|
314
153
|
|
|
315
154
|
public flushInlet() {
|
|
316
|
-
this.lsl.flushInlet({ inletHandle: this.inletHandle })
|
|
155
|
+
this.lsl.flushInlet({ inletHandle: this.inletHandle! })
|
|
317
156
|
}
|
|
318
157
|
|
|
319
158
|
public stopPulling() {
|
|
@@ -326,7 +165,7 @@ export default class LslStreamInlet implements LslInlet {
|
|
|
326
165
|
}
|
|
327
166
|
|
|
328
167
|
private closeStream() {
|
|
329
|
-
this.lsl.closeStream({ inletHandle: this.inletHandle })
|
|
168
|
+
this.lsl.closeStream({ inletHandle: this.inletHandle! })
|
|
330
169
|
}
|
|
331
170
|
|
|
332
171
|
public destroy() {
|
|
@@ -334,43 +173,19 @@ export default class LslStreamInlet implements LslInlet {
|
|
|
334
173
|
this.stopPulling()
|
|
335
174
|
}
|
|
336
175
|
|
|
337
|
-
this.
|
|
338
|
-
this.freeNativePointers()
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
private doDestroyInlet() {
|
|
342
|
-
this.lsl.destroyInlet({ inletHandle: this.inletHandle })
|
|
176
|
+
this.destroyInletIfCreated()
|
|
343
177
|
}
|
|
344
178
|
|
|
345
|
-
private
|
|
346
|
-
this.
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
DataType.U8Array,
|
|
351
|
-
DataType.U8Array,
|
|
352
|
-
],
|
|
353
|
-
paramsValue: [
|
|
354
|
-
this.openStreamErrorBufferPtr,
|
|
355
|
-
this.sampleBufferPtr,
|
|
356
|
-
this.timestampBufferPtr,
|
|
357
|
-
this.pullErrorBufferPtr,
|
|
358
|
-
],
|
|
359
|
-
pointerType: PointerType.CPointer,
|
|
360
|
-
})
|
|
179
|
+
private destroyInletIfCreated() {
|
|
180
|
+
if (this.inletHandle) {
|
|
181
|
+
this.lsl.destroyInlet({ inletHandle: this.inletHandle })
|
|
182
|
+
delete this.inletHandle
|
|
183
|
+
}
|
|
361
184
|
}
|
|
362
185
|
|
|
363
186
|
private get lsl() {
|
|
364
187
|
return LslStreamInlet.lsl
|
|
365
188
|
}
|
|
366
|
-
|
|
367
|
-
private get handleLslError() {
|
|
368
|
-
return LslStreamInlet.handleLslError
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
private get freePointer() {
|
|
372
|
-
return LslStreamInlet.freePointer
|
|
373
|
-
}
|
|
374
189
|
}
|
|
375
190
|
|
|
376
191
|
export interface LslInlet {
|
|
@@ -396,4 +211,9 @@ export interface LslInletOptions {
|
|
|
396
211
|
flushInletOnStop?: boolean
|
|
397
212
|
}
|
|
398
213
|
|
|
214
|
+
export interface PulledData {
|
|
215
|
+
samples: number[]
|
|
216
|
+
timestamps: number[]
|
|
217
|
+
}
|
|
218
|
+
|
|
399
219
|
export type OnDataCallback = (samples: number[], timestamps: number[]) => void
|
|
@@ -25,7 +25,7 @@ export default class LslStreamOutlet implements LslOutlet {
|
|
|
25
25
|
public readonly name: string
|
|
26
26
|
public readonly type: string
|
|
27
27
|
public readonly sourceId: string
|
|
28
|
-
public readonly channelNames: string[]
|
|
28
|
+
public readonly channelNames: readonly string[]
|
|
29
29
|
public readonly channelFormat: ChannelFormat
|
|
30
30
|
public readonly channelCount: number
|
|
31
31
|
public readonly sampleRateHz: number
|
|
@@ -168,7 +168,7 @@ export interface LslOutlet {
|
|
|
168
168
|
readonly name: string
|
|
169
169
|
readonly type: string
|
|
170
170
|
readonly sourceId: string
|
|
171
|
-
readonly channelNames: string[]
|
|
171
|
+
readonly channelNames: readonly string[]
|
|
172
172
|
readonly channelCount: number
|
|
173
173
|
readonly channelFormat: ChannelFormat
|
|
174
174
|
readonly sampleRateHz: number
|
|
@@ -184,7 +184,7 @@ export interface LslOutletOptions {
|
|
|
184
184
|
name: string
|
|
185
185
|
type: string
|
|
186
186
|
sourceId: string
|
|
187
|
-
channelNames: string[]
|
|
187
|
+
channelNames: readonly string[]
|
|
188
188
|
channelFormat: ChannelFormat
|
|
189
189
|
sampleRateHz: number
|
|
190
190
|
chunkSize: number
|
|
@@ -41,7 +41,7 @@ export default class WindowedClockRegressor implements ClockRegressor {
|
|
|
41
41
|
|
|
42
42
|
const anchor = this.anchorTimeFor(deviceTime)
|
|
43
43
|
|
|
44
|
-
const timestamps = Array(chunkSize).fill(0)
|
|
44
|
+
const timestamps: number[] = Array(chunkSize).fill(0)
|
|
45
45
|
|
|
46
46
|
for (let i = 0; i <= chunkSize - 1; i++) {
|
|
47
47
|
const stepsFromLast = chunkSize - 1 - i
|
|
@@ -1,33 +1,20 @@
|
|
|
1
|
-
import { LibndxAdapter,
|
|
1
|
+
import { LibndxAdapter, NativePeripheral } from '@neurodevs/ndx-native'
|
|
2
2
|
|
|
3
3
|
export default class BleGattController implements BleGatt {
|
|
4
4
|
public static Class?: BleGattConstructor
|
|
5
5
|
public static setTimeout = setTimeout
|
|
6
6
|
public static waitAfterMs = 1000
|
|
7
7
|
|
|
8
|
-
private static _ndx?: Libndx
|
|
9
|
-
|
|
10
|
-
public static get ndx() {
|
|
11
|
-
if (!this._ndx) {
|
|
12
|
-
this._ndx = LibndxAdapter.getInstance()
|
|
13
|
-
}
|
|
14
|
-
return this._ndx
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
public static set ndx(value: Libndx) {
|
|
18
|
-
this._ndx = value
|
|
19
|
-
}
|
|
20
|
-
|
|
21
8
|
protected charCallbacks: CharacteristicCallbacks
|
|
22
9
|
protected rssiIntervalMs?: number
|
|
23
|
-
protected
|
|
10
|
+
protected state: BleGattState = { status: 'disconnected' }
|
|
24
11
|
protected log = console
|
|
25
12
|
|
|
26
|
-
private deviceUuid?: string
|
|
27
13
|
private deviceNamePrefix?: string
|
|
28
|
-
private deviceName?: string
|
|
29
14
|
private onConnected?: (peripheral: NativePeripheral) => void
|
|
30
15
|
|
|
16
|
+
private readonly ndx = LibndxAdapter.getInstance()
|
|
17
|
+
|
|
31
18
|
protected constructor(options: BleGattOptions) {
|
|
32
19
|
const {
|
|
33
20
|
deviceUuid,
|
|
@@ -37,7 +24,7 @@ export default class BleGattController implements BleGatt {
|
|
|
37
24
|
rssiIntervalMs,
|
|
38
25
|
} = options
|
|
39
26
|
|
|
40
|
-
this.
|
|
27
|
+
this.state = { status: 'disconnected', uuid: deviceUuid }
|
|
41
28
|
this.deviceNamePrefix = deviceNamePrefix
|
|
42
29
|
this.charCallbacks = charCallbacks
|
|
43
30
|
this.onConnected = onConnected
|
|
@@ -49,10 +36,12 @@ export default class BleGattController implements BleGatt {
|
|
|
49
36
|
}
|
|
50
37
|
|
|
51
38
|
public async connect() {
|
|
52
|
-
if (!this.
|
|
39
|
+
if (!this.state.uuid) {
|
|
53
40
|
await this.discoverUuid()
|
|
54
41
|
}
|
|
55
42
|
|
|
43
|
+
this.state = { status: 'connecting', uuid: this.uuid }
|
|
44
|
+
|
|
56
45
|
this.createBleGattBackend()
|
|
57
46
|
this.startBleGattBackend()
|
|
58
47
|
|
|
@@ -66,7 +55,7 @@ export default class BleGattController implements BleGatt {
|
|
|
66
55
|
const { status, error } = this.ndx.discoverBleUuid({
|
|
67
56
|
namePrefix: this.deviceNamePrefix as string,
|
|
68
57
|
onDiscovered: (uuid: string) => {
|
|
69
|
-
this.
|
|
58
|
+
this.state = { status: 'disconnected', uuid }
|
|
70
59
|
},
|
|
71
60
|
})
|
|
72
61
|
|
|
@@ -78,7 +67,7 @@ export default class BleGattController implements BleGatt {
|
|
|
78
67
|
private async waitForDiscoveredUuid() {
|
|
79
68
|
await new Promise<void>((resolve) => {
|
|
80
69
|
const checkDiscovered = () => {
|
|
81
|
-
if (this.
|
|
70
|
+
if (this.uuid) {
|
|
82
71
|
resolve()
|
|
83
72
|
} else {
|
|
84
73
|
BleGattController.setTimeout(checkDiscovered, 100)
|
|
@@ -119,12 +108,11 @@ export default class BleGattController implements BleGatt {
|
|
|
119
108
|
deviceUuid: this.uuid,
|
|
120
109
|
onConnected: (peripheral: NativePeripheral) => {
|
|
121
110
|
const { name } = peripheral
|
|
122
|
-
this.
|
|
123
|
-
this.connected = true
|
|
111
|
+
this.state = { status: 'connected', uuid: this.uuid, name }
|
|
124
112
|
this.log.info(`Connected to device ${this.uuid}!`)
|
|
125
113
|
this.onConnected?.(peripheral)
|
|
126
114
|
},
|
|
127
|
-
charCallbacks: this.charCallbacks,
|
|
115
|
+
charCallbacks: [...this.charCallbacks],
|
|
128
116
|
})
|
|
129
117
|
|
|
130
118
|
this.throwIfError(status, error)
|
|
@@ -133,7 +121,7 @@ export default class BleGattController implements BleGatt {
|
|
|
133
121
|
private async waitForOnConnected() {
|
|
134
122
|
await new Promise<void>((resolve) => {
|
|
135
123
|
const checkConnected = () => {
|
|
136
|
-
if (this.connected) {
|
|
124
|
+
if (this.state.status === 'connected') {
|
|
137
125
|
resolve()
|
|
138
126
|
} else {
|
|
139
127
|
BleGattController.setTimeout(checkConnected, 100)
|
|
@@ -154,7 +142,7 @@ export default class BleGattController implements BleGatt {
|
|
|
154
142
|
value: string
|
|
155
143
|
) {
|
|
156
144
|
const { status, error } = this.ndx.writeBleGattChar({
|
|
157
|
-
deviceUuid: this.
|
|
145
|
+
deviceUuid: this.requireUuid(),
|
|
158
146
|
charUuid,
|
|
159
147
|
value,
|
|
160
148
|
})
|
|
@@ -166,8 +154,8 @@ export default class BleGattController implements BleGatt {
|
|
|
166
154
|
charCallbacks: CharacteristicCallbacks
|
|
167
155
|
) {
|
|
168
156
|
const { status, error } = this.ndx.registerBleGattCharCallbacks({
|
|
169
|
-
deviceUuid: this.
|
|
170
|
-
charCallbacks,
|
|
157
|
+
deviceUuid: this.requireUuid(),
|
|
158
|
+
charCallbacks: [...charCallbacks],
|
|
171
159
|
})
|
|
172
160
|
|
|
173
161
|
this.throwIfError(status, error)
|
|
@@ -177,29 +165,34 @@ export default class BleGattController implements BleGatt {
|
|
|
177
165
|
|
|
178
166
|
public async disconnect() {
|
|
179
167
|
const { status, error } = this.ndx.stopBleGattBackend({
|
|
180
|
-
deviceUuid: this.
|
|
168
|
+
deviceUuid: this.requireUuid(),
|
|
181
169
|
})
|
|
182
170
|
|
|
183
171
|
this.throwIfError(status, error)
|
|
184
|
-
this.
|
|
172
|
+
this.state = { status: 'disconnected', uuid: this.state.uuid }
|
|
185
173
|
}
|
|
186
174
|
|
|
187
175
|
public get uuid() {
|
|
188
|
-
return this.
|
|
176
|
+
return this.state.uuid ?? ''
|
|
189
177
|
}
|
|
190
178
|
|
|
191
179
|
public get name() {
|
|
192
|
-
return this.
|
|
180
|
+
return this.state.name ?? 'N/A'
|
|
193
181
|
}
|
|
194
182
|
|
|
195
|
-
private
|
|
196
|
-
|
|
183
|
+
private requireUuid() {
|
|
184
|
+
if (!this.uuid) {
|
|
185
|
+
throw new Error(
|
|
186
|
+
'Device uuid is not resolved yet! Must either pass as option to Create or call connect() first.'
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
return this.uuid
|
|
197
190
|
}
|
|
198
191
|
}
|
|
199
192
|
|
|
200
193
|
export interface BleGatt {
|
|
201
|
-
uuid: string
|
|
202
|
-
name: string
|
|
194
|
+
readonly uuid: string
|
|
195
|
+
readonly name: string
|
|
203
196
|
|
|
204
197
|
connect(): Promise<void>
|
|
205
198
|
|
|
@@ -226,7 +219,12 @@ export type BleGattOptions = {
|
|
|
226
219
|
|
|
227
220
|
export type BleGattConstructor = new (options: BleGattOptions) => BleGatt
|
|
228
221
|
|
|
229
|
-
export type
|
|
222
|
+
export type BleGattState =
|
|
223
|
+
| { status: 'disconnected'; uuid?: string; name?: string }
|
|
224
|
+
| { status: 'connecting'; uuid: string; name?: string }
|
|
225
|
+
| { status: 'connected'; uuid: string; name: string }
|
|
226
|
+
|
|
227
|
+
export type CharacteristicCallbacks = readonly {
|
|
230
228
|
charUuid: string
|
|
231
229
|
charName?: string
|
|
232
230
|
onData: (data: Buffer, length: number, timestampSec: number) => void
|
|
@@ -22,21 +22,24 @@ export default class FakeBleGatt implements BleGatt {
|
|
|
22
22
|
|
|
23
23
|
public static fakeCharacteristics: Record<string, unknown> = {}
|
|
24
24
|
public static fakeName = `fake-${generateId()}`
|
|
25
|
+
public static fakeDiscoveredUuid = `fake-uuid-${generateId()}`
|
|
25
26
|
|
|
26
|
-
private _uuid
|
|
27
|
-
private _name
|
|
27
|
+
private _uuid?: string
|
|
28
|
+
private _name?: string
|
|
28
29
|
|
|
29
30
|
public constructor(options?: BleGattOptions) {
|
|
30
31
|
const { deviceUuid } = options ?? {}
|
|
31
32
|
|
|
32
|
-
this._uuid = deviceUuid
|
|
33
|
-
this._name = FakeBleGatt.fakeName
|
|
33
|
+
this._uuid = deviceUuid
|
|
34
34
|
|
|
35
35
|
FakeBleGatt.callsToConstructor.push(options)
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
public async connect() {
|
|
39
39
|
FakeBleGatt.numCallsToConnect++
|
|
40
|
+
|
|
41
|
+
this._uuid = this._uuid ?? FakeBleGatt.fakeDiscoveredUuid
|
|
42
|
+
this._name = FakeBleGatt.fakeName
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
public async writeCharacteristic(
|
|
@@ -60,11 +63,11 @@ export default class FakeBleGatt implements BleGatt {
|
|
|
60
63
|
}
|
|
61
64
|
|
|
62
65
|
public get uuid() {
|
|
63
|
-
return this._uuid
|
|
66
|
+
return this._uuid ?? ''
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
public get name() {
|
|
67
|
-
return this._name
|
|
70
|
+
return this._name ?? 'N/A'
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
public static resetTestDouble() {
|