public void UpdateUtilityAppendsAddComponentOp() { var op = new UpdateOperationsUtility(); string path = "testPath"; string property = "someProperty"; int value = 10; op.AppendAddComponentOp(path, new Dictionary <string, object> { { property, value } }); string operations = op.Serialize(); // There should be a single operation added. var jArray = JArray.Parse(operations); jArray.Count.Should().Be(1); // The patch operation added should be an "add" operation. JToken jObject = jArray.First; jObject.Value <string>(Op).Should().Be(Add); // The value should have a "$metadata" : {} mapping. JObject patchValue = jObject.Value <JObject>(Value); patchValue[Metadata].Should().NotBeNull(); patchValue[Metadata].Should().BeEmpty(); }
private async Task UpdateDigitalTwinComponentPropertyAsync() { // Choose a random value to assign to the targetTemperature property in thermostat1 component int desiredTargetTemperature = Random.Next(0, 100); const string targetTemperaturePropertyName = "targetTemperature"; var updateOperation = new UpdateOperationsUtility(); // First let's take a look at when the property was updated and what was it set to. HttpOperationResponse <TemperatureControllerTwin, DigitalTwinGetHeaders> getDigitalTwinResponse = await _digitalTwinClient .GetDigitalTwinAsync <TemperatureControllerTwin>(_digitalTwinId); ThermostatTwin thermostat1 = getDigitalTwinResponse.Body.Thermostat1; if (thermostat1 != null) { // Thermostat1 is present in the TemperatureController twin. We can add/replace the component-level property "targetTemperature" double?currentComponentTargetTemperature = getDigitalTwinResponse.Body.Thermostat1.TargetTemperature; if (currentComponentTargetTemperature != null) { DateTimeOffset targetTemperatureDesiredLastUpdateTime = getDigitalTwinResponse.Body.Thermostat1.Metadata.TargetTemperature.LastUpdateTime; _logger.LogDebug($"The property {targetTemperaturePropertyName} under component {Thermostat1Component} was last updated on `" + $"{targetTemperatureDesiredLastUpdateTime.ToLocalTime()} `" + $" with a value of {getDigitalTwinResponse.Body.Thermostat1.Metadata.TargetTemperature.DesiredValue}."); // The property path to be replaced should be prepended with a '/' updateOperation.AppendReplacePropertyOp($"/{Thermostat1Component}/{targetTemperaturePropertyName}", desiredTargetTemperature); } else { _logger.LogDebug($"The property {targetTemperaturePropertyName} under component {Thermostat1Component} `" + $"was never set on the {_digitalTwinId} digital twin."); // The property path to be added should be prepended with a '/' updateOperation.AppendAddPropertyOp($"/{Thermostat1Component}/{targetTemperaturePropertyName}", desiredTargetTemperature); } } else { // Thermostat1 is not present in the TemperatureController twin. We will add the component var componentProperty = new Dictionary <string, object> { { targetTemperaturePropertyName, desiredTargetTemperature }, { "$metadata", new object() } }; _logger.LogDebug($"The component {Thermostat1Component} does not exist on the {_digitalTwinId} digital twin."); // The property path to be replaced should be prepended with a '/' updateOperation.AppendAddComponentOp($"/{Thermostat1Component}", componentProperty); } _logger.LogDebug($"Update the {targetTemperaturePropertyName} property under component {Thermostat1Component} on the {_digitalTwinId} `" + $"digital twin to {desiredTargetTemperature}."); HttpOperationHeaderResponse <DigitalTwinUpdateHeaders> updateDigitalTwinResponse = await _digitalTwinClient .UpdateDigitalTwinAsync(_digitalTwinId, updateOperation.Serialize()); _logger.LogDebug($"Update {_digitalTwinId} digital twin response: {updateDigitalTwinResponse.Response.StatusCode}."); // Print the TemperatureController digital twin await GetAndPrintDigitalTwinAsync <TemperatureControllerTwin>(); }
public async Task DigitalTwinWithComponentOperationsAsync() { // Create a new test device instance. TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); string deviceId = testDevice.Id; try { // Create a device client instance over Mqtt, initializing it with the "TemperatureController" model which has "Thermostat" components. var options = new ClientOptions { ModelId = TemperatureControllerModelId, }; using DeviceClient deviceClient = testDevice.CreateDeviceClient(Client.TransportType.Mqtt, options); // Call openAsync() to open the device's connection, so that the ModelId is sent over Mqtt CONNECT packet. await deviceClient.OpenAsync().ConfigureAwait(false); // Perform operations on the digital twin. using var digitalTwinClient = DigitalTwinClient.CreateFromConnectionString(s_connectionString); // Retrieve the digital twin. HttpOperationResponse <TemperatureControllerTwin, DigitalTwinGetHeaders> response = await digitalTwinClient.GetDigitalTwinAsync <TemperatureControllerTwin>(deviceId).ConfigureAwait(false); TemperatureControllerTwin twin = response.Body; twin.Metadata.ModelId.Should().Be(TemperatureControllerModelId); string componentName = "thermostat1"; // Set callback handler for receiving twin property updates. await deviceClient.SetDesiredPropertyUpdateCallbackAsync((patch, context) => { Logger.Trace($"{nameof(DigitalTwinWithComponentOperationsAsync)}: DesiredProperty update received: {patch}, {context}"); return(Task.FromResult(true)); }, deviceClient); // Update the property "targetTemperature" under component "thermostat1" on the digital twin. // NOTE: since this is the first operation on the digital twin, the component "thermostat1" doesn't exist on it yet. // So we will create a property patch that "adds" a component, and updates the property in it. string propertyName = "targetTemperature"; double propertyValue = new Random().Next(0, 100); var propertyValues = new Dictionary <string, object> { { propertyName, propertyValue } }; var ops = new UpdateOperationsUtility(); ops.AppendAddComponentOp($"/{componentName}", propertyValues); string patch = ops.Serialize(); HttpOperationHeaderResponse <DigitalTwinUpdateHeaders> updateResponse = await digitalTwinClient.UpdateDigitalTwinAsync(deviceId, patch); updateResponse.Response.StatusCode.Should().Be(HttpStatusCode.Accepted); // Set callbacks to handle command requests. int expectedCommandStatus = 200; // Set callback to handle root-level command invocation request. string rootCommandName = "reboot"; await deviceClient.SetMethodHandlerAsync(rootCommandName, (request, context) => { Logger.Trace($"{nameof(DigitalTwinWithComponentOperationsAsync)}: Digital twin command {request.Name} received."); string payload = JsonConvert.SerializeObject(request.Name); return(Task.FromResult(new MethodResponse(Encoding.UTF8.GetBytes(payload), expectedCommandStatus))); }, null); // Invoke the root-level command "reboot" on the digital twin. int delay = 1; string rootCommandPayload = JsonConvert.SerializeObject(delay); HttpOperationResponse <DigitalTwinCommandResponse, DigitalTwinInvokeCommandHeaders> rootCommandResponse = await digitalTwinClient.InvokeCommandAsync(deviceId, rootCommandName, rootCommandPayload).ConfigureAwait(false); rootCommandResponse.Body.Status.Should().Be(expectedCommandStatus); rootCommandResponse.Body.Payload.Should().Be(JsonConvert.SerializeObject(rootCommandName)); // Set callback to handle component-level command invocation request. // For a component-level command, the command name is in the format "<component-name>*<command-name>". string componentCommandName = "getMaxMinReport"; string componentCommandNamePnp = $"{componentName}*{componentCommandName}"; await deviceClient.SetMethodHandlerAsync(componentCommandNamePnp, (request, context) => { Logger.Trace($"{nameof(DigitalTwinWithComponentOperationsAsync)}: Digital twin command {request.Name} received."); string payload = JsonConvert.SerializeObject(request.Name); return(Task.FromResult(new MethodResponse(Encoding.UTF8.GetBytes(payload), expectedCommandStatus))); }, null); // Invoke the command "getMaxMinReport" under component "thermostat1" on the digital twin. DateTimeOffset since = DateTimeOffset.Now.Subtract(TimeSpan.FromMinutes(1)); string componentCommandPayload = JsonConvert.SerializeObject(since); HttpOperationResponse <DigitalTwinCommandResponse, DigitalTwinInvokeCommandHeaders> componentCommandResponse = await digitalTwinClient.InvokeComponentCommandAsync(deviceId, componentName, componentCommandName, componentCommandPayload).ConfigureAwait(false); componentCommandResponse.Body.Status.Should().Be(expectedCommandStatus); componentCommandResponse.Body.Payload.Should().Be(JsonConvert.SerializeObject(componentCommandNamePnp)); } finally { // Delete the device. await testDevice.RemoveDeviceAsync().ConfigureAwait(false); } }