Skip to content

Commit

Permalink
Validate 0.4 (#64)
Browse files Browse the repository at this point in the history
* review packable

* clean up

* clean pnp comments

* fix unit tests

* fix cmd response binder

* add proto unit tests

* review generic ack
  • Loading branch information
ridomin authored Sep 29, 2022
1 parent 946845e commit 885e318
Show file tree
Hide file tree
Showing 40 changed files with 290 additions and 135 deletions.
4 changes: 4 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[*.cs]

# CS0067: The event 'MockMqttClient.ConnectedAsync' is never used
dotnet_diagnostic.CS0067.severity = silent
3 changes: 2 additions & 1 deletion MQTTnet.Extensions.MultiCloud.sln
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_actions", "_actions", "{68
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F618E3CF-0552-44BD-A92A-A926AF319D00}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.runsettings = .runsettings
.runsettings.template = .runsettings.template
src\Directory.Build.props = src\Directory.Build.props
Expand All @@ -58,7 +59,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "mqtt-device", "samples\mqtt
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "mqtt-grpc-device", "samples\mqtt-grpc-device\mqtt-grpc-device.csproj", "{AF019503-8813-4967-B858-0DDAE43C2073}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "payload-size", "samples\payload-size\payload-size.csproj", "{DF590535-2FDC-4A0A-9EE4-7C9BF818C7B4}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "payload-size", "samples\payload-size\payload-size.csproj", "{DF590535-2FDC-4A0A-9EE4-7C9BF818C7B4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "memmon-protobuff", "samples\memmon-protobuff\memmon-protobuff.csproj", "{20B75646-CBD2-4E72-8C56-22887A519FA3}"
EndProject
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ This repo focuses on the first part: how to implement things/devices that can wo
- Commands. To invoke specific actions in the device from the solution, acka _c2d messages_
- Properties. To manage the device state, reported by the _device_ and optionally being managed from the _solution_. eg How often the telemetry must be sent. _d2c+c2d messages_

3. Enable solutions to reflect those _interaction patterns_ to create UI experiences, IoT Central, IoTExplorer or [Pnp-Mqtt](https://iotmodels.github.io/iotux-mqtt/) are examples of PnP enabled solutions.
3. Enable solutions to reflect those _interaction patterns_ to create UI experiences, IoT Central, IoTExplorer or [iotux-mqtt](https://iotmodels.github.io/iotux-mqtt/) are examples of PnP enabled solutions.

Read the [IoT Plug and Play convention](https://docs.microsoft.com/azure/iot-develop/overview-iot-plug-and-play) for more details.

Expand Down Expand Up @@ -128,7 +128,7 @@ Read and Update the Device Twin:

```cs
var twin = await client.GetTwinAsync(stoppingToken);
var version = await client.ReportPropertyAsync(new { started = DateTime.Now });
var version = await client.UpdateTwinAsync(new { started = DateTime.Now });
```

Properties Updates (aka Writable Properties) handling:
Expand Down Expand Up @@ -218,7 +218,7 @@ public class memmon : AwsMqttClient, Imemmon
For any MQTT compatible broker

```cs
public class memmon : PnPMqttClient, Imemmon
public class memmon : Imemmon
```

### X509 Support
Expand Down
2 changes: 1 addition & 1 deletion docs/ConnectionSettings.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Connection settings can be established using the API or parsing a connection str
- `SharedAccessKey` Device Shared Access Key
- `SasMinutes` SasToken expire time in minutes, default to `60`.
- `X509Key` __pathtopfx>|<pfxpassword__ see details in [X509Certificates](X509Certificates.md)
- `ModelId` DTDL Model ID in DTMI format to create PnP Devices
- `ModelId` DTDL Model ID in DTMI/Proto format to indicate the model the device implements
- `ModuleId` IoTHub Device Module Identity
- `UserName` Username to be used to authenticate with MQTT Brokers
- `Password` Username to be used to authenticate with MQTT Brokers
Expand Down
1 change: 0 additions & 1 deletion samples/iothub-sample/Device.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
client.OnPropertyUpdateReceived = m =>
{
Console.WriteLine(m.ToString());

return new GenericPropertyAck
{
Value = m.ToJsonString(),
Expand Down
2 changes: 1 addition & 1 deletion samples/mqtt-grpc-device/mqtt-grpc-device.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<RootNamespace>mqtt_grpc_device</RootNamespace>
<IsPackable>false</IsPackable>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
Expand Down
23 changes: 11 additions & 12 deletions samples/payload-size/AvroDeviceClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@
using MQTTnet.Extensions.MultiCloud;
using MQTTnet.Extensions.MultiCloud.BrokerIoTClient;

namespace payload_size
namespace payload_size;

internal class AvroDeviceClient
{
internal class AvroDeviceClient
{
internal ITelemetry<avros.Telemetries> Telemetry { get; set; }
internal IReadOnlyProperty<avros.Properties> Props { get; set; }
internal IReadOnlyProperty<avros.Properties> Prop_SdkInfo { get; set; }
internal ITelemetry<avros.Telemetries> Telemetry { get; set; }
internal IReadOnlyProperty<avros.Properties> Props { get; set; }
internal IReadOnlyProperty<avros.Properties> Prop_SdkInfo { get; set; }

public AvroDeviceClient(IMqttClient mqtt)
{
Telemetry = new TelemetryAvro<avros.Telemetries>(mqtt, new avros.Telemetries().Schema);
Props = new ReadOnlyPropertyAvro<avros.Properties>(mqtt, new avros.Properties().Schema);
Prop_SdkInfo = new ReadOnlyPropertyAvro<avros.Properties>(mqtt, new avros.Properties().Schema) { NameInTopic = true };
}
public AvroDeviceClient(IMqttClient mqtt)
{
Telemetry = new TelemetryAvro<avros.Telemetries>(mqtt, new avros.Telemetries().Schema);
Props = new ReadOnlyPropertyAvro<avros.Properties>(mqtt, new avros.Properties().Schema);
Prop_SdkInfo = new ReadOnlyPropertyAvro<avros.Properties>(mqtt, new avros.Properties().Schema) { NameInTopic = true };
}
}
22 changes: 10 additions & 12 deletions samples/payload-size/ProtoDeviceClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@
using MQTTnet.Extensions.MultiCloud;
using MQTTnet.Extensions.MultiCloud.BrokerIoTClient;

namespace payload_size
namespace payload_size;
internal class ProtoDeviceClient
{
internal class ProtoDeviceClient
{
internal ITelemetry<proto_model.Telemetries> Telemetry { get; set; }
internal IReadOnlyProperty<proto_model.Properties> Props { get; set; }
internal IReadOnlyProperty<proto_model.Properties> Prop_SdkInfo { get; set; }
internal ITelemetry<proto_model.Telemetries> Telemetry { get; set; }
internal IReadOnlyProperty<proto_model.Properties> Props { get; set; }
internal IReadOnlyProperty<proto_model.Properties> Prop_SdkInfo { get; set; }

public ProtoDeviceClient(IMqttClient mqtt)
{
Telemetry = new TelemetryProtobuff<proto_model.Telemetries>(mqtt);
Props = new ReadOnlyPropertyProtobuff<proto_model.Properties>(mqtt);
Prop_SdkInfo = new ReadOnlyPropertyProtobuff<proto_model.Properties>(mqtt) { NameInTopic = true };
}
public ProtoDeviceClient(IMqttClient mqtt)
{
Telemetry = new TelemetryProtobuff<proto_model.Telemetries>(mqtt);
Props = new ReadOnlyPropertyProtobuff<proto_model.Properties>(mqtt);
Prop_SdkInfo = new ReadOnlyPropertyProtobuff<proto_model.Properties>(mqtt) { NameInTopic = true };
}
}
2 changes: 1 addition & 1 deletion samples/payload-size/payload-size.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<RootNamespace>payload_size</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
Expand Down
14 changes: 7 additions & 7 deletions src/MQTTnet.Extensions.MultiCloud.AwsIoTClient/AwsMqttClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ public class AwsMqttClient
{
public IMqttClient Connection { get; private set; }
private readonly ShadowRequestResponseBinder getShadowBinder;


public AwsMqttClient(IMqttClient c, string modelId = "") //: base(c)
{
Connection = c;
var birthMsg =
var birthMsg =
new UTF8JsonSerializer().ToBytes(
new BirthConvention.BirthMessage(BirthConvention.ConnectionStatus.online)
{
ModelId = modelId
});
{
ModelId = modelId
});
_ = Connection.PublishBinaryAsync(
BirthConvention.BirthTopic(Connection.Options.ClientId),
birthMsg,
Expand All @@ -30,9 +30,9 @@ public AwsMqttClient(IMqttClient c, string modelId = "") //: base(c)
getShadowBinder = new ShadowRequestResponseBinder(c);
}

public Task<string> GetShadowAsync(CancellationToken cancellationToken = default) =>
public Task<string> GetShadowAsync(CancellationToken cancellationToken = default) =>
getShadowBinder.GetShadowAsync(cancellationToken);
public Task<string> UpdateShadowAsync(object payload, CancellationToken cancellationToken = default) =>
public Task<string> UpdateShadowAsync(object payload, CancellationToken cancellationToken = default) =>
getShadowBinder.UpdateShadowAsync(payload, cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<Title>AWS IoT Core client base don MQTTnet</Title>
<Description>Provides base APIs to create MQTT PnP clients for AWS Iot Core</Description>
<PackageTags>mqtt; aws iot core; pnp</PackageTags>
<Description>Provides base APIs to create MQTT clients for AWS IoT Core</Description>
<PackageTags>mqtt; aws iot core</PackageTags>
<PackageIcon>iotpnp-128.png</PackageIcon>
</PropertyGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
using MQTTnet.Client;
using MQTTnet.Extensions.MultiCloud.Binders;
using MQTTnet.Extensions.MultiCloud.Serializers;
using System;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks;

namespace MQTTnet.Extensions.MultiCloud.AwsIoTClient.TopicBindings
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Threading.Tasks;
using System.Web;

namespace MQTTnet.Extensions.MultiCloud.AwsIoTClient.TopicBindings;
Expand All @@ -17,8 +17,8 @@ public class ShadowRequestResponseBinder
private readonly ConcurrentDictionary<int, TaskCompletionSource<string>> pendingGetshadowRequests = new();
public Func<string, Task<string>>? OnMessage { get; set; }

readonly IMqttClient connection;
readonly string topicBase = String.Empty;
private readonly IMqttClient connection;
private readonly string topicBase = String.Empty;
public ShadowRequestResponseBinder(IMqttClient connection)
{
this.connection = connection;
Expand All @@ -28,7 +28,7 @@ public ShadowRequestResponseBinder(IMqttClient connection)
connection.ApplicationMessageReceivedAsync += async m =>
{
await Task.Yield();

var topic = m.ApplicationMessage.Topic;
if (topic.StartsWith(topicBase + "/+/accepted"))
{
Expand Down Expand Up @@ -111,15 +111,15 @@ public async Task<string> UpdateShadowAsync(object payload, CancellationToken ca


[DebuggerStepThrough()]
static class RidCounter
private static class RidCounter
{
private static int counter = 0;
internal static int Current => counter;
internal static int NextValue() => Interlocked.Increment(ref counter);
internal static void Reset() => counter = 0;
}

class TopicParser
private class TopicParser
{
internal static (int rid, int shadowVersion) ParseTopic(string topic)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ public interface IHubMqttClient
Task<string> GetTwinAsync(CancellationToken cancellationToken = default);
Task<int> UpdateTwinAsync(object payload, CancellationToken cancellationToken = default);
Task<MqttClientPublishResult> SendTelemetryAsync(object payload, CancellationToken t = default);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
<Description>Implements basic Azure IoT Hub primitives, including:
SaS and X509 Auth, Telemetry, Properties and Commands.

Provides an API surface to implement IoT PnP Devices.</Description>
<PackageTags>mqtt; azure-iot; pnp; dtdl</PackageTags>
Provides an API surface to implement IoT Devices using MQTT.</Description>
<PackageTags>mqtt; azure-iot; dtdl</PackageTags>
<PackageIcon>iotpnp-128.png</PackageIcon>
</PropertyGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
using MQTTnet.Client;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading.Tasks;

namespace MQTTnet.Extensions.MultiCloud.AzureIoTClient
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,20 @@ public async Task<string> GetTwinAsync(CancellationToken cancellationToken = def
public async Task<int> UpdateTwinAsync(object payload, CancellationToken cancellationToken = default)
{
await connection.SubscribeWithReplyAsync("$iothub/twin/res/#");
var rid = RidCounter.NextValue();

byte[] patchBytes;
if (payload is string)
{
patchBytes = Encoding.UTF8.GetBytes((string)payload);
}
else
{
patchBytes = new UTF8JsonSerializer().ToBytes(payload);
}

var rid = RidCounter.NextValue();
var puback = await connection.PublishBinaryAsync(
$"$iothub/twin/PATCH/properties/reported/?$rid={rid}",
new UTF8JsonSerializer().ToBytes(payload),
patchBytes,
MqttQualityOfServiceLevel.AtMostOnce,
false,
cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public GenericDesiredUpdatePropertyBinder(IMqttClient c, TwinRequestResponseBind
var ack = OnProperty_Updated(desired);
if (ack != null)
{
//_ = updTwinBinder.ReportPropertyAsync(ack.BuildAck());
_ = updTwinBinder.UpdateTwinAsync(ack.BuildAck());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public WritableProperty(IMqttClient c, string name)
: base(c, name)
{
TopicTemplate = "$iothub/twin/PATCH/properties/desired/#";
ResponseTopic = "$iothub/twin/PATCH/properties/reported/?$rid={rid}&version={version}";
ResponseTopic = "$iothub/twin/PATCH/properties/reported/?$rid={rid}&$version={version}";
UnwrapRequest = true;
WrapResponse = true;
PreProcessMessage = tp =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
using MQTTnet.Client;
using MQTTnet.Extensions.MultiCloud.Connections;
using System;
using System.Text.Json;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Extensions.MultiCloud.Serializers;

namespace MQTTnet.Extensions.MultiCloud.BrokerIoTClient
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using MQTTnet.Client;
using MQTTnet.Extensions.MultiCloud.Binders;
using MQTTnet.Extensions.MultiCloud.Serializers;

namespace MQTTnet.Extensions.MultiCloud.BrokerIoTClient;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Title>MQTT PnP Client based on MQTTnet</Title>
<Description>Base APIs to create PnP Clients to work with any complaiant MQTT Broker</Description>
<Title>MQTT Client based on MQTTnet</Title>
<Description>Base APIs to create MQTT Clients to work with any complaiant MQTT Broker</Description>
<PackageIcon>iotpnp-128.png</PackageIcon>
</PropertyGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class ReadOnlyProperty<T> : DeviceToCloudBinder<T>, IReadOnlyProperty<T>
{

public ReadOnlyProperty(IMqttClient mqttClient) : this(mqttClient, string.Empty) { }
public ReadOnlyProperty(IMqttClient mqttClient, string name)
public ReadOnlyProperty(IMqttClient mqttClient, string name)
: base(mqttClient, name)
{
TopicPattern = "device/{clientId}/props/{name}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ namespace MQTTnet.Extensions.MultiCloud.BrokerIoTClient;

public class ReadOnlyPropertyAvro<T> : DeviceToCloudBinder<T>, IReadOnlyProperty<T>
{
public ReadOnlyPropertyAvro(IMqttClient mqttClient, Schema schema)
public ReadOnlyPropertyAvro(IMqttClient mqttClient, Schema schema)
: this(mqttClient, string.Empty, schema) { }

public ReadOnlyPropertyAvro(IMqttClient mqttClient, string name, Schema schema)
public ReadOnlyPropertyAvro(IMqttClient mqttClient, string name, Schema schema)
: base(mqttClient, name, new AvroSerializer(schema))
{
TopicPattern = "device/{clientId}/props";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class ReadOnlyPropertyProtobuff<T> : DeviceToCloudBinder<T>, IReadOnlyPro
{
public ReadOnlyPropertyProtobuff(IMqttClient mqttClient) : this(mqttClient, string.Empty) { }

public ReadOnlyPropertyProtobuff(IMqttClient mqttClient, string name)
public ReadOnlyPropertyProtobuff(IMqttClient mqttClient, string name)
: base(mqttClient, name, new ProtobufSerializer())
{
TopicPattern = "device/{clientId}/props";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ namespace MQTTnet.Extensions.MultiCloud.BrokerIoTClient;

public class TelemetryAvro<T> : DeviceToCloudBinder<T>, ITelemetry<T>
{
public TelemetryAvro(IMqttClient mqttClient, Schema schema) :
this(mqttClient, string.Empty, schema) { }
public TelemetryAvro(IMqttClient mqttClient, Schema schema) :
this(mqttClient, string.Empty, schema)
{ }

public TelemetryAvro(IMqttClient mqttClient, string name, Schema schema)
: base(mqttClient, name, new AvroSerializer(schema))
Expand Down
Loading

0 comments on commit 885e318

Please sign in to comment.