diff --git a/main.go b/main.go index 5542f31..13cc396 100644 --- a/main.go +++ b/main.go @@ -23,7 +23,7 @@ var ( wsserverForNodes *websocketserver.Server respondDaemon *respond.Daemon nodes = models.NewNodes() - aliases = models.NewNodes() + //aliases = models.NewNodes() ) func main() { @@ -35,10 +35,11 @@ func main() { saveInterval := time.Second * time.Duration(config.Nodes.SaveInterval) if config.Nodes.Enable { - go nodes.Saver(config.Nodes.NodesPath, saveInterval) + go nodes.Saver(config.Nodes.NodesPath, config.Nodes.GraphsPath, saveInterval) } if config.Nodes.AliasesEnable { - go aliases.Saver(config.Nodes.AliasesPath, saveInterval) + // FIXME what does this do? + //go aliases.Saver(config.Nodes.AliasesPath, saveInterval) } if config.Webserver.Enable { diff --git a/models/graph.go b/models/graph.go new file mode 100644 index 0000000..16d6f33 --- /dev/null +++ b/models/graph.go @@ -0,0 +1,103 @@ +package models + +import ( + "fmt" + "strings" +) + +type Graph struct { + Version int `json:"version"` + Batadv struct { + Links []*GraphLink `json:"links"` + } `json:"batadv"` +} + +type GraphLink struct { + Source string `json:"source"` + Target string `json:"target"` + VPN bool `json:"vpn"` + TQ float32 `json:"tq"` + Bidirect bool `json:"bidirect"` +} + +type GraphBuilder struct { + macToID map[string]string // mapping from MAC address to node id + links map[string]*GraphLink // mapping from $idA-$idB to existing link +} + +func (nodes *Nodes) BuildGraph() *Graph { + builder := &GraphBuilder{ + macToID: make(map[string]string), + links: make(map[string]*GraphLink), + } + + builder.readNodes(nodes.List) + + graph := &Graph{Version: 2} + graph.Batadv.Links = builder.Links() + return graph +} + +func (builder *GraphBuilder) readNodes(nodes map[string]*Node) { + // Fill mac->id map + for sourceId, node := range nodes { + if neighbours := node.Neighbours; neighbours != nil { + for sourceAddress, _ := range neighbours.Batadv { + builder.macToID[sourceAddress] = sourceId + } + } + } + + // Add links + for sourceId, node := range nodes { + if neighbours := node.Neighbours; neighbours != nil { + for _, batadvNeighbours := range neighbours.Batadv { + for targetAddress, link := range batadvNeighbours.Neighbours { + if targetId, found := builder.macToID[targetAddress]; found { + builder.addLink(targetId, sourceId, link.Tq) + } + } + } + } + } +} + +func (builder *GraphBuilder) Links() []*GraphLink { + i := 0 + links := make([]*GraphLink, len(builder.links)) + + for _, link := range builder.links { + links[i] = link + i += 1 + } + return links +} + +func (builder *GraphBuilder) addLink(targetId string, sourceId string, linkTq int) { + // Order IDs to get generate the key + var key string + if strings.Compare(sourceId, targetId) > 0 { + key = fmt.Sprintf("%s-%s", sourceId, targetId) + } else { + key = fmt.Sprintf("%s-%s", targetId, sourceId) + } + + var tq float32 + if linkTq > 0 { + tq = float32(1.0 / (float32(linkTq) / 255.0)) + } + + if link, ok := builder.links[key]; !ok { + builder.links[key] = &GraphLink{ + Source: sourceId, + Target: targetId, + TQ: tq, + } + } else { + // Use lowest of both link qualities + if tq < link.TQ { + link.TQ = tq + } + link.Bidirect = true + } +} diff --git a/models/nodes.go b/models/nodes.go index 861e09d..8616913 100644 --- a/models/nodes.go +++ b/models/nodes.go @@ -62,25 +62,25 @@ func (nodes *Nodes) Get(nodeID string) *Node { } // Saves the cached DB to json file periodically -func (nodes *Nodes) Saver(outputFile string, saveInterval time.Duration) { +func (nodes *Nodes) Saver(nodesPath string, graphPath string, saveInterval time.Duration) { c := time.Tick(saveInterval) for range c { - nodes.save(outputFile) + log.Println("saving", len(nodes.List), "nodes") + nodes.Timestamp = time.Now() + nodes.Lock() + save(nodes, nodesPath) + save(nodes.BuildGraph(), graphPath) + nodes.Unlock() } } -func (nodes *Nodes) save(outputFile string) { - nodes.Timestamp = time.Now() - - nodes.Lock() - data, err := json.Marshal(nodes) - nodes.Unlock() +func save(input interface{}, outputFile string) { + data, err := json.Marshal(input) if err != nil { log.Panic(err) } - log.Println("saving", len(nodes.List), "nodes") tmpFile := outputFile + ".tmp"