Test and document Windows BLE radio status diagnostics

This commit is contained in:
evgeny committed 2026-10-30 12:18:19 +01:00
1 parent 3e1d1ddbfd
commit ace0ce3e85
2 files changed
+80 -1

No files matched your search

+29
View File
@@ -278,6 +278,35 @@ BluetoothError записывается
GATT status: 0=Success, 1=Unreachable, 2=ProtocolError, 3=AccessDenied.
PHY_2M_supported — возможность адаптера, НЕ измеренная скорость соединения;
-1 у необязательных возможностей означает отсутствие API в этой версии Windows.
При Waiting периодическая строка BLE WinRT radio snapshot читает непосредственно
Publisher.Status, GATT.AdvertisementStatus и Watcher.Status. publisher_event — последнее
обработанное событие; *_actual — состояние ОС сейчас, -1=объекта нет, -2=ошибка чтения
(рядом пишется HRESULT). Эти чтения не меняют состояния/соединения и не перезапускают радио.
payload_bytes — тело manufacturer data, manufacturer_ad_bytes включает ещё четыре байта
company ID, AD type и length; это не размер полного пакета, сформированного Windows.
Windows: проверка конкуренции GATT и manufacturer publisher на реальном адаптере:
1. Закрыть VibeChat и другие экземпляры probe на проверяемом ПК.
2. После обычной сборки chatgui перейти в tools/chatgui/build и запустить:
.\ble_advertising_probe.exe
Либо собрать отдельно цель ble_advertising_probe в lib/ble (CMake, Windows).
3. Приложить ble_advertising_probe.log из текущего каталога.
Утилита создаёт тот же GATT-сервис/характеристики, scan и 18-байтовое объявление probe18
текущей wire-версии. Входящие подключения отклоняются: это тест обнаружения, не чат.
Три окна по 20 секунд: gatt_on, gatt_off, gatt_restored. Можно передать длительность
каждого окна 10..60 секунд единственным аргументом; startup ограничен ещё 20 секундами.
Publisher, payload и scan остаются теми же; меняется только реклама GATT-сервиса.
В result: количество наблюдений Started/Waiting при нужном состоянии GATT и работающем
scan, unsettled_samples (переходные состояния), задержка первого Started и конечные
состояния ОС. first_started_ms имеет смысл только при started_samples>0.
Waiting → Started при выключении GATT → Waiting при восстановлении указывает на
конкуренцию ресурсов. Если Waiting остаётся и без GATT, причина этим тестом не установлена.
Started подтверждает обслуживание публикации Windows, но не приём вторым устройством.
Код выхода 0 означает завершённое сравнение, а не успешную передачу во всех трёх окнах.
Ошибка/таймаут — 1, неправильные аргументы — 2. При завершении все объекты освобождаются.
Утилита собирается рядом с vibechat.exe, в пользовательский установщик не включается.
Объяснение приоритета GATT от Microsoft:
https://learn.microsoft.com/en-us/answers/questions/189891/windows10-uwp-app-conflict-ble-advertisement-and-g
Для отдельных кадров, ATT-фрагментов и переходов backpressure использовать discovery=trace.
WinRT TRACE содержит channel (0=DATA, 1=INVITE, 2=ACK), направление, длину и elapsed_us
локальной отправки до её обработки в uasync; это время включает ожидание mailbox.
+51 -1
View File
@@ -90,6 +90,56 @@ static void advertisement_addresses(UASYNC* ua) {
DEBUG_INFO(CAT, "WinRT test PASS: 1809 events use unspecified type; 2004 events preserve public/random/unspecified");
}
struct TestPublisher : implements<TestPublisher, IBluetoothLEAdvertisementPublisher> {
bool read_error = false;
BluetoothLEAdvertisementPublisherStatus Status() {
if (read_error) throw hresult_error(E_ACCESSDENIED, L"test publisher status denied");
return BluetoothLEAdvertisementPublisherStatus::Started;
}
BluetoothLEAdvertisement Advertisement() { CHECK(false); return nullptr; }
void Start() { CHECK(false); }
void Stop() { CHECK(false); }
event_token StatusChanged(const TypedEventHandler<BluetoothLEAdvertisementPublisher,
BluetoothLEAdvertisementPublisherStatusChangedEventArgs>&) { CHECK(false); return {}; }
void StatusChanged(event_token) noexcept { CHECK(false); }
};
struct TestProvider : implements<TestProvider, IGattServiceProvider> {
bool read_error = false;
GattLocalService Service() { CHECK(false); return nullptr; }
GattServiceProviderAdvertisementStatus AdvertisementStatus() {
if (read_error) throw hresult_error(E_ACCESSDENIED, L"test GATT status denied");
return GattServiceProviderAdvertisementStatus::Stopped;
}
void StartAdvertising() { CHECK(false); }
void StartAdvertising(const GattServiceProviderAdvertisingParameters&) { CHECK(false); }
void StopAdvertising() { CHECK(false); }
event_token AdvertisementStatusChanged(const TypedEventHandler<GattServiceProvider,
GattServiceProviderAdvertisementStatusChangedEventArgs>&) { CHECK(false); return {}; }
void AdvertisementStatusChanged(event_token) noexcept { CHECK(false); }
};
/* Снимок отличает ОС от задержанных событий; ошибка одного getter не мешает остальным и не останавливает radio. */
static void radio_snapshot(UASYNC* ua) {
ble_transport owner;
auto s = make_state(ua, owner);
auto empty = s->radio_status();
CHECK(empty.publisher == -1 && empty.service == -1 && empty.scan == -1);
auto publisher = make_self<TestPublisher>(); auto provider = make_self<TestProvider>();
s->publisher = publisher.as<BluetoothLEAdvertisementPublisher>();
s->provider = provider.as<GattServiceProvider>();
s->publisher_status = int(BluetoothLEAdvertisementPublisherStatus::Waiting); s->service_ready = true;
auto actual = s->radio_status();
CHECK(actual.publisher == 2 && actual.service == 1 && actual.scan == -1);
publisher->read_error = true;
actual = s->radio_status(); CHECK(actual.publisher == -2 && actual.service == 1);
publisher->read_error = false; provider->read_error = true;
actual = s->radio_status(); CHECK(actual.publisher == 2 && actual.service == -2);
CHECK(!s->failed && !owner.radio_failures && !owner.link_failures && s->links.size() == 2);
CHECK(s->publisher_status == 1 && s->service_ready);
s->publisher = nullptr; s->provider = nullptr; s->shutdown();
DEBUG_INFO(CAT, "WinRT test PASS: actual radio snapshot tolerates getter failures without changing radio/events/links");
}
/* Ошибка GATT одного пира не переводит radio в failed и не теряет событие другого пира. */
static void link_exception(UASYNC* ua) {
ble_transport owner;
@@ -153,7 +203,7 @@ int main() {
debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO);
init_apartment(apartment_type::multi_threaded);
UASYNC* ua = uasync_create(); CHECK(ua);
advertisement_addresses(ua); link_exception(ua); radio_exception(ua); pending_shutdown(ua);
advertisement_addresses(ua); radio_snapshot(ua); link_exception(ua); radio_exception(ua); pending_shutdown(ua);
uasync_destroy(ua, 0); uninit_apartment();
return 0;
}