Attempt to add Google Cloud Platform as a MQTT server

This commit is contained in:
Denis-Cosmin Nutiu 2019-11-14 19:23:16 +02:00
parent fb4034773c
commit 572c12d43f
5 changed files with 113 additions and 7 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
NucuCar.Sensors/Certificates/*
bin/ bin/
obj/ obj/
/packages/ /packages/

View file

@ -8,8 +8,11 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.25.0" /> <PackageReference Include="Grpc.AspNetCore" Version="2.25.0" />
<PackageReference Include="Iot.Device.Bindings" Version="1.0.0" /> <PackageReference Include="Iot.Device.Bindings" Version="1.0.0" />
<PackageReference Include="jose-jwt" Version="2.5.0" />
<PackageReference Include="libgrpc_csharp_ext.arm7" Version="1.0.8" /> <PackageReference Include="libgrpc_csharp_ext.arm7" Version="1.0.8" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0" />
<PackageReference Include="MQTTnet.Extensions.ManagedClient" Version="3.0.8" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.5.2" />
<PackageReference Include="System.Device.Gpio" Version="1.0.0" /> <PackageReference Include="System.Device.Gpio" Version="1.0.0" />
</ItemGroup> </ItemGroup>
@ -79,4 +82,8 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\NucuCar.Domain\NucuCar.Domain.csproj" /> <ProjectReference Include="..\NucuCar.Domain\NucuCar.Domain.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Certificates" />
</ItemGroup>
</Project> </Project>

View file

@ -1,4 +1,3 @@
using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
@ -9,6 +8,11 @@ namespace NucuCar.Sensors.Telemetry
{ {
public class BackgroundWorker : BackgroundService public class BackgroundWorker : BackgroundService
{ {
private readonly string _projectId;
private readonly string _region;
private readonly string _registryId;
private readonly string _deviceId;
private readonly string _rs256KeyFile;
private readonly bool _serviceEnabled; private readonly bool _serviceEnabled;
private readonly int _interval; private readonly int _interval;
private readonly ILogger _logger; private readonly ILogger _logger;
@ -19,6 +23,11 @@ namespace NucuCar.Sensors.Telemetry
var configSection = configuration.GetSection("Telemetry"); var configSection = configuration.GetSection("Telemetry");
_serviceEnabled = configSection.GetValue<bool>("Enabled"); _serviceEnabled = configSection.GetValue<bool>("Enabled");
_interval = configSection.GetValue<int>("Interval"); _interval = configSection.GetValue<int>("Interval");
_projectId = configSection.GetValue<string>("ProjectId");
_region = configSection.GetValue<string>("Region");
_registryId = configSection.GetValue<string>("RegistryId");
_deviceId = configSection.GetValue<string>("DeviceId");
_rs256KeyFile = configSection.GetValue<string>("RS256File");
} }
protected override async Task ExecuteAsync(CancellationToken stoppingToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
@ -27,13 +36,23 @@ namespace NucuCar.Sensors.Telemetry
{ {
return; return;
} }
await Task.Delay(_interval, stoppingToken); await Task.Delay(_interval, stoppingToken);
using var telemetryService = TelemetryService.Instance; using var telemetryService = TelemetryService.Instance;
telemetryService.SetLogger(_logger);
telemetryService.ProjectId = _projectId;
telemetryService.DeviceId = _deviceId;
telemetryService.RegistryId = _registryId;
telemetryService.Region = _region;
telemetryService.Rs256File = _rs256KeyFile;
await telemetryService.StartAsync();
while (!stoppingToken.IsCancellationRequested) while (!stoppingToken.IsCancellationRequested)
{ {
_logger.LogInformation("Publishing telemetry data!"); _logger.LogInformation("Publishing telemetry data!");
await telemetryService.PublishData(); await telemetryService.PublishDataAsync(stoppingToken);
await Task.Delay(_interval, stoppingToken); await Task.Delay(_interval, stoppingToken);
} }
} }

View file

@ -1,43 +1,112 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using MQTTnet;
using MQTTnet.Client.Options;
using MQTTnet.Extensions.ManagedClient;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
namespace NucuCar.Sensors.Telemetry namespace NucuCar.Sensors.Telemetry
{ {
public class TelemetryService : IDisposable public class TelemetryService : IDisposable
{ {
private readonly List<ITelemetrySensor> _registeredSensors; private readonly List<ITelemetrySensor> _registeredSensors;
private readonly IManagedMqttClient _mqttClient;
private ILogger _logger;
/* Singleton Instance */ /* Singleton Instance */
public static TelemetryService Instance { get; } = new TelemetryService(); public static TelemetryService Instance { get; } = new TelemetryService();
public string ProjectId { get; set; }
public string Region { get; set; }
public string RegistryId { get; set; }
public string DeviceId { get; set; }
public string Rs256File { get; set; }
static TelemetryService() static TelemetryService()
{ {
} }
private string GetMqttPassword()
{
string jwt;
AsymmetricCipherKeyPair keyPair;
using (var sr = new StreamReader(Rs256File))
{
var pr = new PemReader(sr);
keyPair = (AsymmetricCipherKeyPair) pr.ReadObject();
}
var rsaParams = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)keyPair.Private);
using (var rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(rsaParams);
jwt = Jose.JWT.Encode(new Dictionary<string, object>()
{
["iat"] = DateTime.UtcNow,
["exp"] = DateTime.UtcNow.AddMinutes(60),
["aud"] = ProjectId
}, rsa, Jose.JwsAlgorithm.RS256);
}
return jwt;
}
private TelemetryService() private TelemetryService()
{ {
_registeredSensors = new List<ITelemetrySensor>(5); _registeredSensors = new List<ITelemetrySensor>(5);
_mqttClient = new MqttFactory().CreateManagedMqttClient();
} }
public void Dispose() public void Dispose()
{ {
} }
public async Task PublishData() public async Task StartAsync()
{
var options = new ManagedMqttClientOptionsBuilder()
.WithAutoReconnectDelay(TimeSpan.FromSeconds(5))
.WithClientOptions(new MqttClientOptionsBuilder()
.WithClientId($"projects/{ProjectId}/locations/{Region}/registries/{RegistryId}/devices/{DeviceId}")
.WithCredentials("unused", GetMqttPassword())
.WithTcpServer("mqtt.googleapis.com")
.WithTls().Build())
.Build();
_logger.LogInformation("Starting the MQTT client.");
await _mqttClient.StartAsync(options);
}
public async Task PublishDataAsync(CancellationToken cancellationToken)
{ {
foreach (var sensor in _registeredSensors) foreach (var sensor in _registeredSensors)
{ {
var data = sensor.GetTelemetryData(); var data = sensor.GetTelemetryData();
await UploadData(data); await UploadData(data, cancellationToken);
} }
} }
private async Task UploadData(Dictionary<string, double> data) private async Task UploadData(Dictionary<string, double> data, CancellationToken cancellationToken)
{ {
if (cancellationToken.IsCancellationRequested)
{
_logger.LogInformation("Stopping the MQTT client, cancellation requested.");
await _mqttClient.StopAsync();
}
foreach (var entry in data) foreach (var entry in data)
{ {
// TODO: Publish data to IoTCore await _mqttClient.PublishAsync(entry.Key, entry.Value.ToString(CultureInfo.InvariantCulture));
} }
} }
@ -50,5 +119,10 @@ namespace NucuCar.Sensors.Telemetry
{ {
_registeredSensors.Remove(sensor); _registeredSensors.Remove(sensor);
} }
public void SetLogger(ILogger logger)
{
_logger = logger;
}
} }
} }

View file

@ -1,7 +1,12 @@
{ {
"Telemetry": { "Telemetry": {
"Enabled": true, "Enabled": true,
"Interval": 5000 "Interval": 5000,
"ProjectId": "playground-239221",
"DeviceId": "3087236321317137",
"RegistryId": "Development",
"Region": "europe-west1",
"RS256File": "Certificates\\key.key"
}, },
"EnvironmentSensor": { "EnvironmentSensor": {
"Enabled": true, "Enabled": true,