示例#1
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);
        }
        /// <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);
        }
        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="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);
        }
        /// <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);
        }
示例#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 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);
        }
示例#13
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);
        }
        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);
        }
        /// <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);
        }
示例#16
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;
        }
        /// <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;
        }