/// <summary> /// Performs node service discovery. This discovers node level attributes such as the endpoints and /// descriptors, as well as updating routing and neighbor tables - as needed. /// /// If any of the tasks requested are already in the queue, they will not be added again. /// /// If the worker thread is not running, it will be started. /// /// <param name="newTasks">a set of <see cref="NodeDiscoveryTask"/>s to be performed</param> /// </summary> private async Task StartDiscoveryAsync(List <NodeDiscoveryTask> newTasks) { // Tasks are managed in a queue. The worker thread will only remove the task from the queue once the task is // complete. When no tasks are left in the queue, the worker thread will exit. lock (DiscoveryTasks) { Log.Debug("{IeeeAddress}: Node SVC Discovery: starting new tasks {NewTasks}", Node.IeeeAddress, newTasks); // Remove router/coordinator-only tasks if the device is possibly an end node. bool isPossibleEndDevice = IsPossibleEndDevice(); if (!_supportsManagementLqi || isPossibleEndDevice) { newTasks.Remove(NodeDiscoveryTask.NEIGHBORS); } if (!_supportsManagementRouting || isPossibleEndDevice) { newTasks.Remove(NodeDiscoveryTask.ROUTES); } // Make sure there are still tasks to perform if (newTasks.Count == 0) { Log.Debug("{IeeeAddress}: Node SVC Discovery: has no new tasks to perform", Node.IeeeAddress); return; } // Check the current list of tasks to decide if we need to start the worker // This prevents restarting if we didn't add new tasks, which might overload the system bool startWorker = DiscoveryTasks.Count == 0; //||(_futureTask == null); // Add new tasks, avoiding any duplication foreach (NodeDiscoveryTask newTask in newTasks) { if (!DiscoveryTasks.Contains(newTask)) { DiscoveryTasks.Enqueue(newTask); } } if (!startWorker) { Log.Debug("{IeeeAddress}: Node SVC Discovery: already scheduled or running", Node.IeeeAddress); } else { // Create a new node to store the data from this update. // We set the network address so that we can detect the change later if needed. _updatedNode = new ZigBeeNode(NetworkManager, Node.IeeeAddress, Node.NetworkAddress); LastDiscoveryStarted = DateTime.UtcNow; } } Log.Debug("{IeeeAddress}: Node SVC Discovery: scheduled {Task}", Node.IeeeAddress, DiscoveryTasks); await GetNodeServiceDiscoveryTask(); }
/// <summary> /// Stops service discovery and removes any scheduled tasks /// </summary> public void StopDiscovery() { lock (DiscoveryTasks) { DiscoveryTasks.Clear(); } _cancellationTokenSource.Cancel(); Log.Debug("{IeeeAddress}: Node SVC Discovery: stopped", Node.IeeeAddress); }
private async Task Discovery(CancellationToken ct, int cnt) { int retryCnt = cnt;//0; int retryMin = 0; try { Log.Debug("{IeeeAddress}: Node SVC Discovery: running", Node.IeeeAddress); NodeDiscoveryTask?discoveryTask = null; lock (DiscoveryTasks) { if (DiscoveryTasks.Count != 0) { discoveryTask = DiscoveryTasks.Peek(); } } if (discoveryTask == null) { LastDiscoveryCompleted = DateTime.UtcNow; Log.Debug("{IeeeAddress}: Node SVC Discovery: complete", Node.IeeeAddress); NetworkManager.UpdateNode(_updatedNode); IsFinished = true; return; } ct.ThrowIfCancellationRequested(); bool success = false; switch (discoveryTask.Value) { case NodeDiscoveryTask.NWK_ADDRESS: success = await RequestNetworkAddress(); break; case NodeDiscoveryTask.NODE_DESCRIPTOR: success = await RequestNodeDescriptor(); break; case NodeDiscoveryTask.POWER_DESCRIPTOR: success = await RequestPowerDescriptor(); break; case NodeDiscoveryTask.ACTIVE_ENDPOINTS: success = await RequestActiveEndpoints(); break; case NodeDiscoveryTask.ASSOCIATED_NODES: success = await RequestAssociatedNodes(); break; case NodeDiscoveryTask.NEIGHBORS: success = await RequestNeighborTable(); break; case NodeDiscoveryTask.ROUTES: success = await RequestRoutingTable(); break; default: Log.Debug("{IeeeAddress}: Node SVC Discovery: unknown task: {Task}", Node.IeeeAddress, discoveryTask); break; } ct.ThrowIfCancellationRequested(); retryCnt++; int retryDelay = 0; if (success) { lock (DiscoveryTasks) { DiscoveryTasks = new Queue <NodeDiscoveryTask>(DiscoveryTasks.Where(t => t != discoveryTask)); } Log.Debug("{IeeeAddress}: Node SVC Discovery: request {Task} successful.", Node.IeeeAddress, discoveryTask); retryCnt = 0; } else if (retryCnt > MaxBackoff) { Log.Debug("{IeeeAddress}: Node SVC Discovery: request {Task} failed after {Count} attempts.", Node.IeeeAddress, discoveryTask, retryCnt); lock (DiscoveryTasks) { DiscoveryTasks = new Queue <NodeDiscoveryTask>(DiscoveryTasks.Where(t => t != discoveryTask)); } _failedDiscoveryTasks.Add(discoveryTask.Value); // if the network address fails, nothing else will work and this node discoverer instance is finished if (discoveryTask.Value == NodeDiscoveryTask.NWK_ADDRESS && Node.NetworkAddress == 0) { IsFinished = true; StopDiscovery(); return; } retryCnt = 0; } else { retryMin = retryCnt / 4; // We failed with the last request. Wait a bit then retry. retryDelay = (new Random().Next(retryCnt) + 1 + retryMin); /// _retryPeriod; Log.Debug("{IeeeAddress}: Node SVC Discovery: request {Task} failed. Retry {Retry}, wait {Delay} ms before retry.", Node.IeeeAddress, discoveryTask, retryCnt, retryDelay); } // Reschedule the task var tmp = _cancellationTokenSource; _cancellationTokenSource = new CancellationTokenSource(); //await NetworkManager.ScheduleTask(Discovery(_cancellationTokenSource.Token), retryDelay); await Discovery(_cancellationTokenSource.Token, retryCnt); tmp.Cancel(); } catch (TaskCanceledException) { // Eat me } catch (OperationCanceledException) { // Eat me } catch (Exception e) { Log.Error("{IeeeAddress}: Node SVC Discovery: exception: {Exception}", Node.IeeeAddress, e.Message); } }