This commit is contained in:
Darren Chuang 2016-11-29 13:23:11 +08:00
Родитель 0f663eedda
Коммит 3f02cfab0f
9 изменённых файлов: 232 добавлений и 4 удалений

Просмотреть файл

@ -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" />