/// <summary>
        /// Updates the <see cref="Device"/> cache.  All new or changed <see cref="Device"/>s are obtained using a GetFeed() call.  Then, <see cref="Device"/>s that are already in the cache are replaced and any <see cref="Device"/>s not in the cache are added to the cache.
        /// </summary>
        /// <returns></returns>
        async Task UpdateDeviceCacheAsync()
        {
            ConsoleUtility.LogInfoStart("Updating Device cache...");

            // Populate the deviceCache, adding new items and updating existing items with their changed counterparts from the database.  Repeat execution of the GetFeedDeviceAsync method until no more results are returned to ensure that the cache is complete and up-to-date.
            FeedResult <Device> feedResult = null;
            bool keepGoing = true;

            while (keepGoing == true)
            {
                feedResult = await api.GetFeedDeviceAsync(lastDeviceFeedVersion);

                lastDeviceFeedVersion = feedResult.ToVersion;
                foreach (Device feedResultDevice in feedResult.Data)
                {
                    if (deviceCache.ContainsKey(feedResultDevice.Id))
                    {
                        deviceCache[feedResultDevice.Id] = feedResultDevice;
                    }
                    else
                    {
                        deviceCache.Add(feedResultDevice.Id, feedResultDevice);
                    }
                }
                if (feedResult.Data.Count < DefaultFeedResultsLimitDevice)
                {
                    keepGoing = false;
                }
            }

            ConsoleUtility.LogComplete(Common.ConsoleColorForUnchangedData);
            ConsoleUtility.LogListItem($"Device cache records added/updated:", feedResult.Data.Count.ToString(), Common.ConsoleColorForListItems, (feedResult.Data.Count > 0) ? Common.ConsoleColorForChangedData : Common.ConsoleColorForUnchangedData);
        }
Beispiel #2
0
        public static async Task <string> Run(GeotabDataOnlyPlanAPI api, string deviceId, string userId)
        {
            ConsoleUtility.LogExampleStarted(typeof(AddDriverChangeAsyncExample).Name);

            string addedDriverChangeId = "";

            try
            {
                // Set parameter values to apply when adding driver change.
                DateTime      dateTime    = DateTime.Now;
                List <Device> deviceCache = await ExampleUtility.GetAllDevicesAsync(api);

                List <User> userCache = await ExampleUtility.GetAllUsersAsync(api);

                Device device = deviceCache.Where(targetDevice => targetDevice.Id.ToString() == deviceId).First();
                Driver driver = userCache.Where(targetUser => targetUser.Id.ToString() == userId).First() as Driver;

                DriverChangeType driverChangeType = DriverChangeType.Driver;

                ConsoleUtility.LogInfoStart($"Adding driverChange of type '{driverChangeType.ToString()}' for driver '{driver.Id.ToString()}' and device '{device.Id.ToString()}' to database '{api.Credentials.Database}'...");

                addedDriverChangeId = await api.AddDriverChangeAsync(dateTime, device, driver, driverChangeType);

                ConsoleUtility.LogComplete();
                ConsoleUtility.LogInfo($"Added driverChange Id: {addedDriverChangeId}");
            }
            catch (Exception ex)
            {
                ConsoleUtility.LogError(ex);
            }

            ConsoleUtility.LogExampleFinished(typeof(AddDriverChangeAsyncExample).Name);
            return(addedDriverChangeId);
        }
        public static async Task Run(GeotabDataOnlyPlanAPI api, string userId)
        {
            ConsoleUtility.LogExampleStarted(typeof(SetUserAsyncExample).Name);

            try
            {
                // Update a driver with keys to a NON-driver.
                // Set parameter values to apply when adding device.
                string   id          = userId;
                DateTime activeTo    = new(2037, 1, 31);
                string   comment     = "Driver with keys updated to NON-driver";
                string   designation = "Driver 2 Upd";
                string   employeeNo  = "Employee 2 Upd";
                string   firstName   = "John Upd";
                bool     isDriver    = false;
                string   lastName    = "Smith2 Upd";
                string   name        = "jsmith2Upd";
                string   password2   = "Password1!Upd";

                ConsoleUtility.LogInfoStart($"Updating user '{id}' in database '{api.Credentials.Database}'...");

                await api.SetUserAsync(id, activeTo, comment, designation, employeeNo, firstName, isDriver, lastName, name, password2);

                ConsoleUtility.LogComplete();
            }
            catch (Exception ex)
            {
                ConsoleUtility.LogError(ex);
            }

            ConsoleUtility.LogExampleFinished(typeof(SetUserAsyncExample).Name);
        }
        /// <summary>
        /// Updates the <see cref="Diagnostic"/> cache.  All new or changed <see cref="Diagnostic"/>s are obtained using a GetFeed() call.  Then, the <see cref="Controller"/> and <see cref="UnitOfMeasure"/> child objects are "hydrated" with fully-populated cached counterparts. Finally, <see cref="Diagnostic"/>s that are already in the cache are replaced and any <see cref="Diagnostic"/>s not in the cache are added to the cache.
        /// </summary>
        async Task UpdateDiagnosticCacheAsync()
        {
            ConsoleUtility.LogInfoStart("Updating Diagnostic cache...");

            // Populate the diagnosticCache, adding new items and updating existing items with their changed counterparts from the database.  Repeat execution of the GetFeedDiagnosticAsync method until no more results are returned to ensure that the cache is complete and up-to-date.
            FeedResult <Diagnostic> feedResult = null;
            bool keepGoing = true;

            while (keepGoing == true)
            {
                feedResult = await api.GetFeedDiagnosticAsync(lastDiagnosticFeedVersion);

                lastDiagnosticFeedVersion = feedResult.ToVersion;
                foreach (Diagnostic feedResultDiagnostic in feedResult.Data)
                {
                    // Hydrate Controller and UnitOfMeasure objects.
                    Controller controller = feedResultDiagnostic.Controller;
                    if (controller == null)
                    {
                        feedResultDiagnostic.Controller = NoController.Value;
                    }
                    else if (!controller.Equals(NoController.Value))
                    {
                        feedResultDiagnostic.Controller = controllerCache[controller.Id];
                    }
                    UnitOfMeasure unitOfMeasure = feedResultDiagnostic.UnitOfMeasure;
                    if (unitOfMeasure != null)
                    {
                        feedResultDiagnostic.UnitOfMeasure = unitOfMeasureCache[unitOfMeasure.Id];
                    }

                    // Add or update.
                    if (diagnosticCache.ContainsKey(feedResultDiagnostic.Id))
                    {
                        diagnosticCache[feedResultDiagnostic.Id] = feedResultDiagnostic;
                    }
                    else
                    {
                        diagnosticCache.Add(feedResultDiagnostic.Id, feedResultDiagnostic);
                    }
                }
                if (feedResult.Data.Count < DefaultFeedResultsLimitDiagnostic)
                {
                    keepGoing = false;
                }
            }
            ConsoleUtility.LogComplete(Common.ConsoleColorForUnchangedData);
            ConsoleUtility.LogListItem($"Diagnostics added/updated:", feedResult.Data.Count.ToString(), Common.ConsoleColorForListItems, (feedResult.Data.Count > 0) ? Common.ConsoleColorForChangedData : Common.ConsoleColorForUnchangedData);
        }
        public static async Task <string> Run(GeotabDataOnlyPlanAPI api)
        {
            ConsoleUtility.LogExampleStarted(typeof(AddDeviceAsyncExample).Name);

            string addedDeviceId = "";

            try
            {
                // Set parameter values to apply when adding device.
                string serialNumber        = ConsoleUtility.GetUserInput("serial number of device to be added");
                string name                = "Vehicle 1";
                bool   enableDeviceBeeping = true;
                bool   enableDriverIdentificationReminder            = true;
                int    driverIdentificationReminderImmobilizeSeconds = 20;
                bool   enableBeepOnEngineRpm     = true;
                int    engineRpmBeepValue        = 3000;
                bool   enableBeepOnIdle          = true;
                int    idleMinutesBeepValue      = 4;
                bool   enableBeepOnSpeeding      = true;
                int    speedingStartBeepingSpeed = 110;
                int    speedingStopBeepingSpeed  = 100;
                bool   enableBeepBrieflyWhenApprocahingWarningSpeed = true;
                bool   enableBeepOnDangerousDriving           = true;
                int    accelerationWarningThreshold           = 23;
                int    brakingWarningThreshold                = -35;
                int    corneringWarningThreshold              = 27;
                bool   enableBeepWhenSeatbeltNotUsed          = true;
                int    seatbeltNotUsedWarningSpeed            = 11;
                bool   enableBeepWhenPassengerSeatbeltNotUsed = true;
                bool   beepWhenReversing = true;

                ConsoleUtility.LogInfoStart($"Adding device with serial number '{serialNumber}' to database '{api.Credentials.Database}'...");

                addedDeviceId = await api.AddDeviceAsync(serialNumber, name, enableDeviceBeeping, enableDriverIdentificationReminder, driverIdentificationReminderImmobilizeSeconds, enableBeepOnEngineRpm, engineRpmBeepValue, enableBeepOnIdle, idleMinutesBeepValue, enableBeepOnSpeeding, speedingStartBeepingSpeed, speedingStopBeepingSpeed, enableBeepBrieflyWhenApprocahingWarningSpeed, enableBeepOnDangerousDriving, accelerationWarningThreshold, brakingWarningThreshold, corneringWarningThreshold, enableBeepWhenSeatbeltNotUsed, seatbeltNotUsedWarningSpeed, enableBeepWhenPassengerSeatbeltNotUsed, beepWhenReversing);

                ConsoleUtility.LogComplete();
                ConsoleUtility.LogInfo($"Added device Id: {addedDeviceId}");
            }
            catch (Exception ex)
            {
                ConsoleUtility.LogError(ex);
            }

            ConsoleUtility.LogExampleFinished(typeof(AddDeviceAsyncExample).Name);
            return(addedDeviceId);
        }
        public static async Task Run(GeotabDataOnlyPlanAPI api, string deviceId)
        {
            ConsoleUtility.LogExampleStarted(typeof(SetDeviceAsyncExample).Name);

            try
            {
                // Set parameter values to apply when adding device.
                string id   = deviceId;
                string name = "Vehicle 1 Upd";
                bool   enableDeviceBeeping = false;
                bool   enableDriverIdentificationReminder            = false;
                int    driverIdentificationReminderImmobilizeSeconds = 21;
                bool   enableBeepOnEngineRpm     = false;
                int    engineRpmBeepValue        = 3001;
                bool   enableBeepOnIdle          = false;
                int    idleMinutesBeepValue      = 5;
                bool   enableBeepOnSpeeding      = false;
                int    speedingStartBeepingSpeed = 111;
                int    speedingStopBeepingSpeed  = 101;
                bool   enableBeepBrieflyWhenApprocahingWarningSpeed = false;
                bool   enableBeepOnDangerousDriving           = false;
                int    accelerationWarningThreshold           = 24;
                int    brakingWarningThreshold                = -36;
                int    corneringWarningThreshold              = 28;
                bool   enableBeepWhenSeatbeltNotUsed          = false;
                int    seatbeltNotUsedWarningSpeed            = 12;
                bool   enableBeepWhenPassengerSeatbeltNotUsed = false;
                bool   beepWhenReversing = false;

                ConsoleUtility.LogInfoStart($"Updating device '{id}' in database '{api.Credentials.Database}'...");

                List <Device> deviceCache = await ExampleUtility.GetAllDevicesAsync(api);

                Device deviceToSet = deviceCache.Where(targetDevice => targetDevice.Id.ToString() == deviceId).First();
                await api.SetDeviceAsync(deviceToSet, name, enableDeviceBeeping, enableDriverIdentificationReminder, driverIdentificationReminderImmobilizeSeconds, enableBeepOnEngineRpm, engineRpmBeepValue, enableBeepOnIdle, idleMinutesBeepValue, enableBeepOnSpeeding, speedingStartBeepingSpeed, speedingStopBeepingSpeed, enableBeepBrieflyWhenApprocahingWarningSpeed, enableBeepOnDangerousDriving, accelerationWarningThreshold, brakingWarningThreshold, corneringWarningThreshold, enableBeepWhenSeatbeltNotUsed, seatbeltNotUsedWarningSpeed, enableBeepWhenPassengerSeatbeltNotUsed, beepWhenReversing);

                ConsoleUtility.LogComplete();
            }
            catch (Exception ex)
            {
                ConsoleUtility.LogError(ex);
            }

            ConsoleUtility.LogExampleFinished(typeof(SetDeviceAsyncExample).Name);
        }
        /// <summary>
        /// Prompts the user to input the path to a config file and then loads the contents of the config file into a list of <see cref="ConfigItem"/> objects.
        /// </summary>
        /// <param name="configFilePathPromptMessage">A description of the file type being sought (e.g. '<c>config</c>').  For use in user prompts.</param>
        /// <returns>A list of <see cref="ConfigItem"/> objects.</returns>
        static IList <ConfigItem> GetConfigItems(string configFilePathPromptMessage)
        {
            // Get the config file path.
            string configFilePath = ConsoleUtility.GetUserInputFilePath(configFilePathPromptMessage);

            // Load the config file contents.
            ConsoleUtility.LogInfoStart($"Loading configuration information from file '{configFilePath}'...");
            IList <ConfigItem> configItems = null;

            using (FileStream configFile = File.OpenRead(configFilePath))
            {
                configItems = configFile.CsvToList <ConfigItem>(null, 0, 0, '|');
            }
            if (!configItems.Any())
            {
                throw new Exception($"No configuration information was loaded from the CSV file '{configFilePath}'.");
            }
            ConsoleUtility.LogComplete();
            return(configItems);
        }
        /// <summary>
        /// Updates the <see cref="Controller"/> cache.  All <see cref="Controller"/>s are obtained using a Get() call.  Then, <see cref="Controller"/>s that are already in the cache are replaced and any <see cref="Controller"/>s not in the cache are added to the cache.
        /// </summary>
        /// <returns></returns>
        async Task UpdateControllerCacheAsync()
        {
            ConsoleUtility.LogInfoStart("Updating Controller cache...");

            List <Controller> returnedControllers = await api.GetControllersAsync() as List <Controller>;

            foreach (Controller returnedController in returnedControllers)
            {
                if (controllerCache.ContainsKey(returnedController.Id))
                {
                    controllerCache[returnedController.Id] = returnedController;
                }
                else
                {
                    controllerCache.Add(returnedController.Id, returnedController);
                }
            }

            ConsoleUtility.LogComplete(Common.ConsoleColorForUnchangedData);
            ConsoleUtility.LogListItem($"Controller cache records added/updated:", returnedControllers.Count.ToString(), Common.ConsoleColorForListItems, (returnedControllers.Count > 0) ? Common.ConsoleColorForChangedData : Common.ConsoleColorForUnchangedData);
        }
        /// <summary>
        /// Updates the <see cref="FailureMode"/> cache.  All <see cref="FailureMode"/>s are obtained using a Get() call.  Then, <see cref="FailureMode"/>s that are already in the cache are replaced and any <see cref="FailureMode"/>s not in the cache are added to the cache.
        /// </summary>
        /// <returns></returns>
        async Task UpdateFailureModeCacheAsync()
        {
            ConsoleUtility.LogInfoStart("Updating FailureMode cache...");

            List <FailureMode> returnedFailureModes = await api.GetFailureModesAsync() as List <FailureMode>;

            foreach (FailureMode returnedFailureMode in returnedFailureModes)
            {
                if (failureModeCache.ContainsKey(returnedFailureMode.Id))
                {
                    failureModeCache[returnedFailureMode.Id] = returnedFailureMode;
                }
                else
                {
                    failureModeCache.Add(returnedFailureMode.Id, returnedFailureMode);
                }
            }

            ConsoleUtility.LogComplete(Common.ConsoleColorForUnchangedData);
            ConsoleUtility.LogListItem($"FailureMode cache records added/updated:", returnedFailureModes.Count.ToString(), Common.ConsoleColorForListItems, (returnedFailureModes.Count > 0) ? Common.ConsoleColorForChangedData : Common.ConsoleColorForUnchangedData);
        }
        /// <summary>
        /// Updates the <see cref="UnitOfMeasure"/> cache.  All <see cref="UnitOfMeasure"/> objects are obtained using a Get() call.  Then, <see cref="UnitOfMeasure"/> objects that are already in the cache are replaced and any <see cref="UnitOfMeasure"/> objects not in the cache are added to the cache.
        /// </summary>
        /// <returns></returns>
        async Task UpdateUnitOfMeasureCacheAsync()
        {
            ConsoleUtility.LogInfoStart("Updating UnitsOfMeasure cache...");

            List <UnitOfMeasure> returnedUnitsOfMeasure = await api.GetUnitsOfMeasureAsync() as List <UnitOfMeasure>;

            foreach (UnitOfMeasure returnedUnitOfMeasure in returnedUnitsOfMeasure)
            {
                if (unitOfMeasureCache.ContainsKey(returnedUnitOfMeasure.Id))
                {
                    unitOfMeasureCache[returnedUnitOfMeasure.Id] = returnedUnitOfMeasure;
                }
                else
                {
                    unitOfMeasureCache.Add(returnedUnitOfMeasure.Id, returnedUnitOfMeasure);
                }
            }

            ConsoleUtility.LogComplete(Common.ConsoleColorForUnchangedData);
            ConsoleUtility.LogListItem($"UnitsOfMeasure cache records added/updated:", returnedUnitsOfMeasure.Count.ToString(), Common.ConsoleColorForListItems, (returnedUnitsOfMeasure.Count > 0) ? Common.ConsoleColorForChangedData : Common.ConsoleColorForUnchangedData);
        }
Beispiel #11
0
        public static async Task <string> Run(GeotabDataOnlyPlanAPI api)
        {
            ConsoleUtility.LogExampleStarted(typeof(AddUserAsyncExample).Name);

            string addedUserId = "";

            try
            {
                // Add a user that is a driver with a key.
                // Set parameter values to apply when adding user.
                List <Key> keys = new List <Key>();
                Key        key  = new Key(DriverKeyType.CustomNfc, null, "1234567890");
                keys.Add(key);

                string comment                = "User added as driver with key.";
                string designation            = "Driver 2";
                string employeeNo             = "Employee 2";
                string firstName              = "John";
                bool   isDriver               = true;
                string lastName               = "Smith2";
                string name                   = "jsmith2";
                string password2              = "Password1!";
                string licenseNumber          = "ABC123";
                string licenseProvinceOrState = "ON";

                ConsoleUtility.LogInfoStart($"Adding user with username '{name}' to database '{api.Credentials.Database}'...");

                addedUserId = await api.AddUserAsync(comment, designation, employeeNo, firstName, isDriver, lastName, name, password2, keys, licenseNumber, licenseProvinceOrState);

                ConsoleUtility.LogComplete();
                ConsoleUtility.LogInfo($"Added user Id: {addedUserId}");
            }
            catch (Exception ex)
            {
                ConsoleUtility.LogError(ex);
            }

            ConsoleUtility.LogExampleFinished(typeof(AddUserAsyncExample).Name);
            return(addedUserId);
        }
        public static async Task Run(GeotabDataOnlyPlanAPI api, string driverChangeId)
        {
            ConsoleUtility.LogExampleStarted(typeof(RemoveDriverChangeAsyncExample).Name);

            try
            {
                ConsoleUtility.LogInfoStart($"Removing driverChange '{driverChangeId}' from database '{api.Credentials.Database}'...");

                IList <DriverChange> driverChanges = await ExampleUtility.GetAllDriverChangesAsync(api);

                DriverChange driverChangeToRemove = driverChanges.Where(targetDriverChange => targetDriverChange.Id.ToString() == driverChangeId).First();
                await api.RemoveDriverChangeAsync(driverChangeToRemove);

                ConsoleUtility.LogComplete();
            }
            catch (Exception ex)
            {
                ConsoleUtility.LogError(ex);
            }

            ConsoleUtility.LogExampleFinished(typeof(RemoveDriverChangeAsyncExample).Name);
        }
        public static async Task Run(GeotabDataOnlyPlanAPI api, string userId)
        {
            ConsoleUtility.LogExampleStarted(typeof(RemoveUserAsyncExample).Name);

            try
            {
                ConsoleUtility.LogInfoStart($"Removing user '{userId}' from database '{api.Credentials.Database}'...");

                List <User> userCache = await ExampleUtility.GetAllUsersAsync(api);

                User userToRemove = userCache.Where(targetUser => targetUser.Id.ToString() == userId).First();
                await api.RemoveUserAsync(userToRemove);

                ConsoleUtility.LogComplete();
            }
            catch (Exception ex)
            {
                ConsoleUtility.LogError(ex);
            }

            ConsoleUtility.LogExampleFinished(typeof(RemoveUserAsyncExample).Name);
        }
Beispiel #14
0
        public static async Task Run(GeotabDataOnlyPlanAPI api, string deviceId)
        {
            ConsoleUtility.LogExampleStarted(typeof(ArchiveDeviceAsyncExample).Name);

            try
            {
                ConsoleUtility.LogInfoStart($"Archiving device '{deviceId}' in database '{api.Credentials.Database}'...");

                List <Device> deviceCache = await ExampleUtility.GetAllDevicesAsync(api);

                Device deviceToArchive = deviceCache.Where(targetDevice => targetDevice.Id.ToString() == deviceId).First();
                await api.ArchiveDeviceAsync(deviceToArchive);

                ConsoleUtility.LogComplete();
            }
            catch (Exception ex)
            {
                ConsoleUtility.LogError(ex);
            }

            ConsoleUtility.LogExampleFinished(typeof(ArchiveDeviceAsyncExample).Name);
        }
        static async Task Main()
        {
            IList <ConfigItem> configItems;
            bool           trackSpecificVehicles = false;
            IList <Device> devicesToTrack        = null;

            Common.FeedStartOption feedStartOption         = Common.FeedStartOption.CurrentTime;
            DateTime              feedStartSpecificTimeUTC = DateTime.MinValue;
            int                   feedIntervalSeconds      = 60;
            IList <Diagnostic>    diagnosticsToTrack       = null;
            string                parentOutputFolder;
            string                outputFolder;
            int                   maximumFileSizeBytes = 1024000;
            GeotabDataOnlyPlanAPI api;

            string username = "";
            string password = "";
            string server   = "my.geotab.com";
            string database = "";
            string title    = "";

            try
            {
                // Set title.
                title         = AppDomain.CurrentDomain.FriendlyName.Replace(".", " ");
                Console.Title = title;
                ConsoleUtility.LogUtilityStartup(title);

                // Request MyGeotab credentials and database name.
                server   = ConsoleUtility.GetUserInput($"MyGeotab server");
                database = ConsoleUtility.GetUserInput($"Database to run examples against.").ToLower();
                username = ConsoleUtility.GetUserInput($"MyGeotab username");
                password = ConsoleUtility.GetUserInputMasked($"MyGeotab password");

                // Create Geotab Data-Only Plan API instance and authenticate.
                api = new GeotabDataOnlyPlanAPI(server, database, username, password);
                ConsoleUtility.LogInfoStart("Authenticating...");
                await api.AuthenticateAsync();

                ConsoleUtility.LogOk();

                // Load configuration information from the config file.
                configItems = GetConfigItems("configuration");

                // Validate output folder and create subfolder for output files.
                parentOutputFolder = configItems.Where(configItem => configItem.Key == ArgNameOutputFolder).FirstOrDefault().Value;
                if (!Directory.Exists(parentOutputFolder))
                {
                    throw new ArgumentException($"The specified output folder, '{parentOutputFolder}', does not exist.");
                }
                DirectoryInfo directoryInfo = new DirectoryInfo(parentOutputFolder);
                string        subfolderName = $"Output_{DateTime.Now.ToString("yyyyMMdd_HHmmss")}";
                directoryInfo.CreateSubdirectory(subfolderName);
                outputFolder = Path.Combine(directoryInfo.FullName, subfolderName);

                // Validate and set maximum file size.
                string maxFileSizeMBString = configItems.Where(configItem => configItem.Key == ArgNameMaximumFileSizeMB).FirstOrDefault().Value;
                if (int.TryParse(maxFileSizeMBString, out int maxFileSizeMB))
                {
                    if (maxFileSizeMB > 0)
                    {
                        maximumFileSizeBytes = maxFileSizeMB * Common.MegabyteToByteMultiplier;
                    }
                }

                // Get the vehicle tracking option.
                string vehicleTrackingOption = configItems.Where(configItem => configItem.Key == ArgNameVehicleTrackingOption).FirstOrDefault().Value;
                switch (vehicleTrackingOption)
                {
                case nameof(Common.VehicleTrackingOption.Reporting):
                    trackSpecificVehicles = false;
                    break;

                case nameof(Common.VehicleTrackingOption.Specific):
                    trackSpecificVehicles = true;
                    break;

                default:
                    break;
                }

                // If specific vehicles are to be tracked, validate the supplied list of device IDs against the current database.  Discard any supplied items that are not valid device IDs.  If no valid device IDs are supplied, switch back to tracking vehicles that are currently reporting data.
                if (trackSpecificVehicles == true)
                {
                    ConsoleUtility.LogInfo("Validating SpecificVehiclesToTrack...");
                    string   vehicleListSupplied = configItems.Where(configItem => configItem.Key == ArgNameSpecificVehiclesToTrack).FirstOrDefault().Value;
                    string[] vehicleList         = vehicleListSupplied.Split(",");
                    devicesToTrack = new List <Device>();
                    IList <Device> devices = await GetAllDevicesAsync(api);

                    for (int vehicleListIndex = 0; vehicleListIndex < vehicleList.Length; vehicleListIndex++)
                    {
                        string vehicleDeviceId = vehicleList[vehicleListIndex];
                        Device checkedDevice   = devices.Where(device => device.Id.ToString() == vehicleDeviceId).FirstOrDefault();
                        if (checkedDevice == null)
                        {
                            ConsoleUtility.LogListItem($"Note - The following is not a valid device Id", $"{vehicleDeviceId}", Common.ConsoleColorForUnchangedData, Common.ConsoleColorForErrors);
                            continue;
                        }
                        devicesToTrack.Add(checkedDevice);
                    }
                    if (devicesToTrack.Count == 0)
                    {
                        ConsoleUtility.LogWarning($"No valid device IDs have been entered. Switching to tracking of vehicles that are currently reporting data.");
                        trackSpecificVehicles = false;
                    }
                }

                // Get diagnostics to be tracked.
                ConsoleUtility.LogInfo("Validating DiagnosticsToTrack...");
                string   diagnosticListSupplied = configItems.Where(configItem => configItem.Key == ArgNameDiagnosticsToTrack).FirstOrDefault().Value;
                string[] diagnosticList         = diagnosticListSupplied.Split(",");
                diagnosticsToTrack = new List <Diagnostic>();
                IList <Diagnostic> diagnostics = await GetAllDiagnosticsAsync(api);

                for (int diagnosticListIndex = 0; diagnosticListIndex < diagnosticList.Length; diagnosticListIndex++)
                {
                    string     diagnosticId      = diagnosticList[diagnosticListIndex];
                    Diagnostic checkedDiagnostic = diagnostics.Where(diagnostic => diagnostic.Id.ToString() == diagnosticId).FirstOrDefault();
                    if (checkedDiagnostic == null)
                    {
                        ConsoleUtility.LogListItem($"Note - The following is not a valid diagnostic Id", $"{diagnosticId}", Common.ConsoleColorForUnchangedData, Common.ConsoleColorForErrors);
                        continue;
                    }
                    diagnosticsToTrack.Add(checkedDiagnostic);
                }
                if (diagnosticsToTrack.Count == 0)
                {
                    ConsoleUtility.LogWarning($"No valid diagnostic IDs have been entered. As such, no diagnostics will be tracked.");
                }

                // Get the feed start option.
                string feedStartOptionString = configItems.Where(configItem => configItem.Key == ArgNameFeedStartOption).FirstOrDefault().Value;
                switch (feedStartOptionString)
                {
                case nameof(Common.FeedStartOption.CurrentTime):
                    feedStartOption = Common.FeedStartOption.CurrentTime;
                    break;

                case nameof(Common.FeedStartOption.FeedResultToken):
                    feedStartOption = Common.FeedStartOption.FeedResultToken;
                    break;

                case nameof(Common.FeedStartOption.SpecificTime):
                    string feedStartSpecificTimeUTCString = configItems.Where(configItem => configItem.Key == ArgNameFeedStartSpecificTimeUTC).FirstOrDefault().Value;
                    if (DateTime.TryParse(feedStartSpecificTimeUTCString, null, System.Globalization.DateTimeStyles.RoundtripKind, out feedStartSpecificTimeUTC) == false)
                    {
                        ConsoleUtility.LogWarning($"The value of '{feedStartSpecificTimeUTCString}' specified for FeedStartSpecificTimeUTC is invalid. As such, the current date and time will be used instead.");
                        feedStartOption = Common.FeedStartOption.CurrentTime;
                    }
                    else
                    {
                        feedStartOption = Common.FeedStartOption.SpecificTime;
                    }
                    break;

                default:
                    break;
                }

                // Get the feed interval.
                string feedIntervalSecondsString = configItems.Where(configItem => configItem.Key == ArgNameFeedIntervalSeconds).FirstOrDefault().Value;
                if (int.TryParse(feedIntervalSecondsString, out int feedIntervalSecondsInt))
                {
                    if (feedIntervalSecondsInt < ShortestAllowedFeedIntervalSeconds)
                    {
                        ConsoleUtility.LogListItem($"Note - The specified FeedIntervalSeconds value of '{feedIntervalSecondsString}' is less then the shortest allowed value of '{ShortestAllowedFeedIntervalSeconds.ToString()}'.  FeedIntervalSeconds will be set to:", ShortestAllowedFeedIntervalSeconds.ToString(), Common.ConsoleColorForUnchangedData, Common.ConsoleColorForErrors);
                        feedIntervalSeconds = ShortestAllowedFeedIntervalSeconds;
                    }
                    else
                    {
                        feedIntervalSeconds = feedIntervalSecondsInt;
                    }
                }
                else
                {
                    ConsoleUtility.LogListItem($"Note - The specified FeedIntervalSeconds value of '{feedIntervalSecondsString}' is invalid.  FeedIntervalSeconds will be set to:", ShortestAllowedFeedIntervalSeconds.ToString(), Common.ConsoleColorForUnchangedData, Common.ConsoleColorForErrors);
                    feedIntervalSeconds = ShortestAllowedFeedIntervalSeconds;
                }

                // Instantiate a DatabaseWorker to start processing the data feeds.
                bool   continuous        = true;
                Worker worker            = new DatabaseWorker(username, password, database, server, parentOutputFolder, outputFolder, maximumFileSizeBytes, feedIntervalSeconds, feedStartOption, feedStartSpecificTimeUTC, trackSpecificVehicles, devicesToTrack, diagnosticsToTrack);
                var    cancellationToken = new CancellationTokenSource();
                Task   task = Task.Run(async() => await worker.DoWorkAsync(continuous), cancellationToken.Token);
                if (continuous && Console.ReadLine() != null)
                {
                    worker.RequestStop();
                    cancellationToken.Cancel();
                }
            }
            catch (Exception ex)
            {
                ConsoleUtility.LogError(ex);
            }
            finally
            {
                ConsoleUtility.LogUtilityShutdown(title);
                Console.ReadKey();
            }
        }
        /// <summary>
        /// IMPORTANT: This example should be used sparingly - only when a new database is actually required!
        /// </summary>
        /// <param name="api"></param>
        /// <returns></returns>
        public static async Task <string> Run(GeotabDataOnlyPlanAPI api)
        {
            ConsoleUtility.LogExampleStarted(typeof(CreateDatabaseAsyncExample).Name);

            string createDatabaseResult = "";

            try
            {
                // Confirm user wishes to proceed with database creation.
                ConsoleUtility.LogWarning("By proceeding, you will be requesting the creation of a new MyGeotab database.");
                string input = ConsoleUtility.GetUserInput("'y' to confirm you wish to create a new database, or 'n' to cancel.");
                if (input != "y")
                {
                    ConsoleUtility.LogInfo("Cancelled CreateDatabaseAsync example.");
                    return(createDatabaseResult);
                }

                // Re-authenticate against "my.geotab.com".
                ConsoleUtility.LogInfoStart("Reauthenticating against my.geotab.com...");
                await api.AuthenticateAsync("my.geotab.com", "", api.Credentials.UserName, api.Credentials.Password);

                ConsoleUtility.LogComplete();

                string database = "";

                // Generate a database name and ensure that it is not already used.
                bool databaseExists = true;
                while (databaseExists == true)
                {
                    database       = Guid.NewGuid().ToString().Replace("-", "");
                    databaseExists = await api.DatabaseExistsAsync(database);
                }

                ConsoleUtility.LogInfoStartMultiPart($"Creating database named '{database}'.", $"THIS MAY TAKE SEVERAL MINUTES...", Common.ConsoleColorForWarnings);

                // Set parameter values for CreateDatabaseAsync call.
                string username      = api.Credentials.UserName;
                string password      = api.Credentials.Password;
                string companyName   = "Customer XYZ Ltd.";
                string firstName     = "John";
                string lastName      = "Smith";
                string phoneNumber   = "+1 (555) 123-4567";
                string resellerName  = "Reseller 123 Inc.";
                int    fleetSize     = 1;
                bool   signUpForNews = false;
                string timeZoneId    = "America/Toronto";
                string comments      = "some comments";

                // Create database.
                createDatabaseResult = await api.CreateDatabaseAsync(database, username, password, companyName, firstName, lastName, phoneNumber, resellerName, fleetSize, signUpForNews, timeZoneId, comments);

                ConsoleUtility.LogComplete();

                // Get the server and database information for the new database.
                string[] serverAndDatabase = (createDatabaseResult).Split('/');
                string   server            = serverAndDatabase.First();
                string   createdDatabase   = serverAndDatabase.Last();
                ConsoleUtility.LogInfo($"Created database '{createdDatabase}' on server '{server}'.");

                // Authenticate against the new database so that additional api calls (the 'Add', 'Set' and 'Remove' ones in particular) can be executed.
                ConsoleUtility.LogInfoStart($"Authenticating against '{createdDatabase}' database...");
                await api.AuthenticateAsync(server, createdDatabase, api.Credentials.UserName, api.Credentials.Password);

                ConsoleUtility.LogComplete();
            }
            catch (Exception ex)
            {
                ConsoleUtility.LogError(ex);
            }

            ConsoleUtility.LogExampleFinished(typeof(CreateDatabaseAsyncExample).Name);
            return(createDatabaseResult);
        }
Beispiel #17
0
        /// <summary>
        /// Executes the application workflow.
        /// </summary>
        private async Task Execute()
        {
            Console.ForegroundColor = ConsoleColor.White;
            ConsoleUtility.LogUtilityStartup(UtilityName);

            // Get configurtaion information.
            configItems = GeotabSdkUtility.GetConfigItems("configuration");

            string resellerErpAccountId, databaseName, devicesToUpdateFilePath;

            resellerErpAccountId    = configItems.Where(configItem => configItem.Key == ArgNameResellerErpAccountId).FirstOrDefault().Value;
            databaseName            = configItems.Where(configItem => configItem.Key == ArgNameDatabaseName).FirstOrDefault().Value;
            devicesToUpdateFilePath = configItems.Where(configItem => configItem.Key == ArgNameDevicesToUpdateFilePath).FirstOrDefault().Value;

            // Validate input values.
            ConsoleUtility.LogInfoStart("Validating input parameter values...");
            resellerErpAccountId    = ValidationUtility.ValidateStringLength(resellerErpAccountId, 6, ArgNameResellerErpAccountId);
            databaseName            = ValidationUtility.ValidateStringLength(databaseName, 3, ArgNameDatabaseName);
            devicesToUpdateFilePath = ValidationUtility.ValidateStringLength(devicesToUpdateFilePath, 3, ArgNameDevicesToUpdateFilePath);
            ConsoleUtility.LogComplete();

            // Ensure devices to update file exists.
            ConsoleUtility.LogInfoStart("Checking if devices to update file exists...");
            if (!File.Exists(devicesToUpdateFilePath))
            {
                ConsoleUtility.LogInfo($"The file path '{devicesToUpdateFilePath}' entered for '{ArgNameDevicesToUpdateFilePath}' is not valid.");
                return;
            }
            ConsoleUtility.LogComplete();

            // Authenticate MyAdmin API.
            try
            {
                (myAdminApi, myAdminApiUser, myAdminApiUsername, myAdminApiPassword) = await AdminSdkUtility.AuthenticateMyAdminApi();
            }
            catch (Exception e)
            {
                ConsoleUtility.LogError(e);
                // Provide user with a second authentication attempt.
                (myAdminApi, myAdminApiUser, myAdminApiUsername, myAdminApiPassword) = await AdminSdkUtility.AuthenticateMyAdminApi();
            }

            // Authenticate MyGeotab API.
            myGeotabApi = await GeotabSdkUtility.AuthenticateMyGeotabApiAsync(GeotabServer, databaseName, myAdminApiUsername, myAdminApiPassword);

            // Load the devices to update from the CSV file.
            ConsoleUtility.LogInfoStart($"Loading the device information that will be used to update devices in the '{databaseName}' database from file '{devicesToUpdateFilePath}'...");
            IList <DeviceDetails> deviceCandidates = null;

            using (FileStream devicesToImportFile = File.OpenRead(devicesToUpdateFilePath))
            {
                deviceCandidates = devicesToImportFile.CsvToList <DeviceDetails>();
            }
            ConsoleUtility.LogComplete();
            if (!deviceCandidates.Any())
            {
                ConsoleUtility.LogInfo($"No devices were loaded from the CSV file.");
                return;
            }

            // Get existing devices.
            ConsoleUtility.LogInfoStart($"Retrieving device and device database lists...");
            IList <Device> existingDevices = await myGeotabApi.CallAsync <IList <Device> >("Get", typeof(Device)) ?? new List <Device>();

            var currentDeviceDatabases = await AdminSdkUtility.GetCurrentDeviceDatabases(myAdminApi, myAdminApiUser, resellerErpAccountId);

            ConsoleUtility.LogComplete();

            // Process device list, adding new devices to the database and updating existing devices.
            ConsoleUtility.LogInfo($"Processing {deviceCandidates.Count} device(s) - attempting to update devices in MyGeotab database '{databaseName}'.");
            foreach (DeviceDetails deviceCandidate in deviceCandidates)
            {
                // Update device if it already exists in the current database.
                if (GeotabSdkUtility.DeviceExists(deviceCandidate.SerialNumber, existingDevices))
                {
                    try
                    {
                        Device deviceToUpdate = existingDevices.Where(device => device.SerialNumber == deviceCandidate.SerialNumber).First();
                        await GeotabSdkUtility.UpdateDeviceAsync(myGeotabApi, deviceToUpdate, deviceCandidate.Name, deviceCandidate.EnableDeviceBeeping, deviceCandidate.EnableDriverIdentificationReminder, deviceCandidate.DriverIdentificationReminderImmobilizeSeconds, deviceCandidate.EnableBeepOnEngineRpm, deviceCandidate.EngineRpmBeepValue, deviceCandidate.EnableBeepOnIdle, deviceCandidate.IdleMinutesBeepValue, deviceCandidate.EnableBeepOnSpeeding, deviceCandidate.SpeedingStartBeepingSpeed, deviceCandidate.SpeedingStopBeepingSpeed, deviceCandidate.EnableBeepBrieflyWhenApprocahingWarningSpeed, deviceCandidate.EnableBeepOnDangerousDriving, deviceCandidate.AccelerationWarningThreshold, deviceCandidate.BrakingWarningThreshold, deviceCandidate.CorneringWarningThreshold, deviceCandidate.EnableBeepWhenSeatbeltNotUsed, deviceCandidate.SeatbeltNotUsedWarningSpeed, deviceCandidate.EnableBeepWhenPassengerSeatbeltNotUsed, deviceCandidate.BeepWhenReversing);

                        ConsoleUtility.LogListItemWithResult($"{deviceCandidate.SerialNumber} ({deviceCandidate.Name})", $"UPDATED", ConsoleColor.Green);
                    }
                    catch (Exception ex)
                    {
                        ConsoleUtility.LogListItemWithResult($"{deviceCandidate.Name}", $"NOT UPDATED: ERROR updating device: {ex.Message}\n{ex.StackTrace}", ConsoleColor.Red);
                    }
                    continue;
                }

                // Device does not exist in the current database.  Instead of updating, try to add the device to the database instead.
                IList <Group> deviceGroups = new List <Group>();

                // Use the serial number for the name if a name is not provided.
                if (string.IsNullOrEmpty(deviceCandidate.Name))
                {
                    deviceCandidate.Name = deviceCandidate.SerialNumber;
                }

                string deviceCandidateSerialNumber = deviceCandidate.SerialNumber.Replace("-", "");

                // Check if the device is already in any database.
                if (currentDeviceDatabases.Where(database => database.SerialNumber == deviceCandidateSerialNumber).Any())
                {
                    IList <ApiDeviceDatabaseExtended> exitstingDatabases = currentDeviceDatabases.Where(database => database.SerialNumber == deviceCandidateSerialNumber).ToList();

                    StringBuilder existingDatabaseNames = new();
                    foreach (ApiDeviceDatabaseExtended database in exitstingDatabases)
                    {
                        if (existingDatabaseNames.Length > 0)
                        {
                            existingDatabaseNames.Append($", '{database.DatabaseName}'");
                        }
                        else
                        {
                            existingDatabaseNames.Append($"'{database.DatabaseName}'");
                        }
                    }
                    ConsoleUtility.LogListItemWithResult($"{deviceCandidate.Name}", $"NOT UPDATED OR ADDED: Device does not exist in '{databaseName}' database, but already exists in MyGeotab database(s) {existingDatabaseNames}.", ConsoleColor.Red);
                    continue;
                }

                // Assign the device to the Company group.
                deviceGroups.Add(new CompanyGroup());

                // Add the device to the MyGeotab database.
                try
                {
                    // Create the device object.
                    Device newDevice = Device.FromSerialNumber(deviceCandidate.SerialNumber);
                    newDevice.PopulateDefaults();
                    newDevice.Name     = deviceCandidate.Name;
                    newDevice.Groups   = deviceGroups;
                    newDevice.WorkTime = new WorkTimeStandardHours();

                    // Add the device.
                    var addedDevice = await GeotabSdkUtility.AddDeviceAsync(myGeotabApi, newDevice);

                    ConsoleUtility.LogListItemWithResult($"{deviceCandidate.SerialNumber}", $"ADDED: Device did not previously exist in '{databaseName}' database", ConsoleColor.Green);

                    // Update the device properties.
                    await GeotabSdkUtility.UpdateDeviceAsync(myGeotabApi, addedDevice, deviceCandidate.Name, deviceCandidate.EnableDeviceBeeping, deviceCandidate.EnableDriverIdentificationReminder, deviceCandidate.DriverIdentificationReminderImmobilizeSeconds, deviceCandidate.EnableBeepOnEngineRpm, deviceCandidate.EngineRpmBeepValue, deviceCandidate.EnableBeepOnIdle, deviceCandidate.IdleMinutesBeepValue, deviceCandidate.EnableBeepOnSpeeding, deviceCandidate.SpeedingStartBeepingSpeed, deviceCandidate.SpeedingStopBeepingSpeed, deviceCandidate.EnableBeepBrieflyWhenApprocahingWarningSpeed, deviceCandidate.EnableBeepOnDangerousDriving, deviceCandidate.AccelerationWarningThreshold, deviceCandidate.BrakingWarningThreshold, deviceCandidate.CorneringWarningThreshold, deviceCandidate.EnableBeepWhenSeatbeltNotUsed, deviceCandidate.SeatbeltNotUsedWarningSpeed, deviceCandidate.EnableBeepWhenPassengerSeatbeltNotUsed, deviceCandidate.BeepWhenReversing);

                    ConsoleUtility.LogListItemWithResult($"{deviceCandidate.SerialNumber} ({deviceCandidate.Name})", $"UPDATED", ConsoleColor.Green);
                }
                catch (Exception ex)
                {
                    ConsoleUtility.LogListItemWithResult($"{deviceCandidate.Name}", $"NOT UPDATED OR ADDED: Device did not previously exist in '{databaseName}' database, but an error was encountered while attempting to add the device. ERROR adding device: {ex.Message}\n{ex.StackTrace}", ConsoleColor.Red);
                }
            }

            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine("Completed processing.");
            Console.ForegroundColor = ConsoleColor.White;
        }
Beispiel #18
0
        static async Task Main()
        {
            GeotabDataOnlyPlanAPI api;
            string username;
            string password;
            string title = "";

            string lastCreateDatabaseResult;
            string lastAddedDeviceServer         = "";
            string lastAddedDeviceDatabase       = "";
            string lastAddedDeviceId             = "";
            string lastAddedUserServer           = "";
            string lastAddedUserDatabase         = "";
            string lastAddedUserId               = "";
            string lastAddedDriverChangeServer   = "";
            string lastAddedDriverChangeDatabase = "";
            string lastAddedDriverChangeId       = "";

            try
            {
                // Set title.
                title         = AppDomain.CurrentDomain.FriendlyName.Replace(".", " ");
                Console.Title = title;
                ConsoleUtility.LogUtilityStartup(title);

                // Request MyGeotab credentials and database name.
                string server   = ConsoleUtility.GetUserInput($"MyGeotab server");
                string database = ConsoleUtility.GetUserInput($"Database to run examples against.").ToLower();
                username = ConsoleUtility.GetUserInput($"MyGeotab username");
                password = ConsoleUtility.GetUserInputMasked($"MyGeotab password");

                // Create Geotab Data-Only Plan API instance and authenticate.
                api = new GeotabDataOnlyPlanAPI(server, database, username, password);
                ConsoleUtility.LogInfoStart("Authenticating...");
                await api.AuthenticateAsync();

                ConsoleUtility.LogOk();

                bool exit = false;
                while (exit == false)
                {
                    // List the examples that the user may select from.
                    ConsoleUtility.LogExamplesMenuHeader();
                    IList <KeyValuePair <int, string> > listItems = new List <KeyValuePair <int, string> >();
                    string[] GeotabDataOnlyPlanAPIExamples        = Enum.GetNames(typeof(GeotabDataOnlyPlanAPIExample));
                    for (int GeotabDataOnlyPlanAPIExampleId = 0; GeotabDataOnlyPlanAPIExampleId < GeotabDataOnlyPlanAPIExamples.Length; GeotabDataOnlyPlanAPIExampleId++)
                    {
                        listItems.Add(new KeyValuePair <int, string>(GeotabDataOnlyPlanAPIExampleId, GeotabDataOnlyPlanAPIExamples[GeotabDataOnlyPlanAPIExampleId]));
                    }
                    ConsoleUtility.LogListItems(listItems, Common.ConsoleColorForListItemIds, Common.ConsoleColorForListItems);

                    // Get user to select which example to run.
                    bool exampleSelected = false;
                    while (!exampleSelected)
                    {
                        exampleSelected = true;
                        string input = ConsoleUtility.GetUserInput("number of the example to run (from the above list), or 'x' to quit.");

                        if (input == "x" || input == "X")
                        {
                            exit = true;
                            break;
                        }

                        if (int.TryParse(input, out int selection))
                        {
                            switch (selection)
                            {
                            case (int)GeotabDataOnlyPlanAPIExample.AddDeviceAsync:
                                lastAddedDeviceId = await AddDeviceAsyncExample.Run(api);

                                lastAddedDeviceServer   = server;
                                lastAddedDeviceDatabase = database;
                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.AddDriverChangeAsync:
                                if (lastAddedDeviceDatabase == database && lastAddedDeviceId != "" && lastAddedUserDatabase == database && lastAddedUserId != "")
                                {
                                    lastAddedDriverChangeId = await AddDriverChangeAsyncExample.Run(api, lastAddedDeviceId, lastAddedUserId);

                                    lastAddedDriverChangeServer   = server;
                                    lastAddedDriverChangeDatabase = database;
                                }
                                else if (lastAddedDeviceId == "" || lastAddedUserId == "")
                                {
                                    ConsoleUtility.LogError($"The 'AddDeviceAsync' and 'AddUserAsync' examples must be run before the 'AddDriverChangeAsync' example can be run.");
                                }
                                else
                                {
                                    if (lastAddedDeviceDatabase != lastAddedUserDatabase)
                                    {
                                        ConsoleUtility.LogError($"The 'AddDeviceAsync' example was last run against the '{lastAddedDeviceDatabase}' database on the '{lastAddedDeviceServer}' server and added the device '{lastAddedDeviceId}'.  The 'AddUserAsync' example was last run against the '{lastAddedUserDatabase}' database on the '{lastAddedUserServer}' server and added the user '{lastAddedUserId}'.  The 'AddDeviceAsync' and 'AddUserAsync' examples must be run on the same database before the 'AddDriverChangeAsync' example can be run.");
                                    }
                                }
                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.AddTextMessageAsync:
                                if (lastAddedDeviceDatabase == database && lastAddedDeviceId != "" && lastAddedUserDatabase == database && lastAddedUserId != "")
                                {
                                    await AddTextMessageAsyncExample.Run(api, lastAddedDeviceId, lastAddedUserId);
                                }
                                else if (lastAddedDeviceId == "" || lastAddedUserId == "")
                                {
                                    ConsoleUtility.LogError($"The 'AddDeviceAsync' and 'AddUserAsync' examples must be run before the 'AddTextMessageAsync' example can be run.");
                                }
                                else
                                {
                                    if (lastAddedDeviceDatabase != lastAddedUserDatabase)
                                    {
                                        ConsoleUtility.LogError($"The 'AddDeviceAsync' example was last run against the '{lastAddedDeviceDatabase}' database on the '{lastAddedDeviceServer}' server and added the device '{lastAddedDeviceId}'.  The 'AddUserAsync' example was last run against the '{lastAddedUserDatabase}' database on the '{lastAddedUserServer}' server and added the user '{lastAddedUserId}'.  The 'AddDeviceAsync' and 'AddUserAsync' examples must be run on the same database before the 'AddTextMessageAsync' example can be run.");
                                    }
                                }
                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.AddUserAsync:
                                lastAddedUserId = await AddUserAsyncExample.Run(api);

                                lastAddedUserServer   = server;
                                lastAddedUserDatabase = database;
                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.ArchiveDeviceAsync:
                                if (lastAddedDeviceDatabase == database && lastAddedDeviceId != "")
                                {
                                    await ArchiveDeviceAsyncExample.Run(api, lastAddedDeviceId);
                                }
                                else if (lastAddedDeviceId == "")
                                {
                                    ConsoleUtility.LogError($"The 'AddDeviceAsync' example must be run before the 'ArchiveDeviceAsync' example can be run.");
                                }
                                else if (lastAddedDeviceDatabase != database)
                                {
                                    ConsoleUtility.LogError($"The 'AddDeviceAsync' example was last run against the '{lastAddedDeviceDatabase}' database on the '{lastAddedDeviceServer}' server and added the device '{lastAddedDeviceId}'.  Please run the 'AuthenticateAsync' example to authenticate against the '{lastAddedDeviceDatabase}' database on the '{lastAddedDeviceServer}' server before running the 'ArchiveDeviceAsync' example.");
                                }
                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.AuthenticateAsync:
                                await AuthenticateAsyncExample.Run(api);

                                database = api.Credentials.Database;
                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.CreateDatabaseAsync:
                                lastCreateDatabaseResult = await CreateDatabaseAsyncExample.Run(api);

                                // Get the server and database information for the new database.
                                string[] serverAndDatabase = (lastCreateDatabaseResult).Split('/');
                                server   = serverAndDatabase.First();
                                database = serverAndDatabase.Last();
                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.DatabaseExistsAsync:
                                await DatabaseExistsAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GenerateCaptchaAsync:
                                await GenerateCaptchaAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetBinaryDataAsync:
                                await GetBinaryDataAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetControllersAsync:
                                await GetControllersAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetCountOfDeviceAsync:
                                await GetCountOfDeviceAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetCountOfUserAsync:
                                await GetCountOfUserAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetFailureModesAsync:
                                await GetFailureModesAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetFeedCustomDataAsync:
                                await GetFeedCustomDataAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetFeedDeviceAsyncExample:
                                await GetFeedDeviceAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetFeedDiagnosticAsyncExample:
                                await GetFeedDiagnosticAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetFeedDriverChangeAsyncExample:
                                await GetFeedDriverChangeAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetFeedFaultDataAsync:
                                await GetFeedFaultDataAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetFeedLogRecordAsync:
                                await GetFeedLogRecordAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetFeedIoxAddOnAsync:
                                await GetFeedIoxAddOnAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetFeedStatusDataAsync:
                                await GetFeedStatusDataAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetFeedTripAsync:
                                await GetFeedTripAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetFeedUserAsyncExample:
                                await GetFeedUserAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetFlashCodesAsync:
                                await GetFlashCodesAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetSourceAsync:
                                await GetSourceAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetSourcesAsync:
                                await GetSourcesAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetSystemTimeUtcAsync:
                                await GetSystemTimeUtcAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetTimeZonesAsync:
                                await GetTimeZonesAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetUnitOfMeasureAsync:
                                await GetUnitOfMeasureAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetUnitsOfMeasureAsync:
                                await GetUnitsOfMeasureAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetVersionAsync:
                                await GetVersionAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.GetVersionInformationAsync:
                                await GetVersionInformationAsyncExample.Run(api);

                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.RemoveDriverChangeAsync:
                                if (lastAddedDriverChangeDatabase == database && lastAddedDriverChangeId != "")
                                {
                                    await RemoveDriverChangeAsyncExample.Run(api, lastAddedDriverChangeId);

                                    lastAddedDriverChangeDatabase = "";
                                    lastAddedDriverChangeId       = "";
                                }
                                else if (lastAddedDriverChangeId == "")
                                {
                                    ConsoleUtility.LogError($"The 'AddDriverChangeAsync' example must be run before the 'RemoveDriverChangeAsync' example can be run.");
                                }
                                else if (lastAddedDriverChangeDatabase != database)
                                {
                                    ConsoleUtility.LogError($"The 'AddDriverChangeAsync' example was last run against the '{lastAddedDriverChangeDatabase}' database on the '{lastAddedDriverChangeServer}' server and added the DriverChange '{lastAddedDriverChangeId}'.  Please run the 'AuthenticateAsync' example to authenticate against the '{lastAddedDriverChangeDatabase}' database on the '{lastAddedDriverChangeServer}' server before running the 'RemoveDriverChangeAsync' example.");
                                }
                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.RemoveDeviceAsync:
                                if (lastAddedDeviceDatabase == database && lastAddedDeviceId != "")
                                {
                                    await RemoveDeviceAsyncExample.Run(api, lastAddedDeviceId);

                                    lastAddedDeviceDatabase = "";
                                    lastAddedDeviceId       = "";
                                }
                                else if (lastAddedDeviceId == "")
                                {
                                    ConsoleUtility.LogError($"The 'AddDeviceAsync' example must be run before the 'RemoveDeviceAsync' example can be run.");
                                }
                                else if (lastAddedDeviceDatabase != database)
                                {
                                    ConsoleUtility.LogError($"The 'AddDeviceAsync' example was last run against the '{lastAddedDeviceDatabase}' database on the '{lastAddedDeviceServer}' server and added the device '{lastAddedDeviceId}'.  Please run the 'AuthenticateAsync' example to authenticate against the '{lastAddedDeviceDatabase}' database on the '{lastAddedDeviceServer}' server before running the 'RemoveDeviceAsync' example.");
                                }
                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.RemoveUserAsync:
                                if (lastAddedUserDatabase == database && lastAddedUserId != "")
                                {
                                    await RemoveUserAsyncExample.Run(api, lastAddedUserId);

                                    lastAddedUserDatabase = "";
                                    lastAddedUserId       = "";
                                }
                                else if (lastAddedUserId == "")
                                {
                                    ConsoleUtility.LogError($"The 'AddUserAsync' example must be run before the 'RemoveUserAsync' example can be run.");
                                }
                                else if (lastAddedUserDatabase != database)
                                {
                                    ConsoleUtility.LogError($"The 'AddUserAsync' example was last run against the '{lastAddedUserDatabase}' database on the '{lastAddedUserServer}' server and added the user '{lastAddedUserId}'.  Please run the 'AuthenticateAsync' example to authenticate against the '{lastAddedUserDatabase}' database on the '{lastAddedUserServer}' server before running the 'RemoveUserAsync' example.");
                                }
                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.SetDeviceAsync:
                                if (lastAddedDeviceDatabase == database && lastAddedDeviceId != "")
                                {
                                    await SetDeviceAsyncExample.Run(api, lastAddedDeviceId);
                                }
                                else if (lastAddedDeviceId == "")
                                {
                                    ConsoleUtility.LogError($"The 'AddDeviceAsync' example must be run before the 'SetDeviceAsync' example can be run.");
                                }
                                else if (lastAddedDeviceDatabase != database)
                                {
                                    ConsoleUtility.LogError($"The 'AddDeviceAsync' example was last run against the '{lastAddedDeviceDatabase}' database on the '{lastAddedDeviceServer}' server and added the device '{lastAddedDeviceId}'.  Please run the 'AuthenticateAsync' example to authenticate against the '{lastAddedDeviceDatabase}' database on the '{lastAddedDeviceServer}' server before running the 'SetDeviceAsync' example.");
                                }
                                break;

                            case (int)GeotabDataOnlyPlanAPIExample.SetUserAsync:
                                if (lastAddedUserDatabase == database && lastAddedUserId != "")
                                {
                                    await SetUserAsyncExample.Run(api, lastAddedUserId);
                                }
                                else if (lastAddedUserId == "")
                                {
                                    ConsoleUtility.LogError($"The 'AddUserAsync' example must be run before the 'SetUserAsync' example can be run.");
                                }
                                else if (lastAddedUserDatabase != database)
                                {
                                    ConsoleUtility.LogError($"The 'AddUserAsync' example was last run against the '{lastAddedUserDatabase}' database on the '{lastAddedUserServer}' server and added the user '{lastAddedUserId}'.  Please run the 'AuthenticateAsync' example to authenticate against the '{lastAddedUserDatabase}' database on the '{lastAddedUserServer}' server before running the 'SetUserAsync' example.");
                                }
                                break;

                            default:
                                exampleSelected = false;
                                ConsoleUtility.LogError($"The value '{input}' is not valid.");
                                break;
                            }
                        }
                        else
                        {
                            exampleSelected = false;
                            ConsoleUtility.LogError($"The value '{input}' is not valid.");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ConsoleUtility.LogError(ex);
            }
            finally
            {
                ConsoleUtility.LogUtilityShutdown(title);
                _ = Console.ReadKey();
            }
        }
        /// <summary>
        /// Executes the application workflow.
        /// </summary>
        private async Task Execute()
        {
            Console.ForegroundColor = ConsoleColor.White;
            ConsoleUtility.LogUtilityStartup(UtilityName);

            // Get configurtaion information.
            configItems = GeotabSdkUtility.GetConfigItems("configuration");

            string resellerName, resellerErpAccountId, customerCompanyName, customerAccountAdminFirstName, customerAccountAdminLastName, customerAccountAdminEmail, customerPhoneNumber, customerFleetSize, customerSignUpForNews, customerDesiredDatabaseName, customerTimeZoneId, customerDeviceListPath;

            resellerName                  = configItems.Where(configItem => configItem.Key == ArgNameResellerName).FirstOrDefault().Value;
            resellerErpAccountId          = configItems.Where(configItem => configItem.Key == ArgNameResellerErpAccountId).FirstOrDefault().Value;
            customerCompanyName           = configItems.Where(configItem => configItem.Key == ArgNameCustomerCompanyName).FirstOrDefault().Value;
            customerAccountAdminFirstName = configItems.Where(configItem => configItem.Key == ArgNameCustomerAccountAdminFirstName).FirstOrDefault().Value;
            customerAccountAdminLastName  = configItems.Where(configItem => configItem.Key == ArgNameCustomerAccountAdminLastName).FirstOrDefault().Value;
            customerAccountAdminEmail     = configItems.Where(configItem => configItem.Key == ArgNameCustomerAccountAdminEmail).FirstOrDefault().Value;
            customerPhoneNumber           = configItems.Where(configItem => configItem.Key == ArgNameCustomerPhoneNumber).FirstOrDefault().Value;
            customerFleetSize             = configItems.Where(configItem => configItem.Key == ArgNameCustomerFleetSize).FirstOrDefault().Value;
            customerSignUpForNews         = configItems.Where(configItem => configItem.Key == ArgNameCustomerSignUpForNews).FirstOrDefault().Value;
            customerDesiredDatabaseName   = configItems.Where(configItem => configItem.Key == ArgNameCustomerDesiredDatabaseName).FirstOrDefault().Value;
            customerTimeZoneId            = configItems.Where(configItem => configItem.Key == ArgNameCustomerTimeZoneId).FirstOrDefault().Value;
            customerDeviceListPath        = configItems.Where(configItem => configItem.Key == ArgNameCustomerDeviceListPath).FirstOrDefault().Value;

            // Validate input values.
            ConsoleUtility.LogInfoStart("Validating input parameter values...");
            // OPTIONAL: Validate email address if enforcing the use of email addresses for usernames.
            // customerAccountAdminEmail = ValidationUtility.ValidateEmailAddress(customerAccountAdminEmail);
            customerFleetSize             = ValidationUtility.ValidateStringIsInt32(customerFleetSize, "customer fleet size");
            customerSignUpForNews         = ValidationUtility.ValidateStringIsBoolean(customerSignUpForNews, "customer's desire to sign-up for news");
            resellerName                  = ValidationUtility.ValidateStringLength(resellerName, 3, ArgNameResellerName);
            resellerErpAccountId          = ValidationUtility.ValidateStringLength(resellerErpAccountId, 6, ArgNameResellerErpAccountId);
            customerCompanyName           = ValidationUtility.ValidateStringLength(customerCompanyName, 3, ArgNameCustomerCompanyName);
            customerAccountAdminFirstName = ValidationUtility.ValidateStringLength(customerAccountAdminFirstName, 1, ArgNameCustomerAccountAdminFirstName);
            customerAccountAdminLastName  = ValidationUtility.ValidateStringLength(customerAccountAdminLastName, 1, ArgNameCustomerAccountAdminLastName);
            customerPhoneNumber           = ValidationUtility.ValidateStringLength(customerPhoneNumber, 10, ArgNameCustomerPhoneNumber);
            customerDesiredDatabaseName   = ValidationUtility.ValidateStringLength(customerDesiredDatabaseName, 3, ArgNameCustomerDesiredDatabaseName);
            customerTimeZoneId            = ValidationUtility.ValidateStringLength(customerTimeZoneId, 3, ArgNameCustomerTimeZoneId);
            customerDeviceListPath        = ValidationUtility.ValidateStringLength(customerDeviceListPath, 3, ArgNameCustomerDeviceListPath);
            ConsoleUtility.LogComplete();

            // Ensure customer device list file exists.
            ConsoleUtility.LogInfoStart("Checking if customer device list file exists...");
            if (!File.Exists(customerDeviceListPath))
            {
                ConsoleUtility.LogInfo($"The file path '{customerDeviceListPath}' entered for '{ArgNameCustomerDeviceListPath}' is not valid.");
                return;
            }
            ConsoleUtility.LogComplete();

            // Authenticate MyAdmin API.
            try
            {
                (myAdminApi, myAdminApiUser, myAdminApiUsername, myAdminApiPassword) = await AdminSdkUtility.AuthenticateMyAdminApi();
            }
            catch (Exception e)
            {
                ConsoleUtility.LogError(e);
                // Provide user with a second authentication attempt.
                (myAdminApi, myAdminApiUser, myAdminApiUsername, myAdminApiPassword) = await AdminSdkUtility.AuthenticateMyAdminApi();
            }

            // Authenticate MyGeotab API.
            myGeotabApi = await GeotabSdkUtility.AuthenticateMyGeotabApiAsync(GeotabServer, "", myAdminApiUsername, myAdminApiPassword);

            // Check whether customer desired database name is already used.  If so, prompt user for
            // alternates until an unused database name is found.
            ConsoleUtility.LogInfoStart($"Checking whether database name '{customerDesiredDatabaseName}' is available...");
            if (await GeotabSdkUtility.DatabaseExistsAsync(myGeotabApi, customerDesiredDatabaseName))
            {
                bool   tryAnotherDatabaseName = true;
                string proposedDatabaseName   = string.Empty;
                while (tryAnotherDatabaseName)
                {
                    customerDesiredDatabaseName = ConsoleUtility.GetUserInput($"a different database name ('{customerDesiredDatabaseName}' is already used)");
                    tryAnotherDatabaseName      = await GeotabSdkUtility.DatabaseExistsAsync(myGeotabApi, customerDesiredDatabaseName);
                }
            }
            else
            {
                ConsoleUtility.LogComplete();
            }

            // Get the password to be used for the customer's administrative user account which will be created in the customer dataabse.
            string customerAccountAdminPassword = ConsoleUtility.GetVerifiedUserInputMasked($"Desired MyGeotab password for '{customerAccountAdminEmail}' to access '{customerDesiredDatabaseName}' database", $"[RE-ENTER] desired MyGeotab password for '{customerAccountAdminEmail}' to access '{customerDesiredDatabaseName}' database");

            customerAccountAdminPassword = ValidationUtility.ValidatePassword(customerAccountAdminPassword);

            // Validate input parameters that require API access.
            customerTimeZoneId = await GeotabSdkUtility.ValidateTimeZoneIdAsync(myGeotabApi, customerTimeZoneId);

            // Create customer database.
            string createDatabaseResult = await GeotabSdkUtility.CreateDatabaseAsync(myGeotabApi, customerDesiredDatabaseName, myAdminApiUsername
                                                                                     , myAdminApiPassword, customerCompanyName, customerAccountAdminFirstName
                                                                                     , customerAccountAdminLastName, customerPhoneNumber, resellerName, Int32.Parse(customerFleetSize)
                                                                                     , bool.Parse(customerSignUpForNews), customerTimeZoneId);

            // Get the server and database information for the new database.
            string[] serverAndDatabase      = (createDatabaseResult).Split('/');
            string   customerDatabaseServer = serverAndDatabase.First();
            string   customerDatabase       = serverAndDatabase.Last();

            // Authenticate MyGeotab API against the newly-created database:
            myGeotabApi = await GeotabSdkUtility.AuthenticateMyGeotabApiAsync(customerDatabaseServer, customerDatabase, myAdminApiUsername, myAdminApiPassword);

            // Create administrative user in customer database for customer administrator to use.
            IList <User>  existingUsers = new List <User>();
            IList <Group> companyGroups = await GeotabSdkUtility.GetCompanyGroupsAsync(myGeotabApi);

            IList <Group> securityGroups = await GeotabSdkUtility.GetSecurityGroupsAsync(myGeotabApi);

            IList <Group> adminSecurityGroup = GeotabSdkUtility.GetSecurityGroupAsList(GeotabSdkUtility.SecurityGroupName.Administrator, securityGroups);
            IList <Group> companyGroup       = GeotabSdkUtility.GetGroupAsList(GeotabSdkUtility.CompanyGroupName, companyGroups);

            User user = User.CreateBasicUser(null, null, customerAccountAdminEmail, customerAccountAdminFirstName, customerAccountAdminLastName, customerAccountAdminPassword, null, null, null, DateTime.MinValue, DateTime.MaxValue, companyGroup, null, adminSecurityGroup, null);

            user.ChangePassword = true;
            if (GeotabSdkUtility.ValidateUser(user, existingUsers))
            {
                try
                {
                    ConsoleUtility.LogInfoStart($"Adding user '{user.Name}' to database '{myGeotabApi.Database}'...");
                    await GeotabSdkUtility.AddUserAsync(myGeotabApi, user);

                    ConsoleUtility.LogComplete();
                    existingUsers.Add(user);
                }
                catch (Exception exception)
                {
                    ConsoleUtility.LogError($"Error adding user '{user.Name}' to database '{myGeotabApi.Database}'\n{exception.Message}");
                }
            }
            else
            {
                return;
            }

            // Load the list of devices to be imported from the CSV file.
            ConsoleUtility.LogInfoStart($"Loading the list of devices to be imported into the '{customerDesiredDatabaseName}' database from file '{customerDeviceListPath}'...");
            IList <DeviceDetails> deviceCandidates = null;

            using (FileStream devicesToImportFile = File.OpenRead(customerDeviceListPath))
            {
                deviceCandidates = devicesToImportFile.CsvToList <DeviceDetails>();
            }
            ConsoleUtility.LogComplete();
            if (!deviceCandidates.Any())
            {
                ConsoleUtility.LogInfo($"No devices were loaded from the CSV file.");
                return;
            }

            // Get existing devices.
            ConsoleUtility.LogInfoStart($"Retrieving device and device database lists...");
            IList <Device> existingDevices = await myGeotabApi.CallAsync <IList <Device> >("Get", typeof(Device));

            var currentDeviceDatabases = await AdminSdkUtility.GetCurrentDeviceDatabases(myAdminApi, myAdminApiUser, resellerErpAccountId);

            ConsoleUtility.LogComplete();

            // Add devices into the MyGeotab database.
            ConsoleUtility.LogInfo($"Processing {deviceCandidates.Count} device(s) - attempting to add to MyGeotab database '{customerDesiredDatabaseName}'.");
            foreach (DeviceDetails deviceCandidate in deviceCandidates)
            {
                IList <Group> deviceGroups = new List <Group>();

                // Use the serial number for the description since it won't likely be known at this point which vehicle the device will be installed into.
                deviceCandidate.Name = deviceCandidate.SerialNumber;
                string deviceCandidateSerialNumber = deviceCandidate.SerialNumber.Replace("-", "");

                // Check if the device is already in any database.
                if (currentDeviceDatabases.Where(database => database.SerialNumber == deviceCandidateSerialNumber).Any())
                {
                    IList <ApiDeviceDatabaseExtended> existingDatabases = currentDeviceDatabases.Where(database => database.SerialNumber == deviceCandidateSerialNumber).ToList();

                    StringBuilder existingDatabaseNames = new();
                    foreach (ApiDeviceDatabaseExtended database in existingDatabases)
                    {
                        if (existingDatabaseNames.Length > 0)
                        {
                            existingDatabaseNames.Append($", '{database.DatabaseName}'");
                        }
                        else
                        {
                            existingDatabaseNames.Append($"'{database.DatabaseName}'");
                        }
                    }
                    ConsoleUtility.LogListItemWithResult($"{deviceCandidate.Name}", $"NOT ADDED: Device already exists in MyGeotab database(s) {existingDatabaseNames}.", ConsoleColor.Red);
                    continue;
                }

                // Assign the device to the Company group.
                deviceGroups.Add(new CompanyGroup());

                // Add the device to the MyGeotab database.
                try
                {
                    // Create the device object.
                    Device newDevice = Device.FromSerialNumber(deviceCandidate.SerialNumber);
                    newDevice.PopulateDefaults();
                    newDevice.Name     = deviceCandidate.Name;
                    newDevice.Groups   = deviceGroups;
                    newDevice.WorkTime = new WorkTimeStandardHours();

                    // Add the device.
                    await myGeotabApi.CallAsync <Id>("Add", typeof(Device), new { entity = newDevice });

                    ConsoleUtility.LogListItemWithResult($"{deviceCandidate.Name}", $"ADDED", ConsoleColor.Green);
                }
                catch (Exception ex)
                {
                    ConsoleUtility.LogListItemWithResult($"{deviceCandidate.Name}", $"NOT ADDED: ERROR adding device: {ex.Message}\n{ex.StackTrace}", ConsoleColor.Red);
                }
            }
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine("Completed processing.");
            Console.ForegroundColor = ConsoleColor.White;
        }