/// <summary>
        /// Initialize the system properties for a new device.
        /// </summary>
        /// <param name="device"></param>
        /// <param name="iccid"></param>
        /// <returns></returns>
        private static void InitializeSystemProperties(DeviceModel device, string iccid)
        {
            SystemProperties systemProps = new SystemProperties();
            systemProps.ICCID = iccid;

            device.SystemProperties = systemProps;
        }
        /// <summary>
        /// Adds a device to the Device Identity Store and Device Registry
        /// </summary>
        /// <param name="device">Device to add to the underlying repositories</param>
        /// <returns>Device created along with the device identity store keys</returns>
        public async Task<DeviceWithKeys> AddDeviceAsync(DeviceModel device)
        {
            // Validation logic throws an exception if it finds a validation error
            await this.ValidateDevice(device);

            SecurityKeys generatedSecurityKeys = this._securityKeyGenerator.CreateRandomKeys();

            DeviceModel savedDevice = await this.AddDeviceToRepositoriesAsync(device, generatedSecurityKeys);
            return new DeviceWithKeys(savedDevice, generatedSecurityKeys);
        }
        /// <summary>
        /// Initialize the device properties for a new device.
        /// </summary>
        /// <param name="device"></param>
        /// <param name="deviceId"></param>
        /// <param name="isSimulated"></param>
        /// <returns></returns>
        private static void InitializeDeviceProperties(DeviceModel device, string deviceId, bool isSimulated)
        {
            DeviceProperties deviceProps = new DeviceProperties();
            deviceProps.DeviceID = deviceId;
            deviceProps.HubEnabledState = null;
            deviceProps.CreatedTime = DateTime.UtcNow;
            deviceProps.DeviceState = "normal";
            deviceProps.UpdatedTime = null;

            device.DeviceProperties = deviceProps;
        }
        /// <summary>
        /// Build a valid device representation used throughout the app.
        /// </summary>
        /// <param name="deviceId"></param>
        /// <param name="isSimulated"></param>
        /// <param name="iccid"></param>
        /// <returns></returns>
        public static DeviceModel BuildDeviceStructure(string deviceId, bool isSimulated, string iccid)
        {
            DeviceModel device = new DeviceModel();

            InitializeDeviceProperties(device, deviceId, isSimulated);
            InitializeSystemProperties(device, iccid);

            device.Commands = new List<Command>();
            device.CommandHistory = new List<CommandHistory>();
            device.IsSimulatedDevice = isSimulated;

            return device;
        }
 public void GetListOfAvailableIccidsTest()
 {
     var iccids = fixture.Create<List<Iccid>>();
     iccids.Add(new Iccid("id1"));
     IList<DeviceModel> devices = fixture.Create<List<DeviceModel>>();
     var device = new DeviceModel();
     device.DeviceProperties = new DeviceProperties();
     device.DeviceProperties.DeviceID = "id1";
     device.SystemProperties = new SystemProperties();
     device.SystemProperties.ICCID = "id1";
     devices.Add(device);
     cellularService.Setup(mock => mock.GetTerminals()).Returns(iccids);
     var result = cellularExtensions.GetListOfAvailableIccids(devices);
     Assert.Equal(result.Count(), devices.Count - 1);
     Assert.False(result.Contains("id1"));
 }
        /// <summary>
        ///     Adds the provided device to the IoT hub with the provided security keys
        /// </summary>
        /// <param name="device"></param>
        /// <param name="securityKeys"></param>
        /// <returns></returns>
        public async Task<DeviceModel> AddDeviceAsync(DeviceModel device, SecurityKeys securityKeys)
        {
            var iotHubDevice = new Device(device.DeviceProperties.DeviceID);

            var authentication = new AuthenticationMechanism
                                 {
                                     SymmetricKey = new SymmetricKey
                                                    {
                                                        PrimaryKey = securityKeys.PrimaryKey,
                                                        SecondaryKey = securityKeys.SecondaryKey
                                                    }
                                 };

            iotHubDevice.Authentication = authentication;

            await AzureRetryHelper.OperationWithBasicRetryAsync(async () =>
                                                                await this._deviceManager.AddDeviceAsync(iotHubDevice));

            return device;
        }
        /// <summary>
        /// Adds a device to the DocumentDB.
        /// Throws a DeviceAlreadyRegisteredException if a device already exists in the database with the provided deviceId
        /// </summary>
        /// <param name="device"></param>
        /// <returns></returns>
        public async Task<DeviceModel> AddDeviceAsync(DeviceModel device)
        {
            if (device == null)
            {
                throw new ArgumentNullException("device");
            }

            if (string.IsNullOrEmpty(device.id))
            {
                device.id = Guid.NewGuid().ToString();
            }

            DeviceModel existingDevice = await GetDeviceAsync(device.DeviceProperties.DeviceID);
            if (existingDevice != null)
            {
                throw new DeviceAlreadyRegisteredException(device.DeviceProperties.DeviceID);
            }

            var savedDevice = await _documentClient.SaveAsync(device);
            return savedDevice;
        }
        private async Task ProcessEventItem(DeviceModel eventData)
        {
            if (eventData == null || eventData.ObjectType == null)
            {
                Trace.TraceWarning("Event has no ObjectType defined.  No action was taken on Event packet.");
                return;
            }

            string objectType = eventData.ObjectType;

            var objectTypePrefix = _configurationProvider.GetConfigurationSettingValue("ObjectTypePrefix");
            if (string.IsNullOrWhiteSpace(objectTypePrefix))
            {
                objectTypePrefix = "";
            }


            if (objectType == objectTypePrefix + SampleDeviceFactory.OBJECT_TYPE_DEVICE_INFO)
            {
                await ProcessDeviceInfo(eventData);
            }
            else
            {
                Trace.TraceWarning("Unknown ObjectType in event.");
            }
        }
        public IEnumerable<DevicePropertyValueModel> ExtractDevicePropertyValuesModels(
           DeviceModel device)
        {
            DeviceProperties deviceProperties;
            string hostNameValue;
            IEnumerable<DevicePropertyValueModel> propValModels;

            if (device == null)
            {
                throw new ArgumentNullException("device");
            }

            deviceProperties = device.DeviceProperties;
            if (deviceProperties == null)
            {
                throw new DeviceRequiredPropertyNotFoundException("Required DeviceProperties not found");
            }

            propValModels = ExtractPropertyValueModels(deviceProperties);
            hostNameValue = _configProvider.GetConfigurationSettingValue("iotHub.HostName");

            if (!string.IsNullOrEmpty(hostNameValue))
            {
                propValModels = propValModels.Concat(
                        new DevicePropertyValueModel[]
                        {
                            new DevicePropertyValueModel()
                            {
                                DisplayOrder = 0,
                                IsEditable = false,
                                IsIncludedWithUnregisteredDevices = true,
                                Name = "HostName",
                                PropertyType = Models.PropertyType.String,
                                Value = hostNameValue
                            }
                        });
            }

            return propValModels;
        }
        private IList<SelectListItem> GetCommandListItems(DeviceModel device)
        {
            IList<SelectListItem> result = new List<SelectListItem>();
            IList<Command> commands = device.Commands;

            if (commands != null)
            {
                foreach (Command command in commands)
                {
                    if (IsCommandPublic(command))
                    {
                        SelectListItem item = new SelectListItem();
                        item.Value = command.Name;
                        item.Text = command.Name;
                        result.Add(item);
                    }
                }
            }

            return result;
        }
        private bool ValidateDeviceId(DeviceModel device, List<string> validationErrors)
        {
            if (device.DeviceProperties == null || string.IsNullOrWhiteSpace(device.DeviceProperties.DeviceID))
            {
                validationErrors.Add(Strings.ValidationDeviceIdMissing);
                return false;
            }

            return true;
        }
        public IList<DeviceTelemetryFieldModel> ExtractTelemetry(DeviceModel device)
        {
            // Get Telemetry Fields
            if (device != null && device.Telemetry != null)
            {
                var deviceTelemetryFields = new List<DeviceTelemetryFieldModel>();

                foreach (var field in device.Telemetry)
                {
                    // Default displayName to null if not present
                    string displayName = field.DisplayName != null ?
                        field.DisplayName : null;

                    deviceTelemetryFields.Add(new DeviceTelemetryFieldModel
                    {
                        DisplayName = displayName,
                        Name = field.Name,
                        Type = field.Type
                    });
                }

                return deviceTelemetryFields;
            }
            else
            {
                return null;
            }
        }
        private async Task ValidateDevice(DeviceModel device)
        {
            List<string> validationErrors = new List<string>();

            if (ValidateDeviceId(device, validationErrors)) 
            {
                await CheckIfDeviceExists(device, validationErrors);
            }

            if (validationErrors.Count > 0)
            {
                var validationException =
                    new ValidationException(device.DeviceProperties != null ? device.DeviceProperties.DeviceID : null);

                foreach (string error in validationErrors)
                {
                    validationException.Errors.Add(error);
                }

                throw validationException;
            }
        }
        private static bool GetValueMatchesStatus(DeviceModel item, string statusName)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            if (string.IsNullOrEmpty(statusName))
            {
                return false;
            }

            var normalizedStatus = statusName.ToUpperInvariant();
            var enabledState = item.DeviceProperties?.HubEnabledState == null ? (bool?) null : item.DeviceProperties.GetHubEnabledState();

            switch (normalizedStatus)
            {
                case "RUNNING":
                    return enabledState == true;

                case "DISABLED":
                    return enabledState == false;

                case "PENDING":
                    return !enabledState.HasValue;

                default:
                    throw new ArgumentOutOfRangeException("statusName", statusName, "statusName has an unhandled status value.");
            }
        }
        private static void AssignDeviceProperties(DeviceModel device)
        {
            int randomId = Rand.Next(0, _possibleDeviceLocations.Count - 1);
            if (device?.DeviceProperties == null)
            {
                throw new DeviceRequiredPropertyNotFoundException("Required DeviceProperties not found");
            }

            device.DeviceProperties.HubEnabledState = true;
            device.DeviceProperties.Manufacturer = "Contoso Inc.";
            device.DeviceProperties.ModelNumber = "MD-" + randomId;
            device.DeviceProperties.SerialNumber = "SER" + randomId;
            device.DeviceProperties.FirmwareVersion = "1." + randomId;
            device.DeviceProperties.Platform = "Plat-" + randomId;
            device.DeviceProperties.Processor = "i3-" + randomId;
            device.DeviceProperties.InstalledRAM = randomId + " MB";

            // Choose a location among the 16 above and set Lat and Long for device properties
            device.DeviceProperties.Latitude = _possibleDeviceLocations[randomId].Latitude;
            device.DeviceProperties.Longitude = _possibleDeviceLocations[randomId].Longitude;
        }
 private static void AssignCommands(DeviceModel device)
 {
     device.Commands.Add(new Command("PingDevice"));
     device.Commands.Add(new Command("StartTelemetry"));
     device.Commands.Add(new Command("StopTelemetry"));
     device.Commands.Add(new Command("ChangeSetPointTemp", new [] { new Parameter("SetPointTemp", "double") }));
     device.Commands.Add(new Command("DiagnosticTelemetry", new[] { new Parameter("Active", "boolean") }));
     device.Commands.Add(new Command("ChangeDeviceState", new[] { new Parameter("DeviceState", "string") }));
 }
 private static void AssignTelemetry(DeviceModel device)
 {
     device.Telemetry.Add(new Telemetry("Temperature", "Temperature", "double"));
     device.Telemetry.Add(new Telemetry("Humidity", "Humidity", "double"));
 }
 private async Task CheckIfDeviceExists(DeviceModel device, List<string> validationErrors)
 {
     // check if device exists
     if (await this.GetDeviceAsync(device.DeviceProperties.DeviceID) != null)
     {
         validationErrors.Add(Strings.ValidationDeviceExists);
     }
 }
        private async Task ProcessDeviceInfo(DeviceModel deviceInfo)
        {
            string versionAsString = "";
            if (deviceInfo.Version != null)
            {
                versionAsString = deviceInfo.Version;
            }
            switch (versionAsString)
            {
                case SampleDeviceFactory.VERSION_1_0:
                    //Data coming in from the simulator can sometimes turn a boolean into 0 or 1.
                    //Check the HubEnabledState since this is actually displayed and make sure it's in a good format
                    //Should not be required for strongly typed object
                    //DeviceSchemaHelperND.FixDeviceSchema(deviceInfo);
                    if (deviceInfo.IoTHub == null)
                    {
                        throw new DeviceRequiredPropertyNotFoundException("'IoTHubProperties' property is missing");
                    }

                    string name = deviceInfo.IoTHub.ConnectionDeviceId;
                    Trace.TraceInformation("ProcessEventAsync -- DeviceInfo: {0}", name);
                    await _deviceLogic.UpdateDeviceFromDeviceInfoPacketAsync(deviceInfo);

                    break;
                default:
                    Trace.TraceInformation("Unknown version {0} provided in Device Info packet", versionAsString);
                    break;
            }
        }
        /// <summary>
        /// Adds the given device and assigned keys to the underlying repositories 
        /// </summary>
        /// <param name="device">Device to add to repositories</param>
        /// <param name="securityKeys">Keys to assign to the device</param>
        /// <returns>Device that was added to the device registry</returns>
        private async Task<DeviceModel> AddDeviceToRepositoriesAsync(DeviceModel device, SecurityKeys securityKeys)
        {
            DeviceModel registryRepositoryDevice = null;
            ExceptionDispatchInfo capturedException = null;

            // if an exception happens at this point pass it up the stack to handle it
            // (Making this call first then the call against the Registry removes potential issues
            // with conflicting rollbacks if the operation happens to still be in progress.)
            await _iotHubRepository.AddDeviceAsync(device, securityKeys);

            try
            {
                registryRepositoryDevice = await _deviceRegistryCrudRepository.AddDeviceAsync(device);
            }
            catch (Exception ex)
            {
                // grab the exception so we can attempt an async removal of the device from the IotHub
                capturedException = ExceptionDispatchInfo.Capture(ex);

            }

            //Create a device in table storage if it is a simulated type of device 
            //and the document was stored correctly without an exception
            bool isSimulatedAsBool = false;
            try
            {
                isSimulatedAsBool = (bool)device.IsSimulatedDevice;
            }
            catch (InvalidCastException ex)
            {
                Trace.TraceError("The IsSimulatedDevice property was in an invalid format. Exception Error Message: {0}", ex.Message);
            }
            if (capturedException == null && isSimulatedAsBool)
            {
                try
                {
                    await _virtualDeviceStorage.AddOrUpdateDeviceAsync(new InitialDeviceConfig()
                    {
                        DeviceId = device.DeviceProperties.DeviceID,
                        HostName = _configProvider.GetConfigurationSettingValue("iotHub.HostName"),
                        Key = securityKeys.PrimaryKey
                    });
                }
                catch (Exception ex)
                {
                    //if we fail adding to table storage for the device simulator just continue
                    Trace.TraceError("Failed to add simulated device : {0}", ex.Message);
                }
            }


            // Since the rollback code runs async and async code cannot run within the catch block it is run here
            if (capturedException != null)
            {
                // This is a lazy attempt to remove the device from the Iot Hub.  If it fails
                // the device will still remain in the Iot Hub.  A more robust rollback may be needed
                // in some scenarios.
                await _iotHubRepository.TryRemoveDeviceAsync(device.DeviceProperties.DeviceID);
                capturedException.Throw();
            }

            return registryRepositoryDevice;
        }
 /// <summary>
 /// Updates the device in the device registry with the exact device provided in this call.
 /// NOTE: The device provided here should represent the entire device that will be 
 /// serialized into the device registry.
 /// </summary>
 /// <param name="device">Device to update in the device registry</param>
 /// <returns>Device that was saved into the device registry</returns>
 public async Task<DeviceModel> UpdateDeviceAsync(DeviceModel device)
 {
     return await _deviceRegistryCrudRepository.UpdateDeviceAsync(device);
 }
        private IList<SelectListItem> CommandListItems(DeviceModel device)
        {
            if (device.Commands != null)
            {
                return GetCommandListItems(device);
            }

            return new List<SelectListItem>();
        }
        public async Task<DeviceModel> UpdateDeviceFromDeviceInfoPacketAsync(DeviceModel device)
        {
            if (device == null)
            {
                throw new ArgumentNullException("device");
            }

            // Get original device document
            DeviceModel existingDevice = await this.GetDeviceAsync(device.IoTHub.ConnectionDeviceId);

            // Save the command history, original created date, and system properties (if any) of the existing device
            if (existingDevice.DeviceProperties != null)
            {
                DeviceProperties deviceProperties = device.DeviceProperties;
                deviceProperties.CreatedTime = existingDevice.DeviceProperties.CreatedTime;
                existingDevice.DeviceProperties = deviceProperties;
            }

            device.CommandHistory = existingDevice.CommandHistory;

            // Copy the existing system properties, or initialize them if they do not exist
            if (existingDevice.SystemProperties != null)
            {
                device.SystemProperties = existingDevice.SystemProperties;
            }
            else
            {
                device.SystemProperties =  null;
            }
            // If there is Telemetry or Command objects from device, replace instead of merge
            if (device.Telemetry != null)
            {
                existingDevice.Telemetry = device.Telemetry;
            }
            if (device.Commands != null)
            {
                existingDevice.Commands = device.Commands;
            }


            return await _deviceRegistryCrudRepository.UpdateDeviceAsync(existingDevice);
        }
        private bool SearchTypePropertiesForValue(DeviceModel device, string search)
        {
            // if the device or its system properties are null then
            // there's nothing that can be searched on
            if (device?.DeviceProperties == null)
            {
                return false;
            }

            // iterate through the DeviceProperties Properties and look for the search value
            // case insensitive search
            var upperCaseSearch = search.ToUpperInvariant();
            return device.DeviceProperties.ToKeyValuePairs().Any(t =>
                    (t.Value != null) &&
                    t.Value.ToString().ToUpperInvariant().Contains(upperCaseSearch));
        }
        /// <summary>
        /// Sends a command to the provided device and updates the command history of the device
        /// </summary>
        /// <param name="device">Device to send the command to</param>
        /// <param name="commandName">Name of the command to send</param>
        /// <param name="parameters">Parameters to send with the command</param>
        /// <returns></returns>
        private async Task<CommandHistory> SendCommandAsyncWithDevice(DeviceModel device, string commandName, dynamic parameters)
        {
            if (device == null)
            {
                throw new ArgumentNullException("device");
            }

            var deviceId = device.DeviceProperties.DeviceID;
            if (device.Commands.FirstOrDefault(x => x.Name == commandName) == null)
            {
                throw new UnsupportedCommandException(deviceId, commandName);
            }

            var commandHistory = new CommandHistory(commandName, parameters);

            if (device.CommandHistory == null)
            {
                device.CommandHistory = new List<CommandHistory>();    
            }

            device.CommandHistory.Add(commandHistory);

            await _iotHubRepository.SendCommand(deviceId, commandHistory);
            await _deviceRegistryCrudRepository.UpdateDeviceAsync(device);

            return commandHistory;
        }
        /// <summary>
        /// Updates an existing device in the DocumentDB
        /// Throws a DeviceNotRegisteredException is the device does not already exist in the DocumentDB
        /// </summary>
        /// <param name="device"></param>
        /// <returns></returns>
        public async Task<DeviceModel> UpdateDeviceAsync(DeviceModel device)
        {
            if (device == null)
            {
                throw new ArgumentNullException("device");
            }

            if (device.DeviceProperties == null)
            {
                throw new DeviceRequiredPropertyNotFoundException("'DeviceProperties' property is missing");
            }

            if (string.IsNullOrEmpty(device.DeviceProperties.DeviceID))
            {
                throw new DeviceRequiredPropertyNotFoundException("'DeviceID' property is missing");
            }

            DeviceModel existingDevice = await GetDeviceAsync(device.DeviceProperties.DeviceID);
            if (existingDevice == null)
            {
                throw new DeviceNotRegisteredException(device.DeviceProperties.DeviceID);
            }

            string incomingRid = device._rid ?? "";

            if (string.IsNullOrWhiteSpace(incomingRid))
            {
                // copy the existing _rid onto the incoming data if needed
                var existingRid = existingDevice._rid ?? "";
                if (string.IsNullOrWhiteSpace(existingRid))
                {
                    throw new InvalidOperationException("Could not find _rid property on existing device");
                }
                device._rid = existingRid;
            }

            string incomingId = device.id ?? "";

            if (string.IsNullOrWhiteSpace(incomingId))
            {
                // copy the existing id onto the incoming data if needed
                if (existingDevice.DeviceProperties == null)
                {
                    throw new DeviceRequiredPropertyNotFoundException("'DeviceProperties' property is missing");
                }

                var existingId = existingDevice.id ?? "";
                if (string.IsNullOrWhiteSpace(existingId))
                {
                    throw new InvalidOperationException("Could not find id property on existing device");
                }
                device.id = existingId;
            }

            device.DeviceProperties.UpdatedTime = DateTime.UtcNow;
            var savedDevice = await this._documentClient.SaveAsync(device);
            return savedDevice;
        }
        private async Task<DeviceModel> AddOrRemoveSimulatedDevice(DeviceModel repositoryDevice, bool isEnabled)
        {
            var deviceId = repositoryDevice.DeviceProperties.DeviceID;
            if (isEnabled)
            {
                try
                {
                    var securityKeys = await this.GetIoTHubKeysAsync(deviceId);
                    await _virtualDeviceStorage.AddOrUpdateDeviceAsync(new InitialDeviceConfig()
                    {
                        DeviceId = deviceId,
                        HostName = _configProvider.GetConfigurationSettingValue("iotHub.HostName"),
                        Key = securityKeys.PrimaryKey
                    });
                }
                catch (Exception ex)
                {
                    //if we fail adding to table storage for the device simulator just continue
                    Trace.TraceError("Failed to add enabled device to simulated device storage. Device telemetry is expected not to be sent. : {0}", ex.Message);
                }
            }
            else
            {
                try
                {
                    await _virtualDeviceStorage.RemoveDeviceAsync(deviceId);
                }
                catch (Exception ex)
                {
                    //if an exception occurs while attempting to remove the
                    //simulated device from table storage do not roll back the changes.
                    Trace.TraceError("Failed to remove disabled device from simulated device store. Device will keep sending telemetry data. : {0}", ex.Message);
                }
            }

            return repositoryDevice;
        }
 public DeviceWithKeys(DeviceModel device, SecurityKeys securityKeys)
 {
     Device = device;
     SecurityKeys = securityKeys;
 }
        /// <summary>
        /// Modified a Device using a list of 
        /// <see cref="DevicePropertyValueModel" />.
        /// </summary>
        /// <param name="device">
        /// The Device to modify.
        /// </param>
        /// <param name="devicePropertyValueModels">
        /// The list of <see cref="DevicePropertyValueModel" />s for modifying 
        /// <paramref name="device" />.
        /// </param>
        public void ApplyDevicePropertyValueModels(
            DeviceModel device,
            IEnumerable<DevicePropertyValueModel> devicePropertyValueModels)
        {
            if (device == null)
            {
                throw new ArgumentNullException("device");
            }

            if (devicePropertyValueModels == null)
            {
                throw new ArgumentNullException("devicePropertyValueModels");
            }

            if (device.DeviceProperties == null)
            {
                throw new DeviceRequiredPropertyNotFoundException("Required DeviceProperties not found");
            }
            ApplyPropertyValueModels(device.DeviceProperties, devicePropertyValueModels);
        }