DirectX'te yerel yer işareti aktarımları
Yerel yer işareti aktarımları, bir HoloLens cihazının ikinci bir HoloLens cihazı tarafından içeri aktarılacak bir yer işareti dışarı aktarmasını sağlar.
Not
iOS ve Android cihazlar bu yaklaşım tarafından desteklenmez.
Not
Bu makaledeki kod parçacıkları şu anda C++holografik proje şablonunda kullanılan C++17 uyumlu C++/WinRT yerine C++/CX kullanımını göstermektedir. Kavramlar bir C++/WinRT projesi için eşdeğerdir, ancak kodu çevirmeniz gerekir.
Uzamsal çapaları aktarma
SpatialAnchorTransferManager'ı kullanarak Windows Karma Gerçeklik cihazları arasında uzamsal tutturucuları aktarabilirsiniz. Bu API, dünyanın tam yerini bulmak için gereken tüm destekleyici algılayıcı verileriyle bir bağlantı noktası paketlemenize ve ardından bu paketi başka bir cihaza aktarmanıza olanak tanır. İkinci cihazdaki uygulama bu tutturucuyu içeri aktardıktan sonra, her uygulama bu paylaşılan uzamsal bağlantının koordinat sistemini kullanarak hologramları işleyebilir ve bu da gerçek dünyada aynı yerde görünür.
Uzamsal tutturucuların farklı cihaz türleri arasında aktarılamadığını unutmayın; örneğin holoLens uzamsal bağlantı, çevreleyici bir kulaklık kullanılarak locatable olmayabilir. Aktarılan tutturucular iOS veya Android cihazlarla da uyumlu değildir.
Uygulamanızı spatialPerception özelliğini kullanacak şekilde ayarlama
Uygulamanıza SpatialAnchorTransferManager'ı kullanabilmesi için önce SpatialPerception özelliğini kullanma izni verilmelidir. Uzamsal bir bağlantının aktarılması, zaman içinde bu tutturucunun çevresinde toplanan algılayıcı görüntülerinin paylaşılmasını içerdiğinden, hassas bilgiler içerebileceği için bu gereklidir.
Bu özelliği uygulamanızın package.appxmanifest dosyasında bildirin. Bir örnek aşağıda verilmiştir:
<Capabilities>
<uap2:Capability Name="spatialPerception" />
</Capabilities>
Bu özellik uap2 ad alanından gelir. Bildiriminizde bu ad alanına erişmek için package> öğesine xlmns özniteliği <olarak ekleyin. Bir örnek aşağıda verilmiştir:
<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"
>
NOT: Uygulamanızın SpatialAnchor dışarı/içeri aktarma API'lerine erişebilmesi için önce çalışma zamanında bu özelliği istemesi gerekir. Aşağıdaki örneklerde RequestAccessAsync bölümüne bakın.
SpatialAnchorTransferManager ile dışarı aktararak yer işareti verilerini seri hale getirme
SpatialAnchor verilerini dışarı aktarmak (serileştirmek) için kod örneğine bir yardımcı işlevi eklenir. Bu dışarı aktarma API'si, dizeleri yer işaretleri ile ilişkilendiren bir anahtar-değer çiftleri koleksiyonundaki tüm tutturucuları seri hale getirmektedir.
// 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
)
{
İlk olarak veri akışını ayarlamamız gerekir. Bu, 1'e kadar izin verir.) TryExportAnchorsAsync kullanarak verileri uygulamaya ait bir arabelleğe yerleştirin ve 2.) bir WinRT veri akışı olan dışarı aktarılan bayt arabellek akışından verileri std::vector<bayt> olan kendi bellek arabelleğimize okuma.
// 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);
Sistem tarafından dışarı aktarılan yer işaretleri de dahil olmak üzere uzamsal verilere erişmek için izin istememiz gerekir.
// 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);
}
});
İzin alırsak ve yer işaretleri dışarı aktarılırsa veri akışını okuyabiliriz. Burada, verileri okumak için kullanacağımız DataReader ve InputStream'in nasıl oluşturulacağını da göstereceğiz.
// 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);
}
Akıştan baytları okuduktan sonra bunları kendi veri arabelleğimize kaydedebiliriz.
}).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;
}
});
};
Yer işareti verilerini SpatialAnchorTransferManager kullanarak sisteme aktararak seri durumdan çıkarma
Daha önce dışarı aktarılan verileri yüklemek için kod örneğine bir yardımcı işlevi eklenir. Bu seri durumdan çıkarma işlevi, SpatialAnchorStore'nun sağladığına benzer bir anahtar-değer çifti koleksiyonu sağlar; ancak bu verileri ağ yuvası gibi başka bir kaynaktan aldık. Bu verileri çevrimdışı depolamadan önce, uygulama içi bellek kullanarak veya (varsa) uygulamanızın SpatialAnchorStore'sunda işleyebilir ve bunun nedenini belirleyebilirsiniz.
// 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
)
{
İlk olarak, bağlantı verilerine erişmek için akış nesneleri oluşturmamız gerekir. Verileri arabelleğimizden bir sistem arabelleğine yazacağız, bu nedenle bir bayt arabelleğinden sisteme SpatialAnchors olarak yer işaretleri alma hedefimizi gerçekleştirmek için bellek içi veri akışına yazan bir DataWriter oluşturacağız.
// 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);
Bir kez daha, uygulamanın uzamsal yer işareti verilerini dışarı aktarma izni olduğundan emin olmamız gerekir. Bu, kullanıcının ortamı hakkında özel bilgiler içerebilir.
// Request access to transfer spatial anchors.
return create_task(SpatialAnchorTransferManager::RequestAccessAsync()).then(
[&anchorByteDataIn, writer](SpatialPerceptionAccessStatus status)
{
if (status == SpatialPerceptionAccessStatus::Allowed)
{
// Access is allowed.
Erişime izin veriliyorsa, arabellekten sistem veri akışına bayt yazabiliriz.
// 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);
}
Veri akışında bayt depolamada başarılı olursak SpatialAnchorTransferManager kullanarak bu verileri içeri aktarmayı deneyebiliriz.
}).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);
}
Veriler içeri aktarılabiliyorsa dizeleri tutturucularla ilişkilendiren anahtar-değer çiftlerinin harita görünümünü elde ederiz. Bunu kendi bellek içi veri koleksiyonumuza yükleyebilir ve kullanmak istediğimiz bağlantıları aramak için bu koleksiyonu kullanabiliriz.
}).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;
});
}
NOT: Bir tutturucu içeri aktarabilmeniz, bunu hemen kullanabileceğiniz anlamına gelmez. Yer işareti farklı bir odada veya tamamen başka bir fiziksel konumda olabilir; yer işareti, onu alan cihaz, tutturucunun bilinen geçerli ortama göre konumunu geri yüklemek için tutturucunun oluşturulduğu ortam hakkında yeterli görsel bilgiye sahip olana kadar locatable olmaz. İstemci uygulaması, canlı içerik için kullanmaya devam etmeden önce yerel koordinat sisteminize veya başvuru çerçevenize göre tutturucuyu bulmaya çalışmalıdır. Örneğin, tutturucu locatable olmaya başlayana kadar sabit noktasını geçerli koordinat sistemine göre düzenli aralıklarla bulmayı deneyin.
Dikkat Edilmesi Gereken Özel Noktalar
TryExportAnchorsAsync API'si, birden çok SpatialAnchor'ın aynı opak ikili bloba dışarı aktarılmasını sağlar. Ancak, tek bir SpatialAnchor veya birden çok SpatialAnchor'ın tek bir çağrıda dışarı aktarılıp aktarılmadığına bağlı olarak blob'un hangi verileri içereceği konusunda küçük bir fark vardır.
Tek bir SpatialAnchor'u dışarı aktarma
Blob, SpatialAnchor'un çevresindeki ortamın bir gösterimini içerir, böylece ortam SpatialAnchor'u içeri aktaran cihazda tanınabilir. İçeri aktarma işlemi tamamlandıktan sonra yeni SpatialAnchor cihaz tarafından kullanılabilir hale gelecek. Kullanıcının yakın zamanda tutturucunun yakınında olduğunu varsayarsak, locatable olur ve SpatialAnchor'a bağlı hologramlar işlenebilir. Bu hologramlar, SpatialAnchor'u dışarı aktaran orijinal cihazda yaptıkları fiziksel konumda gösterilir.
Birden çok SpatialAnchor dışarı aktarma
Tek bir SpatialAnchor'un dışarı aktarması gibi blob da belirtilen tüm SpatialAnchor'ların yakınında ortamın bir gösterimini içerir. Ayrıca blob, dahil edilen SpatialAnchor'lar arasındaki bağlantılar (aynı fiziksel alanda bulunuyorlarsa) hakkında bilgi içerir. Bu, yakındaki iki SpatialAnchor içeri aktarılırsa, bloba iki SpatialAnchor arasındaki dönüşümü hesaplamak için yeterli veri bulunduğundan cihaz yalnızca ilk SpatialAnchor etrafındaki ortamı tanısa bile ikinci SpatialAnchor'a bağlı bir hologramın locatable olacağı anlamına gelir. İki SpatialAnchor ayrı ayrı dışarı aktarıldıysa (TryExportSpatialAnchors'a yapılan iki ayrı çağrı) blobda ikinci SpatialAnchor'a bağlı hologramların ilki bulunduğunda locatable olması için yeterli veri bulunmayabilir.
Örnek: Windows::Networking::StreamSocket kullanarak bağlayıcı verileri gönderme
Burada, dışarı aktarılan yer işareti verilerini bir TCP ağı üzerinden göndererek nasıl kullanacağınıza yönelik bir örnek sunuyoruz. Bu, HolographicSpatialAnchorTransferSample'dan alınıyor.
WinRT StreamSocket sınıfı PPL görev kitaplığını kullanır. Ağ hataları söz konusu olduğunda, hata yeniden oluşan bir özel durum kullanılarak zincirdeki bir sonraki göreve döndürülür. Özel durum, hata durumunu gösteren bir HRESULT içerir.
Dışarı aktarılan bağlantı verilerini göndermek için TCP ile Windows::Networking::StreamSocketListener kullanma
Bağlantıyı dinleyen bir sunucu örneği oluşturun.
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");
}
}
Bir bağlantı alındığında, bağlantı noktası verilerini göndermek için istemci yuva bağlantısını kullanın.
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());
}
}
Artık dışarı aktarılan yer işareti verilerini içeren bir veri akışı göndermeye başlayabiliriz.
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");
}
});
}
Akışın kendisini göndermeden önce bir üst bilgi paketi göndermemiz gerekir. Bu üst bilgi paketinin sabit uzunlukta olması ve sabit veri akışı olan bayt değişken dizisinin uzunluğunu da belirtmesi gerekir; bu örnekte gönderilecek başka üst bilgi verimiz olmadığından üst bilgimiz 4 bayt uzunluğundadır ve 32 bit işaretsiz bir tamsayı içerir.
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);
Akış uzunluğu bayt cinsinden istemciye gönderildikten sonra veri akışının kendisini yuva akışına yazmaya devam edebiliriz. Bu, sabit depolama baytlarının istemciye gönderilmesine neden olur.
}).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);
});
}
Bu konu başlığında daha önce belirtildiği gibi, ağ hata durumu iletilerini içeren özel durumları işlemeye hazırlıklı olmamız gerekir. Beklenmeyen hatalar için hata ayıklama konsoluna özel durum bilgilerini yazabiliriz. Bu, kod örneğimiz bağlantıyı tamamlayamazsa veya bağlantı verilerini göndermeyi bitiremezse ne olduğuna ilişkin bir ipucu verir.
void SampleAnchorTcpServer::HandleException(Exception^ exception)
{
PrintWstringToDebugConsole(
std::wstring(L"Connection error: ") +
exception->ToString()->Data() +
L"\n"
);
}
Dışarı aktarılan bağlantı verilerini almak için TCP ile Windows::Networking::StreamSocket kullanma
İlk olarak sunucuya bağlanmamız gerekir. Bu kod örneği, StreamSocket oluşturma ve yapılandırmayı ve yuva bağlantısını kullanarak ağ verilerini almak için kullanabileceğiniz bir DataReader oluşturmayı gösterir.
NOT: Bu örnek kodu çalıştırırsanız, istemciyi başlatmadan önce sunucuyu yapılandırdığınızdan ve başlattığınızdan emin olun.
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);
}
}
Bir bağlantımız olduğunda sunucunun veri göndermesini bekleyebiliriz. Bunu, akış veri okuyucusunun LoadAsync'in çağrısıyla yaparız.
Aldığımız ilk bayt kümesi her zaman, önceki bölümde açıklandığı gibi bağlantı veri akışı bayt uzunluğunu gösteren üst bilgi paketi olmalıdır.
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);
}
Üst bilgi paketini aldıktan sonra, kaç bayt bağlantı verisi beklememiz gerektiğini biliyoruz. Bu baytları akıştan okumaya devam edebiliriz.
}).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);
}
});
}
Yer işareti veri akışını almak için kodumuz aşağıdadır. Yine, önce akıştan baytları yükleyeceğiz; StreamSocket ağdan bu miktarda bayt almayı beklediğinden bu işlemin tamamlanması biraz zaman alabilir.
Yükleme işlemi tamamlandığında, bu bayt sayısını okuyabiliriz. Yer işareti veri akışı için beklediğimiz bayt sayısını aldıysak, devam edebilir ve yer işareti verilerini içeri aktarabiliriz; aksi takdirde, bir tür hata olmalı. Örneğin, sunucu örneği veri akışını göndermeyi bitirmeden sonlandırıldığında veya tüm veri akışının istemci tarafından alınabilmesi için ağ kapandığında bu durum oluşabilir.
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);
}
}
Yine bilinmeyen ağ hatalarını işlemeye hazırlıklı olmamız gerekir.
void SampleAnchorTcpClient::HandleException(Exception^ exception)
{
std::wstring error = L"Connection error: ";
error += exception->ToString()->Data();
error += L"\n";
OutputDebugString(error.c_str());
}
İşte hepsi bu! Şimdi, ağ üzerinden alınan tutturucuları bulmaya çalışmak için yeterli bilgiye sahip olmanız gerekir. Yine, istemcinin yer işaretine başarılı bir şekilde yer bulabilmesi için yeterli görsel izleme verilerine sahip olması gerektiğini unutmayın; hemen işe yaramazsa bir süre gezinmeyi deneyin. Yine de işe yaramazsa, sunucunun daha fazla yer işareti göndermesini sağlayın ve istemci için çalışan bir bağlantı üzerinde anlaşmak için ağ iletişimlerini kullanın. HolographicSpatialAnchorTransferSample'ı indirerek, istemci ve sunucu IP'lerinizi yapılandırarak ve bunu istemci ve sunucu HoloLens cihazlarına dağıtarak bunu deneyebilirsiniz.