/// <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);
            }
        }