e1608: probe and docs for the network scan
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
24bdd6b930
commit
dbf145094a
8 files changed
+189
-5
No files matched your search
@@ -6031,6 +6031,9 @@ int run_ui(int argc, char** argv, const Config& cfg, ScopeModel& model, Acquisit
|
||||
if (const char* env = g_getenv("UDPSCOPE_PROBE_E1608_RECONNECT")) {
|
||||
g_timeout_add(static_cast<guint>(std::atoi(env)), e1608_probe_reconnect, &app);
|
||||
}
|
||||
if (const char* env = g_getenv("UDPSCOPE_PROBE_E1608_SCAN")) {
|
||||
g_timeout_add(static_cast<guint>(std::atoi(env)), e1608_scan_probe, &app);
|
||||
}
|
||||
if (const char* env = g_getenv("UDPSCOPE_PROBE_FLATUDP")) {
|
||||
g_timeout_add(static_cast<guint>(std::atoi(env)), flatudp_probe, &app);
|
||||
}
|
||||
|
||||
@@ -1873,6 +1873,12 @@ gboolean e1608_probe(gpointer user);
|
||||
gboolean e1608_probe_report(gpointer user);
|
||||
gboolean e1608_probe_reconnect(gpointer user);
|
||||
gboolean e1608_probe_resumed(gpointer user);
|
||||
/// The E-1608 form's "Scan network" row: presses Scan against the mock, then (phases `_fill`,
|
||||
/// `_rescan`) clicks a result to fill the address, "Use MAC" to fill the MAC, scans again and checks
|
||||
/// the results were replaced, not added to.
|
||||
gboolean e1608_scan_probe(gpointer user);
|
||||
gboolean e1608_scan_probe_fill(gpointer user);
|
||||
gboolean e1608_scan_probe_rescan(gpointer user);
|
||||
/// The flat UDP (MARTe2 UDPSender) source: the signal-list codec and a hand-computed decode, then,
|
||||
/// against `tools/mock_flatudp.py`, the stream, the value continuity, the wrong-size accounting and
|
||||
/// the capture button (`_report`, a second later). `_gap` starts a 100 ms poll that waits for the
|
||||
|
||||
@@ -2458,6 +2458,154 @@ gboolean e1608_probe_reconnect(gpointer user) {
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/// The scan probe's state, alive from phase 1 to phase 3.
|
||||
struct E1608ScanProbe {
|
||||
ConfigForm form;
|
||||
int changes = 0; ///< how many times the form told the dialog it changed
|
||||
std::string want_mac; ///< the board the probe expects (the mock's by default)
|
||||
std::string status; ///< the scan row's subtitle, as read at the end of the first scan
|
||||
std::string ip; ///< the expected board's address, as listed
|
||||
std::size_t first = 0; ///< result rows after the first scan
|
||||
bool listed = false; ///< the expected board was among them
|
||||
bool fill_addr = false;
|
||||
bool fill_mac = false;
|
||||
};
|
||||
|
||||
E1608ScanProbe* g_scan_probe = nullptr;
|
||||
|
||||
void collect_named(GtkWidget* root, const char* name, std::vector<GtkWidget*>& out) {
|
||||
if (root == nullptr) return;
|
||||
if (std::strcmp(gtk_widget_get_name(root), name) == 0) out.push_back(root);
|
||||
for (GtkWidget* c = gtk_widget_get_first_child(root); c != nullptr;
|
||||
c = gtk_widget_get_next_sibling(c))
|
||||
collect_named(c, name, out);
|
||||
}
|
||||
|
||||
std::string scan_status(E1608ScanProbe& p) {
|
||||
GtkWidget* row = find_named(p.form.rows[0], "e1608-scan-row");
|
||||
const char* t = row != nullptr ? adw_expander_row_get_subtitle(ADW_EXPANDER_ROW(row)) : nullptr;
|
||||
return t != nullptr ? t : "";
|
||||
}
|
||||
|
||||
/// A form holding stale values in all three naming fields, so a fill is seen to clear the others.
|
||||
void scan_probe_stale(E1608ScanProbe& p) {
|
||||
SourceConfig c;
|
||||
c.set("address", "1.2.3.4");
|
||||
c.set("mac", "aa:bb:cc:dd:ee:ff");
|
||||
c.set("subnet", "10.0.0");
|
||||
p.form.write(c);
|
||||
p.changes = 0;
|
||||
}
|
||||
|
||||
void scan_probe_click(E1608ScanProbe& p) {
|
||||
GtkWidget* button = find_named(p.form.rows[0], "e1608-scan");
|
||||
if (button != nullptr) g_signal_emit_by_name(button, "clicked");
|
||||
}
|
||||
|
||||
void scan_probe_cleanup() {
|
||||
if (g_scan_probe == nullptr) return;
|
||||
for (GtkWidget* row : g_scan_probe->form.rows) {
|
||||
g_object_ref_sink(row);
|
||||
g_object_unref(row);
|
||||
}
|
||||
delete g_scan_probe;
|
||||
g_scan_probe = nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/// Phase 3, 2 s after the second Scan: the results were replaced (same count), and the one line.
|
||||
gboolean e1608_scan_probe_rescan(gpointer user) {
|
||||
App* a = static_cast<App*>(user);
|
||||
if (a->window == nullptr || g_scan_probe == nullptr) return G_SOURCE_REMOVE;
|
||||
E1608ScanProbe& p = *g_scan_probe;
|
||||
std::vector<GtkWidget*> rows;
|
||||
collect_named(p.form.rows[0], "e1608-result", rows);
|
||||
const bool replaced = rows.size() == p.first;
|
||||
const std::string status = scan_status(p);
|
||||
const bool status_ok = status.find("found") != std::string::npos && status.rfind("No", 0) != 0;
|
||||
const bool ok = p.listed && p.fill_addr && p.fill_mac && replaced && status_ok;
|
||||
std::fprintf(stderr,
|
||||
"[e1608] scan: found=%zu ip=%s mac=%s status='%s' fill_addr=%d fill_mac=%d "
|
||||
"rescan_replaced=%d ok=%d\n",
|
||||
p.first, p.ip.c_str(), p.want_mac.c_str(), p.status.c_str(), p.fill_addr ? 1 : 0,
|
||||
p.fill_mac ? 1 : 0, replaced ? 1 : 0, ok ? 1 : 0);
|
||||
if (!p.listed)
|
||||
std::fprintf(stderr,
|
||||
"[e1608] scan: %s did not answer; no up broadcast interface reaches it, or the "
|
||||
"broadcast was refused (this run proves nothing about the scan)\n",
|
||||
p.want_mac.c_str());
|
||||
std::fflush(stderr);
|
||||
scan_probe_cleanup();
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
/// Phase 2, 2 s after the first Scan: the expected board is listed, a result click fills the address
|
||||
/// only, "Use MAC" fills the MAC only (each telling the dialog exactly once), then Scan again.
|
||||
gboolean e1608_scan_probe_fill(gpointer user) {
|
||||
App* a = static_cast<App*>(user);
|
||||
if (a->window == nullptr || g_scan_probe == nullptr) return G_SOURCE_REMOVE;
|
||||
E1608ScanProbe& p = *g_scan_probe;
|
||||
p.status = scan_status(p);
|
||||
std::vector<GtkWidget*> rows;
|
||||
collect_named(p.form.rows[0], "e1608-result", rows);
|
||||
p.first = rows.size();
|
||||
GtkWidget* mine = nullptr;
|
||||
for (GtkWidget* r : rows) {
|
||||
const char* sub = adw_action_row_get_subtitle(ADW_ACTION_ROW(r));
|
||||
if (sub != nullptr && p.want_mac == sub) mine = r;
|
||||
}
|
||||
p.listed = mine != nullptr;
|
||||
if (mine != nullptr) {
|
||||
const char* title = adw_preferences_row_get_title(ADW_PREFERENCES_ROW(mine));
|
||||
p.ip = title != nullptr ? title : "";
|
||||
|
||||
scan_probe_stale(p);
|
||||
g_signal_emit_by_name(mine, "activated");
|
||||
const SourceConfig by_addr = p.form.read();
|
||||
p.fill_addr = by_addr.get("address") == p.ip && by_addr.get("mac").empty() &&
|
||||
by_addr.get("subnet").empty() && p.changes == 1;
|
||||
|
||||
scan_probe_stale(p);
|
||||
GtkWidget* use = find_named(mine, "e1608-use-mac");
|
||||
if (use != nullptr) g_signal_emit_by_name(use, "clicked");
|
||||
const SourceConfig by_mac = p.form.read();
|
||||
p.fill_mac = by_mac.get("mac") == p.want_mac && by_mac.get("address").empty() &&
|
||||
by_mac.get("subnet").empty() && p.changes == 1;
|
||||
}
|
||||
scan_probe_click(p);
|
||||
g_timeout_add(2000, e1608_scan_probe_rescan, a);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
/// Phase 1: the form, Scan pressed (the row must read `Scanning...` at once), phase 2 in 2 s.
|
||||
gboolean e1608_scan_probe(gpointer user) {
|
||||
App* a = static_cast<App*>(user);
|
||||
if (a->window == nullptr) return G_SOURCE_REMOVE;
|
||||
SourceKind* k = find_kind("e1608");
|
||||
if (k == nullptr) {
|
||||
std::fprintf(stderr, "[e1608] scan: no e1608 kind\n");
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
g_scan_probe = new E1608ScanProbe;
|
||||
E1608ScanProbe& p = *g_scan_probe;
|
||||
p.form = k->make_config_form();
|
||||
const char* env = g_getenv("UDPSCOPE_PROBE_E1608_SCAN_MAC");
|
||||
p.want_mac = env != nullptr ? env : "00:80:2f:00:16:08";
|
||||
p.form.on_change([]() {
|
||||
if (g_scan_probe != nullptr) ++g_scan_probe->changes;
|
||||
});
|
||||
scan_probe_click(p);
|
||||
const std::string now = scan_status(p);
|
||||
if (now != "Scanning...")
|
||||
std::fprintf(stderr, "[e1608] scan: status right after Scan reads '%s', expected 'Scanning...'\n",
|
||||
now.c_str());
|
||||
g_timeout_add(2000, e1608_scan_probe_fill, a);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
/// The mock's channels, as the model must list them: a scalar `Name`, the elements of a `channels`
|
||||
/// vector `Name[k]`, a waveform `Name` at `rate * count`.
|
||||
struct FlatudpExpect {
|
||||
|
||||
Reference in new issue
Block a user