diff --git a/go.mod b/go.mod index a3a8641..d8ab61d 100644 --- a/go.mod +++ b/go.mod @@ -5,12 +5,12 @@ go 1.22.0 toolchain go1.22.2 require ( - github.com/shimmeringbee/bytecodec v0.0.0-20210228205504-1e9e0677347b + github.com/shimmeringbee/bytecodec v0.0.0-20240614104652-9d31c74dcd13 github.com/shimmeringbee/logwrap v0.1.3 - github.com/shimmeringbee/persistence v0.0.0-20240521204303-bf4ab8a6b71b - github.com/shimmeringbee/retry v0.0.0-20221006193055-2ce01bf139c2 - github.com/shimmeringbee/unpi v0.0.0-20210525151328-7ede275a1033 - github.com/shimmeringbee/zigbee v0.0.0-20240614103911-3a30074e1528 + github.com/shimmeringbee/persistence v0.0.0-20240614122634-f587e84f4d9e + github.com/shimmeringbee/retry v0.0.0-20240614104711-064c2726a8b4 + github.com/shimmeringbee/unpi v0.0.0-20240614104715-5284f961bafc + github.com/shimmeringbee/zigbee v0.0.0-20240614104723-f4c0c0231568 github.com/stretchr/testify v1.9.0 golang.org/x/sync v0.7.0 ) @@ -18,6 +18,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/shimmeringbee/zcl v0.0.0-20240614104719-4eee02c0ffd1 // indirect github.com/stretchr/objx v0.5.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index c888379..1600af0 100644 --- a/go.sum +++ b/go.sum @@ -8,19 +8,33 @@ github.com/shimmeringbee/bytecodec v0.0.0-20200216120857-49d677293817/go.mod h1: github.com/shimmeringbee/bytecodec v0.0.0-20210111165458-877359ca1003/go.mod h1:iqI5PkiqY+Xq6Hu22TNhepAY00iJCfk9jiXKBUrMSQQ= github.com/shimmeringbee/bytecodec v0.0.0-20210228205504-1e9e0677347b h1:8q7X6JQwYKjnl+Absfv9m+LbDSBBllqTDDKzmtZ1ybY= github.com/shimmeringbee/bytecodec v0.0.0-20210228205504-1e9e0677347b/go.mod h1:WYnxfxTJ45UQ+xeAuuTSIalcEepgP8Rb7T/OhCaDdgo= +github.com/shimmeringbee/bytecodec v0.0.0-20240614104652-9d31c74dcd13 h1:GiNQq9XzoQerCE/eI8/NEPJ7W+iy0FRSn6whgnPlb3w= +github.com/shimmeringbee/bytecodec v0.0.0-20240614104652-9d31c74dcd13/go.mod h1:WYnxfxTJ45UQ+xeAuuTSIalcEepgP8Rb7T/OhCaDdgo= github.com/shimmeringbee/logwrap v0.1.3 h1:1PqPGdgbeQxACQqc6RUWERn7EnpA1jbiHzXVYFa7q2A= github.com/shimmeringbee/logwrap v0.1.3/go.mod h1:NBAcZCUl6aFOGnWTs8m67EUAmWFZXRhoRQf5nknY8W0= github.com/shimmeringbee/persistence v0.0.0-20240521204303-bf4ab8a6b71b h1:hxMT4WUvcmJVzv4EgSGq4LvEoUZFdysWIznFTJoSRIU= github.com/shimmeringbee/persistence v0.0.0-20240521204303-bf4ab8a6b71b/go.mod h1:Z5euPm65BHgTSRFgaWHByaXejU/J4oUqESV9k0VzQDU= +github.com/shimmeringbee/persistence v0.0.0-20240614103725-eec3415bbc98 h1:JoPko6ryVEW42pd4elhxbACWG1Jsf0EAzuh0ZOPNiPM= +github.com/shimmeringbee/persistence v0.0.0-20240614103725-eec3415bbc98/go.mod h1:Z5euPm65BHgTSRFgaWHByaXejU/J4oUqESV9k0VzQDU= +github.com/shimmeringbee/persistence v0.0.0-20240614122634-f587e84f4d9e h1:2D/91t0thwTrZrFhAPqEBwEUJceHbWXujhB1BqTVLFA= +github.com/shimmeringbee/persistence v0.0.0-20240614122634-f587e84f4d9e/go.mod h1:Z5euPm65BHgTSRFgaWHByaXejU/J4oUqESV9k0VzQDU= github.com/shimmeringbee/retry v0.0.0-20221006193055-2ce01bf139c2 h1:HxpPz7w7SxVf1GmcM5oTK1JK64TGpK1UflweYRSOwC4= github.com/shimmeringbee/retry v0.0.0-20221006193055-2ce01bf139c2/go.mod h1:KYvVq5b7/BSSlWng+AKB5jwNGpc0D7eg8ySWrdPAlms= +github.com/shimmeringbee/retry v0.0.0-20240614104711-064c2726a8b4 h1:YU77guV/6/9nJymm4K1JH6MIx6yE/NfUnFX//yo3GfM= +github.com/shimmeringbee/retry v0.0.0-20240614104711-064c2726a8b4/go.mod h1:KYvVq5b7/BSSlWng+AKB5jwNGpc0D7eg8ySWrdPAlms= github.com/shimmeringbee/unpi v0.0.0-20210111165207-f0210c6942fc/go.mod h1:iAt5R5HT+VC7B9U77uBmN5Z6+DJo4U0z6ag68NH2mMw= github.com/shimmeringbee/unpi v0.0.0-20210525151328-7ede275a1033 h1:PQGdXelNwwcQH58S90MR0xA3GnikCnzt+xpDw0P4qxM= github.com/shimmeringbee/unpi v0.0.0-20210525151328-7ede275a1033/go.mod h1:hOrncW6hd26Z18eayp99i7hNKj0aHtUx1SxXT49aEsk= +github.com/shimmeringbee/unpi v0.0.0-20240614104715-5284f961bafc h1:rK5Dsb3RAoJZcNCsGbFvn8QkSKRWPTWHJFgjU0pCupg= +github.com/shimmeringbee/unpi v0.0.0-20240614104715-5284f961bafc/go.mod h1:hOrncW6hd26Z18eayp99i7hNKj0aHtUx1SxXT49aEsk= +github.com/shimmeringbee/zcl v0.0.0-20240614104719-4eee02c0ffd1 h1:19JMz+jKs8poUPlmF769Z2e+zZjmACS+aLB2BHFTKHE= +github.com/shimmeringbee/zcl v0.0.0-20240614104719-4eee02c0ffd1/go.mod h1:DeGINQ0C9S61qBON9Zm2RArEBX4ap1LyHClfUgSUTEM= github.com/shimmeringbee/zigbee v0.0.0-20240614090423-d67fd427d102 h1:SNuznHuBvY1iEbkOEP0jbmIvn2p0GQGlCNQAUyDmcRQ= github.com/shimmeringbee/zigbee v0.0.0-20240614090423-d67fd427d102/go.mod h1:k5LLUXiOWq3hlNvMecCZRqamocgH9Zp9ocadrAfyCpw= github.com/shimmeringbee/zigbee v0.0.0-20240614103911-3a30074e1528 h1:D5jQVQ/kMjiVp4bYYmuWdKvW81+1tv2arSTgiXKkWmM= github.com/shimmeringbee/zigbee v0.0.0-20240614103911-3a30074e1528/go.mod h1:BDCm9qtlJANPiLY+YRQac/0awPxeUd3FUxUFPh+1w/s= +github.com/shimmeringbee/zigbee v0.0.0-20240614104723-f4c0c0231568 h1:DnZ/kbXJZtihjqB7mz92hhUeP0+v0jYl5DJIznWdlL4= +github.com/shimmeringbee/zigbee v0.0.0-20240614104723-f4c0c0231568/go.mod h1:BDCm9qtlJANPiLY+YRQac/0awPxeUd3FUxUFPh+1w/s= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= diff --git a/network_manager.go b/network_manager.go index 32c78c6..5936041 100644 --- a/network_manager.go +++ b/network_manager.go @@ -60,11 +60,11 @@ func (z *ZStack) networkManager() { z.removeNode(e.IEEEAddress) case ZdoIEEEAddrRsp: if e.WasSuccessful() { - z.nodeTable.addOrUpdate(e.IEEEAddress, e.NetworkAddress, updateDiscovered) + z.nodeTable.addOrUpdate(e.IEEEAddress, e.NetworkAddress, updateDiscovered()) } case ZdoNWKAddrRsp: if e.WasSuccessful() { - z.nodeTable.addOrUpdate(e.IEEEAddress, e.NetworkAddress, updateDiscovered) + z.nodeTable.addOrUpdate(e.IEEEAddress, e.NetworkAddress, updateDiscovered()) } default: z.logger.LogWarn(context.Background(), "Received unknown message type from unpi.", logwrap.Datum("Type", reflect.TypeOf(ue))) @@ -80,7 +80,7 @@ func (z *ZStack) newNode(e ZdoEndDeviceAnnceInd) { deviceLogicalType = zigbee.Router } - z.nodeTable.addOrUpdate(e.IEEEAddress, e.NetworkAddress, logicalType(deviceLogicalType), updateDiscovered, updateReceived) + z.nodeTable.addOrUpdate(e.IEEEAddress, e.NetworkAddress, logicalType(deviceLogicalType), updateDiscovered(), updateReceived()) node, _ := z.nodeTable.getByIEEE(e.IEEEAddress) z.sendEvent(zigbee.NodeJoinEvent{ @@ -152,7 +152,7 @@ func (z *ZStack) processLQITable(lqiResp ZdoMGMTLQIRsp) { continue } - z.nodeTable.addOrUpdate(neighbour.IEEEAddress, neighbour.NetworkAddress, logicalType(neighbour.Status.DeviceType), updateDiscovered) + z.nodeTable.addOrUpdate(neighbour.IEEEAddress, neighbour.NetworkAddress, logicalType(neighbour.Status.DeviceType), updateDiscovered()) if neighbour.Status.Relationship == zigbee.RelationshipChild { z.nodeTable.update(neighbour.IEEEAddress, lqi(neighbour.LQI), depth(neighbour.Depth)) diff --git a/node_receive_message.go b/node_receive_message.go index 8722b3c..417f176 100644 --- a/node_receive_message.go +++ b/node_receive_message.go @@ -42,7 +42,7 @@ func (z *ZStack) startMessageReceiver() { }, }) - z.nodeTable.update(ieee, updateReceived, lqi(msg.LinkQuality)) + z.nodeTable.update(ieee, updateReceived(), lqi(msg.LinkQuality)) }) } diff --git a/node_table.go b/node_table.go index 5758a34..f9a0190 100644 --- a/node_table.go +++ b/node_table.go @@ -1,7 +1,10 @@ package zstack import ( + "github.com/shimmeringbee/persistence" + "github.com/shimmeringbee/persistence/converter" "github.com/shimmeringbee/zigbee" + "strconv" "sync" "time" ) @@ -11,27 +14,29 @@ type nodeTable struct { ieeeToNode map[zigbee.IEEEAddress]*zigbee.Node networkToIEEE map[zigbee.NetworkAddress]zigbee.IEEEAddress lock *sync.RWMutex + + p persistence.Section + loading bool } -func newNodeTable() *nodeTable { - return &nodeTable{ +func newNodeTable(p persistence.Section) *nodeTable { + n := &nodeTable{ callbacks: []func(zigbee.Node){}, ieeeToNode: make(map[zigbee.IEEEAddress]*zigbee.Node), networkToIEEE: make(map[zigbee.NetworkAddress]zigbee.IEEEAddress), lock: &sync.RWMutex{}, + p: p, } + + n.load() + + return n } func (t *nodeTable) registerCallback(cb func(zigbee.Node)) { t.callbacks = append(t.callbacks, cb) } -func (t *nodeTable) Load(nodes []zigbee.Node) { - for _, node := range nodes { - t.addOrUpdate(node.IEEEAddress, node.NetworkAddress, logicalType(node.LogicalType), lqi(node.LQI), depth(node.Depth), setReceived(node.LastReceived), setDiscovered(node.LastDiscovered)) - } -} - func (t *nodeTable) nodes() []zigbee.Node { t.lock.RLock() defer t.lock.RUnlock() @@ -74,17 +79,26 @@ func (t *nodeTable) addOrUpdate(ieeeAddress zigbee.IEEEAddress, networkAddress z t.lock.Lock() node, found := t.ieeeToNode[ieeeAddress] + s := t.p.Section(ieeeAddress.String()) + if found { if node.NetworkAddress != networkAddress { delete(t.networkToIEEE, node.NetworkAddress) node.NetworkAddress = networkAddress + + converter.Store(s, "NetworkAddress", node.NetworkAddress, converter.NetworkAddressEncoder) } } else { - t.ieeeToNode[ieeeAddress] = &zigbee.Node{ + node = &zigbee.Node{ IEEEAddress: ieeeAddress, NetworkAddress: networkAddress, LogicalType: zigbee.Unknown, } + + t.ieeeToNode[ieeeAddress] = node + + converter.Store(s, "NetworkAddress", node.NetworkAddress, converter.NetworkAddressEncoder) + converter.Store(s, "LogicalType", node.LogicalType, converter.LogicalTypeEncoder) } t.networkToIEEE[networkAddress] = ieeeAddress @@ -100,8 +114,14 @@ func (t *nodeTable) update(ieeeAddress zigbee.IEEEAddress, updates ...nodeUpdate node, found := t.ieeeToNode[ieeeAddress] if found { + var s persistence.Section + + if !t.loading { + s = t.p.Section(ieeeAddress.String()) + } + for _, du := range updates { - du(node) + du(node, s) } for _, cb := range t.callbacks { @@ -120,44 +140,113 @@ func (t *nodeTable) remove(ieeeAddress zigbee.IEEEAddress) { delete(t.networkToIEEE, node.NetworkAddress) delete(t.ieeeToNode, node.IEEEAddress) } + + t.p.Delete(ieeeAddress.String()) +} + +func (t *nodeTable) load() { + t.lock.Lock() + t.loading = true + t.lock.Unlock() + + defer func() { + t.lock.Lock() + t.loading = false + t.lock.Unlock() + }() + + for _, key := range t.p.Keys() { + if value, err := strconv.ParseUint(key, 16, 64); err == nil { + s := t.p.Section(key) + ieee := zigbee.IEEEAddress(value) + + na, naFound := converter.Retrieve(s, "NetworkAddress", converter.NetworkAddressDecoder) + if !naFound { + continue + } + + t.addOrUpdate(ieee, na) + + if lt, found := converter.Retrieve(s, "LogicalType", converter.LogicalTypeDecoder); found { + t.update(ieee, logicalType(lt)) + } + + if l, found := s.UInt("LQI"); found { + t.update(ieee, lqi(uint8(l))) + } + + if d, found := s.UInt("Depth"); found { + t.update(ieee, depth(uint8(d))) + } + + if received, found := converter.Retrieve(s, "LastReceived", converter.TimeDecoder); found { + t.update(ieee, setReceived(received)) + + } + + if discovered, found := converter.Retrieve(s, "LastDiscovered", converter.TimeDecoder); found { + t.update(ieee, setDiscovered(discovered)) + } + } + } } -type nodeUpdate func(device *zigbee.Node) +type nodeUpdate func(node *zigbee.Node, p persistence.Section) func logicalType(logicalType zigbee.LogicalType) nodeUpdate { - return func(node *zigbee.Node) { + return func(node *zigbee.Node, p persistence.Section) { node.LogicalType = logicalType + + if p != nil { + converter.Store(p, "LogicalType", node.LogicalType, converter.LogicalTypeEncoder) + } } } func lqi(lqi uint8) nodeUpdate { - return func(node *zigbee.Node) { + return func(node *zigbee.Node, p persistence.Section) { node.LQI = lqi + + if p != nil { + p.Set("LQI", uint64(node.LQI)) + } } } func depth(depth uint8) nodeUpdate { - return func(node *zigbee.Node) { + return func(node *zigbee.Node, p persistence.Section) { node.Depth = depth + + if p != nil { + p.Set("Depth", uint64(node.Depth)) + } } } -func updateReceived(node *zigbee.Node) { - node.LastReceived = time.Now() +func updateReceived() nodeUpdate { + return setReceived(time.Now()) } -func updateDiscovered(node *zigbee.Node) { - node.LastDiscovered = time.Now() +func updateDiscovered() nodeUpdate { + return setDiscovered(time.Now()) } func setReceived(t time.Time) nodeUpdate { - return func(node *zigbee.Node) { + return func(node *zigbee.Node, p persistence.Section) { node.LastReceived = t + + if p != nil { + converter.Store(p, "LastReceived", node.LastReceived, converter.TimeEncoder) + } } } func setDiscovered(t time.Time) nodeUpdate { - return func(node *zigbee.Node) { + return func(node *zigbee.Node, p persistence.Section) { node.LastDiscovered = t + + if p != nil { + converter.Store(p, "LastDiscovered", node.LastDiscovered, converter.TimeEncoder) + } } } diff --git a/node_table_test.go b/node_table_test.go index cce5cf1..831007a 100644 --- a/node_table_test.go +++ b/node_table_test.go @@ -1,6 +1,8 @@ package zstack import ( + "github.com/shimmeringbee/persistence/converter" + "github.com/shimmeringbee/persistence/impl/memory" "github.com/shimmeringbee/zigbee" "github.com/stretchr/testify/assert" "testing" @@ -8,11 +10,12 @@ import ( ) func TestNodeTable(t *testing.T) { - ieee := zigbee.IEEEAddress(0x001122334455) + ieee := zigbee.GenerateLocalAdministeredIEEEAddress() network := zigbee.NetworkAddress(0xaabb) t.Run("an added node can be retrieved by its IEEE address, and has minimum information", func(t *testing.T) { - nt := newNodeTable() + s := memory.New() + nt := newNodeTable(s) nt.addOrUpdate(ieee, network) @@ -22,10 +25,19 @@ func TestNodeTable(t *testing.T) { assert.Equal(t, ieee, node.IEEEAddress) assert.Equal(t, network, node.NetworkAddress) assert.Equal(t, zigbee.Unknown, node.LogicalType) + + assert.Contains(t, s.Keys(), ieee.String()) + + ns := s.Section(ieee.String()) + na, ok := converter.Retrieve(ns, "NetworkAddress", converter.NetworkAddressDecoder) + + assert.True(t, ok) + assert.Equal(t, network, na) }) t.Run("an added node with updates can be retrieved and has updated information", func(t *testing.T) { - nt := newNodeTable() + s := memory.New() + nt := newNodeTable(s) nt.addOrUpdate(ieee, network, logicalType(zigbee.EndDevice)) @@ -33,10 +45,16 @@ func TestNodeTable(t *testing.T) { assert.True(t, found) assert.Equal(t, zigbee.EndDevice, node.LogicalType) + + ns := s.Section(ieee.String()) + lt, ok := converter.Retrieve(ns, "LogicalType", converter.LogicalTypeDecoder) + + assert.True(t, ok) + assert.Equal(t, zigbee.EndDevice, lt) }) t.Run("an added node can be retrieved by its network address", func(t *testing.T) { - nt := newNodeTable() + nt := newNodeTable(memory.New()) nt.addOrUpdate(ieee, network) @@ -45,21 +63,21 @@ func TestNodeTable(t *testing.T) { }) t.Run("a missing node queried by its ieee address returns not found", func(t *testing.T) { - nt := newNodeTable() + nt := newNodeTable(memory.New()) _, found := nt.getByIEEE(ieee) assert.False(t, found) }) t.Run("a missing node queried by its network address returns not found", func(t *testing.T) { - nt := newNodeTable() + nt := newNodeTable(memory.New()) _, found := nt.getByNetwork(network) assert.False(t, found) }) t.Run("removing a node results in it not being found by ieee address", func(t *testing.T) { - nt := newNodeTable() + nt := newNodeTable(memory.New()) nt.addOrUpdate(ieee, network) nt.remove(ieee) @@ -69,17 +87,22 @@ func TestNodeTable(t *testing.T) { }) t.Run("removing a node results in it not being found by network address", func(t *testing.T) { - nt := newNodeTable() + s := memory.New() + nt := newNodeTable(s) nt.addOrUpdate(ieee, network) + assert.Contains(t, s.Keys(), ieee.String()) + nt.remove(ieee) + assert.NotContains(t, s.Keys(), ieee.String()) _, found := nt.getByNetwork(network) assert.False(t, found) }) t.Run("an update using add makes the node available under the new network only, and updates the network address", func(t *testing.T) { - nt := newNodeTable() + s := memory.New() + nt := newNodeTable(s) newNetwork := zigbee.NetworkAddress(0x1234) @@ -93,10 +116,16 @@ func TestNodeTable(t *testing.T) { assert.True(t, found) assert.Equal(t, newNetwork, node.NetworkAddress) + + ns := s.Section(ieee.String()) + na, ok := converter.Retrieve(ns, "NetworkAddress", converter.NetworkAddressDecoder) + + assert.True(t, ok) + assert.Equal(t, newNetwork, na) }) t.Run("an update makes all changes as requested by node updates", func(t *testing.T) { - nt := newNodeTable() + nt := newNodeTable(memory.New()) nt.addOrUpdate(ieee, network) @@ -108,7 +137,7 @@ func TestNodeTable(t *testing.T) { }) t.Run("returns all nodes when queried", func(t *testing.T) { - nt := newNodeTable() + nt := newNodeTable(memory.New()) nt.addOrUpdate(ieee, network) @@ -119,7 +148,7 @@ func TestNodeTable(t *testing.T) { t.Run("callbacks are called for additions", func(t *testing.T) { callbackCalled := false - nt := newNodeTable() + nt := newNodeTable(memory.New()) nt.registerCallback(func(node zigbee.Node) { callbackCalled = true }) @@ -132,7 +161,7 @@ func TestNodeTable(t *testing.T) { t.Run("callbacks are called for additions", func(t *testing.T) { callbackCalled := false - nt := newNodeTable() + nt := newNodeTable(memory.New()) nt.addOrUpdate(zigbee.IEEEAddress(0x00), zigbee.NetworkAddress(0x00)) @@ -140,62 +169,104 @@ func TestNodeTable(t *testing.T) { callbackCalled = true }) - nt.update(zigbee.IEEEAddress(0x00), updateReceived) + nt.update(zigbee.IEEEAddress(0x00), updateReceived()) assert.True(t, callbackCalled) }) - - t.Run("dumping and loading result in the same nodes being present in the table", func(t *testing.T) { - ntOne := newNodeTable() - ntOne.addOrUpdate(zigbee.IEEEAddress(0x01), zigbee.NetworkAddress(0x01)) - ntOneDump := ntOne.nodes() - - ntTwo := newNodeTable() - ntTwo.Load(ntOneDump) - ntTwoDump := ntTwo.nodes() - - assert.Equal(t, ntOneDump, ntTwoDump) - }) } func TestNodeUpdate(t *testing.T) { t.Run("logicalType updates the logical type of node", func(t *testing.T) { node := &zigbee.Node{} - logicalType(zigbee.EndDevice)(node) + s := memory.New() + logicalType(zigbee.EndDevice)(node, s) assert.Equal(t, zigbee.EndDevice, node.LogicalType) + + lt, ok := converter.Retrieve(s, "LogicalType", converter.LogicalTypeDecoder) + + assert.True(t, ok) + assert.Equal(t, zigbee.EndDevice, lt) }) t.Run("lqi updates the lqi of node", func(t *testing.T) { node := &zigbee.Node{} - lqi(48)(node) + s := memory.New() + lqi(48)(node, s) assert.Equal(t, uint8(48), node.LQI) + + l, ok := s.UInt("LQI") + assert.True(t, ok) + assert.Equal(t, uint64(48), l) }) t.Run("depth updates the depth of node", func(t *testing.T) { node := &zigbee.Node{} - depth(3)(node) + s := memory.New() + depth(3)(node, s) assert.Equal(t, uint8(3), node.Depth) + + d, ok := s.UInt("Depth") + assert.True(t, ok) + assert.Equal(t, uint64(3), d) }) t.Run("updateReceived updates the last received time of node", func(t *testing.T) { node := &zigbee.Node{} - updateReceived(node) + s := memory.New() + updateReceived()(node, s) assert.NotEqual(t, time.Time{}, node.LastReceived) + + date, ok := converter.Retrieve(s, "LastReceived", converter.TimeDecoder) + assert.True(t, ok) + assert.True(t, time.Now().After(date)) }) t.Run("updateDiscovered updates the last received time of node", func(t *testing.T) { node := &zigbee.Node{} - updateDiscovered(node) + s := memory.New() + updateDiscovered()(node, s) assert.NotEqual(t, time.Time{}, node.LastDiscovered) + + date, ok := converter.Retrieve(s, "LastDiscovered", converter.TimeDecoder) + assert.True(t, ok) + assert.True(t, time.Now().After(date)) + }) +} + +func TestNodeTable_Load(t *testing.T) { + t.Run("loading a table from persistence contains expected nodes", func(t *testing.T) { + s := memory.New() + + ieee := zigbee.GenerateLocalAdministeredIEEEAddress() + + time := time.UnixMilli(time.Now().UnixMilli()) + + nS := s.Section(ieee.String()) + converter.Store(nS, "NetworkAddress", zigbee.NetworkAddress(0x1122), converter.NetworkAddressEncoder) + converter.Store(nS, "LastReceived", time, converter.TimeEncoder) + converter.Store(nS, "LastDiscovered", time, converter.TimeEncoder) + converter.Store(nS, "LogicalType", zigbee.Router, converter.LogicalTypeEncoder) + nS.Set("LQI", uint64(8)) + nS.Set("Depth", uint64(2)) + + nt := newNodeTable(s) + + node, ok := nt.getByIEEE(ieee) + assert.True(t, ok) + assert.Equal(t, zigbee.NetworkAddress(0x1122), node.NetworkAddress) + assert.Equal(t, time, node.LastDiscovered) + assert.Equal(t, time, node.LastReceived) + assert.Equal(t, uint8(8), node.LQI) + assert.Equal(t, uint8(2), node.Depth) }) } diff --git a/zstack.go b/zstack.go index db3aa9f..54b4ba7 100644 --- a/zstack.go +++ b/zstack.go @@ -97,7 +97,7 @@ func New(uart io.ReadWriter, p persistence.Section) *ZStack { events: make(chan interface{}, DefaultInflightEvents), networkManagerStop: make(chan bool, 1), networkManagerIncoming: make(chan interface{}, DefaultInflightEvents), - nodeTable: newNodeTable(), + nodeTable: newNodeTable(p.Section("NodeTable")), transactionIdStore: transactionIDs, persistence: p, }