mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-05-21 23:24:44 +02:00
feat: Tick-based staggered polling for all sources
Both MeshviewSource and MeshMonitorDataSource now use tick-based staggered polling instead of batch-every-5-minutes: MeshviewSource (30s ticks): - Packets: every tick (30s) - Nodes: every 4 ticks (2 min) - Stats/Edges: every 6 ticks (3 min) - Traceroutes: every 10 ticks (5 min) MeshMonitorDataSource (30s ticks): - Packets: every 2 ticks (60s) - Nodes/Telemetry: every 4 ticks (2 min) - Traceroutes/Channels/Network/Topology: every 10 ticks (5 min) - Solar: every 20 ticks (10 min) Features: - Source health status (avg_response_ms, tick_count, backed_off) - Source coverage analysis (unique vs shared nodes) - Tier 1 DATA SOURCES section shows all source health - Node detail shows source visibility - Incremental packets and telemetry with dedup - Rate limit detection (429) with backoff - Consecutive error exponential backoff - polite_mode config option for shared instances Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
bc644b3ac2
commit
b3c79f12da
5 changed files with 1137 additions and 473 deletions
|
|
@ -36,8 +36,9 @@ PORTNUM_DISPLAY = {
|
|||
"ATAK_FORWARDER": "ATAK",
|
||||
}
|
||||
|
||||
def _clean_portnum(portnum: str) -> str:
|
||||
def _clean_portnum(portnum) -> str:
|
||||
"""Convert raw portnum to display name."""
|
||||
if isinstance(portnum, int): portnum = str(portnum)
|
||||
return PORTNUM_DISPLAY.get(portnum, portnum.replace("_APP", "").replace("_", " ").title())
|
||||
|
||||
|
||||
|
|
@ -175,6 +176,34 @@ class MeshReporter:
|
|||
return f"{local} ({desc})"
|
||||
return local or desc
|
||||
|
||||
|
||||
def _build_source_health_section(self) -> list[str]:
|
||||
"""Build source health section for Tier 1."""
|
||||
lines = []
|
||||
lines.append("")
|
||||
lines.append("DATA SOURCES:")
|
||||
|
||||
for name, source in self.data_store._sources.items():
|
||||
if hasattr(source, 'health_status'):
|
||||
status = source.health_status
|
||||
err_str = f" - {status['last_error']}" if status.get('last_error') else ""
|
||||
backed = " [BACKED OFF]" if status.get('backed_off') else ""
|
||||
polite = " [POLITE]" if status.get('polite_mode') else ""
|
||||
lines.append(
|
||||
f" {name}: {status.get('cached_nodes', 0)} nodes, "
|
||||
f"{status.get('cached_packets', 0)} pkts, "
|
||||
f"avg {status.get('avg_response_ms', 0)}ms"
|
||||
f"{polite}{backed}{err_str}"
|
||||
)
|
||||
else:
|
||||
# Legacy source without health_status
|
||||
node_count = len(source.nodes) if hasattr(source, 'nodes') else 0
|
||||
loaded = "OK" if source.is_loaded else "ERR"
|
||||
err = f" - {source.last_error}" if source.last_error else ""
|
||||
lines.append(f" {name}: [{loaded}] {node_count} nodes{err}")
|
||||
|
||||
return lines
|
||||
|
||||
def build_tier1_summary(self) -> str:
|
||||
"""Build comprehensive mesh health summary with full data for LLM context."""
|
||||
health = self.health_engine.mesh_health
|
||||
|
|
@ -428,6 +457,9 @@ class MeshReporter:
|
|||
if pb["critical"]: parts.append(f"{pb['critical']} battery critical")
|
||||
lines.append(f"POWER (infra): {', '.join(parts)}")
|
||||
|
||||
# Source health section
|
||||
lines.extend(self._build_source_health_section())
|
||||
|
||||
lines.append("")
|
||||
lines.append(f"TOTAL: {health.total_nodes} nodes across {health.total_regions} regions.")
|
||||
|
||||
|
|
@ -899,6 +931,10 @@ class MeshReporter:
|
|||
status = "Single gateway - node goes dark if that gateway fails"
|
||||
lines.append(f" Coverage: {node.avg_gateways:.0f}/{total_gw} gateways ({pct:.0f}%) - {status}")
|
||||
|
||||
# Source visibility
|
||||
if node.sources:
|
||||
lines.append(f" Seen by: {', '.join(node.sources)} ({len(node.sources)} sources)")
|
||||
|
||||
# Neighbors section
|
||||
if node.neighbors:
|
||||
lines.append("")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue