Skip to content

Commit

Permalink
Implement savings and loading from persistence.
Browse files Browse the repository at this point in the history
  • Loading branch information
pwood committed Jun 14, 2024
1 parent 97f6e3b commit 8f0b5c5
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 62 deletions.
11 changes: 6 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,20 @@ 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
)

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
)
14 changes: 14 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
8 changes: 4 additions & 4 deletions network_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Expand All @@ -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{
Expand Down Expand Up @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion node_receive_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (z *ZStack) startMessageReceiver() {
},
})

z.nodeTable.update(ieee, updateReceived, lqi(msg.LinkQuality))
z.nodeTable.update(ieee, updateReceived(), lqi(msg.LinkQuality))
})
}

Expand Down
129 changes: 109 additions & 20 deletions node_table.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package zstack

import (
"github.com/shimmeringbee/persistence"
"github.com/shimmeringbee/persistence/converter"
"github.com/shimmeringbee/zigbee"
"strconv"
"sync"
"time"
)
Expand All @@ -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()
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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)
}
}
}
Loading

0 comments on commit 8f0b5c5

Please sign in to comment.