/// <summary>
        /// Retrieves lists of existing database entities of various types. Later, during data processing, MyGeotab objects are compared against these lists rather than executing select queries on a per-object basis - thereby reducing chattiness of the application and boosting performance.
        /// </summary>
        /// <returns></returns>
        void InitializeListsOfExistingDatabaseEntities()
        {
            MethodBase methodBase = MethodBase.GetCurrentMethod();

            logger.Trace($"Begin {methodBase.ReflectedType.Name}.{methodBase.Name}");

            using (var cancellationTokenSource = new CancellationTokenSource())
            {
                try
                {
                    var getAllDbDutyStatusAvailabilityRecordsTask = DbDutyStatusAvailabilityService.GetAllAsync(connectionInfo, Globals.ConfigurationManager.TimeoutSecondsForDatabaseTasks);

                    Task[] tasks = { getAllDbDutyStatusAvailabilityRecordsTask };

                    Task.WaitAll(tasks);

                    // Sort lists on Id.
                    dbDutyStatusAvailabilityDictionary = getAllDbDutyStatusAvailabilityRecordsTask.Result.ToDictionary(dutyStatusAvailability => Id.Create(dutyStatusAvailability.DriverId));
                }
                catch (AggregateException aggregateException)
                {
                    Globals.HandleConnectivityRelatedAggregateException(aggregateException, Globals.ConnectivityIssueType.Database, "One or more exceptions were encountered during retrieval of lists of existing database entities due to an apparent loss of connectivity with the database.");
                }
                catch (TaskCanceledException taskCanceledException)
                {
                    string errorMessage = $"Task was cancelled. TaskCanceledException: \nMESSAGE [{taskCanceledException.Message}]; \nSOURCE [{taskCanceledException.Source}]; \nSTACK TRACE [{taskCanceledException.StackTrace}]";
                    logger.Warn(errorMessage);
                }
            }

            logger.Trace($"End {methodBase.ReflectedType.Name}.{methodBase.Name}");
        }
        /// <summary>
        /// Iteratively executes the business logic until the application is stopped.
        /// </summary>
        /// <param name="stoppingToken">The <see cref="CancellationToken"/> that can be used to stop execution of the application.</param>
        /// <returns></returns>
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            MethodBase methodBase = MethodBase.GetCurrentMethod();

            logger.Trace($"Begin {methodBase.ReflectedType.Name}.{methodBase.Name}");

            // This is the loop containing all of the business logic that is executed iteratively throughout the lifeime of the application.
            while (!stoppingToken.IsCancellationRequested)
            {
                // Abort if waiting for restoration of connectivity to the MyGeotab server or to the database.
                if (StateMachine.CurrentState == State.Waiting)
                {
                    continue;
                }

                if (initializationCompleted == false)
                {
                    PerformInitializationTasks();
                    continue;
                }

                try
                {
                    logger.Trace($"Started iteration of {methodBase.ReflectedType.Name}.{methodBase.Name}");

                    // Only proceed with data retrieval if the DutyStatusAvailabilityFeedIntervalSeconds has elapsed since data retrieval was last initiated.
                    if (Globals.TimeIntervalHasElapsed(lastDutyStatusAvailabilityDataRetrievalStartTimeUtc, Globals.DateTimeIntervalType.Seconds, Globals.ConfigurationManager.DutyStatusAvailabilityFeedIntervalSeconds))
                    {
                        lastDutyStatusAvailabilityDataRetrievalStartTimeUtc = DateTime.UtcNow;

                        using (var cancellationTokenSource = new CancellationTokenSource())
                        {
                            try
                            {
                                // Get list of active users that are drivers who have accessed the MyGeotab system within the last 30 days and have HosRuleSets assigned.
                                var dbUsers = await DbUserService.GetAllAsync(connectionInfo, cancellationTokenSource, Globals.ConfigurationManager.TimeoutSecondsForDatabaseTasks);

                                var      dutyStatusAvailabilityFeedLastAccessDateCutoffDays = TimeSpan.FromDays(Globals.ConfigurationManager.DutyStatusAvailabilityFeedLastAccessDateCutoffDays);
                                DateTime cutoffLastAccessedTime = DateTime.UtcNow.Subtract(dutyStatusAvailabilityFeedLastAccessDateCutoffDays);
                                var      driverDbUsers          = dbUsers.Where(dbUser => dbUser.IsDriver == true && dbUser.ActiveTo >= DateTime.UtcNow && dbUser.LastAccessDate >= cutoffLastAccessedTime && dbUser.HosRuleSet != HosRuleSetNoneValue).ToList();

                                const int maxBatchSize      = 100;
                                int       currentBatchSize  = 0;
                                int       driverDbUserCount = driverDbUsers.Count;
                                var       calls             = new List <object>();
                                for (int driverDbUserListIndex = 0; driverDbUserListIndex < driverDbUserCount + 1; driverDbUserListIndex++)
                                {
                                    if (currentBatchSize == maxBatchSize || driverDbUserListIndex == driverDbUserCount)
                                    {
                                        DateTime recordChangedTimestampUtc = DateTime.UtcNow;
                                        var      dbDutyStatusAvailabilityEntitiesToInsert = new List <DbDutyStatusAvailability>();
                                        var      dbDutyStatusAvailabilityEntitiesToUpdate = new List <DbDutyStatusAvailability>();

                                        List <object> results;
                                        try
                                        {
                                            // Execute MultiCall.
                                            results = await Globals.MyGeotabAPI.MultiCallAsync(calls.ToArray());
                                        }
                                        catch (Exception exception)
                                        {
                                            // If the exception is related to connectivity, wrap it in a MyGeotabConnectionException. Otherwise, just pass it along.
                                            if (Globals.ExceptionIsRelatedToMyGeotabConnectivityLoss(exception))
                                            {
                                                throw new MyGeotabConnectionException("An exception occurred while attempting to get data from the Geotab API via MultiCallAsync.", exception);
                                            }
                                            else
                                            {
                                                throw;
                                            }
                                        }

                                        // Iterate through the returned DutyStatusAvailability entities.
                                        foreach (var result in results)
                                        {
                                            if (result is List <DutyStatusAvailability> resultDutyStatusAvailabilityList && resultDutyStatusAvailabilityList.Count > 0)
                                            {
                                                var dutyStatusAvailability       = resultDutyStatusAvailabilityList[0];
                                                var dutyStatusAvailabilityDriver = dutyStatusAvailability.Driver;
                                                // Try to find the existing database record for DutyStatusAvailability associated with the subject Driver.
                                                if (dbDutyStatusAvailabilityDictionary.TryGetValue(dutyStatusAvailabilityDriver.Id, out var existingDbDutyStatusAvailability))
                                                {
                                                    // The database already contains a DutyStatusAvailability record for the subject Driver.
                                                    DbDutyStatusAvailability updatedDbDutyStatusAvailability = ObjectMapper.GetDbDutyStatusAvailability(dutyStatusAvailability);
                                                    updatedDbDutyStatusAvailability.id = existingDbDutyStatusAvailability.id;
                                                    updatedDbDutyStatusAvailability.RecordLastChangedUtc       = recordChangedTimestampUtc;
                                                    updatedDbDutyStatusAvailability.DatabaseWriteOperationType = Common.DatabaseWriteOperationType.Update;
                                                    dbDutyStatusAvailabilityDictionary[Id.Create(updatedDbDutyStatusAvailability.DriverId)] = updatedDbDutyStatusAvailability;
                                                    dbDutyStatusAvailabilityEntitiesToUpdate.Add(updatedDbDutyStatusAvailability);
                                                }
                                                else
                                                {
                                                    // A DutyStatusAvailability record associated with the subject Driver has not yet been added to the database. Create a DbDutyStatusAvailability, set its properties and add it to the cache.
                                                    DbDutyStatusAvailability newDbDutyStatusAvailability = ObjectMapper.GetDbDutyStatusAvailability(dutyStatusAvailability);
                                                    newDbDutyStatusAvailability.RecordLastChangedUtc       = recordChangedTimestampUtc;
                                                    newDbDutyStatusAvailability.DatabaseWriteOperationType = Common.DatabaseWriteOperationType.Insert;
                                                    dbDutyStatusAvailabilityDictionary.Add(Id.Create(newDbDutyStatusAvailability.DriverId), newDbDutyStatusAvailability);
                                                    dbDutyStatusAvailabilityEntitiesToInsert.Add(newDbDutyStatusAvailability);
                                                }
                                            }
                                        }

                                        // Send any inserts to the database.
                                        if (dbDutyStatusAvailabilityEntitiesToInsert.Any())
                                        {
                                            try
                                            {
                                                DateTime startTimeUTC = DateTime.UtcNow;
                                                long     dbDutyStatusAvailabilityEntitiesInserted = await DbDutyStatusAvailabilityService.InsertAsync(connectionInfo, dbDutyStatusAvailabilityEntitiesToInsert, cancellationTokenSource, Globals.ConfigurationManager.TimeoutSecondsForDatabaseTasks);

                                                TimeSpan elapsedTime = DateTime.UtcNow.Subtract(startTimeUTC);
                                                double   recordsProcessedPerSecond = (double)dbDutyStatusAvailabilityEntitiesInserted / (double)elapsedTime.TotalSeconds;
                                                logger.Info($"Completed insertion of {dbDutyStatusAvailabilityEntitiesInserted} records into {ConfigurationManager.DbDutyStatusAvailabilityTableName} table in {elapsedTime.TotalSeconds} seconds ({recordsProcessedPerSecond} per second throughput).");
                                            }
                                            catch (Exception)
                                            {
                                                cancellationTokenSource.Cancel();
                                                throw;
                                            }
                                        }

                                        // Send any updates/deletes to the database.
                                        if (dbDutyStatusAvailabilityEntitiesToUpdate.Any())
                                        {
                                            try
                                            {
                                                DateTime startTimeUTC = DateTime.UtcNow;
                                                long     dbDutyStatusAvailabilityEntitiesUpdated = await DbDutyStatusAvailabilityService.UpdateAsync(connectionInfo, dbDutyStatusAvailabilityEntitiesToUpdate, cancellationTokenSource, Globals.ConfigurationManager.TimeoutSecondsForDatabaseTasks);

                                                TimeSpan elapsedTime = DateTime.UtcNow.Subtract(startTimeUTC);
                                                double   recordsProcessedPerSecond = (double)dbDutyStatusAvailabilityEntitiesUpdated / (double)elapsedTime.TotalSeconds;
                                                logger.Info($"Completed updating of {dbDutyStatusAvailabilityEntitiesUpdated} records in {ConfigurationManager.DbDutyStatusAvailabilityTableName} table in {elapsedTime.TotalSeconds} seconds ({recordsProcessedPerSecond} per second throughput).");
                                            }
                                            catch (Exception)
                                            {
                                                cancellationTokenSource.Cancel();
                                                throw;
                                            }
                                        }


                                        // Clear calls list and reset counter.
                                        calls            = new List <object>();
                                        currentBatchSize = 0;
                                    }
                                    if (driverDbUserListIndex == driverDbUserCount)
                                    {
                                        // All drivers have been processed.
                                        break;
                                    }
                                    // Generate Get<DutyStatusAvailability> call for current driver and add to list.
                                    var driverDbUserId = Id.Create(driverDbUsers[driverDbUserListIndex].GeotabId);
                                    var userSearch     = new UserSearch
                                    {
                                        Id = driverDbUserId
                                    };
                                    calls.Add(new object[] { "Get", typeof(DutyStatusAvailability), new { search = new DutyStatusAvailabilitySearch {
                                                                                                              UserSearch = new UserSearch {
                                                                                                                  Id = driverDbUserId
                                                                                                              }
                                                                                                          } }, typeof(List <DutyStatusAvailability>) });

                                    currentBatchSize++;
                                }
                            }
                            catch (TaskCanceledException taskCanceledException)
                            {
                                string errorMessage = $"Task was cancelled. TaskCanceledException: \nMESSAGE [{taskCanceledException.Message}]; \nSOURCE [{taskCanceledException.Source}]; \nSTACK TRACE [{taskCanceledException.StackTrace}]";
                                logger.Warn(errorMessage);
                            }
                        }
                    }
                    else
                    {
                        logger.Debug($"DutyStatusAvailability data retrieval not initiated; {Globals.ConfigurationManager.DutyStatusAvailabilityFeedIntervalSeconds} seconds have not passed since DutyStatusAvailability data retrieval was last initiated.");
                    }

                    logger.Trace($"Completed iteration of {methodBase.ReflectedType.Name}.{methodBase.Name}");
                }
                catch (OperationCanceledException)
                {
                    string errorMessage = $"{nameof(DutyStatusAvailabilityWorker)} process cancelled.";
                    logger.Warn(errorMessage);
                    throw new Exception(errorMessage);
                }
                catch (DatabaseConnectionException databaseConnectionException)
                {
                    HandleException(databaseConnectionException, NLog.LogLevel.Error, $"{nameof(DutyStatusAvailabilityWorker)} process caught an exception");
                }
                catch (MyGeotabConnectionException myGeotabConnectionException)
                {
                    HandleException(myGeotabConnectionException, NLog.LogLevel.Error, $"{nameof(DutyStatusAvailabilityWorker)} process caught an exception");
                }
                catch (Exception ex)
                {
                    // If an exception hasn't been handled to this point, log it and kill the process.
                    HandleException(ex, NLog.LogLevel.Fatal, $"******** {nameof(DutyStatusAvailabilityWorker)} process caught an unhandled exception and will self-terminate now.");
                    System.Diagnostics.Process.GetCurrentProcess().Kill();
                }
            }

            logger.Trace($"End {methodBase.ReflectedType.Name}.{methodBase.Name}");
        }