From a4c18d85250a0b2824e1572d5b2b5b36dfb78ecd Mon Sep 17 00:00:00 2001 From: Jason Michalski Date: Sun, 16 Jun 2024 16:41:37 -0700 Subject: [PATCH] Add node zooming and time filters. --- meshview/web.py | 54 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/meshview/web.py b/meshview/web.py index 1371496..c5bf8ef 100644 --- a/meshview/web.py +++ b/meshview/web.py @@ -629,11 +629,15 @@ async def graph_traceroute(request): @routes.get("/graph/network") async def graph_network(request): + root = request.query.get("root") + depth = int(request.query.get("depth", 5)) + hours = int(request.query.get("hours", 24)) + nodes = {} node_ids = set() traceroutes = [] - for tr in await store.get_traceroutes(datetime.timedelta(days=1)): + for tr in await store.get_traceroutes(datetime.timedelta(hours=hours)): node_ids.add(tr.gateway_node_id) node_ids.add(tr.packet.from_node_id) node_ids.add(tr.packet.to_node_id) @@ -656,7 +660,7 @@ async def graph_network(request): for packet in await store.get_packets( portnum=PortNum.NEIGHBORINFO_APP, - since=datetime.timedelta(days=1) + since=datetime.timedelta(hours=hours) ): _, neighbor_info = decode_payload.decode(packet) node_ids.add(packet.from_node_id) @@ -685,22 +689,52 @@ async def graph_network(request): edges[(src, dest)] += 1 edge_type[(src, dest)] = 'tr' - - graph = pydot.Dot('network', graph_type="digraph", layout="sfdp", overlap="prism", quadtree="normal", repulsiveforce="1.5", k="1") - for node_id in used_nodes: + async def get_node_name(node_id): node = await nodes[node_id] - color = '#000000' if not node: node_name = node_id_to_hex(node_id) else: node_name = f'[{node.short_name}] {node.long_name}\n{node_id_to_hex(node_id)}' - if node.role in ('ROUTER', 'ROUTER_CLIENT', 'REPEATER'): - color = '#0000FF' + return node_name + + if root: + new_used_nodes = set() + new_edges = Counter() + edge_map = {} + for src, dest in edges: + edge_map.setdefault(dest, []).append(src) + + queue = [int(root)] + for i in range(depth): + next_queue = [] + for node in queue: + new_used_nodes.add(node) + for dest in edge_map.get(node, []): + new_used_nodes.add(dest) + new_edges[(dest, node)] += 1 + next_queue.append(dest) + queue = next_queue + + used_nodes = new_used_nodes + edges = new_edges + + #graph = pydot.Dot('network', graph_type="digraph", layout="sfdp", overlap="prism", quadtree="2", repulsiveforce="1.5", k="1", overlap_scaling="1.5", concentrate=True) + #graph = pydot.Dot('network', graph_type="digraph", layout="sfdp", overlap="prism1000", overlap_scaling="-4", sep="1000", pack="true") + graph = pydot.Dot('network', graph_type="digraph", layout="neato", overlap="false", model='subset', concentrate=True, esep="+5") + for node_id in used_nodes: + node = await nodes[node_id] + color = '#000000' + node_name = await get_node_name(node_id) + if node and node.role in ('ROUTER', 'ROUTER_CLIENT', 'REPEATER'): + color = '#0000FF' + elif node and node.role == 'CLIENT_MUTE': + color = '#00FF00' graph.add_node(pydot.Node( str(node_id), label=node_name, shape='box', color=color, + href=f"/graph/network?root={node_id}&depth={depth-1}", )) if edges: @@ -710,6 +744,7 @@ async def graph_network(request): size_ratio = 2. / max_edge_count + for (src, dest), edge_count in edges.items(): size = max(size_ratio * edge_count, .25) arrowsize = max(size_ratio * edge_count, .5) @@ -721,8 +756,9 @@ async def graph_network(request): str(src), str(dest), color=color, + tooltip=f'{await get_node_name(src)} -> {await get_node_name(dest)}', #weight=size, - #penwidth=size, + penwidth=1.75, #arrowsize=arrowsize, ))