153 lines
10 KiB
C++
153 lines
10 KiB
C++
/* Ручной тест Windows: тот же backend, один publisher и неизменный scan; меняется только GATT-реклама.
|
|
* Запускать при закрытом VibeChat. Не является транспортом: входящие соединения отклоняются. */
|
|
#include "../ble_winrt.cpp"
|
|
#include <cstdlib>
|
|
|
|
struct ble_transport { bool ready = false, failed = false; };
|
|
extern "C" void ble_backend_status(ble_transport* t, int ready, const char* reason) {
|
|
DEBUG_INFO(CAT, "BLE advertising probe radio ready=%d reason=%s", ready, reason);
|
|
t->ready = ready; if (!ready) t->failed = true;
|
|
}
|
|
extern "C" void ble_backend_advertising(ble_transport*, ble_state state, const char* reason) {
|
|
DEBUG_INFO(CAT, "BLE advertising probe publication state=%d reason=%s", state, reason);
|
|
}
|
|
extern "C" ble_link_id ble_backend_incoming(ble_transport*, const ble_endpoint* ep) {
|
|
DEBUG_WARN(CAT, "BLE advertising probe rejects incoming connection endpoint=%s; discovery test only", ep->id); return 0;
|
|
}
|
|
extern "C" void ble_backend_broadcast(ble_transport*, const ble_endpoint*, const uint8_t*, size_t, int) {}
|
|
extern "C" void ble_backend_closed(ble_transport* t, ble_link_id, const char*) {
|
|
DEBUG_ERROR(CAT, "BLE advertising probe unexpected connection close"); t->failed = true;
|
|
}
|
|
extern "C" void ble_backend_opened(ble_transport* t, ble_link_id, unsigned) {
|
|
DEBUG_ERROR(CAT, "BLE advertising probe unexpected connection open"); t->failed = true;
|
|
}
|
|
extern "C" void ble_backend_received(ble_transport* t, ble_link_id, ble_wire_channel, const uint8_t*, size_t) {
|
|
DEBUG_ERROR(CAT, "BLE advertising probe unexpected connection data"); t->failed = true;
|
|
}
|
|
extern "C" void ble_backend_sent(ble_transport* t, ble_link_id, int) {
|
|
DEBUG_ERROR(CAT, "BLE advertising probe unexpected connection send"); t->failed = true;
|
|
}
|
|
|
|
/* Только probe: Windows 1809 отвечает на наш StopAdvertising событием Aborted/Success.
|
|
* При работающем GATT либо ненулевом BluetoothError такой ответ остаётся ошибкой. */
|
|
struct ProbeGatt {
|
|
bool requested = true, stop_confirmed = false;
|
|
GattServiceProviderAdvertisementStatus last = GattServiceProviderAdvertisementStatus::Created;
|
|
void changed(WinState& s, GattServiceProviderAdvertisementStatus status, BluetoothError error) {
|
|
last = status;
|
|
DEBUG_INFO(CAT, "BLE advertising probe GATT event requested=%d state=%d error=%d (%s)",
|
|
requested, int(status), int(error), bluetooth_error(error));
|
|
if (error != BluetoothError::Success) { s.radio_error("probe GATT advertising", error); return; }
|
|
if (!requested && (status == GattServiceProviderAdvertisementStatus::Stopped ||
|
|
status == GattServiceProviderAdvertisementStatus::Aborted)) {
|
|
stop_confirmed = true; s.service_ready = false;
|
|
DEBUG_INFO(CAT, "BLE advertising probe requested GATT stop confirmed state=%d error=0", int(status));
|
|
} else if (status == GattServiceProviderAdvertisementStatus::Started) {
|
|
s.service_ready = true;
|
|
} else if (status == GattServiceProviderAdvertisementStatus::Aborted) {
|
|
s.radio_error("probe unexpected GATT advertising abort", error);
|
|
}
|
|
}
|
|
bool matches(int actual) const {
|
|
if (requested) return last == GattServiceProviderAdvertisementStatus::Started && actual == int(last);
|
|
return stop_confirmed && (actual == int(GattServiceProviderAdvertisementStatus::Stopped) ||
|
|
actual == int(GattServiceProviderAdvertisementStatus::Aborted));
|
|
}
|
|
};
|
|
|
|
/* Даём ОС всё окно наблюдения, даже после Started: важен также возврат в Waiting. */
|
|
static bool observe(WinState& s, ble_transport& owner, const ProbeGatt& gatt, const char* phase, unsigned seconds) {
|
|
const auto start = get_time_us(), end = start + uint64_t(seconds) * 1000000;
|
|
uint64_t first_started_ms = 0;
|
|
unsigned started = 0, waiting = 0, unsettled = 0;
|
|
WinState::RadioStatus previous;
|
|
DEBUG_INFO(CAT, "BLE advertising probe begin phase=%s seconds=%u gatt_requested=%d generation=%llu",
|
|
phase, seconds, gatt.requested, (unsigned long long)s.publish_generation);
|
|
do {
|
|
uasync_poll(s.ua, 100);
|
|
if (s.failed || owner.failed) { DEBUG_ERROR(CAT, "BLE advertising probe phase=%s backend failed", phase); return false; }
|
|
auto actual = s.radio_status();
|
|
if (actual.publisher < 0 || actual.service < 0 || actual.scan < 0) {
|
|
DEBUG_ERROR(CAT, "BLE advertising probe phase=%s cannot read radio state", phase); return false;
|
|
}
|
|
if (actual.publisher != previous.publisher || actual.service != previous.service || actual.scan != previous.scan)
|
|
DEBUG_INFO(CAT, "BLE advertising probe sample phase=%s elapsed_ms=%llu publisher=%d service=%d scan=%d",
|
|
phase, (unsigned long long)((get_time_us() - start) / 1000), actual.publisher, actual.service, actual.scan);
|
|
previous = actual;
|
|
if (!gatt.matches(actual.service) || actual.scan != int(BluetoothLEAdvertisementWatcherStatus::Started)) ++unsettled;
|
|
else if (actual.publisher == int(BluetoothLEAdvertisementPublisherStatus::Started)) {
|
|
if (!started) first_started_ms = (get_time_us() - start) / 1000;
|
|
++started;
|
|
} else if (actual.publisher == int(BluetoothLEAdvertisementPublisherStatus::Waiting)) ++waiting;
|
|
} while (get_time_us() < end);
|
|
DEBUG_INFO(CAT, "BLE advertising probe result phase=%s started_samples=%u waiting_samples=%u unsettled_samples=%u "
|
|
"first_started_ms=%llu final_publisher=%d final_service=%d final_scan=%d generation=%llu",
|
|
phase, started, waiting, unsettled, (unsigned long long)first_started_ms,
|
|
previous.publisher, previous.service, previous.scan, (unsigned long long)s.publish_generation);
|
|
if (!gatt.matches(previous.service) || previous.scan != int(BluetoothLEAdvertisementWatcherStatus::Started)) {
|
|
DEBUG_ERROR(CAT, "BLE advertising probe phase=%s did not reach requested GATT/scan state; comparison inconclusive", phase);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO); debug_set_category_level(CAT, DEBUG_LEVEL_DEBUG);
|
|
if (argc == 2 && !std::strcmp(argv[1], "--help")) {
|
|
DEBUG_INFO(CAT, "Usage: ble_advertising_probe.exe [seconds_per_phase=20, range=10..60]; close VibeChat first"); return 0;
|
|
}
|
|
char* tail = nullptr;
|
|
long seconds = argc == 2 ? std::strtol(argv[1], &tail, 10) : 20;
|
|
if (argc > 2 || (tail && (tail == argv[1] || *tail)) || seconds < 10 || seconds > 60) {
|
|
DEBUG_ERROR(CAT, "BLE advertising probe expects 10..60 seconds per phase or --help"); return 2;
|
|
}
|
|
debug_enable_file_output("ble_advertising_probe.log", 1);
|
|
DEBUG_INFO(CAT, "BLE advertising probe: close other VibeChat/probe processes; log=ble_advertising_probe.log; "
|
|
"publisher 1=Waiting 2=Started; GATT 1=Stopped 2=Started 3=Aborted; scan 1=Started");
|
|
UASYNC* ua = uasync_create();
|
|
if (!ua) { DEBUG_ERROR(CAT, "BLE advertising probe cannot create uasync"); return 1; }
|
|
ble_transport owner;
|
|
ble_config config{}; config.node_id = 0x5643424c45544553ULL; config.company_id = 0xffff;
|
|
auto backend = ble_backend_create(&owner, ua, &config);
|
|
bool completed = false;
|
|
try {
|
|
if (!backend) throw std::runtime_error("backend creation failed");
|
|
auto& s = *backend->state;
|
|
auto deadline = get_time_us() + 20000000;
|
|
while (!owner.ready && !owner.failed && get_time_us() < deadline) uasync_poll(ua, 100);
|
|
if (!owner.ready || owner.failed) throw std::runtime_error("scan/GATT startup failed or timed out after 20s");
|
|
auto gatt = std::make_shared<ProbeGatt>();
|
|
gatt->last = GattServiceProviderAdvertisementStatus::Started; /* Startup уже доставил Started в owner.ready. */
|
|
s.service_changed.revoke();
|
|
auto weak = s.weak_from_this();
|
|
s.service_changed = s.provider.AdvertisementStatusChanged(auto_revoke, [weak, gatt](auto const&, auto const& args) {
|
|
if (auto state = weak.lock()) state->post("probe GATT.AdvertisementStatusChanged", 0, [gatt, args](WinState& state) {
|
|
gatt->changed(state, args.Status(), args.Error());
|
|
});
|
|
});
|
|
uint8_t payload[BLE_BROADCAST_MAX];
|
|
int size = ble_identity_encode(payload, config.node_id, "probe18");
|
|
if (size < 0 || ble_backend_advertise(backend, payload, size) != BLE_OK)
|
|
throw std::runtime_error("manufacturer publisher startup failed");
|
|
if (!observe(s, owner, *gatt, "gatt_on", seconds))
|
|
throw std::runtime_error("baseline incomplete");
|
|
DEBUG_INFO(CAT, "BLE advertising probe stopping GATT advertisement; publisher and scan remain unchanged");
|
|
gatt->requested = false; gatt->stop_confirmed = false;
|
|
s.provider.StopAdvertising();
|
|
if (!observe(s, owner, *gatt, "gatt_off", seconds))
|
|
throw std::runtime_error("GATT-off observation incomplete");
|
|
DEBUG_INFO(CAT, "BLE advertising probe restoring GATT advertisement connectable=1 discoverable=1");
|
|
GattServiceProviderAdvertisingParameters parameters; parameters.IsConnectable(true); parameters.IsDiscoverable(true);
|
|
gatt->requested = true; gatt->stop_confirmed = false;
|
|
s.provider.StartAdvertising(parameters);
|
|
if (!observe(s, owner, *gatt, "gatt_restored", seconds))
|
|
throw std::runtime_error("GATT-restored observation incomplete");
|
|
completed = true;
|
|
DEBUG_INFO(CAT, "BLE advertising probe comparison complete; inspect the three phase results. "
|
|
"Started confirms Windows scheduling, not reception by another device");
|
|
} catch (...) { DEBUG_ERROR(CAT, "%s", exception_reason("advertising probe").c_str()); }
|
|
if (backend) ble_backend_destroy(backend);
|
|
uasync_destroy(ua, 0); debug_disable_file_output();
|
|
return completed ? 0 : 1;
|
|
}
|