From 370dc74649eb20abacf94a35bdbb53b5c7447508 Mon Sep 17 00:00:00 2001 From: Hacker-CB Date: Mon, 12 Oct 2020 18:52:46 +0300 Subject: [PATCH] zStack 3.x.0 <-> zStack 3.0.x bidirectional migration (#235) * Fix tests * Fix LEGACY_TCLK_TABLE_START 511 -> 273 (mistake during convert from hex) * Added ZCD_NV_TCLK_SEED to backup. Previously it was ZCD_NV_LEGACY_TCLK_TABLE_START with wrong ID * Fix tests * Fix ZCD_NV_EX_TCLK_TABLE and ZCD_NV_LEGACY_TCLK_TABLE_START tables in backup data in restore tests * Added LEGACY_TCLK_TABLE_START_12 to avoid break on 1.2 Stack * Remove TCLK_SEED from backup * Added ZStack 3.0 <-> 3.1 migration * Added tests * Remove extra blank line * Added test to cover restore ZStack 1.2 error * Backup: Compare version inside backup, not only for current adapter --- src/adapter/z-stack/adapter/backup.ts | 48 ++- test/adapter/z-stack/zStackAdapter.test.ts | 330 +++++++++++++++++---- 2 files changed, 314 insertions(+), 64 deletions(-) diff --git a/src/adapter/z-stack/adapter/backup.ts b/src/adapter/z-stack/adapter/backup.ts index 805b85906978c..c45a3c3811b20 100644 --- a/src/adapter/z-stack/adapter/backup.ts +++ b/src/adapter/z-stack/adapter/backup.ts @@ -6,6 +6,7 @@ import {Subsystem} from '../unpi/constants'; import fs from 'fs'; import equals from 'fast-deep-equal/es6'; import Items from './nvItems'; +import debug from "debug"; const {NvItemsIds, NvSystemIds, ZnpCommandStatus} = Constants.COMMON; @@ -141,11 +142,43 @@ async function Restore(znp: Znp, backupPath: string, options: NetworkOptions): P throw new Error(`Cannot restore backup, backup is for '${backup.adapterType}', current is 'zStack'`); } - if (backup.meta.product != product) { - throw new Error( - `Cannot restore backup, backup is for '${ZnpVersion[backup.meta.product]}', ` + - `current is '${ZnpVersion[product]}'` - ); + if ((product !== ZnpVersion.zStack30x && product !== ZnpVersion.zStack3x0) + || (backup.meta.product !== ZnpVersion.zStack30x && backup.meta.product !== ZnpVersion.zStack3x0 )) { + throw new Error('Backup is only supported for Z-Stack 3'); + } + + // 3.0 3.x + // (0x0111)ZCD_NV_LEGACY_TCLK_TABLE_START -> (0x0004)ZCD_NV_EX_TCLK_TABLE + // (0x0075)ZCD_NV_LEGACY_NWK_SEC_MATERIAL_TABLE_START -> (0x0007)ZCD_NV_EX_NWK_SEC_MATERIAL_TABLE + if (backup.meta.product == ZnpVersion.zStack30x && product == ZnpVersion.zStack3x0) { + debug(`Migrate '${ZnpVersion[backup.meta.product]}' -> '${ZnpVersion[product]}'`); + + backup.data.ZCD_NV_EX_TCLK_TABLE = { + ...items.ZCD_NV_EX_TCLK_TABLE, + value: backup.data.ZCD_NV_LEGACY_TCLK_TABLE_START.value, + len: backup.data.ZCD_NV_LEGACY_TCLK_TABLE_START.len, + }; + + backup.data.ZCD_NV_EX_NWK_SEC_MATERIAL_TABLE = { + ...items.ZCD_NV_EX_NWK_SEC_MATERIAL_TABLE, + value: backup.data.ZCD_NV_LEGACY_NWK_SEC_MATERIAL_TABLE_START.value, + len: backup.data.ZCD_NV_LEGACY_NWK_SEC_MATERIAL_TABLE_START.len, + }; + + } else if (backup.meta.product == ZnpVersion.zStack3x0 && product == ZnpVersion.zStack30x) { + debug(`Migrate '${ZnpVersion[backup.meta.product]}' -> '${ZnpVersion[product]}'`); + + backup.data.ZCD_NV_LEGACY_TCLK_TABLE_START = { + ...items.ZCD_NV_LEGACY_TCLK_TABLE_START, + value: backup.data.ZCD_NV_EX_TCLK_TABLE.value, + len: backup.data.ZCD_NV_EX_TCLK_TABLE.len, + }; + + backup.data.ZCD_NV_LEGACY_NWK_SEC_MATERIAL_TABLE_START = { + ...items.ZCD_NV_LEGACY_NWK_SEC_MATERIAL_TABLE_START, + value: backup.data.ZCD_NV_EX_NWK_SEC_MATERIAL_TABLE.value, + len: backup.data.ZCD_NV_EX_NWK_SEC_MATERIAL_TABLE.len, + }; } if (!equals(backup.data.ZCD_NV_CHANLIST.value, Constants.Utils.getChannelMask(options.channelList))) { @@ -181,7 +214,8 @@ async function Restore(znp: Znp, backupPath: string, options: NetworkOptions): P await znp.request(Subsystem.SYS, 'osalNvWrite', backup.data.ZCD_NV_EXTADDR); await znp.request(Subsystem.SYS, 'osalNvItemInit', ZCD_NV_NIB, null, - [ZnpCommandStatus.NV_ITEM_INITIALIZED]); + [ZnpCommandStatus.SUCCESS, ZnpCommandStatus.NV_ITEM_INITIALIZED]); + await znp.request(Subsystem.SYS, 'osalNvWrite', backup.data.ZCD_NV_NIB); await znp.request(Subsystem.SYS, 'osalNvWrite', backup.data.ZCD_NV_PANID); await znp.request(Subsystem.SYS, 'osalNvWrite', backup.data.ZCD_NV_EXTENDED_PAN_ID); await znp.request(Subsystem.SYS, 'osalNvWrite', backup.data.ZCD_NV_NWK_ACTIVE_KEY_INFO); @@ -202,7 +236,7 @@ async function Restore(znp: Znp, backupPath: string, options: NetworkOptions): P } await znp.request(Subsystem.SYS, 'osalNvItemInit', Items.znpHasConfiguredInit(product), null, - [ZnpCommandStatus.NV_ITEM_INITIALIZED]); + [ZnpCommandStatus.SUCCESS, ZnpCommandStatus.NV_ITEM_INITIALIZED]); await znp.request(Subsystem.SYS, 'osalNvWrite', Items.znpHasConfigured(product)); await znp.request(Subsystem.SYS, 'osalNvItemInit', bdbNodeIsOnANetwork, null, [ZnpCommandStatus.SUCCESS, ZnpCommandStatus.NV_ITEM_INITIALIZED]); diff --git a/test/adapter/z-stack/zStackAdapter.test.ts b/test/adapter/z-stack/zStackAdapter.test.ts index 9632ee5324701..62ba90e68c7df 100644 --- a/test/adapter/z-stack/zStackAdapter.test.ts +++ b/test/adapter/z-stack/zStackAdapter.test.ts @@ -1441,36 +1441,38 @@ describe('zStackAdapter', () => { expect(mockZnpRequest.mock.calls[5][1]).toBe('osalNvItemInit'); expect(mockZnpRequest.mock.calls[5][2].value).toStrictEqual(backup.data.ZCD_NV_NIB.value); expect(mockZnpRequest.mock.calls[6][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[6][2]).toStrictEqual(backup.data.ZCD_NV_PANID); + expect(mockZnpRequest.mock.calls[6][2]).toStrictEqual(backup.data.ZCD_NV_NIB); expect(mockZnpRequest.mock.calls[7][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[7][2]).toStrictEqual(backup.data.ZCD_NV_EXTENDED_PAN_ID); + expect(mockZnpRequest.mock.calls[7][2]).toStrictEqual(backup.data.ZCD_NV_PANID); expect(mockZnpRequest.mock.calls[8][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[8][2]).toStrictEqual(backup.data.ZCD_NV_NWK_ACTIVE_KEY_INFO); + expect(mockZnpRequest.mock.calls[8][2]).toStrictEqual(backup.data.ZCD_NV_EXTENDED_PAN_ID); expect(mockZnpRequest.mock.calls[9][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[9][2]).toStrictEqual(backup.data.ZCD_NV_NWK_ALTERN_KEY_INFO); + expect(mockZnpRequest.mock.calls[9][2]).toStrictEqual(backup.data.ZCD_NV_NWK_ACTIVE_KEY_INFO); expect(mockZnpRequest.mock.calls[10][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[10][2]).toStrictEqual(backup.data.ZCD_NV_APS_USE_EXT_PANID); + expect(mockZnpRequest.mock.calls[10][2]).toStrictEqual(backup.data.ZCD_NV_NWK_ALTERN_KEY_INFO); expect(mockZnpRequest.mock.calls[11][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[11][2]).toStrictEqual(backup.data.ZCD_NV_PRECFGKEY); + expect(mockZnpRequest.mock.calls[11][2]).toStrictEqual(backup.data.ZCD_NV_APS_USE_EXT_PANID); expect(mockZnpRequest.mock.calls[12][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[12][2]).toStrictEqual(backup.data.ZCD_NV_PRECFGKEY_ENABLE); + expect(mockZnpRequest.mock.calls[12][2]).toStrictEqual(backup.data.ZCD_NV_PRECFGKEY); expect(mockZnpRequest.mock.calls[13][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[13][2]).toStrictEqual(backup.data.ZCD_NV_LEGACY_TCLK_TABLE_START); + expect(mockZnpRequest.mock.calls[13][2]).toStrictEqual(backup.data.ZCD_NV_PRECFGKEY_ENABLE); expect(mockZnpRequest.mock.calls[14][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[14][2]).toStrictEqual(backup.data.ZCD_NV_LEGACY_NWK_SEC_MATERIAL_TABLE_START); - expect(mockZnpRequest.mock.calls[15][2].id).toBe(NvItemsIds.ZNP_HAS_CONFIGURED_ZSTACK3); + expect(mockZnpRequest.mock.calls[14][2]).toStrictEqual(backup.data.ZCD_NV_LEGACY_TCLK_TABLE_START); + expect(mockZnpRequest.mock.calls[15][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[15][2]).toStrictEqual(backup.data.ZCD_NV_LEGACY_NWK_SEC_MATERIAL_TABLE_START); expect(mockZnpRequest.mock.calls[16][2].id).toBe(NvItemsIds.ZNP_HAS_CONFIGURED_ZSTACK3); - expect(mockZnpRequest.mock.calls[17][1]).toBe('osalNvItemInit'); - expect(mockZnpRequest.mock.calls[17][2].initvalue).toStrictEqual([1]); - expect(mockZnpRequest.mock.calls[18][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[18][2].value).toStrictEqual([1]); - expect(mockZnpRequest.mock.calls[19][1]).toBe('resetReq'); - expect(mockZnpRequest.mock.calls[20][1]).toBe('getDeviceInfo'); - expect(mockZnpRequest.mock.calls[21][1]).toBe('activeEpReq'); - expect(mockZnpRequest.mock.calls[22][1]).toBe('extFindGroup'); - expect(mockZnpRequest.mock.calls[23][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[23][2].value).toStrictEqual(Buffer.from([0, 8, 0, 0])); - expect(mockZnpRequest).toBeCalledTimes(24); + expect(mockZnpRequest.mock.calls[17][2].id).toBe(NvItemsIds.ZNP_HAS_CONFIGURED_ZSTACK3); + expect(mockZnpRequest.mock.calls[18][1]).toBe('osalNvItemInit'); + expect(mockZnpRequest.mock.calls[18][2].initvalue).toStrictEqual([1]); + expect(mockZnpRequest.mock.calls[19][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[19][2].value).toStrictEqual([1]); + expect(mockZnpRequest.mock.calls[20][1]).toBe('resetReq'); + expect(mockZnpRequest.mock.calls[21][1]).toBe('getDeviceInfo'); + expect(mockZnpRequest.mock.calls[22][1]).toBe('activeEpReq'); + expect(mockZnpRequest.mock.calls[23][1]).toBe('extFindGroup'); + expect(mockZnpRequest.mock.calls[24][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[24][2].value).toStrictEqual(Buffer.from([0, 8, 0, 0])); + expect(mockZnpRequest).toBeCalledTimes(25); }); it('Start restore from backup zStack 3.x.0', async () => { @@ -1546,61 +1548,250 @@ describe('zStackAdapter', () => { expect(mockZnpRequest.mock.calls[5][1]).toBe('osalNvItemInit'); expect(mockZnpRequest.mock.calls[5][2].value).toStrictEqual(backup.data.ZCD_NV_NIB.value); expect(mockZnpRequest.mock.calls[6][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[6][2]).toStrictEqual(backup.data.ZCD_NV_PANID); + expect(mockZnpRequest.mock.calls[6][2]).toStrictEqual(backup.data.ZCD_NV_NIB); expect(mockZnpRequest.mock.calls[7][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[7][2]).toStrictEqual(backup.data.ZCD_NV_EXTENDED_PAN_ID); + expect(mockZnpRequest.mock.calls[7][2]).toStrictEqual(backup.data.ZCD_NV_PANID); expect(mockZnpRequest.mock.calls[8][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[8][2]).toStrictEqual(backup.data.ZCD_NV_NWK_ACTIVE_KEY_INFO); + expect(mockZnpRequest.mock.calls[8][2]).toStrictEqual(backup.data.ZCD_NV_EXTENDED_PAN_ID); expect(mockZnpRequest.mock.calls[9][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[9][2]).toStrictEqual(backup.data.ZCD_NV_NWK_ALTERN_KEY_INFO); + expect(mockZnpRequest.mock.calls[9][2]).toStrictEqual(backup.data.ZCD_NV_NWK_ACTIVE_KEY_INFO); expect(mockZnpRequest.mock.calls[10][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[10][2]).toStrictEqual(backup.data.ZCD_NV_APS_USE_EXT_PANID); + expect(mockZnpRequest.mock.calls[10][2]).toStrictEqual(backup.data.ZCD_NV_NWK_ALTERN_KEY_INFO); expect(mockZnpRequest.mock.calls[11][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[11][2]).toStrictEqual(backup.data.ZCD_NV_PRECFGKEY); + expect(mockZnpRequest.mock.calls[11][2]).toStrictEqual(backup.data.ZCD_NV_APS_USE_EXT_PANID); expect(mockZnpRequest.mock.calls[12][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[12][2]).toStrictEqual(backup.data.ZCD_NV_PRECFGKEY_ENABLE); - expect(mockZnpRequest.mock.calls[13][1]).toBe('nvWrite'); - expect(mockZnpRequest.mock.calls[13][2]).toStrictEqual(backup.data.ZCD_NV_EX_TCLK_TABLE); + expect(mockZnpRequest.mock.calls[12][2]).toStrictEqual(backup.data.ZCD_NV_PRECFGKEY); + expect(mockZnpRequest.mock.calls[13][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[13][2]).toStrictEqual(backup.data.ZCD_NV_PRECFGKEY_ENABLE); expect(mockZnpRequest.mock.calls[14][1]).toBe('nvWrite'); - expect(mockZnpRequest.mock.calls[14][2]).toStrictEqual(backup.data.ZCD_NV_EX_NWK_SEC_MATERIAL_TABLE); - expect(mockZnpRequest.mock.calls[15][2].id).toBe(NvItemsIds.ZNP_HAS_CONFIGURED_ZSTACK3); + expect(mockZnpRequest.mock.calls[14][2]).toStrictEqual(backup.data.ZCD_NV_EX_TCLK_TABLE); + expect(mockZnpRequest.mock.calls[15][1]).toBe('nvWrite'); + expect(mockZnpRequest.mock.calls[15][2]).toStrictEqual(backup.data.ZCD_NV_EX_NWK_SEC_MATERIAL_TABLE); expect(mockZnpRequest.mock.calls[16][2].id).toBe(NvItemsIds.ZNP_HAS_CONFIGURED_ZSTACK3); - expect(mockZnpRequest.mock.calls[17][1]).toBe('osalNvItemInit'); - expect(mockZnpRequest.mock.calls[17][2].initvalue).toStrictEqual([1]); - expect(mockZnpRequest.mock.calls[18][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[18][2].value).toStrictEqual([1]); - expect(mockZnpRequest.mock.calls[19][1]).toBe('resetReq'); - expect(mockZnpRequest.mock.calls[20][1]).toBe('getDeviceInfo'); - expect(mockZnpRequest.mock.calls[21][1]).toBe('activeEpReq'); - expect(mockZnpRequest.mock.calls[22][1]).toBe('extFindGroup'); - expect(mockZnpRequest.mock.calls[23][1]).toBe('osalNvWrite'); - expect(mockZnpRequest.mock.calls[23][2].value).toStrictEqual(Buffer.from([0, 8, 0, 0])); - expect(mockZnpRequest).toBeCalledTimes(24); - }); - - it('Start restore from backup wrong zStack', async () => { + expect(mockZnpRequest.mock.calls[17][2].id).toBe(NvItemsIds.ZNP_HAS_CONFIGURED_ZSTACK3); + expect(mockZnpRequest.mock.calls[18][1]).toBe('osalNvItemInit'); + expect(mockZnpRequest.mock.calls[18][2].initvalue).toStrictEqual([1]); + expect(mockZnpRequest.mock.calls[19][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[19][2].value).toStrictEqual([1]); + expect(mockZnpRequest.mock.calls[20][1]).toBe('resetReq'); + expect(mockZnpRequest.mock.calls[21][1]).toBe('getDeviceInfo'); + expect(mockZnpRequest.mock.calls[22][1]).toBe('activeEpReq'); + expect(mockZnpRequest.mock.calls[23][1]).toBe('extFindGroup'); + expect(mockZnpRequest.mock.calls[24][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[24][2].value).toStrictEqual(Buffer.from([0, 8, 0, 0])); + expect(mockZnpRequest).toBeCalledTimes(25); + }); + + it('Start restore backup migration zStack 3.0.x -> zStack 3.x.0', async () => { mockZnpRequest.mockImplementation((subsystem, command, payload, expectedStatus) => { - if (subsystem === Subsystem.SYS && command === 'osalNvRead' && equalsPartial(payload, {id: NvItemsIds.ZNP_HAS_CONFIGURED_ZSTACK3, offset: 0})) { - return {payload: {value: Buffer.from([0])}}; - } - else if (subsystem === Subsystem.SYS && command === 'version' && equals(payload, {})) { - return {payload: {product: 2}}; + const missing = () => { + const msg = `Not implemented - ${Subsystem[subsystem]} - ${command} - ${JSON.stringify(payload)}`; + console.log(msg) + throw new Error(msg); } - else if (subsystem === Subsystem.SYS && command === 'ping') { + + if (subsystem === Subsystem.SYS && command === 'version' && equals(payload, {})) { + return {payload: {product: 1}}; + } else if (subsystem === Subsystem.ZDO && command === 'extFindGroup') { + return {payload: {status: 0}}; + } else if (subsystem === Subsystem.SYS && command === 'ping') { + return {}; + } else if (subsystem === Subsystem.SYS && command === 'osalNvRead') { + if (equalsPartial(payload, {id: NvItemsIds.ZNP_HAS_CONFIGURED_ZSTACK3, offset: 0})) { + return {payload: {value: Buffer.from([0])}}; + } else { + missing(); + } + } else if (subsystem === Subsystem.SYS && command === 'osalNvItemInit') { + return {}; + } else if (subsystem === Subsystem.SYS && command === 'osalNvWrite') { + return {}; + } else if (subsystem === Subsystem.SYS && command === 'nvWrite') { + return {}; + } else if (subsystem === Subsystem.SYS && command === 'resetReq') { return {}; + } else if (subsystem === Subsystem.UTIL && command === 'getDeviceInfo') { + return {payload: {devicestate: DevStates.ZB_COORD}}; + } else if (subsystem === Subsystem.ZDO && command === 'activeEpReq') { + return {}; + } else if (subsystem === Subsystem.ZDO && command === 'startupFromApp') { + return {}; + } else { + missing(); } + }); - throw new Error('missing'); + mockZnpWaitfor.mockImplementation((type, subsystem, command, payload) => { + const missing = () => { + const msg = `Not implemented - ${Type[type]} - ${Subsystem[subsystem]} - ${command} - ${JSON.stringify(payload)}`; + console.log(msg) + throw new Error(msg); + } + + if (type === Type.AREQ && subsystem === Subsystem.ZDO && command === 'activeEpRsp') { + return waitForResult({payload: {activeeplist: [1, 2, 3, 4, 5, 6, 8, 11, 110, 12, 13, 47, 242]}}); + } else if (type === Type.AREQ && subsystem === Subsystem.ZDO && command === 'stateChangeInd') { + return waitForResult({payload: {activeeplist: []}}); + } else { + missing(); + } }); - const backup = {"adapterType":"zStack","time":"Mon, 19 Aug 2019 16:21:55 GMT","meta":{"product":1},"data":{"ZCD_NV_EXTADDR":{"id":1,"offset":0,"value":[174,68,1,18,0,75,18,0],"len":8},"ZCD_NV_NIB":{"id":33,"offset":0,"value":[230,5,2,16,20,16,0,20,0,0,0,1,5,1,143,7,0,2,5,30,0,0,14,0,0,0,0,0,0,0,0,0,0,114,60,8,0,64,0,0,15,15,4,0,1,0,0,0,1,0,0,0,0,174,68,1,18,0,75,18,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,3,0,1,120,10,1,0,0,65,0,0],"len":110},"ZCD_NV_EXTENDED_PAN_ID":{"id":45,"offset":0,"value":[174,68,1,18,0,75,18,0],"len":8},"ZCD_NV_NWK_ACTIVE_KEY_INFO":{"id":58,"offset":0,"value":[0,1,3,5,7,9,11,13,15,0,2,4,6,8,10,12,13],"len":17},"ZCD_NV_NWK_ALTERN_KEY_INFO":{"id":59,"offset":0,"value":[0,1,3,5,7,9,11,13,15,0,2,4,6,8,10,12,13],"len":17},"ZCD_NV_APS_USE_EXT_PANID":{"id":71,"offset":0,"value":[0,0,0,0,0,0,0,0],"len":8},"ZCD_NV_PRECFGKEY":{"id":98,"offset":0,"value":[1,3,5,7,9,11,13,15,0,2,4,6,8,10,12,13],"len":16},"ZCD_NV_PRECFGKEY_ENABLE":{"id":99,"offset":0,"value":[0],"len":1},"ZCD_NV_TCLK_TABLE_START":{"id":273,"offset":0,"value":[94,15,57,228,82,11,124,39,162,90,56,187,81,51,252,149],"len":16},"ZCD_NV_CHANLIST":{"id":132,"offset":0,"value":[0,8,0,0],"len":4},"ZCD_NV_NWK_SEC_MATERIAL_TABLE_START":{"id":117,"offset":0,"value":[123,63,0,0,174,68,1,18,0,75,18,0],"len":12}}}; + const backup = {"adapterType":"zStack","meta":{"product":2},"data":{"ZCD_NV_EXTADDR":{"id":1,"offset":0,"osal":true,"product":-1,"value":[174,68,1,18,0,75,18,0],"len":8},"ZCD_NV_NIB":{"id":33,"offset":0,"osal":true,"product":-1,"value":[230,5,2,16,20,16,0,20,0,0,0,1,5,1,143,7,0,2,5,30,0,0,14,0,0,0,0,0,0,0,0,0,0,114,60,8,0,64,0,0,15,15,4,0,1,0,0,0,1,0,0,0,0,174,68,1,18,0,75,18,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,3,0,1,120,10,1,0,0,65,0,0],"len":110},"ZCD_NV_PANID":{"id":131,"offset":0,"osal":true,"product":-1,"value":[123,0],"len":2},"ZCD_NV_EXTENDED_PAN_ID":{"id":45,"offset":0,"osal":true,"product":-1,"value":[1,2,3],"len":3},"ZCD_NV_NWK_ACTIVE_KEY_INFO":{"id":58,"offset":0,"osal":true,"product":-1,"value":[0,1,3,5,7,9,11,13,15,0,2,4,6,8,10,12,13],"len":17},"ZCD_NV_NWK_ALTERN_KEY_INFO":{"id":59,"offset":0,"osal":true,"product":-1,"value":[0,1,3,5,7,9,11,13,15,0,2,4,6,8,10,12,13],"len":17},"ZCD_NV_APS_USE_EXT_PANID":{"id":71,"offset":0,"osal":true,"product":-1,"value":[0,0,0,0,0,0,0,0],"len":8},"ZCD_NV_PRECFGKEY":{"id":98,"offset":0,"osal":true,"product":-1,"value":[1,3,5,7,9,11,13,15,0,2,4,6,8,10,12,13],"len":16},"ZCD_NV_PRECFGKEY_ENABLE":{"id":99,"offset":0,"osal":true,"product":-1,"value":[0],"len":1},"ZCD_NV_CHANLIST":{"id":132,"offset":0,"osal":true,"product":-1,"value":[0,8,0,0],"len":4},"ZCD_NV_LEGACY_TCLK_TABLE_START":{"id":273,"product":2,"offset":0,"osal":true,"value":[11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,0,0,0],"len":20},"ZCD_NV_LEGACY_NWK_SEC_MATERIAL_TABLE_START":{"id":117,"product":2,"offset":0,"osal":true,"value":[123,63,0,0,174,68,1,18,0,75,18,0],"len":12}}}; const backupFile = getTempFile(); fs.writeFileSync(backupFile, JSON.stringify(backup), 'utf8'); + adapter = new ZStackAdapter(networkOptions, serialPortOptions, backupFile); + const result = await adapter.start(); + expect(result).toBe('restored'); + expect(Znp).toBeCalledWith("dummy", 800, false); + expect(mockZnpOpen).toBeCalledTimes(1); + expect(mockZnpRequest.mock.calls[0][1]).toBe('ping'); + expect(mockZnpRequest.mock.calls[1][1]).toBe('version'); + expect(mockZnpRequest.mock.calls[2][2].id).toBe(NvItemsIds.ZNP_HAS_CONFIGURED_ZSTACK3); + expect(mockZnpRequest.mock.calls[3][1]).toBe('version'); + expect(mockZnpRequest.mock.calls[4][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[4][2]).toStrictEqual(backup.data.ZCD_NV_EXTADDR); + expect(mockZnpRequest.mock.calls[5][1]).toBe('osalNvItemInit'); + expect(mockZnpRequest.mock.calls[5][2].value).toStrictEqual(backup.data.ZCD_NV_NIB.value); + expect(mockZnpRequest.mock.calls[6][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[6][2]).toStrictEqual(backup.data.ZCD_NV_NIB); + expect(mockZnpRequest.mock.calls[7][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[7][2]).toStrictEqual(backup.data.ZCD_NV_PANID); + expect(mockZnpRequest.mock.calls[8][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[8][2]).toStrictEqual(backup.data.ZCD_NV_EXTENDED_PAN_ID); + expect(mockZnpRequest.mock.calls[9][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[9][2]).toStrictEqual(backup.data.ZCD_NV_NWK_ACTIVE_KEY_INFO); + expect(mockZnpRequest.mock.calls[10][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[10][2]).toStrictEqual(backup.data.ZCD_NV_NWK_ALTERN_KEY_INFO); + expect(mockZnpRequest.mock.calls[11][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[11][2]).toStrictEqual(backup.data.ZCD_NV_APS_USE_EXT_PANID); + expect(mockZnpRequest.mock.calls[12][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[12][2]).toStrictEqual(backup.data.ZCD_NV_PRECFGKEY); + expect(mockZnpRequest.mock.calls[13][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[13][2]).toStrictEqual(backup.data.ZCD_NV_PRECFGKEY_ENABLE); + expect(mockZnpRequest.mock.calls[14][1]).toBe('nvWrite'); + expect(mockZnpRequest.mock.calls[14][2].value).toStrictEqual(backup.data.ZCD_NV_LEGACY_TCLK_TABLE_START.value); + expect(mockZnpRequest.mock.calls[15][1]).toBe('nvWrite'); + expect(mockZnpRequest.mock.calls[15][2].value).toStrictEqual(backup.data.ZCD_NV_LEGACY_NWK_SEC_MATERIAL_TABLE_START.value); + expect(mockZnpRequest.mock.calls[16][2].id).toBe(NvItemsIds.ZNP_HAS_CONFIGURED_ZSTACK3); + expect(mockZnpRequest.mock.calls[17][2].id).toBe(NvItemsIds.ZNP_HAS_CONFIGURED_ZSTACK3); + expect(mockZnpRequest.mock.calls[18][1]).toBe('osalNvItemInit'); + expect(mockZnpRequest.mock.calls[18][2].initvalue).toStrictEqual([1]); + expect(mockZnpRequest.mock.calls[19][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[19][2].value).toStrictEqual([1]); + expect(mockZnpRequest.mock.calls[20][1]).toBe('resetReq'); + expect(mockZnpRequest.mock.calls[21][1]).toBe('getDeviceInfo'); + expect(mockZnpRequest.mock.calls[22][1]).toBe('activeEpReq'); + expect(mockZnpRequest.mock.calls[23][1]).toBe('extFindGroup'); + expect(mockZnpRequest.mock.calls[24][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[24][2].value).toStrictEqual(Buffer.from([0, 8, 0, 0])); + expect(mockZnpRequest).toBeCalledTimes(25); + }); - let error; - try {await adapter.start()} catch (e) {error = e}; - expect(error).toStrictEqual(new Error("Cannot restore backup, backup is for 'zStack3x0', current is 'zStack30x'")); + it('Start restore backup migration zStack 3.x.0 -> zStack 3.0.x', async () => { + mockZnpRequest.mockImplementation((subsystem, command, payload, expectedStatus) => { + const missing = () => { + const msg = `Not implemented - ${Subsystem[subsystem]} - ${command} - ${JSON.stringify(payload)}`; + console.log(msg) + throw new Error(msg); + } + + if (subsystem === Subsystem.SYS && command === 'version' && equals(payload, {})) { + return {payload: {product: 2}}; + } else if (subsystem === Subsystem.SYS && command === 'ping') { + return {}; + } else if (subsystem === Subsystem.ZDO && command === 'extFindGroup') { + return {payload: {status: 0}}; + } else if (subsystem === Subsystem.SYS && command === 'osalNvRead') { + if (equalsPartial(payload, {id: NvItemsIds.ZNP_HAS_CONFIGURED_ZSTACK3, offset: 0})) { + return {payload: {value: Buffer.from([0])}}; + } else { + missing(); + } + } else if (subsystem === Subsystem.SYS && command === 'osalNvItemInit') { + return {}; + } else if (subsystem === Subsystem.SYS && command === 'osalNvWrite') { + return {}; + } else if (subsystem === Subsystem.SYS && command === 'resetReq') { + return {}; + } else if (subsystem === Subsystem.UTIL && command === 'getDeviceInfo') { + return {payload: {devicestate: DevStates.ZB_COORD}}; + } else if (subsystem === Subsystem.ZDO && command === 'activeEpReq') { + return {}; + } else if (subsystem === Subsystem.ZDO && command === 'startupFromApp') { + return {}; + } else { + missing(); + } + }); + + mockZnpWaitfor.mockImplementation((type, subsystem, command, payload) => { + const missing = () => { + const msg = `Not implemented - ${Type[type]} - ${Subsystem[subsystem]} - ${command} - ${JSON.stringify(payload)}`; + console.log(msg) + throw new Error(msg); + } + + if (type === Type.AREQ && subsystem === Subsystem.ZDO && command === 'activeEpRsp') { + return waitForResult({payload: {activeeplist: [1, 2, 3, 4, 5, 6, 8, 11, 110, 12, 13, 47, 242]}}); + } else if (type === Type.AREQ && subsystem === Subsystem.ZDO && command === 'stateChangeInd') { + return waitForResult({payload: {activeeplist: []}}); + } else { + missing(); + } + }); + + const backup = {"adapterType":"zStack","time":"Sat, 12 Oct 2019 11:44:30 GMT","meta":{"product":1},"data":{"ZCD_NV_EXTADDR":{"id":1,"offset":0,"osal":true,"product":-1,"value":[71,98,161,28,0,75,18,0],"len":8},"ZCD_NV_NIB":{"id":33,"offset":0,"osal":true,"product":-1,"value":[153,5,2,0,20,0,0,30,0,0,0,1,5,1,143,0,7,0,2,5,30,0,254,255,0,0,254,255,0,0,0,0,0,0,0,0,255,255,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0,0,221,221,221,221,221,221,221,221,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,3,0,1,120,10,1,0,0,0,0,0,0,0],"len":116},"ZCD_NV_PANID":{"id":131,"offset":0,"osal":true,"product":-1,"value":[123,0],"len":2},"ZCD_NV_EXTENDED_PAN_ID":{"id":45,"offset":0,"osal":true,"product":-1,"value":[1,2,3],"len":3},"ZCD_NV_NWK_ACTIVE_KEY_INFO":{"id":58,"offset":0,"osal":true,"product":-1,"value":[0,1,3,5,7,9,11,13,15,0,2,4,6,8,10,12,13],"len":17},"ZCD_NV_NWK_ALTERN_KEY_INFO":{"id":59,"offset":0,"osal":true,"product":-1,"value":[0,1,3,5,7,9,11,13,15,0,2,4,6,8,10,12,13],"len":17},"ZCD_NV_APS_USE_EXT_PANID":{"id":71,"offset":0,"osal":true,"product":-1,"value":[0,0,0,0,0,0,0,0],"len":8},"ZCD_NV_PRECFGKEY":{"id":98,"offset":0,"osal":true,"product":-1,"value":[1,3,5,7,9,11,13,15,0,2,4,6,8,10,12,13],"len":16},"ZCD_NV_PRECFGKEY_ENABLE":{"id":99,"offset":0,"osal":true,"product":-1,"value":[0],"len":1},"ZCD_NV_CHANLIST":{"id":132,"offset":0,"osal":true,"product":-1,"value":[0,8,0,0],"len":4},"ZCD_NV_EX_TCLK_TABLE":{"sysid":1,"itemid":4,"subid":0,"product":1,"osal":false,"offset":0,"value":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,0,0,0],"len":20},"ZCD_NV_EX_NWK_SEC_MATERIAL_TABLE":{"sysid":1,"itemid":7,"subid":0,"product":1,"osal":false,"offset":0,"value":[1,0,0,0,221,221,221,221,221,221,221,221],"len":12}}}; + const backupFile = getTempFile(); + fs.writeFileSync(backupFile, JSON.stringify(backup), 'utf8'); + + adapter = new ZStackAdapter(networkOptions, serialPortOptions, backupFile); + const result = await adapter.start(); + expect(result).toBe('restored'); + expect(Znp).toBeCalledWith("dummy", 800, false); + expect(mockZnpOpen).toBeCalledTimes(1); + expect(mockZnpRequest.mock.calls[0][1]).toBe('ping'); + expect(mockZnpRequest.mock.calls[1][1]).toBe('version'); + expect(mockZnpRequest.mock.calls[2][2].id).toBe(NvItemsIds.ZNP_HAS_CONFIGURED_ZSTACK3); + expect(mockZnpRequest.mock.calls[3][1]).toBe('version'); + expect(mockZnpRequest.mock.calls[4][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[4][2]).toStrictEqual(backup.data.ZCD_NV_EXTADDR); + expect(mockZnpRequest.mock.calls[5][1]).toBe('osalNvItemInit'); + expect(mockZnpRequest.mock.calls[5][2].value).toStrictEqual(backup.data.ZCD_NV_NIB.value); + expect(mockZnpRequest.mock.calls[6][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[6][2]).toStrictEqual(backup.data.ZCD_NV_NIB); + expect(mockZnpRequest.mock.calls[7][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[7][2]).toStrictEqual(backup.data.ZCD_NV_PANID); + expect(mockZnpRequest.mock.calls[8][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[8][2]).toStrictEqual(backup.data.ZCD_NV_EXTENDED_PAN_ID); + expect(mockZnpRequest.mock.calls[9][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[9][2]).toStrictEqual(backup.data.ZCD_NV_NWK_ACTIVE_KEY_INFO); + expect(mockZnpRequest.mock.calls[10][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[10][2]).toStrictEqual(backup.data.ZCD_NV_NWK_ALTERN_KEY_INFO); + expect(mockZnpRequest.mock.calls[11][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[11][2]).toStrictEqual(backup.data.ZCD_NV_APS_USE_EXT_PANID); + expect(mockZnpRequest.mock.calls[12][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[12][2]).toStrictEqual(backup.data.ZCD_NV_PRECFGKEY); + expect(mockZnpRequest.mock.calls[13][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[13][2]).toStrictEqual(backup.data.ZCD_NV_PRECFGKEY_ENABLE); + expect(mockZnpRequest.mock.calls[14][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[14][2].value).toStrictEqual(backup.data.ZCD_NV_EX_TCLK_TABLE.value); + expect(mockZnpRequest.mock.calls[15][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[15][2].value).toStrictEqual(backup.data.ZCD_NV_EX_NWK_SEC_MATERIAL_TABLE.value); + expect(mockZnpRequest.mock.calls[16][2].id).toBe(NvItemsIds.ZNP_HAS_CONFIGURED_ZSTACK3); + expect(mockZnpRequest.mock.calls[17][2].id).toBe(NvItemsIds.ZNP_HAS_CONFIGURED_ZSTACK3); + expect(mockZnpRequest.mock.calls[18][1]).toBe('osalNvItemInit'); + expect(mockZnpRequest.mock.calls[18][2].initvalue).toStrictEqual([1]); + expect(mockZnpRequest.mock.calls[19][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[19][2].value).toStrictEqual([1]); + expect(mockZnpRequest.mock.calls[20][1]).toBe('resetReq'); + expect(mockZnpRequest.mock.calls[21][1]).toBe('getDeviceInfo'); + expect(mockZnpRequest.mock.calls[22][1]).toBe('activeEpReq'); + expect(mockZnpRequest.mock.calls[23][1]).toBe('extFindGroup'); + expect(mockZnpRequest.mock.calls[24][1]).toBe('osalNvWrite'); + expect(mockZnpRequest.mock.calls[24][2].value).toStrictEqual(Buffer.from([0, 8, 0, 0])); + expect(mockZnpRequest).toBeCalledTimes(25); }); it('Start restore from backup wrong adapter type', async () => { @@ -1917,6 +2108,31 @@ describe('zStackAdapter', () => { expect(error).toStrictEqual(new Error('Backup is only supported for Z-Stack 3')); }); + it('Restore backup for zStack 1.2 error', async () => { + mockZnpRequest.mockImplementation((subsystem, command, payload, expectedStatus) => { + if (subsystem === Subsystem.SYS && command === 'osalNvRead' && equalsPartial(payload, {id: NvItemsIds.ZNP_HAS_CONFIGURED_ZSTACK1, offset: 0})) { + return {payload: {value: Buffer.from([0])}}; + } + else if (subsystem === Subsystem.SYS && command === 'version' && equals(payload, {})) { + return {payload: {product: 0}}; + } + else if (subsystem === Subsystem.SYS && command === 'ping') { + return {}; + } + + throw new Error('missing'); + }); + + const backup = {"adapterType":"zStack","time":"Mon, 19 Aug 2019 16:21:55 GMT","meta":{"product":0}}; + const backupFile = getTempFile(); + fs.writeFileSync(backupFile, JSON.stringify(backup), 'utf8'); + adapter = new ZStackAdapter(networkOptions, serialPortOptions, backupFile); + + let error; + try {await adapter.start()} catch (e) {error = e}; + expect(error).toStrictEqual(new Error('Backup is only supported for Z-Stack 3')); + }); + it('Close adapter', async () => { basicMocks(); await adapter.start();