mirror of
https://github.com/pablorevilla-meshtastic/meshview.git
synced 2026-03-04 23:27:46 +01:00
Merge remote-tracking branch 'origin/master'
# Conflicts: # meshview/templates/map.html
This commit is contained in:
@@ -39,7 +39,6 @@
|
||||
|
||||
{% block body %}
|
||||
<div id="map" style="width: 100%; height: calc(100vh - 270px)"></div>
|
||||
|
||||
<div id="filter-container">
|
||||
<input type="checkbox" class="filter-checkbox" id="filter-routers-only"> Show Routers Only
|
||||
</div>
|
||||
|
||||
@@ -62,6 +62,14 @@
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
color: #333;
|
||||
}
|
||||
.legend-category {
|
||||
margin-right: 10px;
|
||||
code {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
.legend-box {
|
||||
display: inline-block;
|
||||
@@ -69,6 +77,9 @@
|
||||
height: 12px;
|
||||
margin-right: 5px;
|
||||
border-radius: 3px;
|
||||
&.circle {
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
@@ -90,29 +101,83 @@
|
||||
</div>
|
||||
|
||||
<div id="legend">
|
||||
<div><span class="legend-box" style="background-color:#ff5733;"></span> Traceroute</div>
|
||||
<div><span class="legend-box" style="background-color:#3388ff;"></span> Neighbor</div>
|
||||
<div class="legend-category">
|
||||
<div><span class="legend-box" style="background-color: #ff5733"></span> Traceroute</div>
|
||||
<div><span class="legend-box" style="background-color: #049acd"></span> Neighbor</div>
|
||||
</div>
|
||||
<div class="legend-category">
|
||||
<div><span class="legend-box circle" style="background-color: #ff5733"></span> <code>ROUTER</code></div>
|
||||
<div><span class="legend-box circle" style="background-color: #b65224"></span> <code>ROUTER_LATE</code></div>
|
||||
</div>
|
||||
<div class="legend-category">
|
||||
<div><span class="legend-box circle" style="background-color: #007bff"></span> <code>CLIENT</code></div>
|
||||
<div><span class="legend-box circle" style="background-color: #00c3ff"></span> <code>CLIENT_MUTE</code></div>
|
||||
</div>
|
||||
<div class="legend-category">
|
||||
<div><span class="legend-box circle" style="background-color: #ffbf00"></span> Other</div>
|
||||
<div><span class="legend-box circle" style="background-color: #6c757d"></span> Unknown</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const chart = echarts.init(document.getElementById('mynetwork'));
|
||||
|
||||
const colors = {
|
||||
edge: {
|
||||
traceroute: '#ff5733',
|
||||
neighbor: '#049acd',
|
||||
},
|
||||
role: {
|
||||
ROUTER: '#ff5733',
|
||||
ROUTER_LATE: '#b65224',
|
||||
CLIENT: '#007bff',
|
||||
CLIENT_MUTE: '#00c3ff',
|
||||
other: '#ffbf00',
|
||||
unknown: '#6c757d',
|
||||
},
|
||||
selection: '#ff8c00',
|
||||
};
|
||||
|
||||
function getRoleColor(role) {
|
||||
if (!role) return colors.role.unknown;
|
||||
return colors.role[role] || colors.role.other;
|
||||
}
|
||||
|
||||
|
||||
function getSymbolSize (role) {
|
||||
switch (role) {
|
||||
case 'ROUTER': return 30;
|
||||
case 'ROUTER_LATE': return 30;
|
||||
case 'CLIENT': return 15;
|
||||
case 'CLIENT_MUTE': return 7;
|
||||
default: return 15; // Unknown or other roles
|
||||
}
|
||||
}
|
||||
|
||||
function getLabel (role, short_name, long_name) {
|
||||
if (role === 'ROUTER') return long_name;
|
||||
if (role === 'ROUTER_LATE') return long_name;
|
||||
if (role === 'CLIENT') return short_name;
|
||||
if (role === 'CLIENT_MUTE') return short_name;
|
||||
return short_name || '';
|
||||
}
|
||||
|
||||
// --- Nodes ---
|
||||
const nodes = [
|
||||
{% for node in nodes %}
|
||||
{
|
||||
name: "{{ node.node_id }}", // node_id as string
|
||||
value: {{ node.long_name | tojson }}, // display label
|
||||
value: getLabel({{node.role | tojson}}, {{node.short_name | tojson }}, {{node.long_name | tojson}}), // display label
|
||||
symbol: 'circle',
|
||||
symbolSize: 15,
|
||||
itemStyle: { color:'#007bff', opacity:1 },
|
||||
symbolSize: getSymbolSize({{node.role | tojson}}),
|
||||
itemStyle: { color: getRoleColor({{node.role | tojson}}), opacity:1 },
|
||||
label: { show:true, position:'right', color:'#333', fontSize:12, formatter: (p)=>p.data.value },
|
||||
long_name: {{ node.long_name | tojson }},
|
||||
short_name: {{ node.short_name | tojson }},
|
||||
role: {{ node.role | tojson }},
|
||||
hw_model: {{ node.hw_model | tojson }},
|
||||
channel: {{ node.channel | tojson }}
|
||||
}{% if not loop.last %},{% endif %}
|
||||
},
|
||||
{% endfor %}
|
||||
];
|
||||
|
||||
@@ -122,8 +187,12 @@ const edges = [
|
||||
{
|
||||
source: "{{ edge.from }}", // edge source as string
|
||||
target: "{{ edge.to }}", // edge target as string
|
||||
originalColor: "{{ edge.originalColor }}"
|
||||
}{% if not loop.last %},{% endif %}
|
||||
originalColor: colors.edge[{{edge.type | tojson}}],
|
||||
lineStyle: {
|
||||
color: colors.edge[{{edge.type | tojson}}],
|
||||
width: {{edge.weight | tojson}},
|
||||
},
|
||||
},
|
||||
{% endfor %}
|
||||
];
|
||||
|
||||
@@ -156,24 +225,27 @@ function filterByChannel() {
|
||||
|
||||
function updateChart() {
|
||||
const updatedNodes = filteredNodes.map(node=>{
|
||||
let opacity=1, color='#007bff';
|
||||
let opacity=1, color=getRoleColor(node.role), borderColor='transparent', borderWidth=6;
|
||||
if(lastSelectedNode){
|
||||
const connected = filteredEdges.some(e=>
|
||||
(e.source===node.name && e.target===lastSelectedNode) ||
|
||||
(e.target===node.name && e.source===lastSelectedNode)
|
||||
);
|
||||
if(node.name===lastSelectedNode){ color='#ff8c00'; opacity=1; }
|
||||
if(node.name === lastSelectedNode) {
|
||||
opacity=1;
|
||||
borderColor=colors.selection;
|
||||
}
|
||||
else if(connected) opacity=1;
|
||||
else opacity=0.4;
|
||||
}
|
||||
return {...node, itemStyle:{color,opacity}};
|
||||
return {...node, itemStyle:{...node.itemStyle, color,opacity, borderColor, borderWidth}};
|
||||
});
|
||||
|
||||
const updatedEdges = filteredEdges.map(edge=>{
|
||||
let opacity=0.1, width=2;
|
||||
let opacity=0.1, width=edge.lineStyle.width;
|
||||
if(lastSelectedNode){
|
||||
const connected = edge.source===lastSelectedNode || edge.target===lastSelectedNode;
|
||||
opacity=connected?1:0.05; width=connected?2:1;
|
||||
opacity=connected?1:0.05; width=edge.lineStyle.width;
|
||||
}
|
||||
return {...edge, lineStyle:{color:edge.originalColor||'#d3d3d3', width, opacity}};
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ from datetime import timedelta
|
||||
import json
|
||||
import os
|
||||
import ssl
|
||||
from collections import Counter
|
||||
from collections import Counter, defaultdict
|
||||
from dataclasses import dataclass
|
||||
import pydot
|
||||
from google.protobuf import text_format
|
||||
@@ -1234,8 +1234,7 @@ async def chat(request):
|
||||
async def nodegraph(request):
|
||||
nodes = await store.get_nodes(days_active=3) # Fetch nodes for the given channel
|
||||
node_ids = set()
|
||||
edges_set = set() # Track unique edges
|
||||
edge_type = {} # Store type of each edge
|
||||
edges_map = defaultdict(lambda: { "weight": 0, "type": None }) # weight is based on the number of traceroutes and neighbor info packets
|
||||
used_nodes = set() # This will track nodes involved in edges (including traceroutes)
|
||||
since = datetime.timedelta(hours=48)
|
||||
traceroutes = []
|
||||
@@ -1260,8 +1259,8 @@ async def nodegraph(request):
|
||||
# Add traceroute edges with their type and update used_nodes
|
||||
for i in range(len(path) - 1):
|
||||
edge_pair = (path[i], path[i + 1])
|
||||
edges_set.add(edge_pair)
|
||||
edge_type[edge_pair] = "traceroute"
|
||||
edges_map[edge_pair]["weight"] += 1
|
||||
edges_map[edge_pair]["type"] = "traceroute"
|
||||
used_nodes.add(path[i]) # Add all nodes in the traceroute path
|
||||
used_nodes.add(path[i + 1]) # Add all nodes in the traceroute path
|
||||
|
||||
@@ -1276,24 +1275,21 @@ async def nodegraph(request):
|
||||
used_nodes.add(node.node_id)
|
||||
|
||||
edge_pair = (node.node_id, packet.from_node_id)
|
||||
if edge_pair not in edges_set:
|
||||
edges_set.add(edge_pair)
|
||||
edge_type[edge_pair] = "neighbor"
|
||||
edges_map[edge_pair]["weight"] += 1
|
||||
edges_map[edge_pair]["type"] = "neighbor" # Overrides an existing traceroute pairing with neighbor
|
||||
except Exception as e:
|
||||
print(f"Error decoding NeighborInfo packet: {e}")
|
||||
|
||||
# Convert edges_set to a list of dicts with colors
|
||||
# Convert edges_map to a list of dicts with colors
|
||||
max_weight = max(i['weight'] for i in edges_map.values()) if edges_map else 1
|
||||
edges = [
|
||||
{
|
||||
"from": frm,
|
||||
"to": to,
|
||||
"originalColor": "#ff5733" if edge_type[(frm, to)] == "traceroute" else "#3388ff", # Red for traceroute, Blue for neighbor
|
||||
"lineStyle": {
|
||||
"color": "#ff5733" if edge_type[(frm, to)] == "traceroute" else "#3388ff",
|
||||
"width": 2
|
||||
}
|
||||
"type": info["type"],
|
||||
"weight": max([info['weight'] / float(max_weight) * 10, 1]),
|
||||
}
|
||||
for frm, to in edges_set
|
||||
for (frm, to), info in edges_map.items()
|
||||
]
|
||||
|
||||
# Filter nodes to only include those involved in edges (including traceroutes)
|
||||
|
||||
Reference in New Issue
Block a user