Report supported methods
This commit is contained in:
Родитель
0f663eedda
Коммит
3f02cfab0f
|
@ -175,6 +175,7 @@
|
|||
<Compile Include="Helpers\BlobStorageClient.cs" />
|
||||
<Compile Include="Helpers\BlobStorageClientFactory.cs" />
|
||||
<Compile Include="Helpers\BlobStorageReader.cs" />
|
||||
<Compile Include="Helpers\SupportedMethodsHelper.cs" />
|
||||
<Compile Include="Helpers\IAzureTableStorageClient.cs" />
|
||||
<Compile Include="Helpers\IAzureTableStorageClientFactory.cs" />
|
||||
<Compile Include="Helpers\IBlobStorageClientFactory.cs" />
|
||||
|
@ -188,6 +189,7 @@
|
|||
<Compile Include="Models\DeviceModel.cs" />
|
||||
<Compile Include="Models\DeviceProperties.cs" />
|
||||
<Compile Include="Models\IoTHub.cs" />
|
||||
<Compile Include="Models\SupportedMethod.cs" />
|
||||
<Compile Include="Models\SystemProperties.cs" />
|
||||
<Compile Include="Models\Telemetry.cs" />
|
||||
<Compile Include="Schema\SchemaHelper.cs" />
|
||||
|
|
|
@ -152,6 +152,12 @@ namespace Microsoft.Azure.Devices.Applications.RemoteMonitoring.Common.Factory
|
|||
"Controls if the device should send the external temperature as telemetry.",
|
||||
new[] { new Parameter("Active", "boolean") }
|
||||
));
|
||||
device.Commands.Add(new Command(
|
||||
"ChangeDeviceState",
|
||||
DeliveryType.Message,
|
||||
"Sets the device state metadata property that the device reports. This is useful for testing back-end logic.",
|
||||
new[] { new Parameter("DeviceState", "string") }
|
||||
));
|
||||
device.Commands.Add(new Command(
|
||||
"ChangeDeviceState",
|
||||
DeliveryType.Method,
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Microsoft.Azure.Devices.Applications.RemoteMonitoring.Common.Models;
|
||||
using Microsoft.Azure.Devices.Applications.RemoteMonitoring.Common.Models.Commands;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.Azure.Devices.Applications.RemoteMonitoring.Common.Helpers
|
||||
{
|
||||
|
||||
public static class SupportedMethodsHelper
|
||||
{
|
||||
private static string SupportedMethodsKey = "SupportedMethods";
|
||||
|
||||
public static JObject GenerateSupportedMethodsReportedProperty(List<Command> commands)
|
||||
{
|
||||
var methods = new Dictionary<string, SupportedMethod>();
|
||||
foreach (var command in commands.Where(c => c.DeliveryType == DeliveryType.Method))
|
||||
{
|
||||
string normalizedMethodName = NormalizeMethodName(command);
|
||||
methods[normalizedMethodName] = Convert(command);
|
||||
}
|
||||
|
||||
var obj = new JObject();
|
||||
obj[SupportedMethodsKey] = JObject.FromObject(methods);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static void AddSupportedMethodsFromReportedProperty(DeviceModel device, Twin twin)
|
||||
{
|
||||
if (!twin.Properties.Reported.Contains(SupportedMethodsKey))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The property could be either TwinColltion or JObject that depends on where the property is generated from
|
||||
// Convert the property to JObject to unify the type.
|
||||
var methods = JObject.FromObject(twin.Properties.Reported[SupportedMethodsKey]).ToObject<Dictionary<string, SupportedMethod>>();
|
||||
|
||||
foreach (var method in methods)
|
||||
{
|
||||
var command = Convert(method.Value);
|
||||
|
||||
if (command != null && !device.Commands.Any(c => c.Name == command.Name && c.DeliveryType == DeliveryType.Method))
|
||||
{
|
||||
device.Commands.Add(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert Command to SupportedMethod. Array will be convert to an object since the Twin doesn't support array.
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
/// <returns></returns>
|
||||
private static SupportedMethod Convert(Command command)
|
||||
{
|
||||
var method = new SupportedMethod();
|
||||
method.Name = command.Name;
|
||||
method.Description = command.Description;
|
||||
command.Parameters.ForEach(p => method.Parameters.Add(p.Name, p));
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert SupportedMethod back to Command.
|
||||
/// </summary>
|
||||
/// <param name="method"></param>
|
||||
/// <returns></returns>
|
||||
private static Command Convert(SupportedMethod method)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(method.Name))
|
||||
{
|
||||
throw new ArgumentNullException("Method Name");
|
||||
}
|
||||
|
||||
var command = new Command();
|
||||
command.Name = method.Name;
|
||||
command.Description = method.Description;
|
||||
command.DeliveryType = DeliveryType.Method;
|
||||
|
||||
foreach (var parameter in method.Parameters)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(parameter.Value.Type))
|
||||
{
|
||||
throw new ArgumentNullException("Parameter Type");
|
||||
}
|
||||
|
||||
command.Parameters.Add(new Parameter(parameter.Key, parameter.Value.Type));
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.TraceWarning("Failed to covert supported method from reported property : {0}, message: {1}", JsonConvert.SerializeObject(method), ex.Message);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static string NormalizeMethodName(Command command)
|
||||
{
|
||||
var parts = new List<string> { command.Name.Replace("_", "__") };
|
||||
parts.AddRange(command.Parameters.Select(p => p.Type).ToList());
|
||||
|
||||
return string.Join("_", parts);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Azure.Devices.Applications.RemoteMonitoring.Common.Models
|
||||
{
|
||||
public class SupportedMethod
|
||||
{
|
||||
[JsonConstructor]
|
||||
public SupportedMethod()
|
||||
{
|
||||
Parameters = new Dictionary<string, Parameter>();
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public Dictionary<string, Parameter> Parameters { get; set; }
|
||||
public string Description { get; set; }
|
||||
}
|
||||
}
|
|
@ -226,6 +226,8 @@ namespace Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Infr
|
|||
// Get original device document
|
||||
DeviceModel existingDevice = await this.GetDeviceAsync(device.IoTHub.ConnectionDeviceId);
|
||||
|
||||
SupportedMethodsHelper.AddSupportedMethodsFromReportedProperty(device, existingDevice.Twin);
|
||||
|
||||
// Save the command history, original created date, and system properties (if any) of the existing device
|
||||
if (existingDevice.DeviceProperties != null)
|
||||
{
|
||||
|
|
|
@ -29,7 +29,7 @@ static bool g_continueRunning;
|
|||
#define DOWORK_LOOP_NUM 3
|
||||
|
||||
static char connectionString[1024] = { 0 };
|
||||
static char reportedProperties[1024] = { 0 };
|
||||
static char reportedProperties[4096] = { 0 };
|
||||
|
||||
typedef struct EVENT_INSTANCE_TAG
|
||||
{
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Devices.Applications.RemoteMonitoring.Common.Configurations;
|
||||
|
@ -17,6 +18,7 @@ using Microsoft.Azure.Devices.Applications.RemoteMonitoring.Simulator.WebJob.Sim
|
|||
using Microsoft.Azure.Devices.Applications.RemoteMonitoring.Simulator.WebJob.SimulatorCore.Transport.Factory;
|
||||
using Microsoft.Azure.Devices.Common.Exceptions;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.Azure.Devices.Applications.RemoteMonitoring.Simulator.WebJob.SimulatorCore.Devices
|
||||
{
|
||||
|
@ -119,7 +121,7 @@ namespace Microsoft.Azure.Devices.Applications.RemoteMonitoring.Simulator.WebJob
|
|||
{
|
||||
DeviceModel device = DeviceCreatorHelper.BuildDeviceStructure(DeviceID, true, null);
|
||||
device.DeviceProperties = this.DeviceProperties;
|
||||
device.Commands = this.Commands ?? new List<Command>();
|
||||
device.Commands = this.Commands?.Where(c => c.DeliveryType == DeliveryType.Message).ToList() ?? new List<Command>();
|
||||
device.Telemetry = this.Telemetry ?? new List<Common.Models.Telemetry>();
|
||||
device.Version = SampleDeviceFactory.VERSION_1_0;
|
||||
device.ObjectType = SampleDeviceFactory.OBJECT_TYPE_DEVICE_INFO;
|
||||
|
@ -180,7 +182,11 @@ namespace Microsoft.Azure.Devices.Applications.RemoteMonitoring.Simulator.WebJob
|
|||
var deviceConnectionString = Client.IotHubConnectionStringBuilder.Create(HostName, authMethod).ToString();
|
||||
|
||||
// Device properties (InstalledRAM, Processor, etc.) should be treat as reported proerties
|
||||
var reportedProperties = JsonConvert.SerializeObject(DeviceProperties, Formatting.Indented, new JsonSerializerSettings
|
||||
// Supported methods should be treat as reported property
|
||||
var jObject = JObject.FromObject(DeviceProperties);
|
||||
jObject.Merge(JObject.FromObject(SupportedMethodsHelper.GenerateSupportedMethodsReportedProperty(Commands)));
|
||||
|
||||
var reportedProperties = JsonConvert.SerializeObject(jObject, Formatting.Indented, new JsonSerializerSettings
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
ContractResolver = new SkipByNameContractResolver("DeviceID")
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Azure.Devices.Applications.RemoteMonitoring.Common.Helpers;
|
||||
using Microsoft.Azure.Devices.Applications.RemoteMonitoring.Common.Models;
|
||||
using Microsoft.Azure.Devices.Applications.RemoteMonitoring.Common.Models.Commands;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Azure.Devices.Applications.RemoteMonitoring.UnitTests.Common
|
||||
{
|
||||
public class SupportedMethodsHelperTests
|
||||
{
|
||||
[Fact]
|
||||
public void GenerateSupportedMethodsReportedPropertyTest()
|
||||
{
|
||||
var commands = new List<Command>() {
|
||||
// Method with parameters
|
||||
new Command("method1", DeliveryType.Method, "desc1", new List<Parameter>() {
|
||||
new Parameter("p1", "string"),
|
||||
new Parameter("p2", "int")
|
||||
}),
|
||||
// Command, should be ignored
|
||||
new Command("command1", DeliveryType.Method, "desc1", new List<Parameter>() {
|
||||
new Parameter("p1", "int"),
|
||||
new Parameter("p2", "string")
|
||||
}),
|
||||
// Method without parameters
|
||||
new Command("method2", DeliveryType.Method, "desc2"),
|
||||
// Method name with _
|
||||
new Command("method_3", DeliveryType.Method, "desc3"),
|
||||
// Method without name, should be ignored
|
||||
new Command("", DeliveryType.Method, "desc2"),
|
||||
// parameter with no type, should be ignored
|
||||
new Command("method4", DeliveryType.Method, "desc1", new List<Parameter>() {
|
||||
new Parameter("p1", ""),
|
||||
new Parameter("p2", "int")
|
||||
}),
|
||||
};
|
||||
|
||||
var property = SupportedMethodsHelper.GenerateSupportedMethodsReportedProperty(commands);
|
||||
|
||||
JObject supportedMethods = property["SupportedMethods"] as JObject;
|
||||
Assert.Equal(supportedMethods.Count, commands.Where(c => c.DeliveryType == DeliveryType.Method).Count());
|
||||
|
||||
Assert.Equal(supportedMethods["method1_string_int"]["Name"].ToString(), "method1");
|
||||
Assert.Equal(supportedMethods["method1_string_int"]["Description"].ToString(), "desc1");
|
||||
Assert.Equal(supportedMethods["method1_string_int"]["Parameters"]["p1"]["Type"].ToString(), "string");
|
||||
Assert.Equal(supportedMethods["method1_string_int"]["Parameters"]["p2"]["Type"].ToString(), "int");
|
||||
|
||||
Assert.Equal(supportedMethods["method2"]["Name"].ToString(), "method2");
|
||||
Assert.Equal(supportedMethods["method2"]["Description"].ToString(), "desc2");
|
||||
|
||||
Assert.Equal(supportedMethods["method__3"]["Name"].ToString(), "method_3");
|
||||
|
||||
var device = new DeviceModel();
|
||||
var twin = new Twin();
|
||||
twin.Properties.Reported["SupportedMethods"] = supportedMethods;
|
||||
|
||||
SupportedMethodsHelper.AddSupportedMethodsFromReportedProperty(device, twin);
|
||||
Assert.Equal(supportedMethods.Count - 2, device.Commands.Count);
|
||||
foreach(var command in device.Commands)
|
||||
{
|
||||
var srcCommand = commands.FirstOrDefault(c => c.Name == command.Name);
|
||||
Assert.Equal(command.Name, srcCommand.Name);
|
||||
Assert.Equal(command.Description, srcCommand.Description);
|
||||
Assert.Equal(command.Parameters.Count, srcCommand.Parameters.Count);
|
||||
|
||||
foreach (var parameter in command.Parameters)
|
||||
{
|
||||
var srcParameter = srcCommand.Parameters.FirstOrDefault(p => p.Name == parameter.Name);
|
||||
Assert.Equal(parameter.Name, srcParameter.Name);
|
||||
Assert.Equal(parameter.Type, srcParameter.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -202,6 +202,7 @@
|
|||
<Compile Include="Common\StringExtensionTests.cs" />
|
||||
<Compile Include="Common\TimeSpanExtensionTests.cs" />
|
||||
<Compile Include="Common\TwinCollectionExtensionTests.cs" />
|
||||
<Compile Include="Common\SupportedMethodsHelperTests.cs" />
|
||||
<Compile Include="Common\TwinExtensionTests.cs" />
|
||||
<Compile Include="Common\VirtualDeviceTableStorageTests.cs" />
|
||||
<Compile Include="Infrastructure\ActionLogicTest.cs" />
|
||||
|
|
Загрузка…
Ссылка в новой задаче