From e8df7740b801242dbbd58fd63ab3c23a7dc957a8 Mon Sep 17 00:00:00 2001 From: Peter Wood Date: Wed, 13 Jan 2021 17:02:41 +0000 Subject: [PATCH] Add waiting for state change message on CC2531 based adapters during initial network initialisation, this is required to allow time for the application to configure, this was a regression caused by introducing CC26X2R1 support. Closes #21. --- adapter_initialise.go | 35 ++++++++++++++++++++++-- adapter_initialise_test.go | 56 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 86 insertions(+), 5 deletions(-) diff --git a/adapter_initialise.go b/adapter_initialise.go index 57da515..494316f 100644 --- a/adapter_initialise.go +++ b/adapter_initialise.go @@ -45,7 +45,7 @@ func (z *ZStack) Initialise(pctx context.Context, nc zigbee.NetworkConfiguration } z.logger.LogInfo(ctx, "Starting Zigbee stack.") - if err := z.startZigbeeStack(ctx); err != nil { + if err := z.startZigbeeStack(ctx, version); err != nil { return err } @@ -215,10 +215,39 @@ func (z *ZStack) retrieveAdapterAddresses(ctx context.Context) error { }) } -func (z *ZStack) startZigbeeStack(ctx context.Context) error { - return retry.Retry(ctx, DefaultZStackTimeout, DefaultZStackRetries, func(invokeCtx context.Context) error { +func (z *ZStack) startZigbeeStack(ctx context.Context, version Version) error { + if err := retry.Retry(ctx, DefaultZStackTimeout, DefaultZStackRetries, func(invokeCtx context.Context) error { return z.requestResponder.RequestResponse(invokeCtx, ZDOStartUpFromAppRequest{StartDelay: 100}, &ZDOStartUpFromAppRequestReply{}) + }); err != nil { + return err + } + + if version.IsV3() { + return nil + } + + ch := make(chan bool, 1) + defer close(ch) + + err, cancel := z.subscriber.Subscribe(&ZDOStateChangeInd{}, func(v interface{}) { + stateChange := v.(*ZDOStateChangeInd) + + if stateChange.State == DeviceZBCoordinator { + ch <- true + } }) + defer cancel() + + if err != nil { + return err + } + + select { + case <-ch: + return nil + case <-ctx.Done(): + return errors.New("context expired while waiting for adapter start up") + } } func (z *ZStack) waitForCoordinatorStart(ctx context.Context) error { diff --git a/adapter_initialise_test.go b/adapter_initialise_test.go index 7de756f..93daa14 100644 --- a/adapter_initialise_test.go +++ b/adapter_initialise_test.go @@ -452,7 +452,7 @@ func Test_verifyAdapterNetworkConfig(t *testing.T) { } func Test_startZigbeeStack(t *testing.T) { - t.Run("starts zigbee stack and waits for confirmation", func(t *testing.T) { + t.Run("starts zigbee stack for Z-Stack 3.X.X and waits for start response", func(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() @@ -467,11 +467,63 @@ func Test_startZigbeeStack(t *testing.T) { Payload: []byte{0x00}, }) - err := zstack.startZigbeeStack(ctx) + err := zstack.startZigbeeStack(ctx, Version{ProductID: 1}) assert.NoError(t, err) unpiMock.AssertCalls(t) }) + + t.Run("starts zigbee stack for Z-Stack 1.2.X and waits for confirmation", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + unpiMock := unpiTest.NewMockAdapter() + zstack := New(unpiMock, NewNodeTable()) + defer unpiMock.Stop() + + unpiMock.On(SREQ, ZDO, ZDOStartUpFromAppRequestId).Return(Frame{ + MessageType: SRSP, + Subsystem: ZDO, + CommandID: ZDOStartUpFromAppRequestReplyID, + Payload: []byte{0x00}, + }) + + go func() { + time.Sleep(50 * time.Millisecond) + unpiMock.InjectOutgoing(Frame{ + MessageType: AREQ, + Subsystem: ZDO, + CommandID: ZDOStateChangeIndID, + Payload: []byte{0x09}, + }) + }() + + err := zstack.startZigbeeStack(ctx, Version{ProductID: 0}) + assert.NoError(t, err) + + unpiMock.AssertCalls(t) + }) + + t.Run("context timeout for Z-Stack 1.2.X while waiting for state change", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond) + defer cancel() + + unpiMock := unpiTest.NewMockAdapter() + zstack := New(unpiMock, NewNodeTable()) + defer unpiMock.Stop() + + unpiMock.On(SREQ, ZDO, ZDOStartUpFromAppRequestId).Return(Frame{ + MessageType: SRSP, + Subsystem: ZDO, + CommandID: ZDOStartUpFromAppRequestReplyID, + Payload: []byte{0x00}, + }) + + err := zstack.startZigbeeStack(ctx, Version{ProductID: 0}) + assert.Error(t, err) + + unpiMock.AssertCalls(t) + }) } func Test_waitForCoordinatorStart(t *testing.T) {