Helyi horgonyátvitelek a DirectX-ban

Olyan esetekben, amikor nem használhatja az Azure Spatial Anchors-t,a helyi horgonyátvitelek lehetővé teszik, hogy egy HoloLens-eszköz exportálja a horgonyt, és egy második HoloLens importálja.

Megjegyzés

A helyi horgonyok átvitele kevésbé robusztus horgonyhívást biztosít,mint az Azure Spatial Anchors , és az iOS- és Android-eszközök nem támogatják ezt a megközelítést.

Megjegyzés

A cikkben lévő kódrészletek jelenleg a C++/CX használatát mutatják be a C++17-kompatibilis C++/WinRT helyett a C++ holografikusprojektsablonban használt kóddal. Az alapfogalmak egyenértékűek a C++/WinRT-projektekkel, de a kódot le kell fordítania.

Térbeli horgonyok átvitele

Térbeli horgonyokat a Windows Mixed Reality a SpatialAnchorTransferManager használatával. Ez az API lehetővé teszi, hogy összecsomagoz egy horgonyt az összes támogató érzékelőadatmal, amely ahhoz szükséges, hogy megtalálja a világ pontos helyét, majd importálja a csomagot egy másik eszközre. Miután a második eszközön található alkalmazás importálta ezt a horgonyt, minden alkalmazás képes hologramokat renderelni a megosztott térbeli horgony koordinátarendszerével, amely ezután a valós világ ugyanazon helyén fog megjelenni.

Vegye figyelembe, hogy a térbeli horgonyok nem továbbíthatók különböző eszköztípusok között, például előfordulhat, hogy egy HoloLens térbeli horgony nem található modern headset használatával. Az átvitt horgonyok az iOS- vagy Android-eszközökkel sem kompatibilisek.

Az alkalmazás beállítása a spatialPerception funkció használatára

Ahhoz, hogy használni tudja a SpatialAnchorTransferManagert,az alkalmazásnak engedélyt kell adni a SpatialPerception képesség használatára. Ez azért szükséges, mert a térbeli horgonyok átvitele magában foglalja a horgony közelében az idő során összegyűjtött érzékelőképek megosztását, amelyek bizalmas információkat is tartalmazhatnak.

Deklarálja ezt a képességet az alkalmazás package.appxmanifest fájljában. Bemutatunk egy példát:

<Capabilities>
  <uap2:Capability Name="spatialPerception" />
</Capabilities>

A képesség az uap2 névtérből származik. Ahhoz, hogy hozzáférjen a névtérhez a jegyzékfájlban, foglalja bele xlmns attribútumként a Package (Csomag) > elembe. Bemutatunk egy példát:

<Package
    xmlns="https://schemas.microsoft.com/appx/manifest/foundation/windows10"
    xmlns:mp="https://schemas.microsoft.com/appx/2014/phone/manifest"
    xmlns:uap="https://schemas.microsoft.com/appx/manifest/uap/windows10"
    xmlns:uap2="https://schemas.microsoft.com/appx/manifest/uap/windows10/2"
    IgnorableNamespaces="uap mp"
    >

MEGJEGYZÉS: Az alkalmazásnak futásidőben le kell kérnie a képességet, mielőtt hozzáférhet a SpatialAnchor exportálási/importálási API-khoz. Az alábbi példákban lásd: RequestAccessAsync.

Horgonyadatok szerializálása a SpatialAnchorTransferManager exportálásával

A kódminta tartalmaz egy segítő függvényt a SpatialAnchor-adatok exportálásához (szerializálásához). Ez az exportálási API kulcs-érték párok összes horgonyát szerializálja, és sztringeket társít a horgonyokkal.

// ExportAnchorDataAsync: Exports a byte buffer containing all of the anchors in the given collection.
//
// This function will place data in a buffer using a std::vector<byte>. The ata buffer contains one or more
// Anchors if one or more Anchors were successfully imported; otherwise, it is ot modified.
//
task<bool> SpatialAnchorImportExportHelper::ExportAnchorDataAsync(
    vector<byte>* anchorByteDataOut,
    IMap<String^, SpatialAnchor^>^ anchorsToExport
    )
{

Először be kell álltatnunk az adatfolyamot. Ez lehetővé teszi, hogy 1.) a TryExportAnchorsAsync használatával helyezze az adatokat egy, az alkalmazás tulajdonában lévő pufferbe, és 2.) olvassa be az adatokat az exportált bájtpuffer adatfolyamból – amely egy WinRT-adatfolyam – a saját memóriapufferünkbe, amely egy std::vector <> bájt.

// Create a random access stream to process the anchor byte data.
InMemoryRandomAccessStream^ stream = ref new InMemoryRandomAccessStream();
// Get an output stream for the anchor byte stream.
IOutputStream^ outputStream = stream->GetOutputStreamAt(0);

Engedélyt kell kérnünk a térbeli adatok eléréséhez, beleértve a rendszer által exportált horgonyokat is.

// Request access to spatial data.
auto accessRequestedTask = create_taskSpatialAnchorTransferManager::RequestAccessAsync()).then([anchorsToExport, utputStream](SpatialPerceptionAccessStatus status)
{
    if (status == SpatialPerceptionAccessStatus::Allowed)
    {
        // Access is allowed.
        // Export the indicated set of anchors.
        return create_task(SpatialAnchorTransferManager::TryExportAnchorsAsync(
            anchorsToExport,
            outputStream
            ));
    }
    else
    {
        // Access is denied.
        return task_from_result<bool>(false);
    }
});

Ha van engedélyünk, és a horgonyok exportálva vannak, beolvassuk az adatfolyamot. Itt azt is megmutatjuk, hogyan hozhatja létre az adatok beolvasására használt DataReader és InputStream adatokat.

// Get the input stream for the anchor byte stream.
IInputStream^ inputStream = stream->GetInputStreamAt(0);
// Create a DataReader, to get bytes from the anchor byte stream.
DataReader^ reader = ref new DataReader(inputStream);
return accessRequestedTask.then([anchorByteDataOut, stream, reader](bool nchorsExported)
{
    if (anchorsExported)
    {
        // Get the size of the exported anchor byte stream.
        size_t bufferSize = static_cast<size_t>(stream->Size);
        // Resize the output buffer to accept the data from the stream.
        anchorByteDataOut->reserve(bufferSize);
        anchorByteDataOut->resize(bufferSize);
        // Read the exported anchor store into the stream.
        return create_task(reader->LoadAsync(bufferSize));
    }
    else
    {
        return task_from_result<size_t>(0);
    }

Miután beolvassuk a bájtokat a streamből, menthetjük őket a saját adatpufferünkbe, ahogy itt is.

}).then([anchorByteDataOut, reader](size_t bytesRead)
{
    if (bytesRead > 0)
    {
        // Read the bytes from the stream, into our data output buffer.
        reader->ReadBytes(Platform::ArrayReference<byte>(&(*anchorByteDataOut)[0], bytesRead));
        return true;
    }
    else
    {
        return false;
    }
});
};

Horgonyadatok deszerializálása a rendszerbe való importálásával a SpatialAnchorTransferManager használatával

A kódminta tartalmaz egy segítő függvényt a korábban exportált adatok betöltéséhez. Ez a deserializálási függvény a SpatialAnchorStore által megadotthoz hasonló kulcs-érték párok gyűjteményét biztosítja, azzal a kivételsel, hogy ezeket az adatokat egy másik forrásból, például egy hálózati szoftvercsatornából kaptam. Az adatok offline tárolása előtt, az alkalmazás memóriájában vagy (ha van) az alkalmazás SpatialAnchorStore tárolójában feldolgozhatja az adatokat, és indoklást kaphat róla.

// ImportAnchorDataAsync: Imports anchors from a byte buffer that was previously exported.
//
// This function will import all anchors from a data buffer into an in-memory ollection of key, value
// pairs that maps String objects to SpatialAnchor objects. The Spatial nchorStore is not affected by
// this function unless you provide it as the target collection for import.
//
task<bool> SpatialAnchorImportExportHelper::ImportAnchorDataAsync(
    std::vector<byte>& anchorByteDataIn,
    IMap<String^, SpatialAnchor^>^ anchorMapOut
    )
{

Először streamobjektumokat kell létrehoznunk a horgonyadatok eléréséhez. A pufferből egy rendszerpufferbe fogjuk írni az adatokat, ezért létrehozunk egy DataWritert, amely egy memóriabeli adatstreambe ír, hogy megvalósítsa azt a célt, hogy a bájtpufferből a rendszerbe SpatialAnchorsként horgonyokat hozzunk létre.

// Create a random access stream for the anchor data.
InMemoryRandomAccessStream^ stream = ref new InMemoryRandomAccessStream();
// Get an output stream for the anchor data.
IOutputStream^ outputStream = stream->GetOutputStreamAt(0);
// Create a writer, to put the bytes in the stream.
DataWriter^ writer = ref new DataWriter(outputStream);

Ismét meg kell győződnünk arról, hogy az alkalmazásnak van engedélye a térbeli horgonyadatok exportálására, amelyek tartalmazhatnak a felhasználó környezetére vonatkozó privát adatokat is.

// Request access to transfer spatial anchors.
return create_task(SpatialAnchorTransferManager::RequestAccessAsync()).then(
    [&anchorByteDataIn, writer](SpatialPerceptionAccessStatus status)
{
    if (status == SpatialPerceptionAccessStatus::Allowed)
    {
        // Access is allowed.

Ha a hozzáférés engedélyezett, bájtokat írhatunk a pufferből a rendszeradatfolyamba.

// Write the bytes to the stream.
        byte* anchorDataFirst = &anchorByteDataIn[0];
        size_t anchorDataSize = anchorByteDataIn.size();
        writer->WriteBytes(Platform::ArrayReference<byte>(anchorDataFirst, anchorDataSize));
        // Store the stream.
        return create_task(writer->StoreAsync());
    }
    else
    {
        // Access is denied.
        return task_from_result<size_t>(0);
    }

Ha sikerült bájtokat tárolni az adatfolyamban, megpróbálhatja importálni az adatokat a SpatialAnchorTransferManager használatával.

}).then([writer, stream](unsigned int bytesWritten)
{
    if (bytesWritten > 0)
    {
        // Try to import anchors from the byte stream.
        return create_task(writer->FlushAsync())
            .then([stream](bool dataWasFlushed)
        {
            if (dataWasFlushed)
            {
                // Get the input stream for the anchor data.
                IInputStream^ inputStream = stream->GetInputStreamAt(0);
                return create_task(SpatialAnchorTransferManager::TryImportAnchorsAsync(inputStream));
            }
            else
            {
                return task_from_result<IMapView<String^, SpatialAnchor^>^>(nullptr);
            }
        });
    }
    else
    {
        return task_from_result<IMapView<String^, SpatialAnchor^>^>(nullptr);
    }

Ha az adatok importálhatók, a kulcs-érték párok térképnézete a sztringeket horgonyokkal társítja. Ezt betöltheti a saját memória-adatgyűjteményünkbe, és a gyűjtemény használatával olyan horgonyokat keres, amelyekre kíváncsiak vagyunk.

}).then([anchorMapOut](task<Windows::Foundation::Collections::IMapView<String^, SpatialAnchor^>^>  previousTask)
{
    try
    {
        auto importedAnchorsMap = previousTask.get();
        // If the operation was successful, we get a set of imported anchors.
        if (importedAnchorsMap != nullptr)
        {
            for each (auto& pair in importedAnchorsMap)
            {
                // Note that you could look for specific anchors here, if you know their key values.
                auto const& id = pair->Key;
                auto const& anchor = pair->Value;
                // Append "Remote" to the end of the anchor name for disambiguation.
                std::wstring idRemote(id->Data());
                idRemote += L"Remote";
                String^ idRemoteConst = ref new String (idRemote.c_str());
                // Store the anchor in the current in-memory anchor map.
                anchorMapOut->Insert(idRemoteConst, anchor);
            }
            return true;
        }
    }
    catch (Exception^ exception)
    {
        OutputDebugString(L"Error: Unable to import the anchor data buffer bytes into the in-memory anchor collection.\n");
    }
    return false;
});
}

MEGJEGYZÉS: A horgonyok importálása nem feltétlenül jelenti azt, hogy azonnal használható. A horgony lehet egy másik helyiségben vagy egy másik fizikai helyen; A horgony addig nem lesz elérhető, amíg az azt megkapó eszköz nem rendelkezik elegendő vizuális információval arról a környezetről, amelyben a horgony létre lett hozva, hogy visszaállítsa a horgony pozícióját az ismert aktuális környezethez képest. Az ügyfél-implementációnak meg kell próbálnia a helyi koordinátarendszerhez vagy referenciakerethez viszonyítva megtalálni a horgonyt, mielőtt tovább próbálkozik, hogy élő tartalomhoz használja. Próbálja meg például rendszeresen egy aktuális koordinátarendszerhez viszonyítva megtalálni a horgonyt, amíg a horgony el nem indul.

Speciális szempontok

A TryExportAnchorsAsync API lehetővé teszi több SpatialAnchors exportálását ugyanoda az átlátszatlan bináris blobba. Azonban van egy kis különbség abban, hogy a blob milyen adatokat fog tartalmazni, attól függően, hogy egyetlen SpatialAnchor vagy több SpatialAnchors van-e egyetlen hívásban exportálva.

Egyetlen SpatialAnchor exportálása

A blob a SpatialAnchor környékén található környezetet ábrázolja, így a környezet felismerhető a SpatialAnchort importáló eszközön. Az importálás befejezése után az új SpatialAnchor elérhető lesz az eszköz számára. Feltételezve, hogy a felhasználó nemrég a horgony közelében volt, elérhető lesz, és a SpatialAnchorhoz csatolt hologramok megjeleníthetők. Ezek a hologramok ugyanazon a fizikai helyen fognak adatokat mutatni, mint az eredeti eszközön, amely a SpatialAnchort exportálta.

Egyetlen SpatialAnchor exportálása

Több SpatialAnchors exportálása

Akárcsak egyetlen SpatialAnchor exportálása, a blob is a környezetet ábrázolja az összes megadott SpatialAnchors környezet közelében. Emellett a blob információkat tartalmaz a mellékelt SpatialAnchors közötti kapcsolatokról, ha ugyanazon a fizikai térben találhatók. Ez azt jelenti, hogy ha két közeli SpatialAnchors-t importálnak, akkor a második SpatialAnchorhoz csatolt hologram akkor is elérhető lesz, ha az eszköz csak az első SpatialAnchor körül ismeri fel a környezetet, mert a blob tartalmazta a két SpatialAnchors közötti átalakításhoz elegendő adatot. Ha a két SpatialAnchorst egyenként exportálta (a TryExportSpatialAnchors két külön hívása), akkor előfordulhat, hogy a blob nem tartalmaz elég adatot ahhoz, hogy a második SpatialAnchorhoz csatolt hologramok az első helyének megkereshetők legyen.

Több horgony exportálása egyetlen TryExportAnchorsAsynchívással Több horgony különTryExportAnchorsAsync hívással az egyes horgonyok számára

Példa: Horgonyadatok küldése Windows::Hálózat::StreamSocket használatával

Itt egy példát mutatunk be arra, hogyan használhatók az exportált horgonyadatok egy TCP-hálózaton keresztüli küldésével. Ez a HolographicSpatialAnchorTransferSample gyűjteményből van.

A WinRT StreamSocket osztály a PPL feladattárat használja. Hálózati hibák esetén a rendszer visszaadja a hibát a lánc következő feladatának egy újra eldobott kivétel használatával. A kivétel egy HRESULT-t tartalmaz, amely a hiba állapotát jelzi.

A Windows::Networking::StreamSocketListener és a TCP használatával exportált horgonyadatok küldése

Hozzon létre egy kiszolgálópéldányt, amely a kapcsolatot figyeli.

void SampleAnchorTcpServer::ListenForConnection()
{
    // Make a local copy to avoid races with Closed events.
    StreamSocketListener^ streamSocketListener = m_socketServer;
    if (streamSocketListener == nullptr)
    {
        OutputDebugString(L"Server listening for client.\n");
        // Create the web socket connection.
        streamSocketListener = ref new StreamSocketListener();
        streamSocketListener->Control->KeepAlive = true;
        streamSocketListener->BindEndpointAsync(
            SampleAnchorTcpCommon::m_serverHost,
            SampleAnchorTcpCommon::m_tcpPort
            );
        streamSocketListener->ConnectionReceived +=
            ref new Windows::Foundation::TypedEventHandler<StreamSocketListener^, StreamSocketListenerConnectionReceivedEventArgs^>(
                std::bind(&SampleAnchorTcpServer::OnConnectionReceived, this, _1, _2)
                );
        m_socketServer = streamSocketListener;
    }
    else
    {
        OutputDebugString(L"Error: Stream socket listener not created.\n");
    }
}

Amikor kapcsolat érkezik, az ügyfél szoftvercsatorna-kapcsolat használatával küldjön horgonyadatokat.

void SampleAnchorTcpServer::OnConnectionReceived(StreamSocketListener^ listener, StreamSocketListenerConnectionReceivedEventArgs^ args)
{
    m_socketForClient = args->Socket;
    if (m_socketForClient != nullptr)
    {
        // In this example, when the client first connects, we catch it up to the current state of our anchor set.
        OutputToClientSocket(m_spatialAnchorHelper->GetAnchorMap());
    }
}

Most megkezdhetjük az exportált horgonyadatokat tartalmazó adatfolyam elküldését.

void SampleAnchorTcpServer::OutputToClientSocket(IMap<String^, SpatialAnchor^>^ anchorsToSend)
{
    m_anchorTcpSocketStreamWriter = ref new DataWriter(m_socketForClient->OutputStream);
    OutputDebugString(L"Sending stream to client.\n");
    SendAnchorDataStream(anchorsToSend).then([this](task<bool> previousTask)
    {
        try
        {
            bool success = previousTask.get();
            if (success)
            {
                OutputDebugString(L"Anchor data sent!\n");
            }
            else
            {
                OutputDebugString(L"Error: Anchor data not sent.\n");
            }
        }
        catch (Exception^ exception)
        {
            HandleException(exception);
            OutputDebugString(L"Error: Anchor data was not sent.\n");
        }
    });
}

A stream elküldését megelőzően először el kell küldenünk egy fejléccsomagot. Ennek a fejléccsomagnak rögzített hosszúságúnak kell lennie, és meg kell jeleznie a bájtok változótömbje hosszát is, amely a horgonyadatfolyam; ebben a példában nincs más elküldni kívánt fejlécadat, így a fejléc 4 bájt hosszú, és 32 bites, aláíratlan egész számmal van megszűkülve.

Concurrency::task<bool> SampleAnchorTcpServer::SendAnchorDataLengthMessage(size_t dataStreamLength)
{
    unsigned int arrayLength = dataStreamLength;
    byte* data = reinterpret_cast<byte*>(&arrayLength);
    m_anchorTcpSocketStreamWriter->WriteBytes(Platform::ArrayReference<byte>(data, SampleAnchorTcpCommon::c_streamHeaderByteArrayLength));
    return create_task(m_anchorTcpSocketStreamWriter->StoreAsync()).then([this](unsigned int bytesStored)
    {
        if (bytesStored > 0)
        {
            OutputDebugString(L"Anchor data length stored in stream; Flushing stream.\n");
            return create_task(m_anchorTcpSocketStreamWriter->FlushAsync());
        }
        else
        {
            OutputDebugString(L"Error: Anchor data length not stored in stream.\n");
            return task_from_result<bool>(false);
        }
    });
}
Concurrency::task<bool> SampleAnchorTcpServer::SendAnchorDataStreamIMap<String^, SpatialAnchor^>^ anchorsToSend)
{
    return SpatialAnchorImportExportHelper::ExportAnchorDataAsync(
        &m_exportedAnchorStoreBytes,
        anchorsToSend
        ).then([this](bool anchorDataExported)
    {
        if (anchorDataExported)
        {
            const size_t arrayLength = m_exportedAnchorStoreBytes.size();
            if (arrayLength > 0)
            {
                OutputDebugString(L"Anchor data was exported; sending data stream length message.\n");
                return SendAnchorDataLengthMessage(arrayLength);
            }
        }
        OutputDebugString(L"Error: Anchor data was not exported.\n");
        // No data to send.
        return task_from_result<bool>(false);

Miután a stream hossza (bájtban) el lett küldve az ügyfélnek, folytathatjuk az adatfolyam írását a szoftvercsatornába. Ennek hatására a horgonytároló bájtja el lesz küldve az ügyfélnek.

}).then([this](bool dataLengthSent)
    {
        if (dataLengthSent)
        {
            OutputDebugString(L"Data stream length message sent; writing exported anchor store bytes to stream.\n");
            m_anchorTcpSocketStreamWriter->WriteBytes(Platform::ArrayReference<byte>(&m_exportedAnchorStoreBytes[0], m_exportedAnchorStoreBytes.size()));
            return create_task(m_anchorTcpSocketStreamWriter->StoreAsync());
        }
        else
        {
            OutputDebugString(L"Error: Data stream length message not sent.\n");
            return task_from_result<size_t>(0);
        }
    }).then([this](unsigned int bytesStored)
    {
        if (bytesStored > 0)
        {
            PrintWstringToDebugConsole(
                std::to_wstring(bytesStored) +
                L" bytes of anchor data written and stored to stream; flushing stream.\n"
                );
        }
        else
        {
            OutputDebugString(L"Error: No anchor data bytes were written to the stream.\n");
        }
        return task_from_result<bool>(false);
    });
}

A témakörben korábban már említettük, fel kell készülnünk arra, hogy kezelni tudjuk a hálózati hibaállapot-üzeneteket tartalmazó kivételeket. A nem várt hibák esetén a kivételadatokat a hibakeresési konzolba írhatja az így: . Ez arra vonatkozó adatokat ad vissza, hogy mi történt, ha a kódminta nem tudja befejezni a kapcsolatot, vagy ha nem tudja befejezni a horgonyadatok küldését.

void SampleAnchorTcpServer::HandleException(Exception^ exception)
{
    PrintWstringToDebugConsole(
        std::wstring(L"Connection error: ") +
        exception->ToString()->Data() +
        L"\n"
        );
}

Használjon egy Windows::Networking::StreamSocket és TCP protokollt az exportált horgonyadatok fogadásához

Először csatlakoznom kell a kiszolgálóhoz. Ez a kódminta bemutatja, hogyan hozhat létre és konfigurálhat egy StreamSocket-et, valamint egy DataReadert, amely segítségével hálózati adatokat szerezhet be a szoftvercsatorna-kapcsolat használatával.

MEGJEGYZÉS: Ha ezt a mintakódot futtatja, az ügyfél elindítása előtt konfigurálja és indítsa el a kiszolgálót.

task<bool> SampleAnchorTcpClient::ConnectToServer()
{
    // Make a local copy to avoid races with Closed events.
    StreamSocket^ streamSocket = m_socketClient;
    // Have we connected yet?
    if (m_socketClient == nullptr)
    {
        OutputDebugString(L"Client is attempting to connect to server.\n");
        EndpointPair^ endpointPair = ref new EndpointPair(
            SampleAnchorTcpCommon::m_clientHost,
            SampleAnchorTcpCommon::m_tcpPort,
            SampleAnchorTcpCommon::m_serverHost,
            SampleAnchorTcpCommon::m_tcpPort
            );
        // Create the web socket connection.
        m_socketClient = ref new StreamSocket();
        // The client connects to the server.
        return create_task(m_socketClient->ConnectAsync(endpointPair, SocketProtectionLevel::PlainSocket)).then([this](task<void> previousTask)
        {
            try
            {
                // Try getting all exceptions from the continuation chain above this point.
                previousTask.get();
                m_anchorTcpSocketStreamReader = ref new DataReader(m_socketClient->InputStream);
                OutputDebugString(L"Client connected!\n");
                m_anchorTcpSocketStreamReader->InputStreamOptions = InputStreamOptions::ReadAhead;
                WaitForAnchorDataStream();
                return true;
            }
            catch (Exception^ exception)
            {
                if (exception->HResult == 0x80072741)
                {
                    // This code sample includes a very simple implementation of client/server
                    // endpoint detection: if the current instance tries to connect to itself,
                    // it is determined to be the server.
                    OutputDebugString(L"Starting up the server instance.\n");
                    // When we return false, we'll start up the server instead.
                    return false;
                }
                else if ((exception->HResult == 0x8007274c) || // connection timed out
                    (exception->HResult == 0x80072740)) // connection maxed at server end
                {
                    // If the connection timed out, try again.
                    ConnectToServer();
                }
                else if (exception->HResult == 0x80072741)
                {
                    // No connection is possible.
                }
                HandleException(exception);
                return true;
            }
        });
    }
    else
    {
        OutputDebugString(L"A StreamSocket connection to a server already exists.\n");
        return task_from_result<bool>(true);
    }
}

A kapcsolat létrehozása után megvárhatja, amíg a kiszolgáló adatokat küld. Ezt a LoadAsync hívásával tudjuk megtenni a streamadat-olvasón.

A bájtok első halmazának mindig a fejléccsomagnak kell lennie, amely a horgonyadatfolyam bájthosszát jelzi az előző szakaszban leírtak szerint.

void SampleAnchorTcpClient::WaitForAnchorDataStream()
{
    if (m_anchorTcpSocketStreamReader == nullptr)
    {
        // We have not connected yet.
        return;
    }
    OutputDebugString(L"Waiting for server message.\n");
    // Wait for the first message, which specifies the byte length of the string data.
    create_task(m_anchorTcpSocketStreamReader->LoadAsync(SampleAnchorTcpCommon::c_streamHeaderByteArrayLength)).then([this](unsigned int numberOfBytes)
    {
        if (numberOfBytes > 0)
        {
            OutputDebugString(L"Server message incoming.\n");
            return ReceiveAnchorDataLengthMessage();
        }
        else
        {
            OutputDebugString(L"0-byte async task received, awaiting server message again.\n");
            WaitForAnchorDataStream();
            return task_from_result<size_t>(0);
        }

...

task<size_t> SampleAnchorTcpClient::ReceiveAnchorDataLengthMessage()
{
    byte data[4];
    m_anchorTcpSocketStreamReader->ReadBytes(Platform::ArrayReference<byte>(data, SampleAnchorTcpCommon::c_streamHeaderByteArrayLength));
    unsigned int lengthMessageSize = *reinterpret_cast<unsigned int*>(data);
    if (lengthMessageSize > 0)
    {
        OutputDebugString(L"One or more anchors to be received.\n");
        return task_from_result<size_t>(lengthMessageSize);
    }
    else
    {
        OutputDebugString(L"No anchors to be received.\n");
        ConnectToServer();
    }
    return task_from_result<size_t>(0);
}

A fejléccsomag fogadása után már tudjuk, hány bájt horgonyadatra számíthatunk. Továbbléphet, hogy beolvassa ezeket a bájtokat a streamből.

}).then([this](size_t dataStreamLength)
    {
        if (dataStreamLength > 0)
        {
            std::wstring debugMessage = std::to_wstring(dataStreamLength);
            debugMessage += L" bytes of anchor data incoming.\n";
            OutputDebugString(debugMessage.c_str());
            // Prepare to receive the data stream in one or more pieces.
            m_anchorStreamLength = dataStreamLength;
            m_exportedAnchorStoreBytes.clear();
            m_exportedAnchorStoreBytes.resize(m_anchorStreamLength);
            OutputDebugString(L"Loading byte stream.\n");
            return ReceiveAnchorDataStream();
        }
        else
        {
            OutputDebugString(L"Error: Anchor data size not received.\n");
            ConnectToServer();
            return task_from_result<bool>(false);
        }
    });
}

Itt van a horgonyadatfolyam fogadására vonatkozó kódunk. Először is betöltjük a bájtokat a streamből; A művelet befejezése némi időt vehet igénybe, mivel a StreamSocket arra vár, hogy megkapja ezt a bájtot a hálózattól.

A betöltési művelet befejezése után ezt a bájtszámot olvashatjuk. Ha a rögzített adatfolyamhoz várt bájtszámot kaptuk, importáljuk a horgonyadatokat; Ha nem, akkor valamilyen hiba történt. Ez például akkor fordulhat elő, ha a kiszolgálópéldány leáll, mielőtt befejezi az adatfolyam küldését, vagy a hálózat leáll, mielőtt a teljes adatfolyamot megkapják az ügyfél.

task<bool> SampleAnchorTcpClient::ReceiveAnchorDataStream()
{
    if (m_anchorStreamLength > 0)
    {
        // First, we load the bytes from the network socket.
        return create_task(m_anchorTcpSocketStreamReader->LoadAsync(m_anchorStreamLength)).then([this](size_t bytesLoadedByStreamReader)
        {
            if (bytesLoadedByStreamReader > 0)
            {
                // Once the bytes are loaded, we can read them from the stream.
                m_anchorTcpSocketStreamReader->ReadBytes(Platform::ArrayReference<byte>(&m_exportedAnchorStoreBytes[0],
                    bytesLoadedByStreamReader));
                // Check status.
                if (bytesLoadedByStreamReader == m_anchorStreamLength)
                {
                    // The whole stream has arrived. We can process the data.
                    // Informational message of progress complete.
                    std::wstring infoMessage = std::to_wstring(bytesLoadedByStreamReader);
                    infoMessage += L" bytes read out of ";
                    infoMessage += std::to_wstring(m_anchorStreamLength);
                    infoMessage += L" total bytes; importing the data.\n";
                    OutputDebugStringW(infoMessage.c_str());
                    // Kick off a thread to wait for a new message indicating another incoming anchor data stream.
                    WaitForAnchorDataStream();
                    // Process the data for the stream we just received.
                    return SpatialAnchorImportExportHelper::ImportAnchorDataAsync(m_exportedAnchorStoreBytes, m_spatialAnchorHelper->GetAnchorMap());
                }
                else
                {
                    OutputDebugString(L"Error: Fewer than expected anchor data bytes were received.\n");
                }
            }
            else
            {
                OutputDebugString(L"Error: No anchor bytes were received.\n");
            }
            return task_from_result<bool>(false);
        });
    }
    else
    {
        OutputDebugString(L"Warning: A zero-length data buffer was sent.\n");
        return task_from_result<bool>(false);
    }
}

Most is fel kell készülnünk az ismeretlen hálózati hibák kezelésével.

void SampleAnchorTcpClient::HandleException(Exception^ exception)
{
    std::wstring error = L"Connection error: ";
    error += exception->ToString()->Data();
    error += L"\n";
    OutputDebugString(error.c_str());
}

Ennyi az egész! Most már elegendő információnak kell lennie ahhoz, hogy megpróbálja megtalálni a hálózaton keresztül fogadott horgonyokat. Ne feledje, hogy az ügyfélnek elegendő vizuális nyomkövetési adatra van szüksége ahhoz, hogy a terület sikeresen megtalálja a horgonyt; Ha nem működik azonnal, próbálkozzon egy kis idő alatt. Ha továbbra sem működik, a kiszolgáló küldjön több horgonyt, és a hálózati kommunikáció segítségével egyeztetsen egy olyanról, amely az ügyféllel működik. Ezt kipróbálhatja a HolographicSpatialAnchorTransferSample letöltésével, az ügyfél és a kiszolgáló IP-jének konfigurálásával, majd az ügyfél- és kiszolgálóoldali eszközökre HoloLens telepítésével.

Lásd még