Megosztás a következőn keresztül:


Helyi horgonyátvitelek a DirectX-ben

A helyi horgonyátvitelek lehetővé teszik, hogy egy HoloLens-eszköz exportáljon egy horgonyt, amelyet egy második HoloLens-eszköz importál.

Feljegyzés

Ez a megközelítés nem támogatja az iOS- és Android-eszközöket.

Feljegyzés

A cikkben szereplő kódrészletek jelenleg a C++/CX használatát mutatják be a C++17-kompatibilis C++/WinRT helyett, ahogyan a C++ holografikus projektsablonban használják. A fogalmak egyenértékűek egy C++/WinRT-projekt esetében, de a kódot le kell fordítania.

Térbeli horgonyok átvitele

A Térbeli horgonyok a Windows Mixed Reality-eszközök között a SpatialAnchorTransferManager használatával továbbíthatók. Ez az API lehetővé teszi, hogy egy horgonyt csomagoljon össze a világon található pontos hely megtalálásához szükséges összes támogató érzékelőadattal, majd importálja azt egy másik eszközön. Miután a második eszközön az alkalmazás importálta ezt a horgonyt, minden alkalmazás képes hologramokat renderelni a megosztott térbeli horgony koordináta-rendszerével, amely ezután a valós világban ugyanazon a helyen jelenik meg.

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

Az alkalmazás beállítása a spatialPerception képesség használatára

Ahhoz, hogy használni tudja a SpatialAnchorTransferManagert, az alkalmazásnak engedélyt kell adnia a SpatialPerception képesség használatára. Erre azért van szükség, mert a térbeli horgony átvitele magában foglalja a horgony közelében ö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. Példa:

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

A képesség az uap2 névtérből származik. Ha hozzá szeretne férni ehhez a névtérhez a jegyzékben, vegye fel xlmns attribútumként a <Csomag> elembe. Példa:

<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 kell kérnie a képességet, mielőtt hozzáférhet a SpatialAnchor exportálási/importálási API-khoz. Lásd a RequestAccessAsync-et az alábbi példákban.

Horgonyadatok szerializálása a SpatialAnchorTransferManagerrel való exportálással

A kódminta tartalmaz egy segédfüggvényt a SpatialAnchor-adatok exportálásához (szerializálásához). Ez az exportálási API szerializálja a kulcs-érték párok gyűjteményében lévő összes horgonyt, és sztringeket társít a horgonyokhoz.

// 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 állítani az adatfolyamot. Ez lehetővé teszi számunkra, hogy 1.) a TryExportAnchorsAsync használatával helyezze az adatokat az alkalmazás tulajdonában lévő pufferbe, és 2.) adatokat olvas be az exportált bájtpufferfolyamból – amely 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 adatokhoz való hozzáféréshez, 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 kapunk engedélyt, és a horgonyokat exportáljuk, elolvashatjuk az adatfolyamot. Itt azt is bemutatjuk, hogyan hozhatja létre a DataReadert és az InputStreamet, amelyet az adatok olvasásához fogunk használni.

// 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 beolvastuk a bájtokat a streamből, így menthetjük őket a saját adatpufferünkbe.

}).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ással a SpatialAnchorTransferManager használatával

A kódminta tartalmaz egy segédfüggvényt a korábban exportált adatok betöltéséhez. Ez a deszerializálási függvény kulcs-érték párok gyűjteményét biztosítja, hasonlóan ahhoz, amit a SpatialAnchorStore biztosít – kivéve, hogy ezeket az adatokat egy másik forrásból, például egy hálózati szoftvercsatornából szereztük be. Ezeket az adatokat feldolgozhatja és indokolhatja, mielőtt offline, alkalmazáson belüli memóriával vagy (ha van) az alkalmazás SpatialAnchorStore-jával tárolhatja azokat.

// 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órián belüli adatfolyamba ír, hogy megvalósítsuk a célunkat, hogy a bájtpuffer horgonyait SpatialAnchorsként lehessen a rendszerbe bevinni.

// 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ás rendelkezik engedéllyel a térbeli horgonyadatok exportálására, amely magában foglalhatja a felhasználó környezetével kapcsolatos személyes adatokat.

// 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élyezve van, bájtokat írhatunk a pufferből egy 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árolnunk az adatfolyamban, megpróbálhatjuk 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, térképnézetet kapunk a kulcs-érték párokról, és a sztringeket horgonyokkal társítjuk. Ezt betölthetjük a saját memóriabeli adatgyűjtésünkbe, és ezzel a gyűjteménysel megkereshetjük azokat a horgonyokat, amelyeket használni szeretnénk.

}).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: Csak azért, mert importálhat egy horgonyt, nem feltétlenül jelenti azt, hogy azonnal használhatja. Előfordulhat, hogy a horgony egy másik szobában van, vagy teljesen más fizikai helyen; a horgony nem lesz elérhető, amíg a kapott 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 jelenlegi környezethez képest. Az ügyfél-implementációnak meg kell próbálnia a horgonyt a helyi koordinátarendszerhez vagy referenciakerethez viszonyítva, mielőtt folytatná az élő tartalomhoz való használatát. Próbálja meg például a horgonyt egy aktuális koordinátarendszerhez viszonyítva rendszeres időközönként keresni, amíg a horgony el nem kezd lekérni.

Speciális szempontok

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

Egyetlen SpatialAnchor exportálása

A blob a SpatialAnchor közelében található környezet ábrázolását tartalmazza, í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ővé válik az eszköz számára. Feltéve, hogy a felhasználó nemrég járt a horgony közelében, akkor az el lesz helyezhető, és a SpatialAnchorhoz csatolt hologramok renderelhetők. Ezek a hologramok ugyanabban a fizikai helyen jelennek meg, mint az eredeti eszközön, amely exportálta a SpatialAnchort.

Egyetlen SpatialAnchor exportálása

Több SpatialAnchor exportálása

Az egyetlen SpatialAnchor exportálásához hasonlóan a blob az összes megadott SpatialAnchor közelében található környezet ábrázolását tartalmazza. A blob emellett információkat is tartalmaz a mellékelt SpatialAnchorsok közötti kapcsolatokról, ha azok ugyanabban a fizikai térben találhatók. Ez azt jelenti, hogy ha két közeli SpatialAnchor importálva van, akkor a második SpatialAnchorhoz csatolt hologram akkor is megtalálható lenne, ha az eszköz csak az első SpatialAnchor körüli környezetet ismeri fel, mivel a blob tartalmazott elegendő adatot a két SpatialAnchor közötti átalakítás kiszámításához. Ha a két SpatialAnchort egyenként exportálták (két különálló hívás a TryExportSpatialAnchorshoz), akkor előfordulhat, hogy a blob nem tartalmaz elegendő adatot a második SpatialAnchorhoz csatolt hologramok számára, hogy az első helyének helye legyen.

Több horgony exportálva egyetlen TryExportAnchorsAsync-hívással Több horgony exportálása külön TryExportAnchorsAsync hívással minden horgonyhoz

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

Itt bemutatunk egy példát arra, hogyan használhatja az exportált horgonyadatokat egy TCP-hálózaton keresztüli küldéssel. Ez a HolographicSpatialAnchorTransferSample-ből származik.

A WinRT StreamSocket osztály a PPL feladattárat használja. Hálózati hibák esetén a rendszer egy újradobott kivétellel visszaadja a hibát a lánc következő tevékenységének. A kivétel tartalmaz egy HRESULT-t, amely a hiba állapotát jelzi.

Windows::Hálózatkezelés::StreamSocketListener és TCP használatával exportált horgonyadatok küldése

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

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");
    }
}

A kapcsolat fogadásakor az ügyfél szoftvercsatorna-kapcsolatá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 kü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");
        }
    });
}

Mielőtt elküldhetnénk magát a streamet, 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 jelölnie a horgonyadat-adatfolyamként használt bájtok változó tömbjének hosszát is; ebben a példában nincs más küldendő fejlécadatunk, így a fejlécünk 4 bájt hosszú, és 32 bites, aláíratlan egész számot tartalmaz.

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 lett elküldve az ügyfélnek, folytathatjuk az adatfolyam írását a szoftvercsatorna-adatfolyamba. Ez azt eredményezi, hogy 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);
    });
}

Amint azt a jelen témakör korábbi részében is említettük, készen kell állnunk a hálózati hibaállapot-üzeneteket tartalmazó kivételek kezelésére. A nem várt hibák esetén a kivételadatokat a hibakeresési konzolba is megírhatjuk. Ez azt jelzi, 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"
        );
}

Windows::Hálózatkezelés::StreamSocket és TCP használata az exportált horgonyadatok fogadásához

Először is csatlakozni kell a kiszolgálóhoz. Ez a kódminta bemutatja, hogyan hozhat létre és konfigurálhat StreamSocketet, és hogyan hozhat létre egy DataReadert, amellyel hálózati adatokat szerezhet be a szoftvercsatorna-kapcsolat használatával.

MEGJEGYZÉS: Ha ezt a mintakódot futtatja, győződjön meg arról, hogy az ügyfél indí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);
    }
}

Miután létrejött a kapcsolat, megvárhatjuk, amíg a kiszolgáló adatokat küld. Ezt úgy hajtjuk végre, hogy meghívjuk a LoadAsyncet a streamadat-olvasón.

A kapott bájtok első halmazának mindig a fejléccsomagnak kell lennie, amely az előző szakaszban leírt horgonyadat-adatfolyam bájthosszát jelzi.

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);
}

Miután megkapta a fejléccsomagot, tudjuk, hogy hány bájtnyi horgonyadatot kell várnunk. Folytathatjuk a bájtok olvasását 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 találja a horgonyadat-adatfolyam fogadásának kódját. Ismét betöltjük a bájtokat a streamből; ez a művelet eltarthat egy ideig, amíg a StreamSocket arra vár, hogy megkapja ezt a bájtmennyiséget a hálózatról.

Ha a betöltési művelet befejeződött, beolvashatjuk a bájtok számát. Ha megkaptuk a horgonyadat-adatfolyamhoz várt bájtok számát, továbbléphetünk, és importálhatjuk 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 befejezené az adatfolyam küldését, vagy a hálózat leáll, mielőtt a teljes adatfolyamot fogadhatja 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);
    }
}

Ismét fel kell készülnünk az ismeretlen hálózati hibák kezelésére.

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 elegendő információval kell rendelkeznie ahhoz, hogy megpróbálja a hálózaton keresztül kapott horgonyokat lekérni. Vegye figyelembe, hogy az ügyfélnek elegendő vizualizációkövetési adatokkal kell rendelkeznie ahhoz, hogy a hely sikeresen megtalálhassa a horgonyt; ha nem működik azonnal, próbálkozzon egy ideig. Ha még mindig nem működik, a kiszolgáló küldjön több horgonyt, és használjon hálózati kommunikációt az ügyfél számára megfelelő megoldás elfogadásához. Ezt kipróbálhatja a HolographicSpatialAnchorTransferSample letöltésével, az ügyfél- és kiszolgáló IP-címeinek konfigurálásával, valamint az ügyfél- és kiszolgálói HoloLens-eszközökön való üzembe helyezésével.

Lásd még