AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
GenericNetworkTransmitter.cs
Go to the documentation of this file.
1 // Copyright (c) Microsoft Corporation. All rights reserved.
2 // Licensed under the MIT License. See LICENSE in the project root for license information.
3 
4 using System;
5 using System.Collections.Generic;
6 using UnityEngine;
7 
8 #if !UNITY_EDITOR && UNITY_WSA && !(ENABLE_IL2CPP && NET_STANDARD_2_0)
9 using Windows.Networking.Sockets;
10 using Windows.Storage.Streams;
11 using Windows.Networking;
12 using Windows.Foundation;
13 using System.Threading.Tasks;
14 #endif
15 
16 namespace HoloToolkit.Unity.SharingWithUNET
17 {
21  public class GenericNetworkTransmitter : Singleton<GenericNetworkTransmitter>
22  {
23  [Tooltip("The connection port on the machine to use.")]
24  public int SendConnectionPort = 11000;
25 
30  public delegate void OnDataReady(byte[] data);
31 
32 #if UNITY_WSA && !(ENABLE_IL2CPP && NET_STANDARD_2_0)
33  public event OnDataReady DataReadyEvent;
34 #endif
35 
39  private string serverIp;
40 
44  private bool waitingForConnection = false;
45 
49  private byte[] mostRecentDataBuffer;
50 
51 #if !UNITY_EDITOR && UNITY_WSA && !(ENABLE_IL2CPP && NET_STANDARD_2_0)
52  private StreamSocket networkConnection;
56 
60  private StreamSocketListener networkListener;
61 
65  private float timeToDeferFailedConnections = 10.0f;
66 #endif
67 
72  public void SetData(byte[] data)
73  {
74  mostRecentDataBuffer = data;
75  }
76 
81  public void SetServerIp(string newServerIp)
82  {
83  serverIp = newServerIp.Trim();
84  }
85 
90  public bool RequestAndGetData()
91  {
92  return ConnectListener();
93  }
94 
95  private Queue<Action> DeferredActionQueue = new Queue<Action>();
96 
97  private void Update()
98  {
99  lock (DeferredActionQueue)
100  {
101  while (DeferredActionQueue.Count > 0)
102  {
103  DeferredActionQueue.Dequeue()();
104  }
105  }
106  }
107 
108  // A lot of the work done in this class can only be done in UWP. The editor is not a UWP app.
109 #if !UNITY_EDITOR && UNITY_WSA && !(ENABLE_IL2CPP && NET_STANDARD_2_0)
110  private void RequestDataRetry()
111  {
112  if (!RequestAndGetData())
113  {
114  Invoke("RequestDataRetry", timeToDeferFailedConnections);
115  }
116  }
117 #endif
118 
122  public void ConfigureAsServer()
123  {
124 #if !UNITY_EDITOR && UNITY_WSA && !(ENABLE_IL2CPP && NET_STANDARD_2_0)
125  Task t = new Task(() =>
126  {
127  networkListener = new StreamSocketListener();
128  networkListener.ConnectionReceived += NetworkListener_ConnectionReceived;
129  networkListener.BindServiceNameAsync(SendConnectionPort.ToString()).GetResults();
130  }
131  );
132  t.Start();
133 #else
134  Debug.Log("This script is not intended to be run from the Unity Editor");
135  // In order to avoid compiler warnings in the Unity Editor we have to access a few of our fields.
136  Debug.Log(string.Format("serverIP = {0} waitingForConnection = {1} mostRecentDataBuffer = {2}", serverIp, waitingForConnection, mostRecentDataBuffer == null ? "No there" : "there"));
137 #endif
138  }
139 
143  private bool ConnectListener()
144  {
145 #if !UNITY_EDITOR && UNITY_WSA && !(ENABLE_IL2CPP && NET_STANDARD_2_0)
146  if (waitingForConnection)
147  {
148  Debug.Log("Not a good time to connect listener");
149  return false;
150  }
151 
152  waitingForConnection = true;
153  Debug.Log("Connecting to " + serverIp);
154  HostName networkHost = new HostName(serverIp);
155  networkConnection = new StreamSocket();
156 
157  IAsyncAction outstandingAction = networkConnection.ConnectAsync(networkHost, SendConnectionPort.ToString());
158  AsyncActionCompletedHandler aach = new AsyncActionCompletedHandler(RcvNetworkConnectedHandler);
159  outstandingAction.Completed = aach;
160 
161  return true;
162 #else
163  return false;
164 #endif
165  }
166 
167 #if !UNITY_EDITOR && UNITY_WSA && !(ENABLE_IL2CPP && NET_STANDARD_2_0)
168  private void NetworkListener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
175  {
176  // If we have data, send it.
177  if (mostRecentDataBuffer != null)
178  {
179  IOutputStream stream = args.Socket.OutputStream;
180  using (DataWriter writer = new DataWriter(stream))
181  {
182  writer.WriteInt32(mostRecentDataBuffer.Length);
183  writer.WriteBytes(mostRecentDataBuffer);
184  writer.StoreAsync().AsTask().Wait();
185  writer.FlushAsync().AsTask().Wait();
186  }
187  }
188  else
189  {
190  Debug.LogError("No data to send but we've been connected to. This is unexpected.");
191  }
192  }
193 
199  private async void RcvNetworkConnectedHandler(IAsyncAction asyncInfo, AsyncStatus status)
200  {
201  // Status completed is successful.
202  if (status == AsyncStatus.Completed)
203  {
204  DataReader networkDataReader;
205 
206  // Since we are connected, we can read the data being sent to us.
207  using (networkDataReader = new DataReader(networkConnection.InputStream))
208  {
209  // read four bytes to get the size.
210  DataReaderLoadOperation drlo = networkDataReader.LoadAsync(4);
211  while (drlo.Status == AsyncStatus.Started)
212  {
213  // just waiting.
214  }
215 
216  int dataSize = networkDataReader.ReadInt32();
217  if (dataSize < 0)
218  {
219  Debug.Log("Super bad super big data size");
220  }
221 
222  // Need to allocate a new buffer with the dataSize.
223  mostRecentDataBuffer = new byte[dataSize];
224 
225  // Read the data.
226  await networkDataReader.LoadAsync((uint)dataSize);
227  networkDataReader.ReadBytes(mostRecentDataBuffer);
228 
229  // And fire our data ready event.
230  DataReadyEvent?.Invoke(mostRecentDataBuffer);
231  }
232  }
233  else
234  {
235  Debug.Log("Failed to establish connection for rcv. Error Code: " + asyncInfo.ErrorCode);
236  // In the failure case we'll requeue the data and wait before trying again.
237 
238 
239  // And set the defer time so the update loop can do the 'Unity things'
240  // on the main Unity thread.
241  DeferredActionQueue.Enqueue(() =>
242  {
243  Invoke("RequestDataRetry", timeToDeferFailedConnections);
244  });
245  }
246 
247  networkConnection.Dispose();
248  waitingForConnection = false;
249  }
250 #endif
251  }
252 }
void SetServerIp(string newServerIp)
Tells us who to contact if we need data.
bool RequestAndGetData()
Requests data from the server and handles getting the data and firing the dataReadyEvent.
void SetData(byte[] data)
If someone connects to us, this is the data we will send them.
void ConfigureAsServer()
Configures the network transmitter as the source.
For a UWP application this should allow us to send or receive data given a server IP address...
Singleton behaviour class, used for components that should only have one instance.
Definition: Singleton.cs:14