@@ -7,9 +7,8 @@ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi }
7
7
import { DEFAULT_WIRESHARK_IP , DEFAULT_ZEP_UDP_PORT , createWiresharkZEPFrame } from "../src/dev/wireshark" ;
8
8
import { OTRCPDriver , type SourceRouteTableEntry } from "../src/drivers/ot-rcp-driver" ;
9
9
import { SpinelCommandId } from "../src/spinel/commands" ;
10
- import { decodeHdlcFrame } from "../src/spinel/hdlc" ;
11
10
import { SpinelPropertyId } from "../src/spinel/properties" ;
12
- import { SPINEL_HEADER_FLG_SPINEL , type SpinelFrame , decodeSpinelFrame , encodeSpinelFrame } from "../src/spinel/spinel" ;
11
+ import { SPINEL_HEADER_FLG_SPINEL , encodeSpinelFrame } from "../src/spinel/spinel" ;
13
12
import { SpinelStatus } from "../src/spinel/statuses" ;
14
13
import { MACAssociationStatus , type MACCapabilities , type MACHeader , decodeMACFrameControl , decodeMACHeader } from "../src/zigbee/mac" ;
15
14
import { ZigbeeConsts } from "../src/zigbee/zigbee" ;
@@ -62,7 +61,6 @@ import {
62
61
63
62
const randomBigInt = ( ) : bigint => BigInt ( `0x${ randomBytes ( 8 ) . toString ( "hex" ) } ` ) ;
64
63
65
- const RESET_POWER_ON_FRAME_HEX = "7e80060070ee747e" ;
66
64
const COMMON_FFD_MAC_CAP : MACCapabilities = {
67
65
alternatePANCoordinator : false ,
68
66
deviceType : 1 ,
@@ -80,6 +78,61 @@ const COMMON_RFD_MAC_CAP: MACCapabilities = {
80
78
allocateAddress : true ,
81
79
} ;
82
80
81
+ const START_FRAMES_SILABS = {
82
+ protocolVersion : "7e8106010403db0a7e" ,
83
+ ncpVersion :
84
+ "7e820602534c2d4f50454e5448524541442f322e352e322e305f4769744875622d3166636562323235623b2045465233323b204d617220313920323032352031333a34353a343400b5dc7e" ,
85
+ interfaceType : "7e83060303573a7e" ,
86
+ rcpAPIVersion : "7e8406b0010a681f7e" ,
87
+ rcpMinHostAPIVersion : "7e8506b101048ea77e" ,
88
+ resetPowerOn : "7e80060070ee747e" ,
89
+ } ;
90
+ const START_FRAMES_TI = {
91
+ protocolVersion : "7e8106010403db0a7e" ,
92
+ ncpVersion :
93
+ "7e8206024f50454e5448524541442f312e342e302d4b6f656e6b6b2d323032352e322e313b204343313358585f4343323658583b2046656220203320323032352032313a30303a303200ef147e" ,
94
+ interfaceType : "7e83060303573a7e" ,
95
+ rcpAPIVersion : "7e8406b0010be10e7e" ,
96
+ rcpMinHostAPIVersion : "7e8506b101048ea77e" ,
97
+ resetPowerOn : "7e80060070ee747e" ,
98
+ } ;
99
+ const FORM_FRAMES_SILABS = {
100
+ phyEnabled : "7e87062001f2627e" ,
101
+ phyChan : "7e88062114ff8e7e" ,
102
+ phyTxPowerSet : "7e8906257d339b817e" ,
103
+ mac154LAddr : "7e8a06344d325a6e6f486f5a8f327e" ,
104
+ mac154SAddr : "7e8b0635000047f67e" ,
105
+ mac154PANId : "7e8c0636d98579727e" ,
106
+ macRxOnWhenIdleMode : "7e8d060000e68c7e" ,
107
+ macRawStreamEnabled : "7e8e06370108437e" ,
108
+ phyTxPowerGet : "7e8106257d3343647e" ,
109
+ phyRSSIGet : "7e820626983d517e" ,
110
+ phyRXSensitivityGet : "7e8306279c7a127e" ,
111
+ phyCCAThresholdGet : "7e840624b5f0d37e" ,
112
+ } ;
113
+ const FORM_FRAMES_TI = {
114
+ phyEnabled : "7e87062001f2627e" ,
115
+ phyChan : "7e88062114ff8e7e" ,
116
+ phyTxPowerSet : "7e890625052cf47e" ,
117
+ mac154LAddr : "7e8a06344d325a6e6f486f5a8f327e" ,
118
+ mac154SAddr : "7e8b0635000047f67e" ,
119
+ mac154PANId : "7e8c0636d9c57d5d307e" ,
120
+ macRxOnWhenIdleMode : "7e8d060000e68c7e" ,
121
+ macRawStreamEnabled : "7e8e06370108437e" ,
122
+ phyTxPowerGet : "7e81062505f47d317e" ,
123
+ phyRSSIGet : "7e820626ef05567e" ,
124
+ phyRXSensitivityGet : "7e830627a6a38c7e" ,
125
+ phyCCAThresholdGet : "7e8406000297567e" ,
126
+ } ;
127
+ // const STOP_FRAMES_SILABS = {
128
+ // macRawStreamEnabled: "7e8b063700d63c7e",
129
+ // phyEnabled: "7e8c0620006eb37e",
130
+ // }
131
+ // const STOP_FRAMES_TI = {
132
+ // macRawStreamEnabled: "7e8c063700f76b7e",
133
+ // phyEnabled: "7e8d062000d5af7e",
134
+ // }
135
+
83
136
describe ( "OT RCP Driver" , ( ) => {
84
137
let wiresharkSeqNum : number ;
85
138
let wiresharkSocket : Socket | undefined ;
@@ -146,50 +199,50 @@ describe("OT RCP Driver", () => {
146
199
return Buffer . from ( encHdlcFrame . data . subarray ( 0 , encHdlcFrame . length ) ) ;
147
200
} ;
148
201
149
- const mockGetPropertyPayload = ( hex : string ) : SpinelFrame => decodeSpinelFrame ( decodeHdlcFrame ( Buffer . from ( hex , "hex" ) ) ) ;
150
-
151
- const mockStart = async ( driver : OTRCPDriver , loadState = true , timeoutReset = false ) => {
202
+ const mockStart = async ( driver : OTRCPDriver , loadState = true , timeoutReset = false , frames = START_FRAMES_SILABS ) => {
152
203
if ( driver ) {
153
204
let loadStateSpy : ReturnType < typeof vi . spyOn > | undefined ;
154
205
155
206
if ( ! loadState ) {
156
207
loadStateSpy = vi . spyOn ( driver , "loadState" ) . mockResolvedValue ( undefined ) ;
157
208
}
158
209
159
- const getPropertySpy = vi
160
- . spyOn ( driver , "getProperty" )
161
- . mockResolvedValueOnce ( mockGetPropertyPayload ( "7e8106010403db0a7e" ) ) // PROTOCOL_VERSION
162
- . mockResolvedValueOnce (
163
- mockGetPropertyPayload (
164
- "7e820602534c2d4f50454e5448524541442f322e352e322e305f4769744875622d3166636562323235623b2045465233323b204d617220313920323032352031333a34353a343400b5dc7e" ,
165
- ) ,
166
- ) // NCP_VERSION
167
- . mockResolvedValueOnce ( mockGetPropertyPayload ( "7e83060303573a7e" ) ) // INTERFACE_TYPE
168
- . mockResolvedValueOnce ( mockGetPropertyPayload ( "7e8406b0010a681f7e" ) ) // RCP_API_VERSION
169
- . mockResolvedValueOnce ( mockGetPropertyPayload ( "7e8506b101048ea77e" ) ) ; // RCP_MIN_HOST_API_VERSION
170
-
171
- const waitForResetSpy = vi . spyOn ( driver , "waitForReset" ) . mockImplementationOnce ( async ( ) => {
172
- const p = driver . waitForReset ( ) ;
173
-
174
- if ( timeoutReset ) {
175
- await vi . advanceTimersByTimeAsync ( 5500 ) ;
176
- } else {
177
- driver . parser . _transform ( Buffer . from ( RESET_POWER_ON_FRAME_HEX , "hex" ) , "utf8" , ( ) => { } ) ;
178
- await vi . advanceTimersByTimeAsync ( 10 ) ;
210
+ let i = - 1 ;
211
+ const orderedFrames = [
212
+ frames . protocolVersion ,
213
+ frames . ncpVersion ,
214
+ frames . interfaceType ,
215
+ frames . rcpAPIVersion ,
216
+ frames . rcpMinHostAPIVersion ,
217
+ frames . resetPowerOn ,
218
+ ] ;
219
+
220
+ const reply = async ( ) => {
221
+ await vi . advanceTimersByTimeAsync ( 5 ) ;
222
+
223
+ // skip cancel byte
224
+ if ( i >= 0 ) {
225
+ if ( i === 5 && timeoutReset ) {
226
+ await vi . advanceTimersByTimeAsync ( 5500 ) ;
227
+ }
228
+
229
+ driver . parser . _transform ( Buffer . from ( orderedFrames [ i ] , "hex" ) , "utf8" , ( ) => { } ) ;
230
+ await vi . advanceTimersByTimeAsync ( 5 ) ;
179
231
}
180
232
181
- await p ;
182
- } ) ;
233
+ i ++ ;
183
234
184
- await driver . start ( ) ;
185
-
186
- nextTidFromStartup += 1 ; // sendCommand RESET
235
+ if ( i === orderedFrames . length ) {
236
+ driver . writer . removeListener ( "data" , reply ) ;
237
+ }
238
+ } ;
187
239
240
+ driver . writer . on ( "data" , reply ) ;
241
+ await driver . start ( ) ;
188
242
loadStateSpy ?. mockRestore ( ) ;
189
- getPropertySpy . mockRestore ( ) ;
190
- waitForResetSpy . mockRestore ( ) ;
191
-
192
243
await vi . advanceTimersByTimeAsync ( 100 ) ; // flush
244
+
245
+ nextTidFromStartup = driver . currentSpinelTID + 1 ;
193
246
}
194
247
} ;
195
248
@@ -207,17 +260,41 @@ describe("OT RCP Driver", () => {
207
260
208
261
await vi . advanceTimersByTimeAsync ( 100 ) ; // flush
209
262
}
263
+
264
+ nextTidFromStartup = 1 ;
210
265
} ;
211
266
212
- const mockFormNetwork = async ( driver : OTRCPDriver , registerTimers = false ) => {
267
+ const mockFormNetwork = async ( driver : OTRCPDriver , registerTimers = false , frames = FORM_FRAMES_SILABS ) => {
213
268
if ( driver ) {
214
- const setPropertySpy = vi . spyOn ( driver , "setProperty" ) . mockResolvedValue ( ) ;
215
- const getPropertySpy = vi
216
- . spyOn ( driver , "getProperty" )
217
- . mockResolvedValueOnce ( mockGetPropertyPayload ( "7e8106257d3343647e" ) ) // PHY_TX_POWER
218
- . mockResolvedValueOnce ( mockGetPropertyPayload ( "7e82062695d88a7e" ) ) // PHY_RSSI
219
- . mockResolvedValueOnce ( mockGetPropertyPayload ( "7e8306279c7a127e" ) ) // PHY_RX_SENSITIVITY
220
- . mockResolvedValueOnce ( mockGetPropertyPayload ( "7e840624b5f0d37e" ) ) ; // PHY_CCA_THRESHOLD
269
+ let i = 0 ;
270
+ const orderedFrames = [
271
+ frames . phyEnabled ,
272
+ frames . phyChan ,
273
+ frames . phyTxPowerSet ,
274
+ frames . mac154LAddr ,
275
+ frames . mac154SAddr ,
276
+ frames . mac154PANId ,
277
+ frames . macRxOnWhenIdleMode ,
278
+ frames . macRawStreamEnabled ,
279
+ frames . phyTxPowerGet ,
280
+ frames . phyRSSIGet ,
281
+ frames . phyRXSensitivityGet ,
282
+ frames . phyCCAThresholdGet ,
283
+ ] ;
284
+
285
+ const reply = async ( ) => {
286
+ await vi . advanceTimersByTimeAsync ( 5 ) ;
287
+ driver . parser . _transform ( Buffer . from ( orderedFrames [ i ] , "hex" ) , "utf8" , ( ) => { } ) ;
288
+ await vi . advanceTimersByTimeAsync ( 5 ) ;
289
+
290
+ i ++ ;
291
+
292
+ if ( i === orderedFrames . length ) {
293
+ driver . writer . removeListener ( "data" , reply ) ;
294
+ }
295
+ } ;
296
+
297
+ driver . writer . on ( "data" , reply ) ;
221
298
222
299
let registerTimersSpy : ReturnType < typeof vi . spyOn > | undefined ;
223
300
@@ -229,11 +306,11 @@ describe("OT RCP Driver", () => {
229
306
230
307
await driver . formNetwork ( ) ;
231
308
232
- setPropertySpy . mockRestore ( ) ;
233
- getPropertySpy . mockRestore ( ) ;
234
309
registerTimersSpy ?. mockRestore ( ) ;
235
310
236
311
await vi . advanceTimersByTimeAsync ( 100 ) ; // flush
312
+
313
+ nextTidFromStartup = driver . currentSpinelTID + 1 ;
237
314
}
238
315
} ;
239
316
@@ -269,7 +346,7 @@ describe("OT RCP Driver", () => {
269
346
expect ( sendZigbeeNWKLinkStatusSpy ) . toHaveBeenCalledTimes ( 1 + 1 ) ; // *2 by spy mock
270
347
expect ( sendZigbeeNWKRouteReqSpy ) . toHaveBeenCalledTimes ( 1 + 1 ) ; // *2 by spy mock
271
348
272
- nextTidFromStartup += 2 ;
349
+ nextTidFromStartup = driver . currentSpinelTID + 1 ;
273
350
274
351
return [ linksSpy , manyToOneSpy , destination16Spy ] ;
275
352
}
@@ -601,9 +678,11 @@ describe("OT RCP Driver", () => {
601
678
await expect ( driver . resetNetwork ( ) ) . rejects . toThrow ( "Cannot reset network after state already loaded" ) ;
602
679
} ) ;
603
680
604
- it ( "forms network" , async ( ) => {
605
- await mockStart ( driver ) ;
606
- await mockFormNetwork ( driver ) ;
681
+ it ( "starts & forms network - Silabs" , async ( ) => {
682
+ const consoleInfoSpy = vi . spyOn ( console , "info" ) ;
683
+
684
+ await mockStart ( driver , true , false , START_FRAMES_SILABS ) ;
685
+ await mockFormNetwork ( driver , false , FORM_FRAMES_SILABS ) ;
607
686
608
687
expect ( driver . isNetworkUp ( ) ) . toStrictEqual ( true ) ;
609
688
expect ( driver . protocolVersionMajor ) . toStrictEqual ( 4 ) ;
@@ -612,6 +691,33 @@ describe("OT RCP Driver", () => {
612
691
expect ( driver . interfaceType ) . toStrictEqual ( 3 ) ;
613
692
expect ( driver . rcpAPIVersion ) . toStrictEqual ( 10 ) ;
614
693
expect ( driver . rcpMinHostAPIVersion ) . toStrictEqual ( 4 ) ;
694
+
695
+ expect ( consoleInfoSpy ) . toHaveBeenCalledWith (
696
+ expect . stringContaining (
697
+ "ot-rcp-driver: ======== Network started (PHY: txPower=19dBm rssi=-104dBm rxSensitivity=-100dBm ccaThreshold=-75dBm) ========" ,
698
+ ) ,
699
+ ) ;
700
+ } ) ;
701
+
702
+ it ( "starts & forms network - TI" , async ( ) => {
703
+ const consoleInfoSpy = vi . spyOn ( console , "info" ) ;
704
+
705
+ await mockStart ( driver , true , false , START_FRAMES_TI ) ;
706
+ await mockFormNetwork ( driver , false , FORM_FRAMES_TI ) ;
707
+
708
+ expect ( driver . isNetworkUp ( ) ) . toStrictEqual ( true ) ;
709
+ expect ( driver . protocolVersionMajor ) . toStrictEqual ( 4 ) ;
710
+ expect ( driver . protocolVersionMinor ) . toStrictEqual ( 3 ) ;
711
+ expect ( driver . ncpVersion ) . toStrictEqual ( "OPENTHREAD/1.4.0-Koenkk-2025.2.1; CC13XX_CC26XX; Feb 3 2025 21:00:02" ) ;
712
+ expect ( driver . interfaceType ) . toStrictEqual ( 3 ) ;
713
+ expect ( driver . rcpAPIVersion ) . toStrictEqual ( 11 ) ;
714
+ expect ( driver . rcpMinHostAPIVersion ) . toStrictEqual ( 4 ) ;
715
+
716
+ expect ( consoleInfoSpy ) . toHaveBeenCalledWith (
717
+ expect . stringContaining (
718
+ "ot-rcp-driver: ======== Network started (PHY: txPower=5dBm rssi=-17dBm rxSensitivity=-90dBm ccaThreshold=undefineddBm) ========" ,
719
+ ) ,
720
+ ) ;
615
721
} ) ;
616
722
617
723
it ( "throws when trying to form network before state is loaded" , async ( ) => {
0 commit comments