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.
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.
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.