diff --git a/.github/workflows/push2nuget.yml b/.github/workflows/push2nuget.yml index 1c07fe1..0f96c31 100644 --- a/.github/workflows/push2nuget.yml +++ b/.github/workflows/push2nuget.yml @@ -17,9 +17,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v2 with: - dotnet-version: | - 6.0.x - 3.1.x + dotnet-version: 6.0.x - name: Restore dependencies run: dotnet restore diff --git a/samples/iothub-sample/Device.cs b/samples/iothub-sample/Device.cs index 016bdbf..af90480 100644 --- a/samples/iothub-sample/Device.cs +++ b/samples/iothub-sample/Device.cs @@ -24,12 +24,12 @@ public Device(ILogger logger, IConfiguration configuration) protected override async Task ExecuteAsync(CancellationToken stoppingToken) { var connectionSettings = new ConnectionSettings(_configuration.GetConnectionString("cs")); - _logger.LogWarning($"Connecting to: {connectionSettings}"); + _logger.LogWarning("Connecting to: {connectionSettings}", connectionSettings); var client = new HubMqttClient(await HubDpsFactory.CreateFromConnectionSettingsAsync(connectionSettings, stoppingToken)); var v = await client.UpdateTwinAsync(new { started = DateTime.Now }, stoppingToken); - _logger.LogInformation($" Updated Twin to verison: {v} "); + _logger.LogInformation("Updated Twin to verison: {v}", v); var twin = await client.GetTwinAsync(stoppingToken); Console.WriteLine(twin); diff --git a/samples/memmon-protobuff/Device.cs b/samples/memmon-protobuff/Device.cs index 912f1ba..5e35827 100644 --- a/samples/memmon-protobuff/Device.cs +++ b/samples/memmon-protobuff/Device.cs @@ -26,9 +26,7 @@ public class Device : BackgroundService private double telemetryWorkingSet = 0; private const bool default_enabled = true; private const int default_interval = 45; - - private string lastDiscconectReason = string.Empty; - + private MemmonClient client; private ConnectionSettings connectionSettings; @@ -44,7 +42,7 @@ public Device(ILogger logger, IConfiguration configuration, TelemetryCli protected override async Task ExecuteAsync(CancellationToken stoppingToken) { var cs = new ConnectionSettings(_configuration.GetConnectionString("cs")) { ModelId = MemmonClient.ModelId }; - _logger.LogWarning($"Connecting to..{cs}"); + _logger.LogWarning("Connecting to..{cs}", cs); var mqtt = await BrokerClientFactory.CreateFromConnectionSettingsAsync(cs, true, stoppingToken); connectionSettings = cs; mqtt.DisconnectedAsync += Connection_DisconnectedAsync; @@ -63,7 +61,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) client.Props.Interval = default_interval; - await client.AllProperties.SendMessageAsync(client.Props); + await client.AllProperties.SendMessageAsync(client.Props, stoppingToken); RefreshScreen(this); @@ -88,7 +86,7 @@ private async Task Connection_DisconnectedAsync(MQTTnet.Client.MqttClientDisconn _telemetryClient.TrackException(arg.Exception); } - lastDiscconectReason = arg.ReasonString; + //lastDiscconectReason = arg.ReasonString; reconnectCounter++; await Task.Yield(); } diff --git a/samples/memmon-protobuff/_protos/memmon.cs b/samples/memmon-protobuff/_protos/memmon.cs index 0e280ab..93b59f5 100644 --- a/samples/memmon-protobuff/_protos/memmon.cs +++ b/samples/memmon-protobuff/_protos/memmon.cs @@ -13,7 +13,7 @@ namespace _protos internal class MemmonClient { internal const string ModelId = "rido.memmon"; - public Properties Props = new Properties(); + public Properties Props = new(); public IReadOnlyProperty AllProperties { get; set; } public IWritableProperty Property_interval { get; set; } public IWritableProperty Property_enabled { get; set; } diff --git a/samples/memmon-protobuff/memmon-protobuff.csproj b/samples/memmon-protobuff/memmon-protobuff.csproj index f5533d3..f5dfb6f 100644 --- a/samples/memmon-protobuff/memmon-protobuff.csproj +++ b/samples/memmon-protobuff/memmon-protobuff.csproj @@ -9,7 +9,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/samples/memmon/Device.cs b/samples/memmon/Device.cs index 1aaa516..a718f6e 100644 --- a/samples/memmon/Device.cs +++ b/samples/memmon/Device.cs @@ -1,19 +1,16 @@ -using dtmi_rido_pnp_memmon; +using dtmi_rido_memmon; using Humanizer; using Microsoft.ApplicationInsights; using MQTTnet.Extensions.MultiCloud; -using MQTTnet.Extensions.MultiCloud.AzureIoTClient; -using MQTTnet.Extensions.MultiCloud.BrokerIoTClient; using MQTTnet.Extensions.MultiCloud.Connections; using System.Diagnostics; +using System.Runtime.InteropServices; using System.Text; namespace memmon; public class Device : BackgroundService { - private readonly ILogger _logger; - private readonly IConfiguration _configuration; private readonly TelemetryClient _telemetryClient; private readonly Stopwatch clock = Stopwatch.StartNew(); @@ -23,52 +20,44 @@ public class Device : BackgroundService private int reconnectCounter = 0; private double telemetryWorkingSet = 0; + private double managedMemory = 0; private const bool default_enabled = true; - private const int default_interval = 45; + private const int default_interval = 500; private string lastDiscconectReason = string.Empty; private Imemmon client; private ConnectionSettings connectionSettings; + private readonly MemMonFactory memmonFactory; private string infoVersion = string.Empty; - public Device(ILogger logger, IConfiguration configuration, TelemetryClient tc) + public Device(TelemetryClient tc, MemMonFactory clientFactory) { - _logger = logger; - _configuration = configuration; _telemetryClient = tc; + memmonFactory = clientFactory; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - var cs = new ConnectionSettings(_configuration.GetConnectionString("cs")); - _logger.LogWarning($"Connecting to..{cs}"); - var memmonFactory = new MemMonFactory(_configuration); - client = await memmonFactory.CreateMemMonClientAsync(_configuration.GetConnectionString("cs"), stoppingToken); + client = await memmonFactory.CreateMemMonClientAsync("cs", stoppingToken); + client.Connection.DisconnectedAsync += Connection_DisconnectedAsync; + connectionSettings = MemMonFactory.connectionSettings; - _logger.LogWarning("Connected"); - infoVersion = MemMonFactory.NuGetPackageVersion; - + client.Property_enabled.OnMessage = Property_enabled_UpdateHandler; client.Property_interval.OnMessage= Property_interval_UpdateHandler; client.Command_getRuntimeStats.OnMessage= Command_getRuntimeStats_Handler; + client.Command_isPrime.OnMessage = Command_isPrime_Handler; + client.Command_malloc.OnMessage = Command_malloc_Hanlder; + client.Command_free.OnMessage = Command_free_Hanlder; - if (client is HubMqttClient) - { - await TwinInitializer.InitPropertyAsync(client.Connection, client.InitialState, client.Property_interval, "interval", default_interval); - await TwinInitializer.InitPropertyAsync(client.Connection, client.InitialState, client.Property_enabled, "enabled", default_enabled); - } - else - { - await PropertyInitializer.InitPropertyAsync(client.Property_interval, default_interval); - await PropertyInitializer.InitPropertyAsync(client.Property_enabled, default_enabled); - } + await client.Property_enabled.InitPropertyAsync(client.InitialState, default_enabled, stoppingToken); + await client.Property_interval.InitPropertyAsync(client.InitialState, default_interval, stoppingToken); - - await client.Property_started.SendMessageAsync(DateTime.Now); + await client.Property_started.SendMessageAsync(DateTime.Now, stoppingToken); RefreshScreen(this); @@ -76,12 +65,15 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) { if (client.Property_enabled.Value == true) { - telemetryWorkingSet = Environment.WorkingSet; + telemetryWorkingSet = Environment.WorkingSet.Bytes().Megabytes; + managedMemory = GC.GetTotalMemory(true).Bytes().Megabytes; await client.Telemetry_workingSet.SendMessageAsync(telemetryWorkingSet, stoppingToken); + await client.Telemetry_managedMemory.SendMessageAsync(managedMemory, stoppingToken); telemetryCounter++; _telemetryClient.TrackMetric("WorkingSet", telemetryWorkingSet); + _telemetryClient.TrackMetric("managedMemory", managedMemory); } - await Task.Delay(client.Property_interval.Value * 1000, stoppingToken); + await Task.Delay(client.Property_interval.Value, stoppingToken); } } @@ -150,6 +142,51 @@ private async Task> Property_interval_UpdateHandler(int p) return await Task.FromResult(ack); } + private async Task Command_isPrime_Handler(int number) + { + commandCounter++; + IEnumerable Multiples(int number) + { + return from n1 in Enumerable.Range(2, number / 2) + from n2 in Enumerable.Range(2, n1) + where n1 * n2 == number + select $"{n1} x {n2} => {number}"; + } + + bool result = Multiples(number).Any(); + return await Task.FromResult(!result); + + } + + List memory = new(); + IntPtr memoryPtr = IntPtr.Zero; + private async Task Command_malloc_Hanlder(int number) + { + commandCounter++; + for (int i = 0; i < number; i++) + { + memory.Add(i.ToOrdinalWords()); + } + + memoryPtr = Marshal.AllocHGlobal(number); + return await Task.FromResult(string.Empty); + } + + private async Task Command_free_Hanlder(string empty) + { + commandCounter++; + await _telemetryClient.FlushAsync(CancellationToken.None); + memory = new List(); + GC.Collect(2, GCCollectionMode.Forced, false); + if (memoryPtr != IntPtr.Zero) + { + Marshal.FreeHGlobal(memoryPtr); + memoryPtr = IntPtr.Zero; + } + return await Task.FromResult(string.Empty); + } + + private async Task> Command_getRuntimeStats_Handler(DiagnosticsMode req) { commandCounter++; @@ -180,6 +217,8 @@ private async Task> Command_getRuntimeStats_Handler(D result.Add("telemetry: ", telemetryCounter.ToString()); result.Add("command: ", commandCounter.ToString()); result.Add("reconnects: ", reconnectCounter.ToString()); + result.Add("workingSet", Environment.WorkingSet.Bytes().ToString()); + result.Add("GC Memmory", GC.GetTotalAllocatedBytes().Bytes().ToString()); } return await Task.FromResult(result); } @@ -212,7 +251,8 @@ string RenderData() //AppendLineWithPadRight(sb, $"Twin send: {RidCounter.Current}"); AppendLineWithPadRight(sb, $"Command messages: {commandCounter}"); AppendLineWithPadRight(sb, " "); - AppendLineWithPadRight(sb, $"WorkingSet: {telemetryWorkingSet.Bytes()}"); + AppendLineWithPadRight(sb, $"WorkingSet: {telemetryWorkingSet} MB"); + AppendLineWithPadRight(sb, $"ManagedMemory: {managedMemory} MB"); AppendLineWithPadRight(sb, " "); AppendLineWithPadRight(sb, $"Time Running: {TimeSpan.FromMilliseconds(clock.ElapsedMilliseconds).Humanize(3)}"); AppendLineWithPadRight(sb, $"ConnectionStatus: {client.Connection.IsConnected} [{lastDiscconectReason}]"); diff --git a/samples/memmon/MemMonFactory.cs b/samples/memmon/MemMonFactory.cs index d003260..17e0dcd 100644 --- a/samples/memmon/MemMonFactory.cs +++ b/samples/memmon/MemMonFactory.cs @@ -1,4 +1,4 @@ -using dtmi_rido_pnp_memmon; +using dtmi_rido_memmon; using MQTTnet.Extensions.MultiCloud.AwsIoTClient; using MQTTnet.Extensions.MultiCloud.AzureIoTClient; using MQTTnet.Extensions.MultiCloud.BrokerIoTClient; @@ -6,26 +6,30 @@ namespace memmon; -internal class MemMonFactory +public class MemMonFactory { static string nugetPackageVersion = string.Empty; public static string NuGetPackageVersion => nugetPackageVersion; internal static string ComputeDeviceKey(string masterKey, string deviceId) => Convert.ToBase64String(new System.Security.Cryptography.HMACSHA256(Convert.FromBase64String(masterKey)).ComputeHash(System.Text.Encoding.UTF8.GetBytes(deviceId))); - IConfiguration _configuration; + readonly IConfiguration _configuration; + readonly ILogger _logger; internal static ConnectionSettings connectionSettings; - public MemMonFactory(IConfiguration configuration) + public MemMonFactory(IConfiguration configuration, ILogger logger) { - this._configuration = configuration; + _configuration = configuration; + _logger = logger; } - public async Task CreateMemMonClientAsync(string connectionString, CancellationToken cancellationToken = default) + public async Task CreateMemMonClientAsync(string connectinStringName, CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(connectionString, nameof(connectionString)); - connectionSettings = new ConnectionSettings(_configuration.GetConnectionString("cs")); + Imemmon client; + string connectionString = _configuration.GetConnectionString(connectinStringName); + connectionSettings = new ConnectionSettings(connectionString); + _logger.LogWarning("Connecting to..{cs}", connectionSettings); if (connectionString.Contains("IdScope") || connectionString.Contains("SharedAccessKey")) { if (connectionSettings.IdScope != null && _configuration["masterKey"] != null) @@ -34,52 +38,58 @@ public async Task CreateMemMonClientAsync(string connectionString, Canc var masterKey = _configuration.GetValue("masterKey"); var deviceKey = ComputeDeviceKey(masterKey, deviceId); var newCs = $"IdScope={connectionSettings.IdScope};DeviceId={deviceId};SharedAccessKey={deviceKey};SasMinutes={connectionSettings.SasMinutes}"; - return await CreateHubClientAsync(newCs, cancellationToken); + client = await CreateHubClientAsync(newCs, cancellationToken); } else { - return await CreateHubClientAsync(connectionString, cancellationToken); + client = await CreateHubClientAsync(connectionString, cancellationToken); } } else if (connectionSettings.HostName.Contains("amazonaws.com")) { - return await CreateAwsClientAsync(connectionString, cancellationToken); + client = await CreateAwsClientAsync(connectionString, cancellationToken); } else if (connectionSettings.HostName.Contains("azure-devices.net")) { - return await CreateHubClientAsync(connectionString, cancellationToken); + client = await CreateHubClientAsync(connectionString, cancellationToken); } else { - return await CreateBrokerClientAsync(connectionString, cancellationToken); + client = await CreateBrokerClientAsync(connectionString, cancellationToken); } + + _logger.LogWarning("Connected"); + return client; } - static async Task CreateBrokerClientAsync(string connectionString, CancellationToken cancellationToken = default) + static async Task CreateBrokerClientAsync(string connectionString, CancellationToken cancellationToken = default) { var cs = new ConnectionSettings(connectionString) { ModelId = Imemmon.ModelId }; var mqtt = await BrokerClientFactory.CreateFromConnectionSettingsAsync(cs, true, cancellationToken); connectionSettings = BrokerClientFactory.ComputedSettings; - var client = new dtmi_rido_pnp_memmon.mqtt.memmon(mqtt); + var client = new dtmi_rido_memmon.mqtt.memmon(mqtt) + { + InitialState = String.Empty + }; nugetPackageVersion = BrokerClientFactory.NuGetPackageVersion; return client; } - static async Task CreateHubClientAsync(string connectionString, CancellationToken cancellationToken = default) + static async Task CreateHubClientAsync(string connectionString, CancellationToken cancellationToken = default) { var cs = new ConnectionSettings(connectionString) { ModelId = Imemmon.ModelId }; - var hub = await HubDpsFactory.CreateFromConnectionSettingsAsync(cs); + var hub = await HubDpsFactory.CreateFromConnectionSettingsAsync(cs, cancellationToken); connectionSettings = HubDpsFactory.ComputedSettings; - var client = new dtmi_rido_pnp_memmon.hub.memmon(hub); + var client = new dtmi_rido_memmon.hub.memmon(hub); nugetPackageVersion = HubDpsFactory.NuGetPackageVersion; - client.InitialState = await client.GetTwinAsync(); + client.InitialState = await client.GetTwinAsync(cancellationToken); return client; } - static async Task CreateAwsClientAsync(string connectionString, CancellationToken cancellationToken = default) + static async Task CreateAwsClientAsync(string connectionString, CancellationToken cancellationToken = default) { var mqtt = await AwsClientFactory.CreateFromConnectionSettingsAsync(connectionString, cancellationToken); - var client = new dtmi_rido_pnp_memmon.aws.memmon(mqtt); + var client = new dtmi_rido_memmon.aws.memmon(mqtt); nugetPackageVersion = AwsClientFactory.NuGetPackageVersion; return client; } diff --git a/samples/memmon/Program.cs b/samples/memmon/Program.cs index cc6c426..0b7f3e5 100644 --- a/samples/memmon/Program.cs +++ b/samples/memmon/Program.cs @@ -8,6 +8,7 @@ .ConfigureServices(services => { services.AddApplicationInsightsTelemetryWorkerService(); + services.AddSingleton(); services.AddHostedService(); }) .Build(); diff --git a/samples/memmon/Properties/launchSettings.json.template b/samples/memmon/Properties/launchSettings.json.template index d242ba1..ce1b293 100644 --- a/samples/memmon/Properties/launchSettings.json.template +++ b/samples/memmon/Properties/launchSettings.json.template @@ -40,7 +40,7 @@ "mosquitto_tls": { "commandName": "Project", "environmentVariables": { - "ConnectionStrings__cs": "HostName=test.mosquitto.org;ClientId=memmon01;CaFile=test.mosquitto.org.crt" + "ConnectionStrings__cs": "HostName=test.mosquitto.org;ClientId=memmon01;CaFile=test.mosquitto.org.pem" } } } diff --git a/samples/memmon/RidoFY23CA.crt b/samples/memmon/RidoFY23CA.pem similarity index 100% rename from samples/memmon/RidoFY23CA.crt rename to samples/memmon/RidoFY23CA.pem diff --git a/samples/memmon/dtmi_rido_pnp_memmon-1.aws.g.cs b/samples/memmon/dtmi_rido_memmon-2.aws.g.cs similarity index 82% rename from samples/memmon/dtmi_rido_pnp_memmon-1.aws.g.cs rename to samples/memmon/dtmi_rido_memmon-2.aws.g.cs index 32e4746..c6ad71a 100644 --- a/samples/memmon/dtmi_rido_pnp_memmon-1.aws.g.cs +++ b/samples/memmon/dtmi_rido_memmon-2.aws.g.cs @@ -6,7 +6,7 @@ using MQTTnet.Extensions.MultiCloud.AwsIoTClient.TopicBindings; using MQTTnet.Extensions.MultiCloud.BrokerIoTClient; -namespace dtmi_rido_pnp_memmon.aws; +namespace dtmi_rido_memmon.aws; public class memmon : AwsMqttClient, Imemmon { @@ -14,10 +14,15 @@ public class memmon : AwsMqttClient, Imemmon public IWritableProperty Property_enabled { get; set; } public IWritableProperty Property_interval { get; set; } public ITelemetry Telemetry_workingSet { get; set; } + public ITelemetry Telemetry_managedMemory { get; set; } public ICommand> Command_getRuntimeStats { get; set; } public string InitialState => String.Empty; + public ICommand Command_isPrime { get; set; } + public ICommand Command_malloc { get; set; } + public ICommand Command_free { get; set; } + internal memmon(IMqttClient c) : base(c, Imemmon.ModelId) { Property_started = new ReadOnlyProperty(c, "started"); diff --git a/samples/memmon/dtmi_rido_pnp_memmon-1.g.cs b/samples/memmon/dtmi_rido_memmon-2.g.cs similarity index 67% rename from samples/memmon/dtmi_rido_pnp_memmon-1.g.cs rename to samples/memmon/dtmi_rido_memmon-2.g.cs index e1a8544..84e1401 100644 --- a/samples/memmon/dtmi_rido_pnp_memmon-1.g.cs +++ b/samples/memmon/dtmi_rido_memmon-2.g.cs @@ -4,11 +4,11 @@ using MQTTnet.Client; using MQTTnet.Extensions.MultiCloud; -namespace dtmi_rido_pnp_memmon; +namespace dtmi_rido_memmon; public interface Imemmon { - public const string ModelId = "dtmi:rido:pnp:memmon;1"; + public const string ModelId = "dtmi:rido:memmon;2"; public IMqttClient Connection { get; } public string InitialState { get; } @@ -16,7 +16,11 @@ public interface Imemmon public IWritableProperty Property_enabled { get; set; } public IWritableProperty Property_interval { get; set; } public ITelemetry Telemetry_workingSet { get; set; } + public ITelemetry Telemetry_managedMemory { get; set; } public ICommand> Command_getRuntimeStats { get; set; } + public ICommand Command_isPrime { get; set; } + public ICommand Command_malloc { get; set; } + public ICommand Command_free { get; set; } } public enum DiagnosticsMode diff --git a/samples/memmon/dtmi_rido_pnp_memmon-1.hub.g.cs b/samples/memmon/dtmi_rido_memmon-2.hub.g.cs similarity index 66% rename from samples/memmon/dtmi_rido_pnp_memmon-1.hub.g.cs rename to samples/memmon/dtmi_rido_memmon-2.hub.g.cs index 09afcd1..f638967 100644 --- a/samples/memmon/dtmi_rido_pnp_memmon-1.hub.g.cs +++ b/samples/memmon/dtmi_rido_memmon-2.hub.g.cs @@ -5,7 +5,7 @@ using MQTTnet.Extensions.MultiCloud; using MQTTnet.Extensions.MultiCloud.AzureIoTClient; -namespace dtmi_rido_pnp_memmon.hub; +namespace dtmi_rido_memmon.hub; public class memmon : HubMqttClient, Imemmon { @@ -14,7 +14,11 @@ public class memmon : HubMqttClient, Imemmon public IWritableProperty Property_enabled { get; set; } public IWritableProperty Property_interval { get; set; } public ITelemetry Telemetry_workingSet { get; set; } + public ITelemetry Telemetry_managedMemory { get; set; } public ICommand> Command_getRuntimeStats { get; set; } + public ICommand Command_isPrime { get; set; } + public ICommand Command_malloc { get; set; } + public ICommand Command_free { get; set; } public memmon(IMqttClient c) : base(c) { @@ -22,6 +26,10 @@ public memmon(IMqttClient c) : base(c) Property_interval = new WritableProperty(c, "interval"); Property_enabled = new WritableProperty(c, "enabled"); Telemetry_workingSet = new Telemetry(c, "workingSet"); + Telemetry_managedMemory = new Telemetry(c, "managedMemory"); Command_getRuntimeStats = new Command>(c, "getRuntimeStats"); + Command_isPrime = new Command(c, "isPrime"); + Command_malloc = new Command(c, "malloc"); + Command_free = new Command(c, "free"); } } \ No newline at end of file diff --git a/samples/memmon/dtmi_rido_pnp_memmon-1.json b/samples/memmon/dtmi_rido_memmon-2.json similarity index 64% rename from samples/memmon/dtmi_rido_pnp_memmon-1.json rename to samples/memmon/dtmi_rido_memmon-2.json index 0ff97e3..8158c87 100644 --- a/samples/memmon/dtmi_rido_pnp_memmon-1.json +++ b/samples/memmon/dtmi_rido_memmon-2.json @@ -1,6 +1,6 @@ { "@context": "dtmi:dtdl:context;2", - "@id": "dtmi:rido:pnp:memmon;1", + "@id": "dtmi:rido:memmon;2", "@type": "Interface", "contents": [ { @@ -15,17 +15,47 @@ "writable": true }, { - "@type": ["Property", "TimeSpan"], + "@type": [ "Property", "TimeSpan" ], "name": "interval", "schema": "integer", "writable": true, - "unit": "second" + "unit": "millisecond" }, { - "@type": ["Telemetry", "DataSize"], + "@type": [ "Telemetry", "DataSize" ], "name": "workingSet", "schema": "double", - "unit": "byte" + "unit": "mebibyte" + }, + { + "@type": [ "Telemetry", "DataSize" ], + "name": "managedMemory", + "schema": "double", + "unit": "mebibyte" + }, + { + "@type": "Command", + "name": "isPrime", + "request": { + "name": "isPrimeReq", + "schema": "integer" + }, + "response": { + "name": "isPrimeRes", + "schema": "boolean" + } + }, + { + "@type": "Command", + "name": "malloc", + "request": { + "name": "allocations", + "schema": "integer" + } + }, + { + "@type": "Command", + "name": "free" }, { "@type": "Command", diff --git a/samples/memmon/dtmi_rido_pnp_memmon-1.mqtt.g.cs b/samples/memmon/dtmi_rido_memmon-2.mqtt.g.cs similarity index 68% rename from samples/memmon/dtmi_rido_pnp_memmon-1.mqtt.g.cs rename to samples/memmon/dtmi_rido_memmon-2.mqtt.g.cs index 63c654f..3d80669 100644 --- a/samples/memmon/dtmi_rido_pnp_memmon-1.mqtt.g.cs +++ b/samples/memmon/dtmi_rido_memmon-2.mqtt.g.cs @@ -5,7 +5,7 @@ using MQTTnet.Extensions.MultiCloud.BrokerIoTClient; -namespace dtmi_rido_pnp_memmon.mqtt; +namespace dtmi_rido_memmon.mqtt; public class memmon : Imemmon { @@ -15,7 +15,11 @@ public class memmon : Imemmon public IWritableProperty Property_enabled { get; set; } public IWritableProperty Property_interval { get; set; } public ITelemetry Telemetry_workingSet { get; set; } + public ITelemetry Telemetry_managedMemory { get; set; } public ICommand> Command_getRuntimeStats { get; set; } + public ICommand Command_isPrime { get; set; } + public ICommand Command_malloc { get; set; } + public ICommand Command_free { get; set; } internal memmon(IMqttClient c) { @@ -24,6 +28,10 @@ internal memmon(IMqttClient c) Property_interval = new WritableProperty(c, "interval"); Property_enabled = new WritableProperty(c, "enabled"); Telemetry_workingSet = new Telemetry(c, "workingSet"); + Telemetry_managedMemory = new Telemetry(c, "managedMemory"); Command_getRuntimeStats = new Command>(c, "getRuntimeStats"); + Command_isPrime = new Command(c, "isPrime"); + Command_malloc = new Command(c, "malloc"); + Command_free = new Command(c, "free"); } } \ No newline at end of file diff --git a/samples/memmon/memmon.csproj b/samples/memmon/memmon.csproj index b7c231b..6b8d2de 100644 --- a/samples/memmon/memmon.csproj +++ b/samples/memmon/memmon.csproj @@ -32,17 +32,8 @@ - - dtmi_rido_pnp_memmon-1.json - - - dtmi_rido_pnp_memmon-1.json - - - dtmi_rido_pnp_memmon-1.json - - - dtmi_rido_pnp_memmon-1.json + + dtmi_rido_memmon-2.json @@ -57,10 +48,4 @@ PreserveNewest - - - - PreserveNewest - - diff --git a/samples/mqtt-device/ClientFactory.cs b/samples/mqtt-device/ClientFactory.cs index a4174e5..d69a366 100644 --- a/samples/mqtt-device/ClientFactory.cs +++ b/samples/mqtt-device/ClientFactory.cs @@ -12,8 +12,8 @@ internal class ClientFactory internal static string ComputeDeviceKey(string masterKey, string deviceId) => Convert.ToBase64String(new System.Security.Cryptography.HMACSHA256(Convert.FromBase64String(masterKey)).ComputeHash(System.Text.Encoding.UTF8.GetBytes(deviceId))); - static internal ConnectionSettings computedSettings = new ConnectionSettings(); - IConfiguration _configuration; + static internal ConnectionSettings computedSettings = new(); + readonly IConfiguration _configuration; public ClientFactory(IConfiguration configuration) { @@ -59,7 +59,7 @@ public async Task CreateDeviceTemplateClientAsync(CancellationT static async Task CreateHubClientAsync(string connectionString, CancellationToken cancellationToken = default) { var cs = connectionString + ";ModelId=" + Idevicetemplate.ModelId; - var hub = await HubDpsFactory.CreateFromConnectionSettingsAsync(cs); + var hub = await HubDpsFactory.CreateFromConnectionSettingsAsync(cs, cancellationToken); var client = new dtmi_com_example_devicetemplate.hub.devicetemplate(hub); computedSettings = HubDpsFactory.ComputedSettings!; nugetPackageVersion = HubDpsFactory.NuGetPackageVersion; diff --git a/samples/mqtt-device/Device.cs b/samples/mqtt-device/Device.cs index c64dad1..066540e 100644 --- a/samples/mqtt-device/Device.cs +++ b/samples/mqtt-device/Device.cs @@ -32,19 +32,9 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) client.Property_interval.OnMessage = Property_interval_UpdateHandler; client.Command_echo.OnMessage = Cmd_echo_Handler; + await client.Property_interval.InitPropertyAsync(client.InitialState, default_interval, stoppingToken); - await client.Property_sdkInfo.SendMessageAsync(ClientFactory.NuGetPackageVersion); - - if (client is HubMqttClient) - { - HubMqttClient hubClient = (HubMqttClient)client; - client.InitialState = await hubClient.GetTwinAsync(); - await TwinInitializer.InitPropertyAsync(client.Connection, client.InitialState, client.Property_interval, "interval", default_interval); - } - else - { - await PropertyInitializer.InitPropertyAsync(client.Property_interval, default_interval); - } + await client.Property_sdkInfo.SendMessageAsync(ClientFactory.NuGetPackageVersion, stoppingToken); double lastTemp = 21; while (!stoppingToken.IsCancellationRequested) diff --git a/samples/mqtt-device/dtmi_com_example_devicetemplate-1.hub.g.cs b/samples/mqtt-device/dtmi_com_example_devicetemplate-1.hub.g.cs index f1e4360..edda80f 100644 --- a/samples/mqtt-device/dtmi_com_example_devicetemplate-1.hub.g.cs +++ b/samples/mqtt-device/dtmi_com_example_devicetemplate-1.hub.g.cs @@ -19,5 +19,6 @@ public devicetemplate(IMqttClient c) : base(c) Property_interval = new WritableProperty(c, "interval"); Telemetry_temp = new Telemetry(c, "temp"); Command_echo = new Command(c, "echo"); + InitialState = GetTwinAsync().Result; } } diff --git a/samples/mqtt-device/dtmi_com_example_devicetemplate-1.mqtt.g.cs b/samples/mqtt-device/dtmi_com_example_devicetemplate-1.mqtt.g.cs index 471eb74..e430414 100644 --- a/samples/mqtt-device/dtmi_com_example_devicetemplate-1.mqtt.g.cs +++ b/samples/mqtt-device/dtmi_com_example_devicetemplate-1.mqtt.g.cs @@ -21,5 +21,6 @@ public devicetemplate(IMqttClient c) Property_interval = new WritableProperty(c, "interval"); Telemetry_temp = new Telemetry(c, "temp"); Command_echo = new Command(c, "echo"); + InitialState = String.Empty; } } diff --git a/samples/mqtt-grpc-device/mqtt-grpc-device.csproj b/samples/mqtt-grpc-device/mqtt-grpc-device.csproj index 5587418..3b7430f 100644 --- a/samples/mqtt-grpc-device/mqtt-grpc-device.csproj +++ b/samples/mqtt-grpc-device/mqtt-grpc-device.csproj @@ -8,7 +8,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/samples/pi-sense-device/Device.cs b/samples/pi-sense-device/Device.cs index 4d03a9e..ecff451 100644 --- a/samples/pi-sense-device/Device.cs +++ b/samples/pi-sense-device/Device.cs @@ -1,66 +1,52 @@ +using dtmi_rido_pnp_sensehat; using Iot.Device.SenseHat; - using Microsoft.ApplicationInsights; - using MQTTnet.Extensions.MultiCloud; using MQTTnet.Extensions.MultiCloud.Connections; - using System.Net.NetworkInformation; using System.Runtime.InteropServices; using Color = System.Drawing.Color; -using pi_sense_device_protos; -using dtmi_rido_pnp_sensehat; - namespace pi_sense_device; public class Device : BackgroundService { private Isensehat? client; - private readonly ILogger _logger; - private readonly IConfiguration _configuration; private readonly TelemetryClient _telemetryClient; - + private readonly SenseHatFactory _senseHatFactory; + private readonly ILogger _logger; private const int default_interval = 5; - private readonly ConnectionSettings connectionSettings; - public Device(ILogger logger, IConfiguration configuration, TelemetryClient tc) + + public Device(SenseHatFactory factory, TelemetryClient tc, ILogger logger) { - _logger = logger; - _configuration = configuration; + _senseHatFactory = factory; _telemetryClient = tc; - connectionSettings = new ConnectionSettings(_configuration.GetConnectionString("cs")); + _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - var cs = new ConnectionSettings(_configuration.GetConnectionString("cs")); - _logger.LogWarning($"Connecting to .. {cs}"); - var factory = new SenseHatFactory(_configuration); - client = await factory.CreateSenseHatClientAsync(_configuration.GetConnectionString("cs")); - _logger.LogWarning($"Connected to {SenseHatFactory.computedSettings}"); + client = await _senseHatFactory.CreateSenseHatClientAsync("cs", stoppingToken); client.Property_interval.OnMessage = Property_interval_UpdateHandler; client.Property_combineTelemetry.OnMessage = Property_combineTelemetry_UpdateHandler; client.Command_ChangeLCDColor.OnMessage = Cmd_ChangeLCDColor_Handler; - await client.Property_sdkInfo.SendMessageAsync(SenseHatFactory.NuGetPackageVersion); + await client.Property_sdkInfo.SendMessageAsync(SenseHatFactory.NuGetPackageVersion, stoppingToken); client.Property_interval.Value = default_interval; client.Property_combineTelemetry.Value = true; - //await client.Property_interval.InitPropertyAsync(client.InitialState, default_interval, stoppingToken); + await client.Property_combineTelemetry.InitPropertyAsync(client.InitialState, true, stoppingToken); + await client.Property_interval.InitPropertyAsync(client.InitialState, default_interval, stoppingToken); - - //await client.Property_combineTelemetry.InitPropertyAsync(client.InitialState, true, stoppingToken); - //await client.Property_combineTelemetry.ReportPropertyAsync(stoppingToken); - - await client.Property_piri.SendMessageAsync($"os: {Environment.OSVersion}, proc: {RuntimeInformation.ProcessArchitecture}, clr: {Environment.Version}"); + await client.Property_piri.SendMessageAsync($"os: {Environment.OSVersion}, proc: {RuntimeInformation.ProcessArchitecture}, clr: {Environment.Version}", stoppingToken); var netInfo = "eth: " + GetLocalIPv4(); _telemetryClient.TrackTrace(netInfo); - await client.Property_ipaddr.SendMessageAsync(netInfo); + await client.Property_ipaddr.SendMessageAsync(netInfo, stoppingToken); //var tp = new TelemetryProtobuf(client.Connection, string.Empty) ; @@ -70,7 +56,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) ArgumentNullException.ThrowIfNull(client); if (RuntimeInformation.ProcessArchitecture == Architecture.Arm) { - using SenseHat sh = new SenseHat(); + using SenseHat sh = new(); _telemetryClient.TrackMetric("temp1", sh.Temperature.DegreesCelsius); if (client.Property_combineTelemetry.Value) { @@ -81,13 +67,6 @@ await client.SendTelemetryAsync(new AllTelemetries h = sh.Humidity.Percent, p = sh.Pressure.Pascals }, stoppingToken); - //await tp.SendMessageAsync(new Telemetries - //{ - // Temperature1 = sh.Temperature.DegreesCelsius, - // Temperature2 = sh.Temperature2.DegreesCelsius, - // Humidity = sh.Humidity.Percent, - // Pressure =sh.Pressure.Bars - //}); } else { @@ -113,15 +92,7 @@ await client.SendTelemetryAsync(new AllTelemetries h = Environment.WorkingSet / 1000000, p = Environment.WorkingSet / 1000000 }, stoppingToken); - - //await tp.SendMessageAsync(new Telemetries - //{ - // Temperature1 = t1, - // Temperature2 = GenerateSensorReading(t1, 5, 35), - // Humidity = Environment.WorkingSet / 1000000, - // Pressure = Environment.WorkingSet / 1000000 - //}, stoppingToken); - + } else { @@ -132,7 +103,7 @@ await client.SendTelemetryAsync(new AllTelemetries } } int interval = client!.Property_interval.Value; - _logger.LogInformation($"Waiting {interval} s to send telemetry"); + _logger.LogInformation("Waiting {interval} s to send telemetry", interval); await Task.Delay(interval * 1000, stoppingToken); } } @@ -185,7 +156,7 @@ private async Task Cmd_ChangeLCDColor_Handler(string req) if (RuntimeInformation.ProcessArchitecture == Architecture.Arm) { - using SenseHat sh = new SenseHat(); + using SenseHat sh = new (); sh.Fill(color); } else @@ -233,7 +204,7 @@ internal static string GetLocalIPv4() return output; } - private readonly Random random = new Random(); + private readonly Random random = new (); private double GenerateSensorReading(double currentValue, double min, double max) { diff --git a/samples/pi-sense-device/Program.cs b/samples/pi-sense-device/Program.cs index bd43a32..530940a 100644 --- a/samples/pi-sense-device/Program.cs +++ b/samples/pi-sense-device/Program.cs @@ -7,6 +7,7 @@ .ConfigureServices(services => { services.AddHostedService(); + services.AddSingleton(); services.AddApplicationInsightsTelemetryWorkerService(); }) .Build(); diff --git a/samples/pi-sense-device/SenseHatFactory.cs b/samples/pi-sense-device/SenseHatFactory.cs index 73f7ea9..3ca0381 100644 --- a/samples/pi-sense-device/SenseHatFactory.cs +++ b/samples/pi-sense-device/SenseHatFactory.cs @@ -5,32 +5,33 @@ namespace pi_sense_device { - internal class SenseHatFactory + public class SenseHatFactory { private static string nugetPackageVersion = string.Empty; public static string NuGetPackageVersion => nugetPackageVersion; private readonly IConfiguration _configuration; - public SenseHatFactory(IConfiguration configuration) + private readonly ILogger _logger; + public SenseHatFactory(IConfiguration configuration, ILogger logger) { _configuration = configuration; + _logger = logger; } internal static string ComputeDeviceKey(string masterKey, string deviceId) => Convert.ToBase64String(new System.Security.Cryptography.HMACSHA256(Convert.FromBase64String(masterKey)).ComputeHash(System.Text.Encoding.UTF8.GetBytes(deviceId))); - internal static ConnectionSettings computedSettings = new ConnectionSettings(); + internal static ConnectionSettings computedSettings = new (); - public async Task CreateSenseHatClientAsync(string connectionString, CancellationToken cancellationToken = default) + public async Task CreateSenseHatClientAsync(string connectionStringName, CancellationToken cancellationToken = default) { - if (string.IsNullOrEmpty(connectionString)) - { - throw new ArgumentNullException(nameof(connectionString)); - } + Isensehat client; + string connectionString = _configuration.GetConnectionString(connectionStringName); + var cs = new ConnectionSettings(connectionString); + _logger.LogWarning("Connecting to .. {cs}", cs); if (connectionString.Contains("IdScope") || connectionString.Contains("SharedAccessKey")) { - var cs = new ConnectionSettings(_configuration.GetConnectionString("cs")); if (cs.IdScope != null && _configuration["masterKey"] != null) { @@ -38,17 +39,19 @@ public async Task CreateSenseHatClientAsync(string connectionString, var masterKey = _configuration.GetValue("masterKey"); var deviceKey = ComputeDeviceKey(masterKey, deviceId); var newCs = $"IdScope={cs.IdScope};DeviceId={deviceId};SharedAccessKey={deviceKey};SasMinutes={cs.SasMinutes}"; - return await CreateHubClientAsync(newCs, cancellationToken); + client = await CreateHubClientAsync(newCs, cancellationToken); } else { - return await CreateHubClientAsync(connectionString, cancellationToken); + client = await CreateHubClientAsync(connectionString, cancellationToken); } } else { - return await CreateBrokerClientAsync(connectionString, cancellationToken); + client = await CreateBrokerClientAsync(connectionString, cancellationToken); } + _logger.LogWarning("Connected to {cs}", SenseHatFactory.computedSettings); + return client; } private static async Task CreateBrokerClientAsync(string connectionString, CancellationToken cancellationToken = default) @@ -63,7 +66,7 @@ public async Task CreateSenseHatClientAsync(string connectionString, private static async Task CreateHubClientAsync(string connectionString, CancellationToken cancellationToken = default) { var cs = connectionString + ";ModelId=" + Isensehat.ModelId; - var hub = await HubDpsFactory.CreateFromConnectionSettingsAsync(cs); + var hub = await HubDpsFactory.CreateFromConnectionSettingsAsync(cs, cancellationToken); var client = new dtmi_rido_pnp_sensehat.hub.sensehat(hub); computedSettings = HubDpsFactory.ComputedSettings!; nugetPackageVersion = HubDpsFactory.NuGetPackageVersion; diff --git a/samples/pi-sense-device/dtmi_rido_pnp_sensehat-1.hub.g.cs b/samples/pi-sense-device/dtmi_rido_pnp_sensehat-1.hub.g.cs index 120744e..ada9437 100644 --- a/samples/pi-sense-device/dtmi_rido_pnp_sensehat-1.hub.g.cs +++ b/samples/pi-sense-device/dtmi_rido_pnp_sensehat-1.hub.g.cs @@ -39,6 +39,7 @@ internal sensehat(IMqttClient c) : base(c) { WrapMessage = false }; + InitialState = GetTwinAsync().Result; } public async Task SendTelemetryAsync(AllTelemetries payload, CancellationToken t = default) { diff --git a/samples/pi-sense-device/dtmi_rido_pnp_sensehat-1.mqtt.g.cs b/samples/pi-sense-device/dtmi_rido_pnp_sensehat-1.mqtt.g.cs index a298b60..82593ee 100644 --- a/samples/pi-sense-device/dtmi_rido_pnp_sensehat-1.mqtt.g.cs +++ b/samples/pi-sense-device/dtmi_rido_pnp_sensehat-1.mqtt.g.cs @@ -44,6 +44,7 @@ internal sensehat(IMqttClient c) { WrapMessage = false }; + InitialState = String.Empty; } public async Task SendTelemetryAsync(AllTelemetries payload, CancellationToken t = default) diff --git a/samples/pi-sense-device/pi-sense-device.csproj b/samples/pi-sense-device/pi-sense-device.csproj index 3914a5e..780c8be 100644 --- a/samples/pi-sense-device/pi-sense-device.csproj +++ b/samples/pi-sense-device/pi-sense-device.csproj @@ -31,12 +31,12 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index d8928af..c01cc5b 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -19,7 +19,7 @@ all - 3.5.109 + 3.5.* \ No newline at end of file diff --git a/src/MQTTnet.Extensions.MultiCloud.AwsIoTClient/AwsClientFactory.cs b/src/MQTTnet.Extensions.MultiCloud.AwsIoTClient/AwsClientFactory.cs index 496e5cc..1bfd63f 100644 --- a/src/MQTTnet.Extensions.MultiCloud.AwsIoTClient/AwsClientFactory.cs +++ b/src/MQTTnet.Extensions.MultiCloud.AwsIoTClient/AwsClientFactory.cs @@ -16,7 +16,7 @@ public static async Task CreateFromConnectionSettingsAsync(string c public static async Task CreateFromConnectionSettingsAsync(ConnectionSettings cs, CancellationToken cancellationToken = default) { MqttClient? mqtt = new MqttFactory().CreateMqttClient(MqttNetTraceLogger.CreateTraceLogger()) as MqttClient; - var connAck = await mqtt!.ConnectAsync(new MqttClientOptionsBuilder().WithConnectionSettings(cs).Build()); + var connAck = await mqtt!.ConnectAsync(new MqttClientOptionsBuilder().WithConnectionSettings(cs).Build(), cancellationToken); if (connAck.ResultCode != MqttClientConnectResultCode.Success) { throw new ApplicationException($"Cannot connect to {cs}"); diff --git a/src/MQTTnet.Extensions.MultiCloud.AwsIoTClient/TopicBindings/AwsWritablePropertyUTFJson.cs b/src/MQTTnet.Extensions.MultiCloud.AwsIoTClient/TopicBindings/AwsWritablePropertyUTFJson.cs index 1b0bafa..977d3a9 100644 --- a/src/MQTTnet.Extensions.MultiCloud.AwsIoTClient/TopicBindings/AwsWritablePropertyUTFJson.cs +++ b/src/MQTTnet.Extensions.MultiCloud.AwsIoTClient/TopicBindings/AwsWritablePropertyUTFJson.cs @@ -28,5 +28,10 @@ public Task SendMessageAsync(Ack payload, CancellationToken cancellationToken { throw new System.NotImplementedException(); } + + public Task InitPropertyAsync(string intialState, T defaultValue, CancellationToken cancellationToken = default) + { + throw new System.NotImplementedException(); + } } } diff --git a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/Command.cs b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/Command.cs index 828b071..a73292f 100644 --- a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/Command.cs +++ b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/Command.cs @@ -8,7 +8,30 @@ public class Command : CloudToDeviceBinder, ICommand : CloudToDeviceBinder, ICommand +{ + public Command(IMqttClient client, string name) + : base(client, name) + { + SubscribeTopicPattern = "$iothub/methods/POST/#"; + RequestTopicPattern = "$iothub/methods/POST/{name}/#"; + ResponseTopicPattern = "$iothub/methods/res/200/?$rid={rid}"; + } +} + +public class Command : CloudToDeviceBinder, ICommand +{ + public Command(IMqttClient client, string name) + : base(client, name) + { + SubscribeTopicPattern = "$iothub/methods/POST/#"; + RequestTopicPattern = "$iothub/methods/POST/{name}/#"; + ResponseTopicPattern = "$iothub/methods/res/200/?$rid={rid}"; + } +} \ No newline at end of file diff --git a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/ConnectionTimer.cs b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/ConnectionTimer.cs new file mode 100644 index 0000000..5f0d6f6 --- /dev/null +++ b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/ConnectionTimer.cs @@ -0,0 +1,7 @@ +namespace MQTTnet.Extensions.MultiCloud.AzureIoTClient +{ + internal static class ConnectionTimer + { + internal static Timer? reconnectTimer; + } +} \ No newline at end of file diff --git a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/Dps/MqttDpsClient.cs b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/Dps/MqttDpsClient.cs index 6ca244c..b286b21 100644 --- a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/Dps/MqttDpsClient.cs +++ b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/Dps/MqttDpsClient.cs @@ -9,7 +9,7 @@ public class MqttDpsClient { private readonly IMqttClient mqttClient; private int rid = 0; - private readonly TaskCompletionSource tcs = new TaskCompletionSource(); + private readonly TaskCompletionSource tcs = new(); private readonly string modelId; public MqttDpsClient(IMqttClient c, string mid) { @@ -34,7 +34,7 @@ public MqttDpsClient(IMqttClient c, string mid) // TODO: ready retry-after await Task.Delay(2500); //avoid throtling var pollTopic = $"$dps/registrations/GET/iotdps-get-operationstatus/?$rid={rid++}&operationId={dpsRes.OperationId}"; - var puback = await mqttClient.PublishBinaryAsync(pollTopic, new byte[0]); + var puback = await mqttClient.PublishBinaryAsync(pollTopic, Array.Empty()); } else { diff --git a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/HubDpsFactory.cs b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/HubDpsFactory.cs index 56b4f6c..689de69 100644 --- a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/HubDpsFactory.cs +++ b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/HubDpsFactory.cs @@ -7,7 +7,6 @@ namespace MQTTnet.Extensions.MultiCloud.AzureIoTClient { public class HubDpsFactory { - private static Timer? reconnectTimer; public static string NuGetPackageVersion => $"{ThisAssembly.AssemblyName} {ThisAssembly.NuGetPackageVersion}"; public static ConnectionSettings? ComputedSettings { get; private set; } @@ -34,7 +33,7 @@ public static async Task CreateFromConnectionSettingsAsync(Connecti } else { - connAck = await mqtt!.ConnectAsync(new MqttClientOptionsBuilder().WithAzureIoTHubCredentials(cs).Build(), cancellationToken); + connAck = await mqtt!.ConnectAsync(new MqttClientOptionsBuilder().WithConnectionSettings(cs).Build(), cancellationToken); } if (connAck.ResultCode != MqttClientConnectResultCode.Success) { @@ -54,14 +53,13 @@ private static MqttClientConnectResult ConnectWithTimer(IMqttClient mqtt, Connec Trace.TraceInformation("Reconnecting before SasToken expires"); var connAck = mqtt.ConnectAsync( new MqttClientOptionsBuilder() - .WithAzureIoTHubCredentials(connectionSettings) + .WithConnectionSettings(connectionSettings) .WithKeepAlivePeriod(TimeSpan.FromSeconds(connectionSettings.KeepAliveInSeconds)) .Build(), cancellationToken).Result; mqtt.ReSuscribe(); - - reconnectTimer = new Timer(o => + ConnectionTimer.reconnectTimer = new Timer(o => { ConnectWithTimer(mqtt, connectionSettings, cancellationToken); }, null, (connectionSettings.SasMinutes * 60 * 1000) - 10, 0); diff --git a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/HubMqttClient.cs b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/HubMqttClient.cs index 92eb09e..f3815dd 100644 --- a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/HubMqttClient.cs +++ b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/HubMqttClient.cs @@ -24,15 +24,6 @@ public HubMqttClient(IMqttClient c) genericDesiredUpdateProperty = new GenericDesiredUpdatePropertyBinder(c, twinOperationsBinder!); } - - - //public async Task InitState() - //{ - // //await command.InitSubscriptionsAsync(); - // //await genericDesiredUpdateProperty.InitiSubscriptionsAsync(); - // InitialState = await GetTwinAsync(); - //} - public Func OnCommandReceived { get => command.OnCmdDelegate!; @@ -45,10 +36,8 @@ public Func OnPropertyUpdateReceived set => genericDesiredUpdateProperty.OnProperty_Updated = value; } - public Task GetTwinAsync(CancellationToken cancellationToken = default) => - twinOperationsBinder.GetTwinAsync(cancellationToken); - public Task UpdateTwinAsync(object payload, CancellationToken cancellationToken = default) => - twinOperationsBinder.UpdateTwinAsync(payload, cancellationToken); + public Task GetTwinAsync(CancellationToken cancellationToken = default) => twinOperationsBinder.GetTwinAsync(cancellationToken); + public Task UpdateTwinAsync(object payload, CancellationToken cancellationToken = default) => twinOperationsBinder.UpdateTwinAsync(payload, cancellationToken); public Task SendTelemetryAsync(object payload, CancellationToken t = default) => Connection.PublishBinaryAsync($"devices/{Connection.Options.ClientId}/messages/events/", new UTF8JsonSerializer().ToBytes(payload), diff --git a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/PropertyParser.cs b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/PropertyParser.cs deleted file mode 100644 index 5403de0..0000000 --- a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/PropertyParser.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Text.Json.Nodes; - -namespace MQTTnet.Extensions.MultiCloud.AzureIoTClient -{ - public class PropertyParser - { - public static JsonNode ReadPropertyFromDesired(JsonNode desired, string propertyName, string componentName) - { - JsonNode? result = null; - if (string.IsNullOrEmpty(componentName)) - { - result = desired?[propertyName]; - } - //else - //{ - // if (desired[componentName] != null && - // desired[componentName][propertyName] != null && - // desired[componentName]["__t"] != null && - // desired[componentName]["__t"].GetValue() == "c") - // { - // result = desired?[componentName][propertyName]; - // } - //} - - return result!; - } - } -} diff --git a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/TaskTimeoutExtension.cs b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/TaskTimeoutExtension.cs deleted file mode 100644 index 6c68015..0000000 --- a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/TaskTimeoutExtension.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Diagnostics; - -namespace MQTTnet.Extensions.MultiCloud.AzureIoTClient -{ - public static class TaskTimeoutExtension - { - public static async Task TimeoutAfter(this Task source, TimeSpan timeout) - { - var actualTimeout = timeout; - if (Debugger.IsAttached) - { - actualTimeout = timeout.Add(TimeSpan.FromSeconds(300)); - } - if (await Task.WhenAny(source, Task.Delay(actualTimeout)) != source) - { - throw new TimeoutException(); - } - return await source; - } - } -} diff --git a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/TopicParser.cs b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/TopicParser.cs deleted file mode 100644 index c532360..0000000 --- a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/TopicParser.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Web; - -namespace MQTTnet.Extensions.MultiCloud.AzureIoTClient -{ - internal class TopicParser - { - internal static (int rid, int twinVersion) ParseTopic(string topic) - { - var segments = topic.Split('/'); - int twinVersion = -1; - int rid = -1; - if (topic.Contains("?")) - { - var qs = HttpUtility.ParseQueryString(segments[segments.Length - 1]); - int.TryParse(qs["$rid"], out rid); - twinVersion = Convert.ToInt32(qs["$version"]); - } - return (rid, twinVersion); - } - } -} diff --git a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/TwinInitializer.cs b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/TwinInitializer.cs index ff39261..e146346 100644 --- a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/TwinInitializer.cs +++ b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/TwinInitializer.cs @@ -5,28 +5,9 @@ namespace MQTTnet.Extensions.MultiCloud.AzureIoTClient { - public class TwinInitializer + internal class TwinInitializer { - public static async Task InitPropertyAsync(IMqttClient client, string twin, IWritableProperty prop, string propName, T defaultValue) - { - var ack = InitFromTwin(twin, propName, defaultValue); - prop.Version = ack.Version; - Ack acceptedAck; - if (prop.OnMessage != null) - { - acceptedAck = await prop.OnMessage.Invoke(ack.Value); - } - else - { - acceptedAck = ack; - acceptedAck.Description = "Init from default value"; - acceptedAck.Status = 203; - } - var roBinder = new ReadOnlyProperty>(client, propName); - await roBinder.SendMessageAsync(acceptedAck); - } - - private static Ack InitFromTwin(string twinJson, string propName, T defaultValue) + internal static Ack InitFromTwin(string twinJson, string propName, T defaultValue) { if (string.IsNullOrEmpty(twinJson)) { diff --git a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/TwinRequestResponseBinder.cs b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/TwinRequestResponseBinder.cs index 338313a..ca3fb64 100644 --- a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/TwinRequestResponseBinder.cs +++ b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/TwinRequestResponseBinder.cs @@ -11,8 +11,8 @@ namespace MQTTnet.Extensions.MultiCloud.AzureIoTClient; public class TwinRequestResponseBinder { internal int lastRid = -1; - private readonly ConcurrentDictionary> pendingGetTwinRequests = new ConcurrentDictionary>(); - private readonly ConcurrentDictionary> pendingUpdateTwinRequests = new ConcurrentDictionary>(); + private readonly ConcurrentDictionary> pendingGetTwinRequests = new(); + private readonly ConcurrentDictionary> pendingUpdateTwinRequests = new(); public Func>? OnMessage { get; set; } private readonly IMqttClient connection; @@ -26,7 +26,7 @@ public TwinRequestResponseBinder(IMqttClient connection) var topic = m.ApplicationMessage.Topic; if (topic.StartsWith("$iothub/twin/res/200")) { - string msg = Encoding.UTF8.GetString(m.ApplicationMessage.Payload ?? new byte[0]); + string msg = Encoding.UTF8.GetString(m.ApplicationMessage.Payload ?? Array.Empty()); (int rid, _) = TopicParser.ParseTopic(topic); if (pendingGetTwinRequests.TryGetValue(rid, out var tcs)) { @@ -40,7 +40,7 @@ public TwinRequestResponseBinder(IMqttClient connection) } else if (topic.StartsWith("$iothub/twin/res/204")) { - string msg = Encoding.UTF8.GetString(m.ApplicationMessage.Payload ?? new byte[0]); + string msg = Encoding.UTF8.GetString(m.ApplicationMessage.Payload ?? Array.Empty()); (int rid, int version) = TopicParser.ParseTopic(topic); if (pendingUpdateTwinRequests.TryGetValue(rid, out var tcs)!) { @@ -62,13 +62,15 @@ public TwinRequestResponseBinder(IMqttClient connection) public async Task GetTwinAsync(CancellationToken cancellationToken = default) { - await connection.SubscribeAsync("$iothub/twin/res/#"); + + await connection.SubscribeWithReplyAsync("$iothub/twin/res/#", cancellationToken); + var rid = RidCounter.NextValue(); lastRid = rid; // for testing var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var puback = await connection.PublishBinaryAsync( $"$iothub/twin/GET/?$rid={rid}", - new byte[0], + Array.Empty(), MqttQualityOfServiceLevel.AtMostOnce, false, cancellationToken); @@ -93,11 +95,11 @@ public async Task GetTwinAsync(CancellationToken cancellationToken = def public async Task UpdateTwinAsync(object payload, CancellationToken cancellationToken = default) { - await connection.SubscribeWithReplyAsync("$iothub/twin/res/#"); + await connection.SubscribeWithReplyAsync("$iothub/twin/res/#", cancellationToken); byte[] patchBytes; - if (payload is string) + if (payload is string @string) { - patchBytes = Encoding.UTF8.GetBytes((string)payload); + patchBytes = Encoding.UTF8.GetBytes(@string); } else { @@ -148,11 +150,13 @@ internal static (int rid, int twinVersion) ParseTopic(string topic) var segments = topic.Split('/'); int twinVersion = -1; int rid = -1; - if (topic.Contains("?")) + if (topic.Contains('?')) { - var qs = HttpUtility.ParseQueryString(segments[segments.Length - 1]); - int.TryParse(qs["$rid"], out rid); - twinVersion = Convert.ToInt32(qs["$version"]); + var qs = HttpUtility.ParseQueryString(segments[^1]); + if (int.TryParse(qs["$rid"], out rid)) + { + twinVersion = Convert.ToInt32(qs["$version"]); + } } return (rid, twinVersion); } diff --git a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/Untyped/GenericCommandBinder.cs b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/Untyped/GenericCommandBinder.cs index 9a48684..3c3dc7f 100644 --- a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/Untyped/GenericCommandBinder.cs +++ b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/Untyped/GenericCommandBinder.cs @@ -1,4 +1,5 @@ using MQTTnet.Client; +using MQTTnet.Extensions.MultiCloud.Binders; using System.Text; namespace MQTTnet.Extensions.MultiCloud.AzureIoTClient.Untyped @@ -20,16 +21,16 @@ public GenericCommand(IMqttClient c) var segments = topic.Split('/'); var cmdName = segments[3]; string msg = Encoding.UTF8.GetString(m.ApplicationMessage.Payload); - GenericCommandRequest req = new GenericCommandRequest() + GenericCommandRequest req = new() { CommandName = cmdName, CommandPayload = msg }; if (OnCmdDelegate != null && req != null) { - (int rid, _) = TopicParser.ParseTopic(topic); + var tp = TopicParser.ParseTopic(topic); GenericCommandResponse response = OnCmdDelegate.Invoke(req); - _ = connection.PublishStringAsync($"$iothub/methods/res/{response.Status}/?$rid={rid}", response.ReponsePayload); + _ = connection.PublishStringAsync($"$iothub/methods/res/{response.Status}/?$rid={tp.Rid}", response.ReponsePayload); } } await Task.Yield(); diff --git a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/Untyped/GenericPropertyAck.cs b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/Untyped/GenericPropertyAck.cs index 318dec7..566c231 100644 --- a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/Untyped/GenericPropertyAck.cs +++ b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/Untyped/GenericPropertyAck.cs @@ -12,61 +12,53 @@ public class GenericPropertyAck public string BuildAck() { - using (MemoryStream ms = new MemoryStream()) + using MemoryStream ms = new(); + using JsonDocument doc = JsonDocument.Parse(Value!); + using Utf8JsonWriter writer = new(ms); + writer.WriteStartObject(); + //writer.WriteStartObject("properties"); + //writer.WriteStartObject("reported"); + foreach (var el in doc.RootElement.EnumerateObject()) { - using (JsonDocument doc = JsonDocument.Parse(Value!)) + if (!el.Name.StartsWith("$")) { - using (Utf8JsonWriter writer = new Utf8JsonWriter(ms)) + writer.WritePropertyName(el.Name); + writer.WriteStartObject(); + writer.WriteNumber("ac", Status); + writer.WriteNumber("av", Version); + writer.WriteString("ad", Description); + switch (el.Value.ValueKind) { - writer.WriteStartObject(); - //writer.WriteStartObject("properties"); - //writer.WriteStartObject("reported"); - foreach (var el in doc.RootElement.EnumerateObject()) - { - if (!el.Name.StartsWith("$")) + case JsonValueKind.String: + writer.WriteString("value", el.Value.ToString()); + break; + case JsonValueKind.Number: + writer.WriteNumber("value", el.Value.GetDouble()); + break; + case JsonValueKind.True: + case JsonValueKind.False: + writer.WriteBoolean("value", el.Value.GetBoolean()); + break; + case JsonValueKind.Object: + writer.WriteStartObject("value"); + foreach (var so in el.Value.EnumerateObject()) { - writer.WritePropertyName(el.Name); - writer.WriteStartObject(); - writer.WriteNumber("ac", Status); - writer.WriteNumber("av", Version); - writer.WriteString("ad", Description); - switch (el.Value.ValueKind) - { - case JsonValueKind.String: - writer.WriteString("value", el.Value.ToString()); - break; - case JsonValueKind.Number: - writer.WriteNumber("value", el.Value.GetDouble()); - break; - case JsonValueKind.True: - case JsonValueKind.False: - writer.WriteBoolean("value", el.Value.GetBoolean()); - break; - case JsonValueKind.Object: - writer.WriteStartObject("value"); - foreach (var so in el.Value.EnumerateObject()) - { - so.WriteTo(writer); - } - writer.WriteEndObject(); - break; - } - writer.WriteEndObject(); + so.WriteTo(writer); } - } - //writer.WriteEndObject(); - //writer.WriteEndObject(); - writer.WriteEndObject(); - writer.Flush(); - ms.Position = 0; - using (StreamReader sr = new StreamReader(ms)) - { - string res = sr.ReadToEnd(); - return res; - } + writer.WriteEndObject(); + break; } + writer.WriteEndObject(); } } + //writer.WriteEndObject(); + //writer.WriteEndObject(); + writer.WriteEndObject(); + writer.Flush(); + ms.Position = 0; + using StreamReader sr = new(ms); + string res = sr.ReadToEnd(); + return res; } } } \ No newline at end of file diff --git a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/WritableProperty.cs b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/WritableProperty.cs index 2437361..7a1abb0 100644 --- a/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/WritableProperty.cs +++ b/src/MQTTnet.Extensions.MultiCloud.AzureIoTClient/WritableProperty.cs @@ -15,6 +15,7 @@ public WritableProperty(IMqttClient c, string name) { _name = name; _connection = c; + SubscribeTopicPattern = "$iothub/twin/PATCH/properties/desired/#"; RequestTopicPattern = "$iothub/twin/PATCH/properties/desired/#"; ResponseTopicPattern = "$iothub/twin/PATCH/properties/reported/?$rid={rid}"; UnwrapRequest = true; @@ -30,4 +31,12 @@ public async Task SendMessageAsync(Ack payload, CancellationToken cancellatio var prop = new ReadOnlyProperty>(_connection, _name); await prop.SendMessageAsync(payload, cancellationToken); } + + public async Task InitPropertyAsync(string intialState, T defaultValue, CancellationToken cancellationToken = default) + { + Ack ack = TwinInitializer.InitFromTwin(intialState, _name, defaultValue); + Version = ack.Version; + Value = ack.Value; + await SendMessageAsync(ack, cancellationToken); + } } diff --git a/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/BrokerClientFactory.cs b/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/BrokerClientFactory.cs index 358ef6b..7927083 100644 --- a/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/BrokerClientFactory.cs +++ b/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/BrokerClientFactory.cs @@ -18,7 +18,7 @@ public static async Task CreateFromConnectionSettingsAsync(Connecti var connAck = await mqtt!.ConnectAsync(new MqttClientOptionsBuilder() .WithConnectionSettings(cs, withBirth) .WithProtocolVersion(Formatter.MqttProtocolVersion.V311) - .Build()); + .Build(), cancellationToken); ComputedSettings = cs; if (connAck.ResultCode != MqttClientConnectResultCode.Success) { @@ -35,7 +35,7 @@ public static async Task CreateFromConnectionSettingsAsync(Connecti var pubAck = await mqtt.PublishBinaryAsync( BirthConvention.BirthTopic(mqtt.Options.ClientId), birthPayload, - Protocol.MqttQualityOfServiceLevel.AtLeastOnce, true); + Protocol.MqttQualityOfServiceLevel.AtLeastOnce, true, cancellationToken); if (pubAck.ReasonCode != MqttClientPublishReasonCode.Success) { throw new ApplicationException($"Error publishing Birth {cs}"); diff --git a/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/Command.cs b/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/Command.cs index d35a148..357363d 100644 --- a/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/Command.cs +++ b/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/Command.cs @@ -8,7 +8,31 @@ public class Command : CloudToDeviceBinder, ICommand : CloudToDeviceBinder, ICommand +{ + public Command(IMqttClient client, string name) + : base(client, name) + { + SubscribeTopicPattern = "device/{clientId}/commands/{name}"; + RequestTopicPattern = "device/{clientId}/commands/{name}"; + ResponseTopicPattern = "device/{clientId}/commands/{name}/resp"; + } +} + +public class Command : CloudToDeviceBinder, ICommand +{ + public Command(IMqttClient client, string name) + : base(client, name) + { + SubscribeTopicPattern = "device/{clientId}/commands/{name}"; + RequestTopicPattern = "device/{clientId}/commands/{name}"; + ResponseTopicPattern = "device/{clientId}/commands/{name}/resp"; + } +} + diff --git a/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/CommandProtobuff.cs b/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/CommandProtobuff.cs index ae00115..43c348a 100644 --- a/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/CommandProtobuff.cs +++ b/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/CommandProtobuff.cs @@ -10,6 +10,7 @@ public class CommandProtobuff : CloudToDeviceBinder, IComman public CommandProtobuff(IMqttClient client, string name, MessageParser parser) : base(client, name, new ProtobufSerializer(parser)) { + UnwrapRequest = false; RequestTopicPattern = "device/{clientId}/cmd/{name}"; ResponseTopicPattern = "device/{clientId}/cmd/{name}/resp"; } diff --git a/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/PropertyInitializer.cs b/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/PropertyInitializer.cs deleted file mode 100644 index 643db6d..0000000 --- a/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/PropertyInitializer.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.NetworkInformation; -using System.Text; -using System.Threading.Tasks; - -namespace MQTTnet.Extensions.MultiCloud.BrokerIoTClient; - -public class PropertyInitializer -{ - public static async Task InitPropertyAsync(IWritableProperty prop, T defaultValue) - { - if (prop.Version == -1) - { - prop.Value = defaultValue; - prop.Version = 0; - await prop.SendMessageAsync(new Ack() - { - Status = 203, - Value = defaultValue, - Description = "init default value", - Version = prop.Version - }); - } - } -} diff --git a/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/WritableProperty.cs b/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/WritableProperty.cs index c5d4437..271bbcd 100644 --- a/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/WritableProperty.cs +++ b/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/WritableProperty.cs @@ -16,9 +16,11 @@ public WritableProperty(IMqttClient c, string name) _name = name; _connection = c; - RequestTopicPattern = "device/{clientId}/props/{name}/set/#"; + SubscribeTopicPattern = "device/{clientId}/props/{name}/set/#"; + RequestTopicPattern = "device/{clientId}/props/{name}/set"; ResponseTopicPattern = "device/{clientId}/props/{name}/ack"; RetainResponse = true; + CleanRetained = true; PreProcessMessage = tp => { Version = tp.Version; @@ -34,4 +36,19 @@ public async Task SendMessageAsync(Ack payload, CancellationToken cancellatio }; await prop.SendMessageAsync(payload, cancellationToken); } + + public async Task InitPropertyAsync(string intialState, T defaultValue, CancellationToken cancellationToken = default) + { + Ack ack = new() + { + Value = defaultValue, + Version = 0, + Status = 203, + Description = "init from default value" + }; + Value = ack.Value; + Version = ack.Version; + await SendMessageAsync(ack, cancellationToken); + + } } diff --git a/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/WritablePropertyProtobuff.cs b/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/WritablePropertyProtobuff.cs index a4c77b5..5f26ff5 100644 --- a/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/WritablePropertyProtobuff.cs +++ b/src/MQTTnet.Extensions.MultiCloud.BrokerIoTClient/WritablePropertyProtobuff.cs @@ -5,16 +5,44 @@ namespace MQTTnet.Extensions.MultiCloud.BrokerIoTClient { - public class WritablePropertyProtobuff : CloudToDeviceBinder, IWritableProperty + public class WritablePropertyProtobuff : CloudToDeviceBinder, IWritableProperty, IDeviceToCloud { + readonly IMqttClient _connection; + readonly string _name; public T? Value { get; set; } = default!; public int? Version { get; set; } = -1; public WritablePropertyProtobuff(IMqttClient connection, string name, MessageParser parser) : base(connection, name, new ProtobufSerializer(parser)) { + _connection = connection; + _name = name; + SubscribeTopicPattern = "device/{clientId}/props/{name}/set"; RequestTopicPattern = "device/{clientId}/props/{name}/set"; ResponseTopicPattern = "device/{clientId}/props/{name}/ack"; RetainResponse = true; + PreProcessMessage = tp => + { + Version = tp.Version; + }; } + + public async Task SendMessageAsync(TResp payload, CancellationToken cancellationToken = default) + { + var prop = new ReadOnlyProperty(_connection, _name) + { + TopicPattern = "device/{clientId}/props/{name}/ack", + WrapMessage = false + }; + await prop.SendMessageAsync(payload, cancellationToken); + } + + public async Task InitPropertyAsync(string intialState, TResp defaultValue, CancellationToken cancellationToken = default) + { + TResp payload = default!; //TODO use generic ACK for protos + await SendMessageAsync(payload, cancellationToken); + + } + + } } diff --git a/src/MQTTnet.Extensions.MultiCloud/Binders/CloudToDeviceBinder.cs b/src/MQTTnet.Extensions.MultiCloud/Binders/CloudToDeviceBinder.cs index 2a89d2e..cdc0207 100644 --- a/src/MQTTnet.Extensions.MultiCloud/Binders/CloudToDeviceBinder.cs +++ b/src/MQTTnet.Extensions.MultiCloud/Binders/CloudToDeviceBinder.cs @@ -13,8 +13,9 @@ public abstract class CloudToDeviceBinder : ICloudToDevice protected bool UnwrapRequest = false; protected bool WrapResponse = false; - protected bool RetainResponse = false; + protected bool CleanRetained = false; + public Func>? OnMessage { get; set; } @@ -33,21 +34,17 @@ public CloudToDeviceBinder(IMqttClient connection, string name, IMessageSerializ var topic = m.ApplicationMessage.Topic; if (topic.StartsWith(requestTopicPattern!.Replace("/#", string.Empty))) { - if (OnMessage != null) + if (serializer.TryReadFromBytes(m.ApplicationMessage.Payload, UnwrapRequest ? _name : string.Empty, out T req)) { var tp = TopicParser.ParseTopic(topic); PreProcessMessage?.Invoke(tp); - T req = serializer.FromBytes(m.ApplicationMessage.Payload, UnwrapRequest ? _name : string.Empty)!; - if (req != null) + TResp resp = await OnMessage?.Invoke(req)!; + + if (resp != null) { - TResp resp = await OnMessage.Invoke(req); byte[] responseBytes = serializer.ToBytes(resp, WrapResponse ? _name : string.Empty); - - string? resTopic = responseTopicPattern? - .Replace("{rid}", tp.Rid.ToString()) - .Replace("{version}", tp.Version.ToString()); - + string? resTopic = responseTopicPattern?.Replace("{rid}", tp.Rid.ToString()).Replace("{version}", tp.Version.ToString()); _ = connection.PublishAsync( new MqttApplicationMessageBuilder() .WithTopic(resTopic) @@ -55,10 +52,11 @@ public CloudToDeviceBinder(IMqttClient connection, string name, IMessageSerializ .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce) .WithRetainFlag(RetainResponse) .Build()); - } - else - { - Trace.TraceWarning($"Cannot parse incoming message name: {_name} payload: {Encoding.UTF8.GetString(m.ApplicationMessage.Payload)}"); + + if (CleanRetained && m.ApplicationMessage.Retain) // clean retain once received + { + _ = connection.PublishBinaryAsync(topic, null, MqttQualityOfServiceLevel.AtLeastOnce, true); + } } } } @@ -72,7 +70,7 @@ protected string? RequestTopicPattern set { requestTopicPattern = value?.Replace("{clientId}", _connection.Options.ClientId).Replace("{name}", _name)!; - _ = _connection.SubscribeWithReplyAsync(requestTopicPattern); + } } @@ -85,4 +83,15 @@ protected string? ResponseTopicPattern responseTopicPattern = value?.Replace("{clientId}", _connection.Options.ClientId).Replace("{name}", _name)!; } } + + private string? subscribeTopicPattern; + protected string? SubscribeTopicPattern + { + get => subscribeTopicPattern; + set + { + subscribeTopicPattern = value?.Replace("{clientId}", _connection.Options.ClientId).Replace("{name}", _name)!; + _ = _connection.SubscribeWithReplyAsync(subscribeTopicPattern); + } + } } diff --git a/src/MQTTnet.Extensions.MultiCloud/Binders/TopicParser.cs b/src/MQTTnet.Extensions.MultiCloud/Binders/TopicParser.cs index c82133e..0880436 100644 --- a/src/MQTTnet.Extensions.MultiCloud/Binders/TopicParser.cs +++ b/src/MQTTnet.Extensions.MultiCloud/Binders/TopicParser.cs @@ -2,18 +2,25 @@ namespace MQTTnet.Extensions.MultiCloud.Binders { - internal class TopicParser + public class TopicParser { - internal static TopicParameters ParseTopic(string topic) + public static TopicParameters ParseTopic(string topic) { var segments = topic.Split('/'); int twinVersion = -1; int rid = -1; - if (topic.Contains("?")) + if (topic.Contains('?')) { - var qs = HttpUtility.ParseQueryString(segments[segments.Length - 1]); - int.TryParse(qs["$rid"], out rid); - twinVersion = Convert.ToInt32(qs["$version"]); + var qs = HttpUtility.ParseQueryString(segments[^1]); + if (int.TryParse(qs["$version"], out int v)) + { + twinVersion = v; + } + + if (int.TryParse(qs["$rid"], out int r)) + { + rid = r; + } } return new TopicParameters() { Rid = rid, Version = twinVersion }; } diff --git a/src/MQTTnet.Extensions.MultiCloud/Connections/ConnectionSettings.cs b/src/MQTTnet.Extensions.MultiCloud/Connections/ConnectionSettings.cs index 8460017..b8d3024 100644 --- a/src/MQTTnet.Extensions.MultiCloud/Connections/ConnectionSettings.cs +++ b/src/MQTTnet.Extensions.MultiCloud/Connections/ConnectionSettings.cs @@ -52,7 +52,7 @@ public ConnectionSettings() CleanSession = Default_CleanSession == "true"; } - public static ConnectionSettings FromConnectionString(string cs) => new ConnectionSettings(cs); + public static ConnectionSettings FromConnectionString(string cs) => new(cs); public ConnectionSettings(string cs) => ParseConnectionString(cs); private static string GetStringValue(IDictionary dict, string propertyName, string defaultValue = "") diff --git a/src/MQTTnet.Extensions.MultiCloud/Connections/SasAuth.cs b/src/MQTTnet.Extensions.MultiCloud/Connections/SasAuth.cs index cd34684..09c064c 100644 --- a/src/MQTTnet.Extensions.MultiCloud/Connections/SasAuth.cs +++ b/src/MQTTnet.Extensions.MultiCloud/Connections/SasAuth.cs @@ -10,10 +10,8 @@ internal static string GetUserName(string hostName, string deviceId, string mode internal static string Sign(string requestString, string key) { - using (var algorithm = new System.Security.Cryptography.HMACSHA256(Convert.FromBase64String(key))) - { - return Convert.ToBase64String(algorithm.ComputeHash(Encoding.UTF8.GetBytes(requestString))); - } + using var algorithm = new System.Security.Cryptography.HMACSHA256(Convert.FromBase64String(key)); + return Convert.ToBase64String(algorithm.ComputeHash(Encoding.UTF8.GetBytes(requestString))); } internal static string CreateSasToken(string resource, string sasKey, int minutes) diff --git a/src/MQTTnet.Extensions.MultiCloud/Connections/StringToDictionaryExtension.cs b/src/MQTTnet.Extensions.MultiCloud/Connections/StringToDictionaryExtension.cs index ad60bfc..00bb2b6 100644 --- a/src/MQTTnet.Extensions.MultiCloud/Connections/StringToDictionaryExtension.cs +++ b/src/MQTTnet.Extensions.MultiCloud/Connections/StringToDictionaryExtension.cs @@ -17,9 +17,8 @@ internal static IDictionary ToDictionary(this string valuePairSt .Cast() .Select(m => new string[] { m.Result("$1"), - valuePairString.Substring( - m.Index + m.Value.Length, - (m.NextMatch().Success ? m.NextMatch().Index : valuePairString.Length) - (m.Index + m.Value.Length)) + valuePairString[ + (m.Index + m.Value.Length)..(m.NextMatch().Success ? m.NextMatch().Index : valuePairString.Length)] }); if (!parts.Any() || parts.Any(p => p.Length != 2)) diff --git a/src/MQTTnet.Extensions.MultiCloud/Connections/WithAzureIoTHubCredentials.cs b/src/MQTTnet.Extensions.MultiCloud/Connections/WithAzureIoTHubCredentials.cs index 3f88d9d..ea56cb4 100644 --- a/src/MQTTnet.Extensions.MultiCloud/Connections/WithAzureIoTHubCredentials.cs +++ b/src/MQTTnet.Extensions.MultiCloud/Connections/WithAzureIoTHubCredentials.cs @@ -5,18 +5,26 @@ namespace MQTTnet.Extensions.MultiCloud.Connections; public static partial class MqttNetExtensions { - public static MqttClientOptionsBuilder WithAzureIoTHubCredentials(this MqttClientOptionsBuilder builder, ConnectionSettings? cs) + internal static MqttClientOptionsBuilder WithAzureIoTHubCredentials(this MqttClientOptionsBuilder builder, ConnectionSettings? cs) { if (cs?.Auth == AuthType.Sas) { - cs.ClientId = cs.DeviceId; + if (string.IsNullOrEmpty(cs.ModuleId)) + { + cs.ClientId = cs.DeviceId; + } + else + { + cs.ClientId = $"{cs.DeviceId}/{cs.ModuleId}"; + } return builder.WithAzureIoTHubCredentialsSas(cs.HostName!, cs.DeviceId!, cs.ModuleId!, cs.SharedAccessKey!, cs.ModelId!, cs.SasMinutes, cs.TcpPort); } else if (cs?.Auth == AuthType.X509) { var cert = X509ClientCertificateLocator.Load(cs.X509Key!); string clientId = X509CommonNameParser.GetCNFromCertSubject(cert); - if (clientId.Contains("/")) //is a module + cs.ClientId = clientId; + if (clientId.Contains('/')) //is a module { var segmentsId = clientId.Split('/'); cs.DeviceId = segmentsId[0]; @@ -43,7 +51,6 @@ public static MqttClientOptionsBuilder WithAzureIoTHubCredentialsSas(this MqttCl builder .WithTcpServer(hostName, tcpPort) .WithTls() - .WithClientId(deviceId) .WithCredentials(username, password); } else @@ -52,7 +59,6 @@ public static MqttClientOptionsBuilder WithAzureIoTHubCredentialsSas(this MqttCl builder .WithTcpServer(hostName, tcpPort) .WithTls() - .WithClientId($"{deviceId}/{moduleId}") .WithCredentials(username, password); } return builder; @@ -64,7 +70,6 @@ public static MqttClientOptionsBuilder WithAzureIoTHubCredentialsX509(this MqttC builder .WithTcpServer(hostName, tcpPort) - .WithClientId(clientId) .WithCredentials(new MqttClientCredentials(SasAuth.GetUserName(hostName, clientId, modelId))) .WithTls(new MqttClientOptionsBuilderTlsParameters { diff --git a/src/MQTTnet.Extensions.MultiCloud/Connections/WithConnectionSettings.cs b/src/MQTTnet.Extensions.MultiCloud/Connections/WithConnectionSettings.cs index e16c0a8..fea78b2 100644 --- a/src/MQTTnet.Extensions.MultiCloud/Connections/WithConnectionSettings.cs +++ b/src/MQTTnet.Extensions.MultiCloud/Connections/WithConnectionSettings.cs @@ -6,21 +6,28 @@ public static partial class MqttNetExtensions { public static MqttClientOptionsBuilder WithConnectionSettings(this MqttClientOptionsBuilder builder, ConnectionSettings cs, bool withLWT = false) { - builder - .WithTimeout(TimeSpan.FromSeconds(30)) - .WithTcpServer(cs.HostName, cs.TcpPort) - .WithKeepAlivePeriod(TimeSpan.FromSeconds(cs.KeepAliveInSeconds)) - .WithCleanSession(cs.CleanSession) - .WithTlsSettings(cs); - - if (!string.IsNullOrEmpty(cs.Password)) + if (cs.HostName != null && cs.HostName.Contains("azure-devices.net")) { - builder.WithCredentials(cs.UserName, cs.Password); + builder.WithAzureIoTHubCredentials(cs); } - - if (cs.ClientId == "{machineName}") + else { - cs.ClientId = Environment.MachineName; + builder + .WithTimeout(TimeSpan.FromSeconds(30)) + .WithTcpServer(cs.HostName, cs.TcpPort) + .WithKeepAlivePeriod(TimeSpan.FromSeconds(cs.KeepAliveInSeconds)) + .WithCleanSession(cs.CleanSession) + .WithTlsSettings(cs); + + if (!string.IsNullOrEmpty(cs.Password)) + { + builder.WithCredentials(cs.UserName, cs.Password); + } + + if (cs.ClientId == "{machineName}") + { + cs.ClientId = Environment.MachineName; + } } builder.WithClientId(cs.ClientId); diff --git a/src/MQTTnet.Extensions.MultiCloud/Connections/WithTlsSettings.cs b/src/MQTTnet.Extensions.MultiCloud/Connections/WithTlsSettings.cs index 491182f..d9275f2 100644 --- a/src/MQTTnet.Extensions.MultiCloud/Connections/WithTlsSettings.cs +++ b/src/MQTTnet.Extensions.MultiCloud/Connections/WithTlsSettings.cs @@ -5,7 +5,7 @@ namespace MQTTnet.Extensions.MultiCloud.Connections; public static partial class MqttNetExtensions { - public static MqttClientOptionsBuilder WithTlsSettings(this MqttClientOptionsBuilder builder, ConnectionSettings cs) + internal static MqttClientOptionsBuilder WithTlsSettings(this MqttClientOptionsBuilder builder, ConnectionSettings cs) { var tls = new MqttClientOptionsBuilderTlsParameters { @@ -30,8 +30,7 @@ public static MqttClientOptionsBuilder WithTlsSettings(this MqttClientOptionsBui certs.Add(caCert); tls.CertificateValidationHandler = ea => { -#if NET6_0 - X509Chain chain = new X509Chain(); + X509Chain chain = new(); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag; @@ -42,10 +41,6 @@ public static MqttClientOptionsBuilder WithTlsSettings(this MqttClientOptionsBui var x5092 = new X509Certificate2(ea.Certificate); var res = chain.Build(x5092); return res; -#endif -#if NETSTANDARD2_1 - return ea.Certificate.Issuer == caCert.Subject; -#endif }; } tls.Certificates = certs; diff --git a/src/MQTTnet.Extensions.MultiCloud/Connections/X509ClientCertificateLocator.cs b/src/MQTTnet.Extensions.MultiCloud/Connections/X509ClientCertificateLocator.cs index 85d0f47..5005a50 100644 --- a/src/MQTTnet.Extensions.MultiCloud/Connections/X509ClientCertificateLocator.cs +++ b/src/MQTTnet.Extensions.MultiCloud/Connections/X509ClientCertificateLocator.cs @@ -3,7 +3,7 @@ namespace MQTTnet.Extensions.MultiCloud.Connections; -public class X509ClientCertificateLocator +internal class X509ClientCertificateLocator { public static X509Certificate2 Load(string certSettings) { @@ -17,20 +17,18 @@ public static X509Certificate2 Load(string certSettings) } else if (certSettings.Length == 40) //thumbprint { - using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) + using X509Store store = new(StoreName.My, StoreLocation.CurrentUser); + store.Open(OpenFlags.ReadOnly); + var certs = store.Certificates.Find(X509FindType.FindByThumbprint, certSettings, false); + if (certs != null && certs.Count > 0) { - store.Open(OpenFlags.ReadOnly); - var certs = store.Certificates.Find(X509FindType.FindByThumbprint, certSettings, false); - if (certs != null && certs.Count > 0) - { - cert = certs[0]; - } - store.Close(); + cert = certs[0]; } + store.Close(); } else if (certSettings.Contains(".pem|")) //mycert.pem|mycert.key { -#if NET6_0_OR_GREATER + var segments = certSettings.Split('|'); var pemPath = segments[0]; var keyPath = segments[1]; @@ -47,9 +45,6 @@ public static X509Certificate2 Load(string certSettings) var thisCert = X509Certificate2.CreateFromEncryptedPemFile(pemPath, keyPasswd, keyPath); cert = new X509Certificate2(thisCert.Export(X509ContentType.Pkcs12)); } -#else - throw new NotSupportedException("PEM files not supported before net6"); -#endif } else { diff --git a/src/MQTTnet.Extensions.MultiCloud/Connections/X509CommonNameParser.cs b/src/MQTTnet.Extensions.MultiCloud/Connections/X509CommonNameParser.cs index 8ffd18c..3489d83 100644 --- a/src/MQTTnet.Extensions.MultiCloud/Connections/X509CommonNameParser.cs +++ b/src/MQTTnet.Extensions.MultiCloud/Connections/X509CommonNameParser.cs @@ -3,7 +3,7 @@ namespace MQTTnet.Extensions.MultiCloud.Connections; -public static class X509CommonNameParser +internal static class X509CommonNameParser { public static string GetCNFromCertSubject(X509Certificate2 cert) { diff --git a/src/MQTTnet.Extensions.MultiCloud/ICommand.cs b/src/MQTTnet.Extensions.MultiCloud/ICommand.cs index 92232a7..971d94d 100644 --- a/src/MQTTnet.Extensions.MultiCloud/ICommand.cs +++ b/src/MQTTnet.Extensions.MultiCloud/ICommand.cs @@ -1,3 +1,7 @@ namespace MQTTnet.Extensions.MultiCloud; +public interface ICommand : ICloudToDevice { } + +public interface ICommand : ICloudToDevice { } + public interface ICommand : ICloudToDevice { } diff --git a/src/MQTTnet.Extensions.MultiCloud/IMessageSerializer.cs b/src/MQTTnet.Extensions.MultiCloud/IMessageSerializer.cs index 8910bb7..4a11404 100644 --- a/src/MQTTnet.Extensions.MultiCloud/IMessageSerializer.cs +++ b/src/MQTTnet.Extensions.MultiCloud/IMessageSerializer.cs @@ -3,5 +3,5 @@ public interface IMessageSerializer { byte[] ToBytes(T payload, string name = ""); - T? FromBytes(byte[] payload, string name = ""); + bool TryReadFromBytes(byte[] payload, string name, out T result); } diff --git a/src/MQTTnet.Extensions.MultiCloud/IWritableProperty.cs b/src/MQTTnet.Extensions.MultiCloud/IWritableProperty.cs index f7a5ba1..2e9c1b4 100644 --- a/src/MQTTnet.Extensions.MultiCloud/IWritableProperty.cs +++ b/src/MQTTnet.Extensions.MultiCloud/IWritableProperty.cs @@ -4,10 +4,12 @@ public interface IWritableProperty : ICloudToDevice { T? Value { get; set; } int? Version { get; set; } + Task InitPropertyAsync(string intialState, TResp defaultValue, CancellationToken cancellationToken = default); } public interface IWritableProperty : ICloudToDevice>, IDeviceToCloud> { T? Value { get; set; } int? Version { get; set; } + Task InitPropertyAsync(string intialState, T defaultValue, CancellationToken cancellationToken = default); } diff --git a/src/MQTTnet.Extensions.MultiCloud/MQTTnet.Extensions.MultiCloud.csproj b/src/MQTTnet.Extensions.MultiCloud/MQTTnet.Extensions.MultiCloud.csproj index bbd71e4..e87e254 100644 --- a/src/MQTTnet.Extensions.MultiCloud/MQTTnet.Extensions.MultiCloud.csproj +++ b/src/MQTTnet.Extensions.MultiCloud/MQTTnet.Extensions.MultiCloud.csproj @@ -1,17 +1,15 @@  - net6.0 + net6.0 enable enable MQTTnet Abstraction to create Mqtt clients. Provides abstraction APIs to create MQTT Devices iotpnp-128.png - - True \ @@ -20,10 +18,9 @@ - + - - + diff --git a/src/MQTTnet.Extensions.MultiCloud/Serializers/AvroSerializer.cs b/src/MQTTnet.Extensions.MultiCloud/Serializers/AvroSerializer.cs index 919c288..fd19de3 100644 --- a/src/MQTTnet.Extensions.MultiCloud/Serializers/AvroSerializer.cs +++ b/src/MQTTnet.Extensions.MultiCloud/Serializers/AvroSerializer.cs @@ -11,24 +11,25 @@ public AvroSerializer(Avro.Schema s) schema = s; } - public T? FromBytes(byte[] payload, string name = "") - { - using MemoryStream mem = new(payload); - BinaryDecoder decoder = new(mem); - SpecificDefaultReader reader = new(schema, schema); - T result = default!; - reader.Read(result, decoder); - return result; - } - public byte[] ToBytes(T payload, string name = "") { - using MemoryStream ms = new MemoryStream(); - BinaryEncoder encoder = new BinaryEncoder(ms); - SpecificDefaultWriter writer = new SpecificDefaultWriter(schema); + using MemoryStream ms = new(); + BinaryEncoder encoder = new(ms); + SpecificDefaultWriter writer = new(schema); writer.Write(payload, encoder); ms.Position = 0; byte[] bytes = ms.ToArray(); return bytes; } + + public bool TryReadFromBytes(byte[] payload, string name, out T result) + { + using MemoryStream mem = new(payload); + BinaryDecoder decoder = new(mem); + SpecificDefaultReader reader = new(schema, schema); + T val = default!; + reader.Read(val, decoder); + result = val; + return true; + } } diff --git a/src/MQTTnet.Extensions.MultiCloud/Serializers/MsgPackSerializer.cs b/src/MQTTnet.Extensions.MultiCloud/Serializers/MsgPackSerializer.cs index c195f17..f8d20ba 100644 --- a/src/MQTTnet.Extensions.MultiCloud/Serializers/MsgPackSerializer.cs +++ b/src/MQTTnet.Extensions.MultiCloud/Serializers/MsgPackSerializer.cs @@ -4,6 +4,11 @@ namespace MQTTnet.Extensions.MultiCloud.Serializers; public class MsgPackSerializer : IMessageSerializer { - public T FromBytes(byte[] payload, string name = "") => MessagePackSerializer.Deserialize(payload); public byte[] ToBytes(T payload, string name = "") => MessagePackSerializer.Serialize(payload); + + public bool TryReadFromBytes(byte[] payload, string name, out T result) + { + result = MessagePackSerializer.Deserialize(payload); + return true; + } } diff --git a/src/MQTTnet.Extensions.MultiCloud/Serializers/ProtobufSerializer.cs b/src/MQTTnet.Extensions.MultiCloud/Serializers/ProtobufSerializer.cs index 6a85ee7..8f66531 100644 --- a/src/MQTTnet.Extensions.MultiCloud/Serializers/ProtobufSerializer.cs +++ b/src/MQTTnet.Extensions.MultiCloud/Serializers/ProtobufSerializer.cs @@ -7,6 +7,34 @@ public class ProtobufSerializer : IMessageSerializer private readonly MessageParser? _parser; public ProtobufSerializer() { } public ProtobufSerializer(MessageParser parser) => _parser = parser; - public T FromBytes(byte[] payload, string name = "") => (T)_parser!.ParseFrom(payload); public byte[] ToBytes(T payload, string name = "") => (payload as IMessage).ToByteArray(); + + public bool TryReadFromBytes(byte[] payload, string name, out T result) + { + if (payload == null || payload.Length == 0) + { + result = default!; + return false; + } + bool found = false; + IMessage msg = _parser!.ParseFrom(payload); + if (string.IsNullOrEmpty(name)) + { + found = true; + result = (T)msg; + } + else + { + if (msg.ToString()!.Contains(name)) // find better way + { + result = (T)msg; + found = true; + } + else + { + result = default!; + } + } + return found; + } } diff --git a/src/MQTTnet.Extensions.MultiCloud/Serializers/UTF8JsonSerializer.cs b/src/MQTTnet.Extensions.MultiCloud/Serializers/UTF8JsonSerializer.cs index 5fb4693..992ecad 100644 --- a/src/MQTTnet.Extensions.MultiCloud/Serializers/UTF8JsonSerializer.cs +++ b/src/MQTTnet.Extensions.MultiCloud/Serializers/UTF8JsonSerializer.cs @@ -28,30 +28,47 @@ public static T FromString(string s) => JsonSerializer.Deserialize(s, })!; } - // TODO convert to TryReadFromBytes - public T? FromBytes(byte[] payload, string name = "") + public byte[] ToBytes(T payload, string name = "") { if (string.IsNullOrEmpty(name)) { - return Json.FromString(Encoding.UTF8.GetString(payload))!; + return Encoding.UTF8.GetBytes(Json.Stringify(payload!)); } - JsonDocument jdoc = JsonDocument.Parse(payload); - if (jdoc.RootElement.TryGetProperty(name, out JsonElement prop)) + else { - return prop.Deserialize()!; + var patch = new Dictionary { { name, payload } }; + return Encoding.UTF8.GetBytes(Json.Stringify(patch)); } - return default; } - public byte[] ToBytes(T payload, string name = "") + + public bool TryReadFromBytes(byte[] payload, string name, out T result) { + if (payload == null || payload.Length==0) + { + result = default!; + return false; + } + + bool found = false; if (string.IsNullOrEmpty(name)) { - return Encoding.UTF8.GetBytes(Json.Stringify(payload!)); + found = true; + result = Json.FromString(Encoding.UTF8.GetString(payload))!; } else { - var patch = new Dictionary { { name, payload } }; - return Encoding.UTF8.GetBytes(Json.Stringify(patch)); + string payloadString = Encoding.UTF8.GetString(payload); + JsonDocument payloadJson = JsonDocument.Parse(payloadString); + if (payloadJson.RootElement.TryGetProperty(name, out JsonElement propValue)) + { + found = true; + result = propValue.Deserialize()!; + } + else + { + result = default!; + } } + return found; } } diff --git a/src/MQTTnet.Extensions.MultiCloud/SubscribeExtension.cs b/src/MQTTnet.Extensions.MultiCloud/SubscribeExtension.cs index 94b8f1d..9bf3281 100644 --- a/src/MQTTnet.Extensions.MultiCloud/SubscribeExtension.cs +++ b/src/MQTTnet.Extensions.MultiCloud/SubscribeExtension.cs @@ -5,7 +5,7 @@ namespace MQTTnet.Extensions.MultiCloud; public static class SubscribeExtension { - private static readonly List subscriptions = new List(); + private static readonly List subscriptions = new(); public static async Task SubscribeWithReplyAsync(this IMqttClient client, string topic, CancellationToken cancellationToken = default) { diff --git a/src/MQTTnet.Extensions.MultiCloud/TaskTimeoutExtension.cs b/src/MQTTnet.Extensions.MultiCloud/TaskTimeoutExtension.cs index e2900f3..303f758 100644 --- a/src/MQTTnet.Extensions.MultiCloud/TaskTimeoutExtension.cs +++ b/src/MQTTnet.Extensions.MultiCloud/TaskTimeoutExtension.cs @@ -1,10 +1,17 @@ -namespace MQTTnet.Extensions.MultiCloud; +using System.Diagnostics; + +namespace MQTTnet.Extensions.MultiCloud; public static class TaskTimeoutExtension { public static async Task TimeoutAfter(this Task source, TimeSpan timeout) { - if (await Task.WhenAny(source, Task.Delay(timeout)) != source) + var actualTimeout = timeout; + if (Debugger.IsAttached) + { + actualTimeout = timeout.Add(TimeSpan.FromSeconds(300)); + } + if (await Task.WhenAny(source, Task.Delay(actualTimeout)) != source) { throw new TimeoutException(); } diff --git a/tests/MQTTnet.Extensions.MultiCloud.IntegrationTests/AwsClientFixture.cs b/tests/MQTTnet.Extensions.MultiCloud.IntegrationTests/AwsClientFixture.cs index c58cf3d..c25dd33 100644 --- a/tests/MQTTnet.Extensions.MultiCloud.IntegrationTests/AwsClientFixture.cs +++ b/tests/MQTTnet.Extensions.MultiCloud.IntegrationTests/AwsClientFixture.cs @@ -6,7 +6,7 @@ namespace MQTTnet.Extensions.MultiCloud.IntegrationTests { public class AwsClientFixture { - private readonly ConnectionSettings cs = new ConnectionSettings() + private readonly ConnectionSettings cs = new() { HostName = "a38jrw6jte2l2x-ats.iot.us-west-1.amazonaws.com", ClientId = "testdevice22", @@ -22,7 +22,7 @@ public async Task GetUpdateShadow() var awsClient = new AwsMqttClient(client); var shadow = await awsClient.GetShadowAsync(); Assert.NotNull(shadow); - var updRes = await awsClient.UpdateShadowAsync(new + await awsClient.UpdateShadowAsync(new { name = "rido3" }); diff --git a/tests/MQTTnet.Extensions.MultiCloud.IntegrationTests/IoTHubConnectionFixture.cs b/tests/MQTTnet.Extensions.MultiCloud.IntegrationTests/IoTHubConnectionFixture.cs index 32fe6c3..a0d124b 100644 --- a/tests/MQTTnet.Extensions.MultiCloud.IntegrationTests/IoTHubConnectionFixture.cs +++ b/tests/MQTTnet.Extensions.MultiCloud.IntegrationTests/IoTHubConnectionFixture.cs @@ -28,7 +28,7 @@ public async Task DeviceSas() }; var connAck = await client.ConnectAsync(new MqttClientOptionsBuilder() - .WithAzureIoTHubCredentials(cs) + .WithConnectionSettings(cs) .Build()); Assert.Equal(MqttClientConnectResultCode.Success, connAck.ResultCode); Assert.True(client.IsConnected); @@ -51,7 +51,7 @@ public async Task ModuleSas() SharedAccessKey = Convert.ToBase64String(Encoding.UTF8.GetBytes(Guid.Empty.ToString("N"))) }; var connAck = await client.ConnectAsync(new MqttClientOptionsBuilder() - .WithAzureIoTHubCredentials(cs) + .WithConnectionSettings(cs) .Build()); Assert.Equal(MqttClientConnectResultCode.Success, connAck.ResultCode); Assert.True(client.IsConnected); @@ -72,7 +72,7 @@ public async Task DeviceCert() X509Key = "ca-device.pem|ca-device.key" }; var connAck = await client.ConnectAsync(new MqttClientOptionsBuilder() - .WithAzureIoTHubCredentials(cs) + .WithConnectionSettings(cs) .Build()); Assert.Equal(MqttClientConnectResultCode.Success, connAck.ResultCode); Assert.True(client.IsConnected); @@ -93,7 +93,7 @@ public async Task ModuleCert() X509Key = "ca-module.pem|ca-module.key" }; var connAck = await client.ConnectAsync(new MqttClientOptionsBuilder() - .WithAzureIoTHubCredentials(cs) + .WithConnectionSettings(cs) .Build()); Assert.Equal(MqttClientConnectResultCode.Success, connAck.ResultCode); Assert.True(client.IsConnected); diff --git a/tests/MQTTnet.Extensions.MultiCloud.IntegrationTests/e2e/BrokerE2EFixture.cs b/tests/MQTTnet.Extensions.MultiCloud.IntegrationTests/e2e/BrokerE2EFixture.cs index 20913a9..13ffd7e 100644 --- a/tests/MQTTnet.Extensions.MultiCloud.IntegrationTests/e2e/BrokerE2EFixture.cs +++ b/tests/MQTTnet.Extensions.MultiCloud.IntegrationTests/e2e/BrokerE2EFixture.cs @@ -9,7 +9,7 @@ namespace MQTTnet.Extensions.MultiCloud.IntegrationTests.e2e { public class BrokerE2EFixture { - private readonly ConnectionSettings cs = new ConnectionSettings() + private readonly ConnectionSettings cs = new() { HostName = "localhost", ClientId = "e2e-device", @@ -18,7 +18,7 @@ public class BrokerE2EFixture UserName = "user", Password = "password" }; - private readonly ConnectionSettings scs = new ConnectionSettings() + private readonly ConnectionSettings scs = new() { HostName = "localhost", ClientId = "e2e-app", diff --git a/tests/MQTTnet.Extensions.MultiCloud.IntegrationTests/e2e/HubEndToEndFixture.cs b/tests/MQTTnet.Extensions.MultiCloud.IntegrationTests/e2e/HubEndToEndFixture.cs index 72622b4..fd7ec61 100644 --- a/tests/MQTTnet.Extensions.MultiCloud.IntegrationTests/e2e/HubEndToEndFixture.cs +++ b/tests/MQTTnet.Extensions.MultiCloud.IntegrationTests/e2e/HubEndToEndFixture.cs @@ -15,8 +15,23 @@ public class HubEndToEndFixture private const int defaultInterval = 23; private readonly RegistryManager rm = RegistryManager.CreateFromConnectionString(hubConnectionString); + + [Fact(Skip = "hangs test")] + public async Task GetTwinReturnsJson() + { + //var deviceId = "integ-test" + new Random().Next(100); + //var device = await GetOrCreateDeviceAsync(deviceId); + //var hubConnection = await HubDpsFactory.CreateFromConnectionSettingsAsync($"HostName={hubName};DeviceId={deviceId};SharedAccessKey={device.Authentication.SymmetricKey.PrimaryKey}"); + var cs = "HostName=rido-freetier.azure-devices.net;DeviceId=testdevice;SharedAccessKey=MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA="; + var hubConnection = await HubDpsFactory.CreateFromConnectionSettingsAsync(cs); + Assert.True(hubConnection.IsConnected); + var hubClient = new HubMqttClient(hubConnection); + var twin = await hubClient.GetTwinAsync(); + Assert.True(twin.Length > 0); + } + //[Fact, Trait("e2e", "hub")] - public async Task NewDeviceSendDefaults() + internal async Task NewDeviceSendDefaults() { var deviceId = "memmon-test" + new Random().Next(100); var device = await GetOrCreateDeviceAsync(deviceId); @@ -33,7 +48,7 @@ public async Task NewDeviceSendDefaults() var twin = await td.GetTwinAsync(); - await TwinInitializer.InitPropertyAsync(td.Connection, twin, td.Property_interval, "interval", defaultInterval); + //await TwinInitializer.InitPropertyAsync(td.Connection, twin, td.Property_interval, "interval", defaultInterval); await Task.Delay(500); var serviceTwin = await rm.GetTwinAsync(deviceId); var intervalTwin = serviceTwin.Properties.Reported["interval"]; @@ -48,7 +63,7 @@ public async Task NewDeviceSendDefaults() } //[Fact(Skip = "investigate timeout")] - public async Task DeviceReadsSettingsAtStartup() + internal async Task DeviceReadsSettingsAtStartup() { var deviceId = "memmon-test" + new Random().Next(100); @@ -110,7 +125,7 @@ public async Task DeviceReadsSettingsAtStartup() } //[Fact(Skip = "Threading issues"), Trait("e2e", "hub")] - public async Task UpdatesDesiredPropertyWhenOnline() + private async Task UpdatesDesiredPropertyWhenOnline() { var deviceId = "memmon-test" + new Random().Next(100); @@ -162,7 +177,7 @@ public async Task UpdatesDesiredPropertyWhenOnline() } //[Fact, Trait("e2e", "hub")] - public async Task CommandsGetCalled() + internal async Task CommandsGetCalled() { var deviceId = "memmon-test" + new Random().Next(100); var device = await GetOrCreateDeviceAsync(deviceId); diff --git a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/BrokerJsonBindings/CommandBinderFixture.cs b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/BrokerJsonBindings/CommandBinderFixture.cs new file mode 100644 index 0000000..891adc2 --- /dev/null +++ b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/BrokerJsonBindings/CommandBinderFixture.cs @@ -0,0 +1,82 @@ +using MQTTnet.Extensions.MultiCloud.BrokerIoTClient; +using MQTTnet.Extensions.MultiCloud.Serializers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace MQTTnet.Extensions.MultiCloud.UnitTests.BrokerJsonBindings +{ + public class CommandBinderFixture + { + [Fact] + public void CommandWithReqResp() + { + MockMqttClient mockMqtt = new(); + Command cmd = new(mockMqtt, "aCmdReqResp"); + bool cmdReceived = false; + cmd.OnMessage = async req => + { + cmdReceived = true; + return await Task.FromResult(req.ToString()); + }; + mockMqtt.SimulateNewBinaryMessage("device/mock/commands/aCmdReqResp",new UTF8JsonSerializer().ToBytes(2)); + Assert.True(cmdReceived); + Assert.Equal("device/mock/commands/aCmdReqResp/resp", mockMqtt.topicRecceived); + Assert.Equal("\"2\"", mockMqtt.payloadReceived); + } + + [Fact] + public void CommandWithReq() + { + MockMqttClient mockMqtt = new(); + Command cmd = new(mockMqtt, "aCmdReq"); + bool cmdReceived = false; + cmd.OnMessage = async req => + { + cmdReceived = true; + return await Task.FromResult(string.Empty); + }; + mockMqtt.SimulateNewBinaryMessage("device/mock/commands/aCmdReq", new UTF8JsonSerializer().ToBytes(2)); + Assert.True(cmdReceived); + Assert.Equal("device/mock/commands/aCmdReq/resp", mockMqtt.topicRecceived); + Assert.Equal("\"\"", mockMqtt.payloadReceived); + } + + [Fact] + public void CommandWithRes() + { + MockMqttClient mockMqtt = new(); + Command cmd = new(mockMqtt, "aCmdRes"); + bool cmdReceived = false; + cmd.OnMessage = async req => + { + cmdReceived = true; + return await Task.FromResult(1); + }; + mockMqtt.SimulateNewBinaryMessage("device/mock/commands/aCmdRes", new UTF8JsonSerializer().ToBytes("")); + Assert.True(cmdReceived); + Assert.Equal("device/mock/commands/aCmdRes/resp", mockMqtt.topicRecceived); + Assert.Equal("1", mockMqtt.payloadReceived); + } + + [Fact] + public void CommandEmpty() + { + MockMqttClient mockMqtt = new(); + Command cmd = new(mockMqtt, "aCmd"); + bool cmdReceived = false; + cmd.OnMessage = async req => + { + cmdReceived = true; + return await Task.FromResult(string.Empty); + }; + mockMqtt.SimulateNewBinaryMessage("device/mock/commands/aCmd", new UTF8JsonSerializer().ToBytes("")); + Assert.True(cmdReceived); + Assert.Equal("device/mock/commands/aCmd/resp", mockMqtt.topicRecceived); + Assert.Equal("\"\"", mockMqtt.payloadReceived); + } + } +} diff --git a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/BrokerJsonBindings/WritablePropertyFixture.cs b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/BrokerJsonBindings/WritablePropertyFixture.cs index 26aeb6c..a0efb71 100644 --- a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/BrokerJsonBindings/WritablePropertyFixture.cs +++ b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/BrokerJsonBindings/WritablePropertyFixture.cs @@ -1,9 +1,5 @@ using MQTTnet.Extensions.MultiCloud.BrokerIoTClient; using MQTTnet.Extensions.MultiCloud.Serializers; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; using Xunit; @@ -14,8 +10,8 @@ public class WritablePropertyFixture [Fact] public void ReceiveWPWithVersion() { - MockMqttClient mockMqtt = new MockMqttClient(); - WritableProperty wp = new WritableProperty(mockMqtt, "aStringProp"); + MockMqttClient mockMqtt = new(); + WritableProperty wp = new(mockMqtt, "aStringProp"); Assert.Equal(-1, wp.Version); Assert.Null(wp.Value); bool propReceived = false; @@ -24,8 +20,8 @@ public void ReceiveWPWithVersion() propReceived = true; wp.Value = message; return await Task.FromResult( - new Ack - { + new Ack + { Value = message, Version = wp.Version, Status = 200 diff --git a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/ConnectionOptionsBuilderExtensionsFixture.cs b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/ConnectionOptionsBuilderExtensionsFixture.cs index 39c6b72..f9cba0d 100644 --- a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/ConnectionOptionsBuilderExtensionsFixture.cs +++ b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/ConnectionOptionsBuilderExtensionsFixture.cs @@ -10,7 +10,7 @@ public class ConnectionOptionsBuilderExtensionsFixture [Fact] public void DoNotInferClientFromUserNameWhenNotSet() { - MqttClientOptionsBuilder builder = new MqttClientOptionsBuilder(); + MqttClientOptionsBuilder builder = new(); var cs = new ConnectionSettings { UserName = "user", Password = "password" }; builder.WithConnectionSettings(cs); Assert.Null(cs.ClientId); @@ -19,7 +19,7 @@ public void DoNotInferClientFromUserNameWhenNotSet() [Fact] public void BasicAuthRespectedClientId() { - MqttClientOptionsBuilder builder = new MqttClientOptionsBuilder(); + MqttClientOptionsBuilder builder = new(); var cs = new ConnectionSettings { UserName = "user", Password = "password", ClientId = "client" }; builder.WithConnectionSettings(cs); Assert.Equal("client", cs.ClientId); @@ -28,7 +28,7 @@ public void BasicAuthRespectedClientId() [Fact] public void ClientIDMachineNanme() { - MqttClientOptionsBuilder builder = new MqttClientOptionsBuilder(); + MqttClientOptionsBuilder builder = new (); var cs = new ConnectionSettings { UserName = "user", Password = "password", ClientId = "client" }; builder.WithConnectionSettings(cs); Assert.Equal("client", cs.ClientId); @@ -41,7 +41,7 @@ public void ClientIDMachineNanme() builder.WithConnectionSettings(cs); Assert.Equal(Environment.MachineName, cs.ClientId); - cs = new ConnectionSettings { X509Key="onething.pfx|1234", ClientId = "{machineName}" }; + cs = new ConnectionSettings { X509Key = "onething.pfx|1234", ClientId = "{machineName}" }; builder.WithConnectionSettings(cs); Assert.Equal(Environment.MachineName, cs.ClientId); @@ -54,7 +54,7 @@ public void ClientIDMachineNanme() [Fact] public void InferClientFromCertWhenNotSet() { - MqttClientOptionsBuilder builder = new MqttClientOptionsBuilder(); + MqttClientOptionsBuilder builder = new(); var cs = new ConnectionSettings { X509Key = "onething.pfx|1234" }; builder.WithConnectionSettings(cs); Assert.Equal("onething", cs.ClientId); @@ -63,7 +63,7 @@ public void InferClientFromCertWhenNotSet() [Fact] public void X509AuthRespectedClientId() { - MqttClientOptionsBuilder builder = new MqttClientOptionsBuilder(); + MqttClientOptionsBuilder builder = new(); var cs = new ConnectionSettings { X509Key = "onething.pfx|1234", ClientId = "client" }; builder.WithConnectionSettings(cs); Assert.Equal("client", cs.ClientId); diff --git a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/ConnectionSettingsFixture.cs b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/ConnectionSettingsFixture.cs index 6a3ba97..1a0d1b4 100644 --- a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/ConnectionSettingsFixture.cs +++ b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/ConnectionSettingsFixture.cs @@ -56,7 +56,7 @@ public void ParseConnectionString() public void InvalidValuesDontUseDefaults() { string cs = "HostName=.azure-devices.net;DeviceId=;SharedAccessKey=;MaxRetries=-2;SasMinutes=aa;RetryInterval=4.3"; - ConnectionSettings dcs = new ConnectionSettings(cs); + ConnectionSettings dcs = new(cs); Assert.Equal(".azure-devices.net", dcs.HostName); Assert.Equal("", dcs.DeviceId); Assert.Equal("", dcs.SharedAccessKey); @@ -103,7 +103,7 @@ public void ParseConnectionStringWithAllValues() [Fact] public void ToStringReturnConnectionString() { - ConnectionSettings dcs = new ConnectionSettings() + ConnectionSettings dcs = new() { HostName = "h", DeviceId = "d", @@ -117,7 +117,7 @@ public void ToStringReturnConnectionString() [Fact] public void ToStringReturnConnectionStringWithModule() { - ConnectionSettings dcs = new ConnectionSettings() + ConnectionSettings dcs = new() { HostName = "h", DeviceId = "d", diff --git a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/GenericPropertyAckFixture.cs b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/GenericPropertyAckFixture.cs index fc3e087..2a8bba7 100644 --- a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/GenericPropertyAckFixture.cs +++ b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/GenericPropertyAckFixture.cs @@ -8,7 +8,7 @@ public class GenericPropertyAckFixture [Fact] public void BuildAck() { - GenericPropertyAck ack = new GenericPropertyAck() + GenericPropertyAck ack = new() { Status = 200, Value = Json.Stringify(new { myProp = "myVal" }) diff --git a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/HubClient/HubWritablePropertyUTFJsonFixture.cs b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/HubClient/HubWritablePropertyUTFJsonFixture.cs index fa47a77..ba701a6 100644 --- a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/HubClient/HubWritablePropertyUTFJsonFixture.cs +++ b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/HubClient/HubWritablePropertyUTFJsonFixture.cs @@ -25,7 +25,7 @@ public void DesiredPropGetsTriggeredAndIsReportedBackWithAck() Value = p }); }; - + var desiredMsg = new Dictionary { @@ -51,8 +51,8 @@ public void DesiredPropGetsTriggeredAndIsReportedBackWithAck() [Fact] public void ReceiveWPWithVersion() { - MockMqttClient mockMqtt = new MockMqttClient(); - WritableProperty wp = new WritableProperty(mockMqtt, "aStringProp"); + MockMqttClient mockMqtt = new(); + WritableProperty wp = new(mockMqtt, "aStringProp"); Assert.Equal(-1, wp.Version); Assert.Null(wp.Value); bool propReceived = false; @@ -60,7 +60,7 @@ public void ReceiveWPWithVersion() { propReceived = true; wp.Value = message; - + return await Task.FromResult( new Ack { @@ -71,7 +71,7 @@ public void ReceiveWPWithVersion() }; mockMqtt.SimulateNewBinaryMessage("$iothub/twin/PATCH/properties/desired/?$rid=1&$version=3", - new UTF8JsonSerializer().ToBytes(new { aStringProp = "string value" } )); + new UTF8JsonSerializer().ToBytes(new { aStringProp = "string value" })); Assert.True(propReceived); Assert.Equal(3, wp.Version); Assert.Equal("string value", wp.Value); diff --git a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/HubClient/TwinWritablePropertyFixture.cs b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/HubClient/TwinWritablePropertyFixture.cs index a2681aa..a4a8198 100644 --- a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/HubClient/TwinWritablePropertyFixture.cs +++ b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/HubClient/TwinWritablePropertyFixture.cs @@ -1,7 +1,4 @@ using MQTTnet.Client; -using System.Collections.Generic; -using System.Threading.Tasks; -using Xunit; namespace MQTTnet.Extensions.MultiCloud.UnitTests.HubClient { @@ -12,13 +9,13 @@ internal class AComplexObj public class TwinWritablePropertyFixture { - private static string Stringify(object o) => System.Text.Json.JsonSerializer.Serialize(o); + //private static string Stringify(object o) => System.Text.Json.JsonSerializer.Serialize(o); - private readonly IMqttClient connection; + //private readonly IMqttClient connection; public TwinWritablePropertyFixture() { - connection = new MockMqttClient(); + // connection = new MockMqttClient(); } diff --git a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/Json.cs b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/Json.cs index b106ab6..9c1a360 100644 --- a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/Json.cs +++ b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/Json.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Text.Json; using System.Text.Json.Serialization; -using System.Text.Json; -using System.Threading.Tasks; namespace MQTTnet.Extensions.MultiCloud.UnitTests { diff --git a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/MockMqttClient.cs b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/MockMqttClient.cs index 8610523..11ec89f 100644 --- a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/MockMqttClient.cs +++ b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/MockMqttClient.cs @@ -19,7 +19,7 @@ public MockMqttClient() public bool IsConnected => throw new NotImplementedException(); - + public MqttClientOptions Options => new() { ClientId = "mock" }; public string payloadReceived; diff --git a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/PropertyAckFixture.cs b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/PropertyAckFixture.cs index 73db4fd..0abf82c 100644 --- a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/PropertyAckFixture.cs +++ b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/PropertyAckFixture.cs @@ -1,7 +1,4 @@ -using System; -using Xunit; - -namespace MQTTnet.Extensions.MultiCloud.UnitTests +namespace MQTTnet.Extensions.MultiCloud.UnitTests { internal class AComplexObj { diff --git a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/ProtoBindings/ProtoClientFixture.cs b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/ProtoBindings/ProtoClientFixture.cs index 6f5d493..534342a 100644 --- a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/ProtoBindings/ProtoClientFixture.cs +++ b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/ProtoBindings/ProtoClientFixture.cs @@ -1,10 +1,6 @@ using mqtt_grpc_device; using mqtt_grpc_device_protos; using MQTTnet.Extensions.MultiCloud.Serializers; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; using Xunit; @@ -22,13 +18,13 @@ public void ClientReceivesCommand() { commandReceived = true; return await Task.FromResult( - new mqtt_grpc_device_protos.echoResponse() - { + new mqtt_grpc_device_protos.echoResponse() + { OutEcho = m.InEcho + m.InEcho }); }; - mockMqtt.SimulateNewBinaryMessage("device/mock/cmd/echo", - new ProtobufSerializer().ToBytes(new echoRequest { InEcho = "hi"})); + mockMqtt.SimulateNewBinaryMessage("device/mock/cmd/echo", + new ProtobufSerializer().ToBytes(new echoRequest { InEcho = "hi" })); Assert.True(commandReceived); Assert.Equal("device/mock/cmd/echo/resp", mockMqtt.topicRecceived); } @@ -45,13 +41,13 @@ public void ClientReceivesProp() return await Task.FromResult( new mqtt_grpc_device_protos.ack() { - Status = 200, - Description = "prop accepted", - Value = Google.Protobuf.WellKnownTypes.Any.Pack(m) + Status = 200, + Description = "prop accepted", + Value = Google.Protobuf.WellKnownTypes.Any.Pack(m) }); }; - mockMqtt.SimulateNewBinaryMessage("device/mock/props/interval/set", - new ProtobufSerializer().ToBytes(new Properties() { Interval = 5})); + mockMqtt.SimulateNewBinaryMessage("device/mock/props/interval/set", + new ProtobufSerializer().ToBytes(new Properties() { Interval = 5 })); Assert.True(propReceived); Assert.Equal("device/mock/props/interval/ack", mockMqtt.topicRecceived); } diff --git a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/ProtobuffSerializerFixture.cs b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/ProtobuffSerializerFixture.cs new file mode 100644 index 0000000..e3c45f8 --- /dev/null +++ b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/ProtobuffSerializerFixture.cs @@ -0,0 +1,44 @@ +using Google.Protobuf; +using mqtt_grpc_device_protos; +using MQTTnet.Extensions.MultiCloud.Serializers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace MQTTnet.Extensions.MultiCloud.UnitTests +{ + public class ProtobuffSerializerFixture + { + [Fact] + public void TryDeserialize() + { + Properties props = new() + { + Interval = 3 + }; + ProtobufSerializer ser = new(Properties.Parser); + byte[] payload = props.ToByteArray(); + if (ser.TryReadFromBytes(payload, "interval", out Properties propVal)) + { + Assert.Equal(3, propVal.Interval); + } + else + { + Assert.Fail("incorrect prop found"); + } + + if (ser.TryReadFromBytes(payload, "sdkInfo", out Properties propVal2)) + { + Assert.Fail("incorrect found"); + } + else + { + Assert.Null(propVal2); + } + + } + } +} diff --git a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/UtfJsonSerializerFixture.cs b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/UtfJsonSerializerFixture.cs new file mode 100644 index 0000000..aec6d58 --- /dev/null +++ b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/UtfJsonSerializerFixture.cs @@ -0,0 +1,32 @@ +using MQTTnet.Extensions.MultiCloud.Serializers; +using System.Text; +using Xunit; + +namespace MQTTnet.Extensions.MultiCloud.UnitTests; + +public class UtfJsonSerializerFixture +{ + [Fact] + public void TryDeserializeOk() + { + UTF8JsonSerializer ser = new(); + byte[] payload = Encoding.UTF8.GetBytes(Json.Stringify(new { myBool = true })); + if (ser.TryReadFromBytes(payload, "myBool", out bool propVal)) + { + Assert.True(propVal); + } + else + { + Assert.Fail("prop not found"); + } + + if (ser.TryReadFromBytes(payload, "notFound", out bool propVal2)) + { + Assert.Fail("bad found"); + } + else + { + Assert.False(propVal2); + } + } +} diff --git a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/X509CertificateLocatorFixture.cs b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/X509CertificateLocatorFixture.cs index 97897b9..d0e43c3 100644 --- a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/X509CertificateLocatorFixture.cs +++ b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/X509CertificateLocatorFixture.cs @@ -20,7 +20,7 @@ public void ParseCertFromPFX() public void LoadCertFromStore() { var testCert = X509ClientCertificateLocator.Load("onething.pfx|1234"); - X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser); + X509Store store = new (StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadWrite); store.Add(testCert); diff --git a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/X509CommonNameParserFixture.cs b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/X509CommonNameParserFixture.cs index 988a1ba..f966990 100644 --- a/tests/MQTTnet.Extensions.MultiCloud.UnitTests/X509CommonNameParserFixture.cs +++ b/tests/MQTTnet.Extensions.MultiCloud.UnitTests/X509CommonNameParserFixture.cs @@ -1,8 +1,5 @@ using MQTTnet.Extensions.MultiCloud.Connections; -using System; -using System.Collections.Generic; using System.Security.Cryptography.X509Certificates; -using System.Text; using Xunit; namespace MQTTnet.Extensions.MultiCloud.UnitTests