HoloLens не может отправлять или получать данные через BT и TCP - PullRequest
0 голосов
/ 11 сентября 2018

Я работаю с HoloLens ( Unity-UWP ) и пытаюсь установить соединение с ПК ( UWP ) или телефоном Android ( Xamarin ).До сих пор я пробовал клиент и хост с Bluetooth и TCP (даже с двумя версиями с разными библиотеками) на Android и UWP.Я держал код полностью отделенным от пользовательского интерфейса, чтобы его было легче использовать, понимать и модульно.Action<string> используется для вывода результатов (журналы ошибок и отправленные сообщения).

Все, что не на HoloLens, работает нормально (даже если это точно такой же код).Он работал с ПК (UWP) на Android с переключением клиента и хоста.Но это даже не работает между HoloLens и ПК (UWP).Поведение варьировалось от сбоев (в основном для Bluetooth) до мгновенного отключения.Последние тесты привели к отключению, как только будут получены байты.Он мог даже прочитать первые 4 байта (uint для длины следующего сообщения UTF-8), но затем он был отключен.Казалось, что другие устройства работают нормально.

Что я знаю: возможности установлены, код работает, проблема, вероятно, является общей для всего, что связано с сетью и HoloLens.

Итак, вопрос в том, является ли Unity или HoloLens несовместимым с тем, что я использую?То, что я использовал, стоит упомянуть: StreamSocket, BinaryWriter, BinaryReader, Task (async, await).Или HoloLens активно блокирует связь с приложениями на других устройствах?Я знаю, что он может подключаться к устройствам с Bluetooth и что он может подключаться через TCP, и похоже, что людям удается заставить его работать.Есть ли известные проблемы?Или есть что-то с Unity, что может быть причиной плохой настройки?Нужно ли использовать асинхронные методы или только синхронизировать?Существуют ли проблемы несовместимости с задачами / потоками и Unity? это , возможно, проблема (неспособность дать согласие на разрешения)?

Еще одна вещь, которую нужно отметить, - я не могу пропинговать HoloLens через его IP-адрес с помощью cmd, даже если IP-адрес правильный.

Буду признателен за любой совет, ответ или предположение.Я могу предоставить больше информации по запросу (см. Также комментарии ниже).Я бы предложил сосредоточиться на TCP-соединении , так как оно работает лучше и выглядит более «базовым».Вот код:

using System;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using Windows.Networking;
using Windows.Networking.Sockets;

#region Common
public abstract class TcpCore
{
    protected StreamSocket Socket;
    protected BinaryWriter BWriter;
    protected BinaryReader BReader;
    protected Task ReadingTask;

    public bool DetailedInfos { get; set; } = false;
    public bool Listening { get; protected set; }

    public ActionSingle<string> MessageOutput { get; protected set; } = new ActionSingle<string> (); // Used for message and debug output. They wrap an Action and allow safer use.
    public ActionSingle<string> LogOutput { get; protected set; } = new ActionSingle<string> ();

    protected const string USED_PORT = "1337";
    protected readonly Encoding USED_ENCODING = Encoding.UTF8;

    public abstract void Disconnect ();

    protected void StartCommunication ()
    {
        Stream streamOut = Socket.OutputStream.AsStreamForWrite ();
        Stream streamIn = Socket.InputStream.AsStreamForRead ();
        BWriter = new BinaryWriter (streamOut); //{ AutoFlush = true };
        BReader = new BinaryReader (streamIn);

        LogOutput.Trigger ("Connection established.");
        ReadingTask = new Task (() => StartReading ());
        ReadingTask.Start ();
    }

    public void SendMessage (string message)
    {
        // There's no need to send a zero length message.
        if (string.IsNullOrEmpty (message)) return;

        // Make sure that the connection is still up and there is a message to send.
        if (Socket == null || BWriter == null) { LogOutput.Trigger ("Cannot send message: No clients connected."); return; }

        uint length = (uint) message.Length;
        byte[] countBuffer = BitConverter.GetBytes (length);
        byte[] buffer = USED_ENCODING.GetBytes (message);

        if (DetailedInfos) LogOutput.Trigger ("Sending: " + message);

        BWriter.Write (countBuffer);
        BWriter.Write (buffer);
        BWriter.Flush ();
    }

    protected void StartReading ()
    {
        if (DetailedInfos) LogOutput.Trigger ("Starting to listen for input.");
        Listening = true;
        while (Listening)
        {
            try
            {
                if (DetailedInfos) LogOutput.Trigger ("Starting a listen iteration.");

                // Based on the protocol we've defined, the first uint is the size of the message. [UInt (4)] + [Message (1*n)] - The UInt describes the length of the message (=n).
                uint length = BReader.ReadUInt32 ();

                if (DetailedInfos) LogOutput.Trigger ("ReadLength: " + length.ToString ());

                MessageOutput.Trigger ("A");
                byte[] messageBuffer = BReader.ReadBytes ((int) length);
                MessageOutput.Trigger ("B");
                string message = USED_ENCODING.GetString (messageBuffer);
                MessageOutput.Trigger ("Received Message: " + message);
            }
            catch (Exception e)
            {
                // If this is an unknown status it means that the error is fatal and retry will likely fail.
                if (SocketError.GetStatus (e.HResult) == SocketErrorStatus.Unknown)
                {
                    // Seems to occur on disconnects. Let's not throw().
                    Listening = false;
                    Disconnect ();
                    LogOutput.Trigger ("Unknown error occurred: " + e.Message);
                    break;
                }
                else
                {
                    Listening = false;
                    Disconnect ();
                    break;
                }
            }
        }
        LogOutput.Trigger ("Stopped to listen for input.");
    }
}
#endregion

#region Client
public class GTcpClient : TcpCore
{
    public async void Connect (string target, string port = USED_PORT) // Target is IP address.
    {
        try
        {
            Socket = new StreamSocket ();
            HostName serverHost = new HostName (target);
            await Socket.ConnectAsync (serverHost, port);

            LogOutput.Trigger ("Connection successful to: " + target + ":" + port);
            StartCommunication ();
        }
        catch (Exception e)
        {
            LogOutput.Trigger ("Connection error: " + e.Message);
        }
    }

    public override void Disconnect ()
    {
        Listening = false;
        if (BWriter != null) { BWriter.Dispose (); BWriter.Dispose (); BWriter = null; }
        if (BReader != null) { BReader.Dispose (); BReader.Dispose (); BReader = null; }
        if (Socket != null) { Socket.Dispose (); Socket = null; }
        if (ReadingTask != null) { ReadingTask = null; }
    }
}
#endregion

#region Server
public class GTcpServer : TcpCore
{
    private StreamSocketListener socketListener;

    public bool AutoResponse { get; set; } = false;

    public async void StartServer ()
    {
        try
        {
            //Create a StreamSocketListener to start listening for TCP connections.
            socketListener = new StreamSocketListener ();

            //Hook up an event handler to call when connections are received.
            socketListener.ConnectionReceived += ConnectionReceived;

            //Start listening for incoming TCP connections on the specified port. You can specify any port that's not currently in use.
            await socketListener.BindServiceNameAsync (USED_PORT);
        }
        catch (Exception e)
        {
            LogOutput.Trigger ("Connection error: " + e.Message);
        }
    }

    private void ConnectionReceived (StreamSocketListener listener, StreamSocketListenerConnectionReceivedEventArgs args)
    {
        try
        {
            listener.Dispose ();
            Socket = args.Socket;
            if (DetailedInfos) LogOutput.Trigger ("Connection received from: " + Socket.Information.RemoteAddress + ":" + Socket.Information.RemotePort);
            StartCommunication ();
        }
        catch (Exception e)
        {
            LogOutput.Trigger ("Connection Received error: " + e.Message);
        }
    }

    public override void Disconnect ()
    {
        Listening = false;
        if (socketListener != null) { socketListener.Dispose (); socketListener = null; }
        if (BWriter != null) { BWriter.Dispose (); BWriter.Dispose (); BWriter = null; }
        if (BReader != null) { BReader.Dispose (); BReader.Dispose (); BReader = null; }
        if (Socket != null) { Socket.Dispose (); Socket = null; }
        if (ReadingTask != null) { ReadingTask = null; }
    }
}
#endregion

Ответы [ 2 ]

0 голосов
/ 13 сентября 2018

Ответ Threading.

Для тех, у кого могут быть подобные проблемы, я нашел решение. Это было связано с самим Unity, а не с HoloLens. Моя проблема заключалась в том, что я написал свой код отдельно в своем собственном классе, а не смешал его с кодом пользовательского интерфейса, что сделало бы его 1. нечитаемым и 2. не модульным для использования. Поэтому я попробовал лучший подход к кодированию (на мой взгляд). Каждый мог скачать его, легко интегрировать и иметь базовый код для обмена текстовыми сообщениями. Хотя это не было проблемой для Xamarin и UWP, оно было проблемой для самого Unity (а также для решения Unity-UWP).

Кажется, что принимающая сторона Bluetooth и TCP создала собственный поток (может быть, даже что-то еще, но я не делал этого активно), который не смог записать в основной поток в Unity, , который обрабатывает только GameObjects (как и выходной журнал). Таким образом, я получил странные результаты логов, когда я тестировал его на HoloLens.

Я создал новый код TCP, который работает для Unity, но не для решения Unity-UWP, используя TcpClient / TcpListener, чтобы попробовать другую версию с TCP-соединением. К счастью, когда я запустил это в редакторе, он, наконец, указал на саму проблему: не удалось получить доступ к основному потоку, который записал бы в пользовательский интерфейс - текстовое поле для вывода журнала. Чтобы решить эту проблему, мне просто нужно было использовать метод Unity Update (), чтобы установить вывод текста. Сами переменные все еще могут быть доступны, но не GameObjects.

0 голосов
/ 12 сентября 2018

По совпадению, я только что реализовал BT-соединение между HoloLens и приложением UWP. Я следовал за образцом в https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/BluetoothRfcommChat.

В качестве возможностей я установил «Bluetooth» (разумеется), «Интернет (клиент и сервер)» и «Частные сети (клиент и сервер)». Затем выполняются следующие шаги на стороне сервера:

  1. Создайте RfcommServiceProvider для своего собственного или существующего (например, OBEX объекта push) идентификатора службы.

  2. Создайте StreamSocketListener и подключите его ConnectionReceived Событие.

  3. Привязать имя службы к слушателю: listener.BindServiceNameAsync(provider.ServiceId.AsString(), SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);

  4. Если у вас есть собственный идентификатор службы, задайте его имя вместе с другими атрибутами, которые вы можете настроить. См. Образец, связанный выше для этого. Я думаю, это в основном необязательно.

  5. Начало рекламы услуги BT: provider.StartAdvertising(listener, true);

После подключения клиента в StreamSocketListenerConnectionReceivedEventArgs появляется StreamSocket, который можно использовать для создания DataReader и DataWriter, как в любом другом потоке. Если вы хотите разрешить только одного клиента, вы также можете прекратить рекламу прямо сейчас.

На стороне клиента вы бы:

  1. Показать DevicePicker и позволить пользователю выбрать одноранговое устройство. Не забудьте установить фильтр, например picker.Filter.SupportedDeviceSelectors.Add(BluetoothDevice.GetDeviceSelectorFromPairingState(true));. Вы также можете разрешить непарные устройства, но вам нужно позвонить PairAsync, прежде чем вы сможете продолжить в шаге 2. Кроме того, я думаю, что в этом случае нет способа обойти диалог согласия пользователя. , поэтому я бы порекомендовал сопряжение раньше. Если честно, я не проверял, работает ли непарный материал на HoloLens.

  2. Вы получаете экземпляр DeviceInformation от сборщика, который можно использовать для получения устройства BT, например await BluetoothDevice.FromIdAsync(info.Id);

  3. Получите сервисы от устройства, такие как device.GetRfcommServicesAsync(BluetoothCacheMode.Uncached); и выберите тот, который вас интересует. Обратите внимание, что я обнаружил, что встроенная фильтрация работает не так, как ожидалось, поэтому я просто перечислил результат и сравнил UUIDs вручную. Я считаю, что реализация UWP в какой-то момент выполняет сравнение строк с учетом регистра, что может привести к тому, что запрошенная служба не будет отображаться, хотя она там есть.

  4. Как только вы нашли свой сервис, который я теперь буду называть s, создайте StreamSocket и подключитесь с помощью socket.ConnectAsync(s.ConnectionHostName, s.ConnectionServiceName, SocketProtectionLevel.PlainSocket);

Опять же, вы не можете создавать потоковые программы чтения и записи, как на стороне сервера.

...