Skip to content
Snippets Groups Projects
neighbours.js 7.16 KiB
Newer Older
  • Learn to ignore specific revisions
  • "use strict"
    define([ "lib/helper", "lib/gui/signalgraph", "lib/gui/signal"],
    function (Helper, SignalGraph, Signal) {
    
      var graphColors = ["#396AB1", "#DA7C30", "#3E9651", "#CC2529", "#535154", "#6B4C9A", "#922428", "#948B3D"]
      //graphColors = ["#7293CB", "#E1974C", "#84BA5B", "#D35E60", "#808585", "#9067A7", "#AB6857", "#CCC210"];
    
      var inactiveTime = 200
    
      function SignalEntry(graph, color, stream) {
        var signal = new Signal(color)
        var remove = graph.add(signal)
    
        var unsubscribe = stream.onValue(update)
    
        this.destroy = function () {
          unsubscribe()
          remove()
        }
    
        this.getSignal = function () {
          return signal
        }
    
        return this
    
        function update(d) {
          if ("wifi" in d)
            signal.set(d.wifi.inactive > inactiveTime ? null : d.wifi.signal)
        }
      }
    
      function TableEntry(parent, nodeInfo, color, stream, mgmtBus, signal) {
        var el = document.createElement("tr")
        parent.appendChild(el)
    
        var tdHostname = document.createElement("td")
        var tdTQ = document.createElement("td")
        var tdSignal = document.createElement("td")
        var tdDistance = document.createElement("td")
        var tdInactive = document.createElement("td")
    
        el.appendChild(tdHostname)
        el.appendChild(tdTQ)
        el.appendChild(tdSignal)
        el.appendChild(tdDistance)
        el.appendChild(tdInactive)
    
        var marker = document.createElement("span")
        marker.textContent = ""
        marker.style.color = color
        tdHostname.appendChild(marker)
    
        var hostname = document.createElement("span")
        tdHostname.appendChild(hostname)
    
        var infoSet = false
        var unsubscribe = stream.onValue(update)
    
        el.onmouseenter = function () {
          el.classList.add("highlight")
          signal.setHighlight(true)
        }
    
        el.onmouseleave = function () {
          el.classList.remove("highlight")
          signal.setHighlight(false)
        }
    
        el.destroy = function () {
          unsubscribe()
          parent.removeChild(el)
        }
    
        return el
    
        function update(d) {
          if ("wifi" in d) {
            var signal = d.wifi.signal
            var inactive = d.wifi.inactive
    
            el.classList.toggle("inactive", inactive > inactiveTime)
    
            tdSignal.textContent = signal
            tdInactive.textContent = Math.round(inactive / 1000) + " s"
          }
    
          if ("batadv" in d)
            tdTQ.textContent = Math.round(d.batadv.tq / 2.55) + " %"
          else
            tdTQ.textContent = ""
    
          if (infoSet)
            return
    
          if ("nodeInfo" in d) {
              infoSet = true
    
              var link = document.createElement("a")
              link.textContent = d.nodeInfo.hostname
              link.href = "#"
              link.nodeInfo = d.nodeInfo
              link.onclick = function () {
                mgmtBus.pushEvent("goto", this.nodeInfo)
                return false
              }
    
              while (hostname.firstChild)
                hostname.removeChild(hostname.firstChild)
    
              hostname.appendChild(link)
    
              try {
                var distance = Helper.haversine(nodeInfo.location.latitude, nodeInfo.location.longitude,
                                                d.nodeInfo.location.latitude, d.nodeInfo.location.longitude)
    
                tdDistance.textContent = Math.round(distance * 1000) + " m"
              } catch (e) {
                tdDistance.textContent = ""
              }
          } else
            hostname.textContent = d.id
        }
      }
    
      function Interface(parent, nodeInfo, iface, stream, mgmtBus) {
        var colors = graphColors.slice(0)
    
        var el = document.createElement("div")
        el.ifname = iface
        parent.appendChild(el)
    
        var h = document.createElement("h3")
        h.textContent = iface
        el.appendChild(h)
    
        var table = document.createElement("table")
        var tr = document.createElement("tr")
        table.appendChild(tr)
        table.classList.add("datatable")
    
        var th = document.createElement("th")
        th.textContent = "Knoten"
        tr.appendChild(th)
    
        th = document.createElement("th")
        th.textContent = "TQ"
        tr.appendChild(th)
    
        th = document.createElement("th")
        th.textContent = "dBm"
        tr.appendChild(th)
    
        th = document.createElement("th")
        th.textContent = "Entfernung"
        tr.appendChild(th)
    
        th = document.createElement("th")
        th.textContent = "Inaktiv"
        tr.appendChild(th)
    
        el.appendChild(table)
    
        var wrapper = document.createElement("div")
        wrapper.className = "signalgraph"
        el.appendChild(wrapper)
    
        var canvas = document.createElement("canvas")
        canvas.className = "signal-history"
        canvas.height = 200
        wrapper.appendChild(canvas)
    
        var graph = new SignalGraph(canvas, -100, 0, true)
    
        var stopStream = stream.skipDuplicates(sameKeys).onValue(update)
    
        var managedNeighbours = {}
    
        function update(d) {
          var notUpdated = new Set()
          var id
    
          for (id in managedNeighbours)
            notUpdated.add(id)
    
          for (id in d) {
            if (!(id in managedNeighbours)) {
              var neighbourStream = stream.map("."  + id).filter( function (d) { return d !== undefined })
              var color = colors.shift()
              var signal = new SignalEntry(graph, color, neighbourStream)
              managedNeighbours[id] = { views: [ signal,
                                                 new TableEntry(table, nodeInfo, color, neighbourStream, mgmtBus, signal.getSignal())
                                               ],
                                        color: color
                                      }
            }
    
            notUpdated.delete(id)
          }
    
          for (id in notUpdated) {
            managedNeighbours[id].views.forEach( function (d) { d.destroy() })
            colors.push(managedNeighbours[id].color)
            delete managedNeighbours[id]
          }
        }
    
    
        el.destroy = function () {
          stopStream()
    
          for (var id in managedNeighbours)
            managedNeighbours[id].views.forEach( function (d) { d.destroy() })
    
          el.removeChild(h)
          el.removeChild(wrapper)
          el.removeChild(table)
        }
      }
    
      function sameKeys(a, b) {
        a = Object.keys(a).sort()
        b = Object.keys(b).sort()
    
        return !(a < b || a > b)
      }
    
    
      function getter(k) {
        return function(obj) {
          return obj[k]
        }
      }
    
    
      return function (nodeInfo, stream, mgmtBus) {
        var stopStream, div
    
        function render(el) {
          div = document.createElement("div")
          el.appendChild(div)
    
          stopStream = stream.skipDuplicates(sameKeys).onValue(update)
    
          function update(d) {
            var have = {}
            var remove = []
            if (div.hasChildNodes()) {
              var children = div.childNodes
              for (var i = 0; i < children.length; i++) {
                var a = children[i]
                if (a.ifname in d)
                  have[a.ifname] = true
                else {
                  a.destroy()
                  remove.push(a)
                }
              }
            }
    
            remove.forEach(function (d) { div.removeChild(d) })
    
    
              if (!(k in have))
    
                new Interface(div, nodeInfo, k, stream.map(getter(k)), mgmtBus)
            }
    
          }
        }
    
        function destroy() {
          stopStream()
    
          while (div.firstChild) {
            div.firstChild.destroy()
            div.removeChild(div.firstChild)
          }
        }
    
        return { title: document.createTextNode("Nachbarknoten")
               , render: render
               , destroy: destroy
               }
      }
    })