Symbian C ++ - синхронное обнаружение Bluetooth с таймаутом с использованием RHostResolver - PullRequest
0 голосов
/ 27 июля 2010

Я пишу приложение в Qt для развертывания на платформе Symbian S60.К сожалению, он должен иметь функциональность Bluetooth - ничего особенного, только простое гнездо клиента RFCOMM и обнаружение устройства.Точнее говоря, ожидается, что приложение будет работать на двух платформах - Windows PC и вышеупомянутой S60.

Конечно, поскольку в Qt отсутствует поддержка Bluetooth, его необходимо кодировать в нативном API - Winsock2 для Windows и Symbian C ++.на S60 - я кодирую простой слой абстракции.И у меня есть некоторые проблемы с частью обнаружения в Symbian.

Вызов обнаружения на уровне абстракции должен работать синхронно - он блокируется до конца обнаружения и возвращает все устройства как QList.У меня нет точного кода прямо сейчас, но у меня было что-то вроде этого:

RHostResolver resolver;
TInquirySockAddr addr;
// OMITTED: resolver and addr initialization

TRequestStatus err;
TNameEntry entry;
resolver.GetByAddress(addr, entry, err);
while (true) {
    User::WaitForRequest(err);
    if (err == KErrHostResNoMoreResults) {
       break;
    } else if (err != KErrNone) {
        // OMITTED: error handling routine, not very important right now
    }

    // OMITTED: entry processing, adding to result QList

    resolver.Next(entry, err);
}
resolver.Close();

Да, я знаю, что User::WaitForRequest это зло , это кодирование, похожее на Symbian,Я должен использовать активные объекты и так далее.Но это просто не то, что мне нужно.Мне нужен простой, синхронный способ обнаружения устройств.

И приведенный выше код работает .Однако есть одна странность - я хотел бы иметь тайм-аут во время открытия.То есть я хочу, чтобы обнаружение занимало не более, скажем, 15 секунд - параметризованное при вызове функции.Я пытался сделать что-то вроде этого:

RTimer timer;
TRequestStatus timerStatus;
timer.CreateLocal();

RHostResolver resolver;
TInquirySockAddr addr;
// OMITTED: resolver and addr initialization

TRequestStatus err;
TNameEntry entry;

timer.After(timerStatus, timeout*1000000);

resolver.GetByAddress(addr, entry, err);
while (true) {
    User::WaitForRequest(err, timerStatus);

    if (timerStatus != KRequestPending) { // timeout
        resolver.Cancel();
        User::WaitForRequest(err);
        break;
    }

    if (err == KErrHostResNoMoreResults) {
        timer.Cancel();
        User::WaitForRequest(timerStatus);
        break;
    } else if (err != KErrNone) {
        // OMITTED: error handling routine, not very important right now
    }

    // OMITTED: entry processing, adding to result QList

    resolver.Next(entry, err);
}
timer.Close();
resolver.Close();

И этот код своего рода работает.Более того, он работает функционально правильно - тайм-аут работает, устройства, обнаруженные до сих пор, возвращаются, и если обнаружение заканчивается раньше, то оно завершается без ожидания таймера.Проблема в том, что она оставляет в программе случайный поток.Это означает, что при выходе из приложения его процесс все еще загружается в фоновом режиме, ничего не делая.И я не из тех программистов, которые были бы довольны «исправлением», например, заставив кнопку «выйти» убить процесс, вместо того, чтобы грациозно выходить.Покинуть чужой поток кажется слишком серьезной утечкой ресурсов.

Есть ли способ решить эту проблему?Я не против переписать все с нуля, даже используя совершенно разные API (если мы говорим о нативных API Symbian), я просто хочу, чтобы это работало.Я немного читал об активных объектах, но это не похоже на то, что мне нужно, поскольку мне просто нужно, чтобы это работало синхронно ... В случае больших изменений, я был бы признателен за более подробные объяснения, так как яновичок в Symbian C ++, и мне не нужно осваивать его - этот маленький модуль Bluetooth, вероятно, - все, что мне нужно написать в обозримом будущем.

Заранее благодарен за любую помощь!:)

Ответы [ 2 ]

0 голосов
/ 04 августа 2010

На вопрос уже дан ответ, но ... Если бы вы использовали активные объекты, я бы предложил использовать вложенный активный планировщик (класс CActiveSchedulerWait).Затем вы можете передать его своим активным объектам (CPeriodic для таймера и некоторым другим CActive для Bluetooth), и один из них остановит этот вложенный планировщик в своем методе RunL ().Более того, при таком подходе ваш вызов становится синхронным для вызывающего, и ваш поток будет изящно закрыт после выполнения вызова.

Если вы заинтересованы в решении, найдите примеры CActiveSchedulerWait или простоспроси меня, и я дам тебе пример кода.

0 голосов
/ 28 июля 2010

Код у вас выглядит нормально для меня. Вы пропустили обычную ловушку, заключающуюся в том, что вы не используете все запросы, которые вы отправили. Предполагая, что вы также отменяете таймер и выполняете User::WaitForRequest(timerStatus) внутри условия обработки ошибки, оно должно работать.

Я предполагаю, что вас беспокоит то, что ваш основной поток не может запросить выход из этого потока. Вы можете сделать это примерно следующим образом:

  • Передайте указатель на TRequestStatus в поток, когда он создается вашим основным потоком. Назовите это exitStatus.
  • Когда вы делаете User::WaitForRequest, также подождите exitStatus.
  • Основной поток выполнит bluetoothThread.RequestComplete(exitStatus, KErrCancel), когда захочет выйти из подпотока, где bluetoothThread - это объект RThread, созданный основным потоком.
  • в подпотоке, когда сигнализируется exitStatus, выйдите из цикла, чтобы завершить поток. Вам необходимо убедиться, что вы отменяете и используете запросы таймера и Bluetooth.
  • основной поток должен выполнить bluetoothThread.Logon и дождаться сигнала, ожидающего выхода из потока Bluetooth.

Скорее всего, будут еще некоторые тонкости, чтобы правильно обрабатывать все случаи ошибок и т. Д.

Я надеюсь, что я не лаю совсем не здесь ...

...