public async Task <IReadOnlyList <WifiLedBulb> > Scan(int millisecondsTimeout) { //Delete old bulb list m_discoveredBulbs.Clear(); //Create UDP Client for discovery broadcast using (UdpClient discovery_client = new UdpClient()) { //Send magic packet to get controllers to announce themselves IPEndPoint ip = new IPEndPoint(IPAddress.Broadcast, DISCOVERY_PORT); byte[] bytes = Encoding.ASCII.GetBytes("HF-A11ASSISTHREAD"); discovery_client.Send(bytes, bytes.Length, ip); //Listen for their return packets IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, DISCOVERY_PORT); m_cancelScanSource = new CancellationTokenSource(millisecondsTimeout); while (true) { //Hack in a way to allow a CancellationToken for ReceiveAsync //Based heavily on https://stackoverflow.com/questions/19404199/how-to-to-make-udpclient-receiveasync-cancelable var receive_task = discovery_client.ReceiveAsync(); var tcs = new TaskCompletionSource <bool>(); using (m_cancelScanSource.Token.Register(s => tcs.TrySetResult(true), null)) { if (await Task.WhenAny(receive_task, tcs.Task) == receive_task) { //ReceiveAsync was successful, parse the reply string message = Encoding.ASCII.GetString(receive_task.Result.Buffer); string[] bulb_data = message.Split(','); var bulb = new WifiLedBulb(bulb_data[0], bulb_data[1], bulb_data[2]); m_discoveredBulbs.Add(bulb); DiscoveredBulb?.Invoke(bulb); } else { //Cancelled (or timed out), close out socket discovery_client.Close(); m_cancelScanSource.Dispose(); m_cancelScanSource = null; break; } } } } return(m_discoveredBulbs.AsReadOnly()); }
/*Scan * Sends out a UDP packet over the network and listens for response * returns a Task... still need to investigate how this works * assuming it returns null until an actual return value is sent, * in this case a List of Bulb objects. * * @params: * millisecondsTimeout: timeout for each individual bulb if no response is received. (non functional) * maxRetries: retries after not hearing from a single bulb (non functional) */ public async Task <List <Bulb> > Scan(int millisecondsTimeout = 2000, int maxRetries = 2) { //Delete old bulb list m_discoveredBulbs.Clear(); //Create UDP Client for discovery broadcast using (UdpClient discovery_client = new UdpClient()) { //Send magic packet to get controllers to announce themselves IPEndPoint ip = new IPEndPoint(IPAddress.Broadcast, DISCOVERY_PORT); m_cancelScanSource = new CancellationTokenSource(millisecondsTimeout); Console.WriteLine("sending magic packet"); discovery_client.Send(MAGIC_UDP_PACKET, MAGIC_UDP_PACKET.Length, ip); //Send magic packet to get controllers to announce themselves IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, DISCOVERY_PORT); //Listen for their return packets while (true) { //Hack in a way to allow a CancellationToken for ReceiveAsync //Based heavily on https://stackoverflow.com/questions/19404199/how-to-to-make-udpclient-receiveasync-cancelable var receive_task = discovery_client.ReceiveAsync(); var tcs = new TaskCompletionSource <bool>(); //??? confused about the using identifier and wrapping curly brackets using (m_cancelScanSource.Token.Register(s => tcs.TrySetResult(true), null)) { //if the cancellation token isn't true continue, else break the loop if (await Task.WhenAny(receive_task, tcs.Task) == receive_task) { //ReceiveAsync was successful, encode the reply into ASCII and parse string message = Encoding.ASCII.GetString(receive_task.Result.Buffer); Console.WriteLine(message); //When encoded to ASCII and converted to a string, data arrives in this pattern: // 'ipaddress,macaddress,typeid' // for example "192.168.1.21,6001940ED006,ZJ2101" // split the data and save to variables string[] bulb_data = message.Split(','); string ipAddress = bulb_data[0]; string macAddress = bulb_data[1]; string typeID = bulb_data[2]; //instantiate a bulb object in "BulbsFactory" Class. Could just as easily be a... //BulbsFactory method. var bulb = BulbsFactory.CreateBulb(ipAddress, macAddress, typeID); // If BulbsFactory can't figure out what the bulb type is, it will set to null // We dont add null bulbs to our list of Bulb objects. So we 'continue', // skipping bulbList.Add(bulb); if (bulb == null) { continue; } //add our newly created Bulb object to a list so that we may access it later m_discoveredBulbs.Add(bulb); DiscoveredBulb?.Invoke(bulb); } else //Cancelled (or timed out), close out socket { Console.WriteLine(m_discoveredBulbs.Count); //close our port so it doesn't timeout causing errors discovery_client.Close(); //??? destroy our cancelationsource m_cancelScanSource.Dispose(); //??? set our cancelationsource to null. This may be why scanRetry isn't working m_cancelScanSource = null; //break out of the first 'while (true)' loop as we have no more bulbs to add break; } } } } return(m_discoveredBulbs); }