Repository navigation
fix: diagnostics multi device discovery - #3
Conversation
There was a problem hiding this comment.
Pull request overview
This PR improves multi-device diagnostics discovery in the diag_scanner example by querying each newly discovered device directly (via explicit address) and adds a Runtime::request(...) overload to support explicit-address requests without relying on service discovery.
Changes:
- Added
Runtime::request(const Addr& target, ...)to send requests to a specific peer address, bypassing SD provider selection. - Updated
diag_scannerto query devices immediately on discovery and to use explicit-address requests for per-device polling and DTC clearing. - Enabled the built-in diagnostics service in
desktop_peerso it can be discovered and queried by scanners.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| include/sero/runtime.hpp | Adds explicit-target request overload to bypass service discovery for multi-provider scenarios. |
| examples/diag_scanner.cpp | Refactors discovery/query loop to query each discovered device directly and adjust dashboard timing logic. |
| examples/desktop_peer.cpp | Enables built-in diagnostics service during startup. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| app.last_tick_ms = now; | ||
| app.last_diag_ms = now; | ||
|
|
||
| rt.enable_diagnostics(now); |
There was a problem hiding this comment.
enable_diagnostics() returns bool and can fail (e.g., if service registration or offer fails). The return value is currently ignored, so the example can silently run without diagnostics being discoverable. Consider checking the return value and printing an error / exiting early if diagnostics can’t be enabled.
| rt.enable_diagnostics(now); | |
| if (!rt.enable_diagnostics(now)) { | |
| std::fprintf(stderr, "[main] Failed to enable diagnostics; diagnostics will be unavailable.\n"); | |
| } |
| // Use the explicit-address overload so each device gets its own requests, | ||
| // regardless of which provider the SD layer currently tracks. | ||
| g_rt->request(dev.addr, sero::DIAG_SERVICE_ID, | ||
| static_cast<uint16_t>(DM::DIAG_GET_DEVICE_INFO), | ||
| nullptr, 0, on_device_info, &dev, REQUEST_TIMEOUT_MS, now_ms); | ||
|
|
There was a problem hiding this comment.
query_device uses &dev (pointer to an element inside the global std::vector<DiscoveredDevice> devices) as the request callback context. If another device is discovered while requests are still pending, a subsequent devices.push_back() can reallocate and move elements, invalidating those pointers and leading to use-after-free in the response callbacks. Use stable storage (e.g., std::deque / std::list / std::vector<std::unique_ptr<...>>) or pass a stable identifier (e.g., the device Addr) and look up the device inside the callback.
| auto rid = request_tracker_.allocate(service_id, method_id, | ||
| timeout_ms, now_ms, | ||
| callback, user_ctx); | ||
| if (!rid) return std::nullopt; // table full | ||
|
|
||
| bool auth = should_auth_outgoing(target); | ||
| MessageHeader hdr = make_request_header(service_id, method_id, | ||
| payload_length, *rid, auth); | ||
| hdr.sequence_counter = e2e_.next_sequence(); | ||
| std::size_t total = build_message(hdr, payload, payload_length, auth, target); | ||
| if (!transport_.send(target, tx_buffer_, total)) { | ||
| request_tracker_.complete(*rid, ReturnCode::E_NOT_OK, nullptr, 0); | ||
| diag_.increment(DiagnosticCounter::DroppedMessages); | ||
| return std::nullopt; | ||
| } | ||
| return rid; |
There was a problem hiding this comment.
This new request(const Addr& target, ...) overload duplicates the send/build/tracking logic from the existing request(service_id, ...) method. To reduce the risk of future behavior drift (e.g., diagnostics counters, auth flags, header fields, failure handling), consider refactoring both overloads to share a single internal helper that takes the resolved destination address.
| /// Send a REQUEST to an explicit target address, bypassing SD lookup. | ||
| /// Use this when you manage multiple providers of the same service_id | ||
| /// (e.g. a diagnostic scanner querying each discovered device directly). | ||
| std::optional<uint32_t> request(const Addr& target, | ||
| uint16_t service_id, | ||
| uint16_t method_id, | ||
| const uint8_t* payload, | ||
| std::size_t payload_length, | ||
| RequestCallback callback, | ||
| void* user_ctx, | ||
| uint32_t timeout_ms, | ||
| uint32_t now_ms) | ||
| { | ||
| auto rid = request_tracker_.allocate(service_id, method_id, | ||
| timeout_ms, now_ms, | ||
| callback, user_ctx); | ||
| if (!rid) return std::nullopt; // table full | ||
|
|
||
| bool auth = should_auth_outgoing(target); | ||
| MessageHeader hdr = make_request_header(service_id, method_id, | ||
| payload_length, *rid, auth); | ||
| hdr.sequence_counter = e2e_.next_sequence(); | ||
| std::size_t total = build_message(hdr, payload, payload_length, auth, target); | ||
| if (!transport_.send(target, tx_buffer_, total)) { | ||
| request_tracker_.complete(*rid, ReturnCode::E_NOT_OK, nullptr, 0); | ||
| diag_.increment(DiagnosticCounter::DroppedMessages); | ||
| return std::nullopt; | ||
| } | ||
| return rid; | ||
| } |
There was a problem hiding this comment.
The new request(const Addr& target, ...) overload isn’t covered by existing unit tests (current test/test_runtime.cpp only exercises the SD-resolving request(service_id, ...)). Adding a test that verifies this overload sends to the provided target even when SD has no provider (and that responses complete the correct callback) would help prevent regressions.
|
@copilot open a new pull request to apply changes based on the comments in this thread |
|
@mad201802 I've opened a new pull request, #4, to work on those changes. Once the pull request is ready, I'll request review from you. |
* Initial plan * fix: address review feedback - check enable_diagnostics return, stable storage, request_impl refactor, new tests Co-authored-by: mad201802 <45396189+mad201802@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mad201802 <45396189+mad201802@users.noreply.github.com>
This pull request introduces improvements to device querying and dashboard rendering in the diagnostics scanner example, and adds support for explicit-address requests in the runtime. The changes ensure each discovered device is queried directly and promptly, resulting in more accurate and timely dashboard updates. Additionally, the codebase is refactored for clarity and robustness in service discovery and request handling.
Diagnostics scanner improvements:
query_devicewith the explicit device address, ensuring data is available quickly and dashboard updates are timely.g_last_scan_ms,g_dashboard_printed) for more reliable timing and single dashboard render per cycle. [1] [2] [3]Service discovery robustness:
find_serviceto keep discovery live and responsive to newly appearing devices.Runtime API enhancement:
requestmethod inRuntime(runtime.hpp) that allows sending requests to an explicit target address, bypassing service discovery. This is essential for scenarios with multiple providers of the same service ID.These changes collectively make the diagnostics scanner more responsive and accurate, and extend the runtime with a useful new capability for direct device communication.