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