/// <summary> /// Create patch twin model to upload /// </summary> /// <param name="existing"></param> /// <param name="update"></param> public static DeviceTwinModel Patch(this GatewayRegistration existing, GatewayRegistration update) { var twin = new DeviceTwinModel { Etag = existing?.Etag, Tags = new Dictionary <string, VariantValue>(), Properties = new TwinPropertiesModel { Desired = new Dictionary <string, VariantValue>() } }; // Tags if (update?.IsDisabled != null && update.IsDisabled != existing?.IsDisabled) { twin.Tags.Add(nameof(GatewayRegistration.IsDisabled), (update?.IsDisabled ?? false) ? true : (bool?)null); twin.Tags.Add(nameof(GatewayRegistration.NotSeenSince), (update?.IsDisabled ?? false) ? DateTime.UtcNow : (DateTime?)null); } if (update?.SiteId != existing?.SiteId) { twin.Tags.Add(TwinProperty.SiteId, update?.SiteId); } twin.Tags.Add(nameof(GatewayRegistration.DeviceType), update?.DeviceType); twin.Id = update?.DeviceId ?? existing?.DeviceId; return(twin); }
/// <summary> /// Make sure to get the registration information from the right place. /// Reported (truth) properties take precedence over desired. However, /// if there is nothing reported, it means the endpoint is not currently /// serviced, thus we use desired as if they are attributes of the /// endpoint. /// </summary> /// <param name="twin"></param> /// <param name="onlyServerState">Only desired endpoint should be returned /// this means that you will look at stale information.</param> /// <returns></returns> public static EndpointRegistration FromTwin(DeviceTwinModel twin, bool onlyServerState) { if (twin == null) { return(null); } if (twin.Tags == null) { twin.Tags = new Dictionary <string, JToken>(); } var consolidated = FromTwin(twin, twin.GetConsolidatedProperties()); var desired = (twin.Properties?.Desired == null) ? null : FromTwin(twin, twin.Properties.Desired); if (!onlyServerState) { consolidated.MarkAsInSyncWith(desired); return(consolidated); } desired?.MarkAsInSyncWith(consolidated); return(desired); }
/// <summary> /// Decode tags and property into registration object /// </summary> /// <param name="twin"></param> /// <param name="properties"></param> /// <returns></returns> public static GatewayRegistration ToGatewayRegistration(this DeviceTwinModel twin, Dictionary <string, VariantValue> properties) { if (twin == null) { return(null); } var tags = twin.Tags ?? new Dictionary <string, VariantValue>(); var registration = new GatewayRegistration { // Device DeviceId = twin.Id, Etag = twin.Etag, // Connected Connected = twin.IsConnected() ?? false, // Tags IsDisabled = tags.GetValueOrDefault <bool>(nameof(GatewayRegistration.IsDisabled), null), NotSeenSince = tags.GetValueOrDefault <DateTime>(nameof(GatewayRegistration.NotSeenSince), null), Type = tags.GetValueOrDefault <string>(TwinProperty.Type, null), SiteId = tags.GetValueOrDefault <string>(TwinProperty.SiteId, null), // Properties }; return(registration); }
/// <summary> /// Make sure to get the registration information from the right place. /// Reported (truth) properties take precedence over desired. However, /// if there is nothing reported, it means the endpoint is not currently /// serviced, thus we use desired as if they are attributes of the /// endpoint. /// </summary> /// <param name="twin"></param> /// <param name="onlyServerState">Only desired endpoint should be returned /// this means that you will look at stale information.</param> /// <returns></returns> public static EndpointRegistration ToEndpointRegistration(this DeviceTwinModel twin, bool onlyServerState) { if (twin == null) { return(null); } if (twin.Tags == null) { twin.Tags = new Dictionary <string, JToken>(); } var consolidated = ToEndpointRegistration(twin, twin.GetConsolidatedProperties()); var desired = (twin.Properties?.Desired == null) ? null : ToEndpointRegistration(twin, twin.Properties.Desired); if (!onlyServerState) { consolidated._isInSync = consolidated.IsInSyncWith(desired); return(consolidated); } if (desired != null) { desired._isInSync = desired.IsInSyncWith(consolidated); } return(desired); }
/// <inheritdoc/> public async Task OnJobCreatingAsync(IJobService manager, JobInfoModel job) { var jobDeviceId = GetJobDeviceId(job); try { var deviceTwin = await _ioTHubTwinServices.GetAsync(jobDeviceId); if (deviceTwin == null) { deviceTwin = new DeviceTwinModel { Id = jobDeviceId }; await _ioTHubTwinServices.CreateAsync(deviceTwin); } var cs = await _ioTHubTwinServices.GetConnectionStringAsync(deviceTwin.Id); if (job.JobConfiguration?.Type == JTokenType.Object && job.JobConfiguration is JObject o) { var connectionString = JToken.FromObject(cs.ToString()); if (o.ContainsKey(TwinProperties.ConnectionString)) { o[TwinProperties.ConnectionString] = connectionString; } else { o.Add(TwinProperties.ConnectionString, connectionString); } _logger.Debug("Added connection string to job {id}", jobDeviceId); } } catch (Exception ex) { _logger.Error(ex, "Error while creating IoT Device."); } }
/// <summary> /// Make sure to get the registration information from the right place. /// Reported (truth) properties take precedence over desired. However, /// if there is nothing reported, it means the endpoint is not currently /// serviced, thus we use desired as if they are attributes of the /// endpoint. /// </summary> /// <param name="twin"></param> /// <param name="connected"></param> /// <returns></returns> public static GatewayRegistration ToGatewayRegistration(this DeviceTwinModel twin, out bool connected) { if (twin == null) { connected = false; return(null); } if (twin.Tags == null) { twin.Tags = new Dictionary <string, JToken>(); } var consolidated = ToGatewayRegistration(twin, twin.GetConsolidatedProperties()); var desired = (twin.Properties?.Desired == null) ? null : ToGatewayRegistration(twin, twin.Properties.Desired); connected = consolidated.Connected; if (desired != null) { desired.Connected = connected; if (desired.SiteId == null && consolidated.SiteId != null) { // Not set by user, but by config, so fake user desiring it. desired.SiteId = consolidated.SiteId; } } return(desired); }
/// <inheritdoc/> public async Task OnJobCreatingAsync(IJobService manager, JobInfoModel job) { if (job.JobConfiguration?.IsObject != true) { return; } try { var jobDeviceId = GetJobDeviceId(job); var deviceTwin = await _ioTHubTwinServices.FindAsync(jobDeviceId); if (deviceTwin == null) { deviceTwin = new DeviceTwinModel { Id = jobDeviceId }; await _ioTHubTwinServices.CreateOrUpdateAsync(deviceTwin, true); } var cs = await _ioTHubTwinServices.GetConnectionStringAsync(deviceTwin.Id); job.JobConfiguration[TwinProperties.ConnectionString].AssignValue(cs.ToString()); _logger.Debug("Added connection string to job {id}", jobDeviceId); } catch (Exception ex) { _logger.Error(ex, "Error while creating IoT Device."); } }
/// <inheritdoc/> public async Task <DeviceTwinModel> PatchAsync(DeviceTwinModel twin, bool force, CancellationToken ct) { try { Twin update; // Then update twin assuming it now exists. If fails, retry... var etag = string.IsNullOrEmpty(twin.Etag) || force ? "*" : twin.Etag; if (!string.IsNullOrEmpty(twin.ModuleId)) { update = await _registry.UpdateTwinAsync(twin.Id, twin.ModuleId, twin.ToTwin(true), etag, ct); } else { // Patch device update = await _registry.UpdateTwinAsync(twin.Id, twin.ToTwin(true), etag, ct); } return(update.ToModel()); } catch (Exception e) { _logger.Verbose(e, "Create or update failed "); throw e.Rethrow(); } }
/// <summary> /// Convert twin to registration information. /// </summary> /// <param name="twin"></param> /// <param name="onlyServerState"></param> /// <returns></returns> public static BaseRegistration ToRegistration(DeviceTwinModel twin, bool onlyServerState = false) { if (twin == null) { return(null); } var type = twin.Tags.GetValueOrDefault <string>(nameof(DeviceType), null); if (string.IsNullOrEmpty(type) && twin.Properties.Reported != null) { type = twin.Properties.Reported.GetValueOrDefault <string>(TwinProperty.kType, null); } switch (type?.ToLowerInvariant() ?? "") { case "endpoint": return(EndpointRegistration.FromTwin(twin, onlyServerState)); case "application": return(ApplicationRegistration.FromTwin(twin)); case "supervisor": return(SupervisorRegistration.FromTwin(twin, onlyServerState)); } return(null); }
public async Task get_iotHub_update_device_twin() { DeviceTwinRepository repo = new DeviceTwinRepository(iotHubConnectionString); var tags = new DeviceTwinTagsModel() { ProductFamily = "ProductFamilyTest", ProductName = "ProductNameTest", RetailerName = "RetailerTest", ManufacturedDate = DateTime.Now, ShippedDate = DateTime.Now, RetailerRegion = "Chicago" }; var device = await repo.CreateAndInitializeDeviceTwin("unittestdevice", tags); Assert.Equal("unittestdevice", device.Id); // get device twin var deviceTwin = await repo.GetDeviceTwinAsync(device.Id); Assert.NotNull(deviceTwin); var deviceTwinModel = new DeviceTwinModel() { DeviceId = "unittestdevice", /*Location = new DeviceLocation() * { * AddressLine1 = "1100, S King Drive", * City = "Chicago", * State = "IL", * ZipCode = "50630", * Country = "US", * AdditionalNotes="Test Notes" * }, * Features = new List<DeviceFeature>() * { * new DeviceFeature(){ Name = "Just Test Feature", * Value = true } * * }, * ActivationDate = DateTime.Now, * ShipDate = DateTime.Now.AddDays(2)*/ }; //await repo.UpdateDeviceTwinTagsAsync(deviceTwinModel); await repo.InitializeDeviceTwinDesiredFeaturesAsync(new DeviceTwinDesiredFeaturesModel() { DeviceId = device.Id /*, ActivatedFeatures = deviceTwinModel.Features*/ }); // get device twin deviceTwin = await repo.GetDeviceTwinAsync(device.Id); //Assert.Equal(1,deviceTwin.Tags.Count); //Assert.True(deviceTwin.Tags.Contains("location")); //Assert.Equal(1, deviceTwin.Properties.Desired.Count); //Assert.True(deviceTwin.Properties.Desired.Contains("features")); }
/// <summary> /// Create patch twin model to upload /// </summary> /// <param name="existing"></param> /// <param name="update"></param> public static DeviceTwinModel Patch(this SupervisorRegistration existing, SupervisorRegistration update) { var twin = new DeviceTwinModel { Etag = existing?.Etag, Tags = new Dictionary <string, JToken>(), Properties = new TwinPropertiesModel { Desired = new Dictionary <string, JToken>() } }; // Tags if (update?.IsDisabled != null && update.IsDisabled != existing?.IsDisabled) { twin.Tags.Add(nameof(SupervisorRegistration.IsDisabled), (update?.IsDisabled ?? false) ? true : (bool?)null); twin.Tags.Add(nameof(SupervisorRegistration.NotSeenSince), (update?.IsDisabled ?? false) ? DateTime.UtcNow : (DateTime?)null); } if (update?.SiteOrGatewayId != existing?.SiteOrGatewayId) { twin.Tags.Add(nameof(SupervisorRegistration.SiteOrGatewayId), update?.SiteOrGatewayId); } // Settings var certUpdate = update?.Certificate?.DecodeAsByteArray()?.SequenceEqualsSafe( existing?.Certificate.DecodeAsByteArray()); if (!(certUpdate ?? true)) { twin.Properties.Desired.Add(nameof(SupervisorRegistration.Certificate), update?.Certificate == null ? null : JToken.FromObject(update.Certificate)); twin.Tags.Add(nameof(SupervisorRegistration.Thumbprint), update?.Certificate?.DecodeAsByteArray()?.ToSha1Hash()); } if (update?.LogLevel != existing?.LogLevel) { twin.Properties.Desired.Add(nameof(SupervisorRegistration.LogLevel), update?.LogLevel == null ? null : JToken.FromObject(update.LogLevel)); } if (update?.SiteId != existing?.SiteId) { twin.Properties.Desired.Add(TwinProperty.SiteId, update?.SiteId); } twin.Tags.Add(nameof(SupervisorRegistration.DeviceType), update?.DeviceType); twin.Id = update?.DeviceId ?? existing?.DeviceId; twin.ModuleId = update?.ModuleId ?? existing?.ModuleId; return(twin); }
/// <summary> /// Create patch twin model to upload /// </summary> /// <param name="existing"></param> /// <param name="update"></param> protected static DeviceTwinModel Patch( BaseRegistration existing, BaseRegistration update) { var twin = new DeviceTwinModel { Etag = existing?.Etag, Tags = new Dictionary <string, JToken>(), Properties = new TwinPropertiesModel { Desired = new Dictionary <string, JToken>() } }; // Tags if (update?.ApplicationId != null && update.ApplicationId != existing?.ApplicationId) { twin.Tags.Add(nameof(ApplicationId), update.ApplicationId); } if (update?.IsDisabled != null && update.IsDisabled != existing?.IsDisabled) { twin.Tags.Add(nameof(IsDisabled), (update?.IsDisabled ?? false) ? true : (bool?)null); twin.Tags.Add(nameof(NotSeenSince), (update?.IsDisabled ?? false) ? DateTime.UtcNow : (DateTime?)null); } if (update?.SiteOrSupervisorId != existing?.SiteOrSupervisorId) { twin.Tags.Add(nameof(SiteOrSupervisorId), update?.SiteOrSupervisorId); } if (update?.SupervisorId != existing?.SupervisorId) { twin.Tags.Add(nameof(SupervisorId), update?.SupervisorId); } if (update?.SiteId != existing?.SiteId) { twin.Tags.Add(nameof(SiteId), update?.SiteId); } var certUpdate = update?.Certificate.DecodeAsByteArray().SequenceEqualsSafe( existing?.Certificate.DecodeAsByteArray()); if (!(certUpdate ?? true)) { twin.Tags.Add(nameof(Certificate), update?.Certificate == null ? null : JToken.FromObject(update.Certificate)); twin.Tags.Add(nameof(Thumbprint), update?.Certificate?.DecodeAsByteArray()?.ToSha1Hash()); } twin.Tags.Add(nameof(DeviceType), update?.DeviceType); return(twin); }
/// <summary> /// Get identity of device twin /// </summary> /// <param name="deviceTwin"></param> /// <returns></returns> private static string GetIdentity(DeviceTwinModel deviceTwin) { var identity = deviceTwin.Id; if (!string.IsNullOrEmpty(deviceTwin.ModuleId)) { identity += $"/{deviceTwin.ModuleId}"; } return(identity); }
/// <summary> /// Create or update a device /// </summary> /// <param name="deviceTwinObj">DeviceTwinModel</param> /// <returns>DeviceModel</returns> public async Task <DeviceModel> CreateOrUpdateDevice(DeviceTwinModel deviceTwinObj) { if (deviceTwinObj.DeviceId == Guid.Empty) { throw new Exception($"No device found that matches DeviceId: {deviceTwinObj.DeviceId}"); } DeviceDAO deviceDAO = await _repoDevices.GetItemAsync(deviceTwinObj.DeviceId); //Create if (deviceDAO == null) { return(await CreateDevice(deviceTwinObj)); } //Update deviceDAO.IoTDevice = true; if (deviceTwinObj.Properties?.Desired != null) { deviceDAO.Desired = deviceTwinObj.Properties.Desired; } if (deviceTwinObj.Properties?.Reported != null) { deviceDAO.Reported = deviceTwinObj.Properties.Reported; } if (deviceTwinObj.Tags != null) { deviceDAO.DeviceType = deviceTwinObj.Tags.DeviceType; deviceDAO.Enabled = deviceTwinObj.Tags.Enabled; deviceDAO.Custom = deviceTwinObj.Tags.Custom; deviceDAO.Name = deviceTwinObj.Tags.Name; deviceDAO.Location1 = deviceTwinObj.Tags.Location1; deviceDAO.Location2 = deviceTwinObj.Tags.Location2; deviceDAO.Location3 = deviceTwinObj.Tags.Location3; deviceDAO.SSID = deviceTwinObj.Tags.SSID; deviceDAO.Sensor = deviceTwinObj.Tags.Sensor; deviceDAO.Geolocation = _mapper.Map <GeolocationDAOObject>(deviceTwinObj.Tags.Geolocation); } try { await _repoDevices.UpdateItemAsync(deviceDAO); } catch (DocumentClientException e) { //Update concurrency issue, retrying if (e.StatusCode == HttpStatusCode.PreconditionFailed) { return(await CreateOrUpdateDevice(deviceTwinObj)); } throw e; } return(_mapper.Map <DeviceModel>(deviceDAO)); }
/// <inheritdoc/> public Task <DeviceTwinModel> CreateAsync(DeviceTwinModel twin, bool force, CancellationToken ct) { if (twin == null) { throw new ArgumentNullException(nameof(twin)); } if (string.IsNullOrEmpty(twin.Id)) { throw new ArgumentNullException(nameof(twin.Id)); } // Retry transient errors return(Retry.WithExponentialBackoff(_logger, ct, async() => { // First try create device try { var device = NewRequest($"/devices/{twin.Id}"); device.SetContent(new { deviceId = twin.Id, capabilities = twin.Capabilities }); var response = await _httpClient.PutAsync(device, ct); response.Validate(); } catch (ConflictingResourceException) when(!string.IsNullOrEmpty(twin.ModuleId) || force) { // Continue onward } catch (Exception e) { _logger.Debug(e, "Create device failed in CreateOrUpdate"); } if (!string.IsNullOrEmpty(twin.ModuleId)) { // Try create module try { var module = NewRequest( $"/devices/{twin.Id}/modules/{twin.ModuleId}"); module.SetContent(new { deviceId = twin.Id, moduleId = twin.ModuleId }); var response = await _httpClient.PutAsync(module, ct); response.Validate(); } catch (ConflictingResourceException) when(force) { } catch (Exception e) { _logger.Debug(e, "Create module failed in CreateOrUpdate"); } } return await PatchAsync(twin, true, ct); // Force update of twin }, kMaxRetryCount)); }
/// <summary> /// Decode tags and property into registration object /// </summary> /// <param name="twin"></param> /// <param name="properties"></param> /// <returns></returns> public static PublisherRegistration ToPublisherRegistration(this DeviceTwinModel twin, Dictionary <string, JToken> properties) { if (twin == null) { return(null); } var tags = twin.Tags ?? new Dictionary <string, JToken>(); var connected = twin.IsConnected(); var registration = new PublisherRegistration { // Device DeviceId = twin.Id, ModuleId = twin.ModuleId, Etag = twin.Etag, // Tags IsDisabled = tags.GetValueOrDefault <bool>(nameof(PublisherRegistration.IsDisabled), null), NotSeenSince = tags.GetValueOrDefault <DateTime>(nameof(PublisherRegistration.NotSeenSince), null), Thumbprint = tags.GetValueOrDefault <string>(nameof(PublisherRegistration.Thumbprint), null), // Properties Certificate = properties.GetValueOrDefault <Dictionary <string, string> >(nameof(PublisherRegistration.Certificate), null), LogLevel = properties.GetValueOrDefault <TraceLogLevel>(nameof(PublisherRegistration.LogLevel), null), JobOrchestratorUrl = properties.GetValueOrDefault <string>(nameof(PublisherRegistration.JobOrchestratorUrl), null), JobCheckInterval = properties.GetValueOrDefault <TimeSpan>(nameof(PublisherRegistration.JobCheckInterval), null), MaxWorkers = properties.GetValueOrDefault <int>(nameof(PublisherRegistration.MaxWorkers), null), HeartbeatInterval = properties.GetValueOrDefault <TimeSpan>(nameof(PublisherRegistration.HeartbeatInterval), null), Capabilities = properties.GetValueOrDefault <Dictionary <string, string> >(nameof(PublisherRegistration.Capabilities), null), SiteId = properties.GetValueOrDefault <string>(TwinProperty.SiteId, null), Connected = connected ?? properties.GetValueOrDefault(TwinProperty.Connected, false), Type = properties.GetValueOrDefault <string>(TwinProperty.Type, null) }; return(registration); }
/// <inheritdoc/> public async Task <DeviceTwinModel> CreateOrUpdateAsync(DeviceTwinModel twin, bool forceUpdate) { if (string.IsNullOrEmpty(twin.Etag)) { // First try create device try { var device = await _registry.AddDeviceAsync(twin.ToDevice()); } catch (DeviceAlreadyExistsException) { // Expected for update } catch (Exception e) { _logger.Debug(e, "Create device failed in CreateOrUpdate"); } } try { Twin update; // Then update twin assuming it now exists. If fails, retry... var etag = string.IsNullOrEmpty(twin.Etag) || forceUpdate ? "*" : twin.Etag; if (!string.IsNullOrEmpty(twin.ModuleId)) { if (string.IsNullOrEmpty(twin.Etag)) { // Try create module try { var module = await _registry.AddModuleAsync(twin.ToModule()); } catch (DeviceAlreadyExistsException) { // Expected for update } catch (Exception e) { _logger.Debug(e, "Create module failed in CreateOrUpdate"); } } update = await _registry.UpdateTwinAsync(twin.Id, twin.ModuleId, twin.ToTwin(true), etag); } else { // Patch device update = await _registry.UpdateTwinAsync(twin.Id, twin.ToTwin(true), etag); } return(update.ToModel()); } catch (Exception e) { _logger.Debug(e, "Create or update failed "); throw; } }
private void SetTagPropertiesThatExist(IDictionary <string, object> providedData, DeviceModel existingModel, DeviceTwinModel updateModel) { updateModel.Tags.DeviceType = GetValueIfProvided("DeviceType", dm => dm.DeviceType, providedData, existingModel); updateModel.Tags.Enabled = GetValueIfProvided("Enabled", dm => dm.Enabled, providedData, existingModel); updateModel.Tags.Location1 = GetValueIfProvided("Location1", dm => dm.Location1, providedData, existingModel); updateModel.Tags.Location2 = GetValueIfProvided("Location2", dm => dm.Location2, providedData, existingModel); updateModel.Tags.Location3 = GetValueIfProvided("Location3", dm => dm.Location3, providedData, existingModel); updateModel.Tags.Name = GetValueIfProvided("Name", dm => dm.Name, providedData, existingModel); updateModel.Tags.Sensor = GetValueIfProvided("Sensor", dm => dm.Sensor, providedData, existingModel); updateModel.Tags.SSID = GetValueIfProvided("SSID", dm => dm.SSID, providedData, existingModel); }
/// <summary> /// Update twin /// </summary> /// <param name="twin"></param> public void UpdateTwin(DeviceTwinModel twin) { Twin.Tags = Merge(Twin.Tags, twin.Tags); if (Twin.Properties == null) { Twin.Properties = new TwinPropertiesModel(); } Twin.Properties.Desired = Merge( Twin.Properties.Desired, twin.Properties?.Desired); Twin.Properties.Reported = Merge( Twin.Properties.Reported, twin.Properties?.Reported); Twin.LastActivityTime = DateTime.UtcNow; Twin.Etag = Device.Etag = Guid.NewGuid().ToString(); }
/// <inheritdoc/> public Task <DeviceTwinModel> PatchAsync(DeviceTwinModel twin, bool force, CancellationToken ct) { if (twin == null) { throw new ArgumentNullException(nameof(twin)); } if (string.IsNullOrEmpty(twin.Id)) { throw new ArgumentNullException(nameof(twin.Id)); } return(Retry.WithExponentialBackoff(_logger, ct, async() => { // Then update twin assuming it now exists. If fails, retry... var patch = NewRequest( $"/twins/{ToResourceId(twin.Id, twin.ModuleId)}"); patch.Headers.Add("If-Match", $"\"{(string.IsNullOrEmpty(twin.Etag) || force ? "*" : twin.Etag)}\""); if (!string.IsNullOrEmpty(twin.ModuleId)) { // Patch module patch.SetContent(new { deviceId = twin.Id, moduleId = twin.ModuleId, tags = twin.Tags ?? new Dictionary <string, JToken>(), properties = new { desired = twin.Properties?.Desired ?? new Dictionary <string, JToken>() } }); } else { // Patch device patch.SetContent(new { deviceId = twin.Id, tags = twin.Tags ?? new Dictionary <string, JToken>(), properties = new { desired = twin.Properties?.Desired ?? new Dictionary <string, JToken>() } }); } { var response = await _httpClient.PatchAsync(patch, ct); response.Validate(); var result = response.GetContent <DeviceTwinModel>(); _logger.Information( "{id} ({moduleId}) created or updated ({twinEtag} -> {resultEtag})", twin.Id, twin.ModuleId ?? string.Empty, twin.Etag ?? "*", result.Etag); return result; } }, kMaxRetryCount)); }
/// <summary> /// Create a device /// </summary> /// <param name="deviceTwinObj">DeviceTwinModel</param> /// <returns>DeviceModel</returns> public async Task <DeviceModel> CreateDevice(DeviceTwinModel deviceTwinObj) { //If device doesn't exist, throw exception DeviceDAO deviceEntity = _mapper.Map <DeviceDAO>(deviceTwinObj); deviceEntity.Id = await _repoDevices.CreateItemAsync(deviceEntity); if (_repoDevices.IsDocumentKeyNull(deviceEntity)) { throw new Exception($"An error occured when creating a new device: {deviceTwinObj.DeviceId}"); } return(_mapper.Map <DeviceModel>(deviceEntity)); }
/// <summary> /// Convert twin to module model /// </summary> /// <param name="t"></param> /// <returns></returns> private static DiscoveredModuleModel ToDiscoveredModuleModel(DeviceTwinModel t) { if (t == null) { return(null); } var model = new DiscoveredModuleModel { Id = t.ModuleId, ImageName = t.ModuleId, ImageHash = null, Version = t.Version?.ToString(), Status = t.Status }; return(model); }
/// <inheritdoc/> public async Task OnJobAssignmentAsync(IJobService manager, JobInfoModel job, string workerId) { if (job.JobConfiguration?.IsObject != true) { return; } if (string.IsNullOrEmpty(workerId)) { throw new ArgumentNullException("empty WorkerId provided"); } try { var edgeDeviceTwin = await _ioTHubTwinServices.FindAsync(workerId.Split("_publisher")[0]); if (edgeDeviceTwin == null) { _logger.Error("IoT Edge Device not found."); return; } var jobDeviceId = GetJobDeviceId(job); var deviceTwin = await _ioTHubTwinServices.FindAsync(jobDeviceId); if (deviceTwin == null) { deviceTwin = new DeviceTwinModel { Id = jobDeviceId, DeviceScope = edgeDeviceTwin.DeviceScope }; await _ioTHubTwinServices.CreateOrUpdateAsync(deviceTwin, true); } else { if (deviceTwin.DeviceScope != edgeDeviceTwin.DeviceScope) { deviceTwin.DeviceScope = edgeDeviceTwin.DeviceScope; await _ioTHubTwinServices.CreateOrUpdateAsync(deviceTwin, true); } } var cs = await _ioTHubTwinServices.GetConnectionStringAsync(deviceTwin.Id); job.JobConfiguration[TwinProperties.ConnectionString].AssignValue(cs.ToString()); _logger.Debug("Added connection string to job {id}", jobDeviceId); } catch (Exception ex) { _logger.Error(ex, "Error while assigning the Job's IoT Device."); } }
/// <summary> /// Make sure to get the registration information from the right place. /// Reported (truth) properties take precedence over desired. However, /// if there is nothing reported, it means the endpoint is not currently /// serviced, thus we use desired as if they are attributes of the /// endpoint. /// </summary> /// <param name="twin"></param> /// <param name="onlyServerState">Only desired endpoint should be returned /// this means that you will look at stale information.</param> /// <param name="connected"></param> /// <returns></returns> public static SupervisorRegistration ToSupervisorRegistration(this DeviceTwinModel twin, bool onlyServerState, out bool connected) { if (twin == null) { connected = false; return(null); } if (twin.Tags == null) { twin.Tags = new Dictionary <string, VariantValue>(); } var consolidated = ToSupervisorRegistration(twin, twin.GetConsolidatedProperties()); var desired = (twin.Properties?.Desired == null) ? null : ToSupervisorRegistration(twin, twin.Properties.Desired); connected = consolidated.Connected; if (desired != null) { desired.Connected = connected; if (desired.SiteId == null && consolidated.SiteId != null) { // Not set by user, but by config, so fake user desiring it. desired.SiteId = consolidated.SiteId; } if (desired.LogLevel == null && consolidated.LogLevel != null) { // Not set by user, but reported, so set as desired desired.LogLevel = consolidated.LogLevel; } desired.Version = consolidated.Version; } if (!onlyServerState) { consolidated._isInSync = consolidated.IsInSyncWith(desired); return(consolidated); } if (desired != null) { desired._isInSync = desired.IsInSyncWith(consolidated); } return(desired); }
/// <summary> /// Create patch twin model to upload /// </summary> /// <param name="existing"></param> /// <param name="update"></param> /// <param name="serializer"></param> public static DeviceTwinModel Patch(this SupervisorRegistration existing, SupervisorRegistration update, IJsonSerializer serializer) { var twin = new DeviceTwinModel { Etag = existing?.Etag, Tags = new Dictionary <string, VariantValue>(), Properties = new TwinPropertiesModel { Desired = new Dictionary <string, VariantValue>() } }; // Tags if (update?.IsDisabled != null && update.IsDisabled != existing?.IsDisabled) { twin.Tags.Add(nameof(SupervisorRegistration.IsDisabled), (update?.IsDisabled ?? false) ? true : (bool?)null); twin.Tags.Add(nameof(SupervisorRegistration.NotSeenSince), (update?.IsDisabled ?? false) ? DateTime.UtcNow : (DateTime?)null); } if (update?.SiteOrGatewayId != existing?.SiteOrGatewayId) { twin.Tags.Add(nameof(SupervisorRegistration.SiteOrGatewayId), update?.SiteOrGatewayId); } // Settings if (update?.LogLevel != existing?.LogLevel) { twin.Properties.Desired.Add(nameof(SupervisorRegistration.LogLevel), update?.LogLevel == null ? null : serializer.FromObject(update.LogLevel.ToString())); } if (update?.SiteId != existing?.SiteId) { twin.Properties.Desired.Add(TwinProperty.SiteId, update?.SiteId); } twin.Tags.Add(nameof(SupervisorRegistration.DeviceType), update?.DeviceType); twin.Id = update?.DeviceId ?? existing?.DeviceId; twin.ModuleId = update?.ModuleId ?? existing?.ModuleId; return(twin); }
public async Task <DeviceModel> CreateOrUpdateDevice(DeviceTwinModel deviceObj) { RestRequest request = await PrepareQuery("Devices", Method.POST); request.AddParameter("application/json", JsonConvert.SerializeObject(deviceObj), ParameterType.RequestBody); var queryResult = await _client.ExecuteTaskAsync <DeviceModel>(request); if (queryResult.IsSuccessful) { return(queryResult.Data); } else { _logger.LogError($"CreationModel: Error while adding a device: {queryResult.StatusCode}"); } return(null); }
/// <summary> /// Convert device twin registration property to registration model /// </summary> /// <param name="twin"></param> /// <param name="onlyServerState">Only desired should be returned /// this means that you will look at stale information.</param> /// <param name="skipInvalid"></param> /// <returns></returns> private static EndpointInfoModel TwinModelToEndpointRegistrationModel( DeviceTwinModel twin, bool onlyServerState, bool skipInvalid) { // Convert to twin registration var registration = twin.ToEntityRegistration(onlyServerState) as EndpointRegistration; if (registration == null) { if (skipInvalid) { return(null); } throw new ResourceNotFoundException( $"{twin.Id} is not a registered opc ua endpoint."); } return(registration.ToServiceModel()); }
/// <summary> /// Decode tags and property into registration object /// </summary> /// <param name="twin"></param> /// <param name="properties"></param> /// <returns></returns> public static SupervisorRegistration ToSupervisorRegistration(this DeviceTwinModel twin, Dictionary <string, JToken> properties) { if (twin == null) { return(null); } var tags = twin.Tags ?? new Dictionary <string, JToken>(); var connected = twin.IsConnected(); var registration = new SupervisorRegistration { // Device DeviceId = twin.Id, ModuleId = twin.ModuleId, Etag = twin.Etag, // Tags IsDisabled = tags.GetValueOrDefault <bool>(nameof(SupervisorRegistration.IsDisabled), null), NotSeenSince = tags.GetValueOrDefault <DateTime>(nameof(SupervisorRegistration.NotSeenSince), null), Thumbprint = tags.GetValueOrDefault <string>(nameof(SupervisorRegistration.Thumbprint), null), // Properties Certificate = properties.GetValueOrDefault <Dictionary <string, string> >(nameof(SupervisorRegistration.Certificate), null), LogLevel = properties.GetValueOrDefault <TraceLogLevel>(nameof(SupervisorRegistration.LogLevel), null), SiteId = properties.GetValueOrDefault <string>(TwinProperty.SiteId, null), Connected = connected ?? properties.GetValueOrDefault(TwinProperty.Connected, false), Type = properties.GetValueOrDefault <string>(TwinProperty.Type, null) }; return(registration); }
/// <summary> /// Convert twin to registration information. /// </summary> /// <param name="twin"></param> /// <param name="onlyServerState"></param> /// <returns></returns> public static EntityRegistration ToEntityRegistration(this DeviceTwinModel twin, bool onlyServerState = false) { if (twin == null) { return(null); } var type = twin.Tags.GetValueOrDefault <string>(nameof(EntityRegistration.DeviceType), null); if (string.IsNullOrEmpty(type) && twin.Properties.Reported != null) { type = twin.Properties.Reported.GetValueOrDefault <string>(TwinProperty.Type, null); if (string.IsNullOrEmpty(type)) { type = twin.Tags.GetValueOrDefault <string>(TwinProperty.Type, null); } } if (IdentityType.Gateway.EqualsIgnoreCase(type)) { return(twin.ToGatewayRegistration()); } if (IdentityType.Application.EqualsIgnoreCase(type)) { return(twin.ToApplicationRegistration()); } if (IdentityType.Endpoint.EqualsIgnoreCase(type)) { return(twin.ToEndpointRegistration(onlyServerState)); } if (IdentityType.Supervisor.EqualsIgnoreCase(type)) { return(twin.ToSupervisorRegistration(onlyServerState)); } if (IdentityType.Publisher.EqualsIgnoreCase(type)) { return(twin.ToPublisherRegistration(onlyServerState)); } if (IdentityType.Discoverer.EqualsIgnoreCase(type)) { return(twin.ToDiscovererRegistration(onlyServerState)); } // ... return(null); }
/// <summary> /// Make sure to get the registration information from the right place. /// Reported (truth) properties take precedence over desired. However, /// if there is nothing reported, it means the endpoint is not currently /// serviced, thus we use desired as if they are attributes of the /// endpoint. /// </summary> /// <param name="twin"></param> /// <param name="connected"></param> /// <returns></returns> public static GatewayRegistration ToGatewayRegistration(this DeviceTwinModel twin, out bool connected) { if (twin == null) { connected = false; return(null); } if (twin.Tags == null) { twin.Tags = new Dictionary <string, VariantValue>(); } var consolidated = ToGatewayRegistration(twin, twin.GetConsolidatedProperties()); connected = consolidated.Connected; return(consolidated); }