mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-05-01 11:02:56 +02:00
Add multibyte trace output. Closes #127.
This commit is contained in:
@@ -40,6 +40,10 @@ logger = logging.getLogger(__name__)
|
||||
router = APIRouter(prefix="/contacts", tags=["contacts"])
|
||||
|
||||
|
||||
TRACE_HASH_BYTES = 4
|
||||
TRACE_FLAGS_4BYTE = 2
|
||||
|
||||
|
||||
def _ambiguous_contact_detail(err: AmbiguousPublicKeyPrefixError) -> str:
|
||||
sample = ", ".join(key[:12] for key in err.matches[:2])
|
||||
return (
|
||||
@@ -373,17 +377,17 @@ async def delete_contact(public_key: str) -> dict:
|
||||
async def request_trace(public_key: str) -> TraceResponse:
|
||||
"""Send a single-hop trace to a contact and wait for the result.
|
||||
|
||||
The trace path contains the contact's 1-byte pubkey hash as the sole hop
|
||||
(no intermediate repeaters). The radio firmware requires at least one
|
||||
node in the path.
|
||||
The trace path contains the contact's 4-byte pubkey hash as the sole hop
|
||||
(no intermediate repeaters). This uses TRACE's dedicated width flags rather
|
||||
than the radio's normal path_hash_mode setting.
|
||||
"""
|
||||
require_connected()
|
||||
|
||||
contact = await _resolve_contact_or_404(public_key)
|
||||
|
||||
tag = random.randint(1, 0xFFFFFFFF)
|
||||
# First 2 hex chars of pubkey = 1-byte hash used by the trace protocol
|
||||
contact_hash = contact.public_key[:2]
|
||||
# Use a 4-byte contact hash for low-collision direct trace targeting.
|
||||
contact_hash = contact.public_key[: TRACE_HASH_BYTES * 2]
|
||||
|
||||
# Trace does not need auto-fetch suspension: response arrives as TRACE_DATA
|
||||
# from the reader loop, not via get_msg().
|
||||
@@ -394,7 +398,11 @@ async def request_trace(public_key: str) -> TraceResponse:
|
||||
logger.info(
|
||||
"Sending trace to %s (tag=%d, hash=%s)", contact.public_key[:12], tag, contact_hash
|
||||
)
|
||||
result = await mc.commands.send_trace(path=contact_hash, tag=tag)
|
||||
result = await mc.commands.send_trace(
|
||||
path=contact_hash,
|
||||
tag=tag,
|
||||
flags=TRACE_FLAGS_4BYTE,
|
||||
)
|
||||
|
||||
if result.type == EventType.ERROR:
|
||||
raise HTTPException(status_code=500, detail=f"Failed to send trace: {result.payload}")
|
||||
|
||||
@@ -268,7 +268,7 @@ export function ChatHeader({
|
||||
title={
|
||||
activeContactIsPrefixOnly
|
||||
? 'Direct Trace unavailable until the full contact key is known'
|
||||
: 'Direct Trace. Send a zero-hop packet to this contact and display out and back SNR'
|
||||
: 'Direct Trace. Send a direct trace probe to this contact and display out and back SNR'
|
||||
}
|
||||
aria-label="Direct Trace"
|
||||
disabled={activeContactIsPrefixOnly}
|
||||
|
||||
@@ -81,13 +81,14 @@ export function PathModal({
|
||||
) : hasSinglePath ? (
|
||||
<>
|
||||
This shows <em>one route</em> that this message traveled through the mesh network.
|
||||
Repeaters may be incorrectly identified due to prefix collisions between heard and
|
||||
non-heard repeater advertisements.
|
||||
Repeater identities are inferred from locally known advert and path data, so some
|
||||
hops may be missing or misidentified when that data is incomplete.
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
This message was received via <strong>{paths.length} different routes</strong>.
|
||||
Repeaters may be incorrectly identified due to prefix collisions.
|
||||
Repeater identities are inferred from locally known advert and path data, so some
|
||||
hops may be missing or misidentified when that data is incomplete.
|
||||
</>
|
||||
)}
|
||||
</DialogDescription>
|
||||
|
||||
@@ -483,6 +483,11 @@ class TestTraceRoute:
|
||||
await request_trace(KEY_A)
|
||||
|
||||
assert exc.value.status_code == 500
|
||||
mc.commands.send_trace.assert_awaited_once_with(
|
||||
path=KEY_A[:8],
|
||||
tag=1234,
|
||||
flags=2,
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_wait_timeout_returns_504(self, test_db):
|
||||
@@ -500,6 +505,11 @@ class TestTraceRoute:
|
||||
await request_trace(KEY_A)
|
||||
|
||||
assert exc.value.status_code == 504
|
||||
mc.commands.send_trace.assert_awaited_once_with(
|
||||
path=KEY_A[:8],
|
||||
tag=1234,
|
||||
flags=2,
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_success_returns_remote_and_local_snr(self, test_db):
|
||||
@@ -520,6 +530,11 @@ class TestTraceRoute:
|
||||
assert response.remote_snr == 5.5
|
||||
assert response.local_snr == 3.2
|
||||
assert response.path_len == 2
|
||||
mc.commands.send_trace.assert_awaited_once_with(
|
||||
path=KEY_A[:8],
|
||||
tag=1234,
|
||||
flags=2,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user