Lokala fästpunktsöverföringar i DirectX
Med lokala fästpunktsöverföringar kan en HoloLens-enhet exportera en fästpunkt som ska importeras av en andra HoloLens-enhet.
Kommentar
iOS- och Android-enheter stöds inte av den här metoden.
Kommentar
Kodfragmenten i den här artikeln visar för närvarande användningen av C++/CX i stället för C++17-kompatibel C++/WinRT som används i C++-holografisk projektmall. Begreppen är likvärdiga för ett C++/WinRT-projekt, men du måste översätta koden.
Överföra spatiala fästpunkter
Du kan överföra spatiala fästpunkter mellan Windows Mixed Reality-enheter med hjälp av SpatialAnchorTransferManager. Med det här API:et kan du paketera en fästpunkt med alla stödsensordata som behövs för att hitta den exakta platsen i världen och sedan importera paketet på en annan enhet. När appen på den andra enheten har importerat fästpunkten kan varje app återge hologram med hjälp av koordinatsystemet för den delade rumsliga fästpunkten, som sedan visas på samma plats i verkligheten.
Observera att rumsliga fästpunkter inte kan överföras mellan olika enhetstyper, till exempel kan en rumslig HoloLens-fästpunkt inte vara locatable med hjälp av ett uppslukande headset. Överförda fästpunkter är inte heller kompatibla med iOS- eller Android-enheter.
Konfigurera din app så att den använder spatialPerception-funktionen
Appen måste beviljas behörighet att använda spatialperception-funktionen innan den kan använda SpatialAnchorTransferManager. Detta är nödvändigt eftersom överföring av en rumslig fästpunkt innebär att dela sensorbilder som samlats in över tid i närheten av fästpunkten, vilket kan innehålla känslig information.
Deklarera den här funktionen i filen package.appxmanifest för din app. Här är ett exempel:
<Capabilities>
<uap2:Capability Name="spatialPerception" />
</Capabilities>
Funktionen kommer från uap2-namnområdet . Om du vill få åtkomst till det här namnområdet i manifestet tar du med det som ett xlmns-attribut i <paketelementet> . Här är ett exempel:
<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"
>
Obs! Din app måste begära funktionen vid körning innan den kan komma åt SpatialAnchor-export-/import-API:er. Se RequestAccessAsync i exemplen nedan.
Serialisera ankardata genom att exportera dem med SpatialAnchorTransferManager
En hjälpfunktion ingår i kodexemplet för att exportera (serialisera) SpatialAnchor-data . Det här export-API:et serialiserar alla fästpunkter i en samling nyckel/värde-par som associerar strängar med fästpunkter.
// 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
)
{
Först måste vi konfigurera dataströmmen. Detta gör att vi kan 1.) använd TryExportAnchorsAsync för att placera data i en buffert som ägs av appen och 2.) läsa data från den exporterade bytebuffertströmmen – som är en WinRT-dataström – till vår egen minnesbuffert, som är en std::vector<byte>.
// 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);
Vi måste be om behörighet att komma åt rumsliga data, inklusive fästpunkter som exporteras av systemet.
// 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);
}
});
Om vi får behörighet och fästpunkter exporteras kan vi läsa dataströmmen. Här visar vi också hur du skapar DataReader och InputStream som vi ska använda för att läsa data.
// 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);
}
När vi har läst byte från strömmen kan vi spara dem i vår egen databuffert så här.
}).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;
}
});
};
Deserialisera ankardata genom att importera dem till systemet med hjälp av SpatialAnchorTransferManager
En hjälpfunktion ingår i kodexemplet för att läsa in tidigare exporterade data. Den här deserialiseringsfunktionen tillhandahåller en samling nyckel/värde-par som liknar vad SpatialAnchorStore tillhandahåller – förutom att vi fick dessa data från en annan källa, till exempel en nätverkssocket. Du kan bearbeta och resonera om dessa data innan du lagrar dem offline, med hjälp av appens minne eller (om tillämpligt) appens SpatialAnchorStore.
// 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
)
{
Först måste vi skapa strömobjekt för att komma åt fästpunktsdata. Vi kommer att skriva data från vår buffert till en systembuffert, så vi skapar en DataWriter som skriver till en minnesintern dataström för att uppnå vårt mål att hämta fästpunkter från en bytebuffert till systemet som SpatialAnchors.
// 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);
Återigen måste vi se till att appen har behörighet att exportera spatiala fästpunktsdata, vilket kan innehålla privat information om användarens miljö.
// Request access to transfer spatial anchors.
return create_task(SpatialAnchorTransferManager::RequestAccessAsync()).then(
[&anchorByteDataIn, writer](SpatialPerceptionAccessStatus status)
{
if (status == SpatialPerceptionAccessStatus::Allowed)
{
// Access is allowed.
Om åtkomst tillåts kan vi skriva byte från bufferten till en systemdataström.
// 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);
}
Om vi lyckades lagra byte i dataströmmen kan vi försöka importera dessa data med hjälp av SpatialAnchorTransferManager.
}).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);
}
Om data kan importeras får vi en kartvy över nyckel/värde-par som associerar strängar med fästpunkter. Vi kan läsa in detta i vår egen minnesintern datainsamling och använda den samlingen för att leta efter fästpunkter som vi är intresserade av att använda.
}).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;
});
}
Obs! Bara för att du kan importera en fästpunkt betyder det inte nödvändigtvis att du kan använda den direkt. Fästpunkten kan finnas i ett annat rum eller en helt annan fysisk plats. fästpunkten kommer inte att vara locatable förrän enheten som tog emot den har tillräckligt med visuell information om miljön som fästpunkten skapades i, för att återställa fästpunktens position i förhållande till den kända aktuella miljön. Klientimplementeringen bör försöka hitta fästpunkten i förhållande till det lokala koordinatsystemet eller referensramen innan du fortsätter att försöka använda den för liveinnehåll. Prova till exempel att hitta fästpunkten i förhållande till ett aktuellt koordinatsystem regelbundet tills fästpunkten börjar vara locatable.
Särskilda överväganden
Api:et TryExportAnchorsAsync tillåter att flera SpatialAnchors exporteras till samma täckande binära blob. Det finns dock en subtil skillnad i vilka data bloben ska innehålla, beroende på om en enda SpatialAnchor eller flera SpatialAnchors exporteras i ett enda anrop.
Export av en enskild SpatialAnchor
Blobben innehåller en representation av miljön i närheten av SpatialAnchor så att miljön kan identifieras på den enhet som importerar SpatialAnchor. När importen är klar kommer den nya SpatialAnchor att vara tillgänglig för enheten. Förutsatt att användaren nyligen har varit i närheten av fästpunkten blir den locatable och hologram som är kopplade till SpatialAnchor kan renderas. Dessa hologram visas på samma fysiska plats som de gjorde på den ursprungliga enheten som exporterade SpatialAnchor.
Export av flera SpatialAnchors
Precis som exporten av en enda SpatialAnchor innehåller blobben en representation av miljön i närheten av alla angivna SpatialAnchors. Dessutom innehåller blobben information om anslutningarna mellan de inkluderade SpatialAnchors, om de finns i samma fysiska utrymme. Det innebär att om två närliggande SpatialAnchors importeras skulle ett hologram som är kopplat till den andra SpatialAnchor vara locatable även om enheten bara känner igen miljön runt den första SpatialAnchor, eftersom tillräckligt med data för att beräkna transformering mellan de två SpatialAnchors inkluderades i bloben. Om de två SpatialAnchors exporterades individuellt (två separata anrop till TryExportSpatialAnchors) kanske det inte finns tillräckligt med data i blobben för att hologram som är kopplade till den andra SpatialAnchor ska vara locatable när den första finns.
Exempel: Skicka fästpunktsdata med hjälp av en Windows::Networking::StreamSocket
Här ger vi ett exempel på hur du använder exporterade fästpunktsdata genom att skicka dem över ett TCP-nätverk. Detta kommer från HolographicSpatialAnchorTransferSample.
Klassen WinRT StreamSocket använder PPL-aktivitetsbiblioteket. Vid nätverksfel returneras felet till nästa uppgift i kedjan med hjälp av ett undantag som genereras igen. Undantaget innehåller en HRESULT som anger felstatusen.
Använd en Windows::Networking::StreamSocketListener med TCP för att skicka exporterade fästpunktsdata
Skapa en serverinstans som lyssnar efter en anslutning.
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");
}
}
När en anslutning tas emot använder du klientanslutningen för att skicka fästpunktsdata.
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());
}
}
Nu kan vi börja skicka en dataström som innehåller exporterade fästpunktsdata.
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");
}
});
}
Innan vi kan skicka själva dataströmmen måste vi först skicka ett huvudpaket. Det här huvudpaketet måste ha fast längd och måste också ange längden på den variabelmatris med byte som är ankardataströmmen. I det här exemplet har vi inga andra sidhuvuddata att skicka, så rubriken är 4 byte lång och innehåller ett 32-bitars heltal utan tecken.
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);
När strömlängden i byte har skickats till klienten kan vi fortsätta att skriva själva dataströmmen till socketströmmen. Detta gör att fästpunktslagerbyte skickas till klienten.
}).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);
});
}
Som tidigare nämnts i det här avsnittet måste vi vara beredda att hantera undantag som innehåller meddelanden om nätverksfelstatus. För fel som inte är förväntade kan vi skriva undantagsinformationen till felsökningskonsolen så här. Detta ger oss en ledtråd om vad som hände om vårt kodexempel inte kan slutföra anslutningen eller om den inte kan slutföra sändningen av fästpunktsdata.
void SampleAnchorTcpServer::HandleException(Exception^ exception)
{
PrintWstringToDebugConsole(
std::wstring(L"Connection error: ") +
exception->ToString()->Data() +
L"\n"
);
}
Använd en Windows::Networking::StreamSocket med TCP för att ta emot exporterade fästpunktsdata
Först måste vi ansluta till servern. Det här kodexemplet visar hur du skapar och konfigurerar en StreamSocket och skapar en DataReader som du kan använda för att hämta nätverksdata med socketanslutningen.
Obs! Om du kör den här exempelkoden kontrollerar du att du konfigurerar och startar servern innan du startar klienten.
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);
}
}
När vi har en anslutning kan vi vänta tills servern skickar data. Det gör vi genom att anropa LoadAsync i dataströmsläsaren.
Den första uppsättningen byte som vi får bör alltid vara huvudpaketet, vilket anger längden på ankardataströmmens byte enligt beskrivningen i föregående avsnitt.
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);
}
När vi har tagit emot huvudpaketet vet vi hur många byte av fästpunktsdata vi bör förvänta oss. Vi kan fortsätta att läsa dessa byte från strömmen.
}).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);
}
});
}
Här är vår kod för att ta emot ankardataströmmen. Återigen läser vi först in byte från strömmen. Den här åtgärden kan ta lite tid att slutföra eftersom StreamSocket väntar på att få den mängden byte från nätverket.
När inläsningsåtgärden är klar kan vi läsa det antalet byte. Om vi fick det antal byte som vi förväntar oss för ankardataströmmen kan vi fortsätta och importera fästpunktsdata. om inte, måste det ha uppstått något slags fel. Detta kan till exempel inträffa när serverinstansen avslutas innan den kan slutföra sändningen av dataströmmen, eller om nätverket slutar fungera innan hela dataströmmen kan tas emot av klienten.
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);
}
}
Återigen måste vi vara beredda att hantera okända nätverksfel.
void SampleAnchorTcpClient::HandleException(Exception^ exception)
{
std::wstring error = L"Connection error: ";
error += exception->ToString()->Data();
error += L"\n";
OutputDebugString(error.c_str());
}
Det var allt! Nu bör du ha tillräckligt med information för att försöka hitta fästpunkterna som tas emot över nätverket. Observera återigen att klienten måste ha tillräckligt med visuella spårningsdata för att utrymmet ska kunna hitta fästpunkten. Om det inte fungerar direkt kan du prova att gå runt ett tag. Om det fortfarande inte fungerar låter du servern skicka fler fästpunkter och använda nätverkskommunikation för att komma överens om en som fungerar för klienten. Du kan prova detta genom att ladda ned HolographicSpatialAnchorTransferSample, konfigurera klient- och server-IP-adresser och distribuera det till klient- och server-HoloLens-enheter.