2019-11-14 14:45:32 +00:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
2019-11-14 17:23:16 +00:00
|
|
|
using System.Globalization;
|
|
|
|
using System.IO;
|
|
|
|
using System.Security.Cryptography;
|
|
|
|
using System.Threading;
|
2019-11-14 14:45:32 +00:00
|
|
|
using System.Threading.Tasks;
|
2019-11-14 17:23:16 +00:00
|
|
|
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;
|
2019-11-14 14:45:32 +00:00
|
|
|
|
|
|
|
namespace NucuCar.Sensors.Telemetry
|
|
|
|
{
|
|
|
|
public class TelemetryService : IDisposable
|
|
|
|
{
|
|
|
|
private readonly List<ITelemetrySensor> _registeredSensors;
|
2019-11-14 17:23:16 +00:00
|
|
|
private readonly IManagedMqttClient _mqttClient;
|
|
|
|
private ILogger _logger;
|
2019-11-14 14:45:32 +00:00
|
|
|
|
|
|
|
/* Singleton Instance */
|
|
|
|
public static TelemetryService Instance { get; } = new TelemetryService();
|
2019-11-14 17:23:16 +00:00
|
|
|
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; }
|
2019-11-14 14:45:32 +00:00
|
|
|
|
|
|
|
static TelemetryService()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-11-14 17:23:16 +00:00
|
|
|
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,
|
2019-11-15 15:53:20 +00:00
|
|
|
["exp"] = DateTime.UtcNow.AddDays(60),
|
2019-11-14 17:23:16 +00:00
|
|
|
["aud"] = ProjectId
|
|
|
|
}, rsa, Jose.JwsAlgorithm.RS256);
|
|
|
|
}
|
|
|
|
|
|
|
|
return jwt;
|
|
|
|
}
|
|
|
|
|
2019-11-14 14:45:32 +00:00
|
|
|
private TelemetryService()
|
|
|
|
{
|
|
|
|
_registeredSensors = new List<ITelemetrySensor>(5);
|
2019-11-14 17:23:16 +00:00
|
|
|
|
|
|
|
_mqttClient = new MqttFactory().CreateManagedMqttClient();
|
2019-11-14 14:45:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-11-14 17:23:16 +00:00
|
|
|
public async Task StartAsync()
|
|
|
|
{
|
|
|
|
_logger.LogInformation("Starting the MQTT client.");
|
2019-11-15 15:53:20 +00:00
|
|
|
ManagedMqttClientOptions options;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
catch (IOException e)
|
|
|
|
{
|
|
|
|
_logger.LogCritical(e.Message);
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
2019-11-14 17:23:16 +00:00
|
|
|
await _mqttClient.StartAsync(options);
|
2019-11-15 15:53:20 +00:00
|
|
|
_logger.LogInformation("Started the MQTT client!");
|
2019-11-14 17:23:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public async Task PublishDataAsync(CancellationToken cancellationToken)
|
2019-11-14 14:45:32 +00:00
|
|
|
{
|
|
|
|
foreach (var sensor in _registeredSensors)
|
|
|
|
{
|
|
|
|
var data = sensor.GetTelemetryData();
|
2019-11-15 15:53:20 +00:00
|
|
|
if (data == null)
|
|
|
|
{
|
|
|
|
_logger.LogWarning($"Warning! Data for {sensor.GetIdentifier()} is null!");
|
|
|
|
continue;
|
|
|
|
}
|
2019-11-14 17:23:16 +00:00
|
|
|
await UploadData(data, cancellationToken);
|
2019-11-14 14:45:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-14 17:23:16 +00:00
|
|
|
private async Task UploadData(Dictionary<string, double> data, CancellationToken cancellationToken)
|
2019-11-14 14:45:32 +00:00
|
|
|
{
|
2019-11-14 17:23:16 +00:00
|
|
|
if (cancellationToken.IsCancellationRequested)
|
|
|
|
{
|
|
|
|
_logger.LogInformation("Stopping the MQTT client, cancellation requested.");
|
|
|
|
await _mqttClient.StopAsync();
|
|
|
|
}
|
|
|
|
|
2019-11-14 14:45:32 +00:00
|
|
|
foreach (var entry in data)
|
|
|
|
{
|
2019-11-14 17:23:16 +00:00
|
|
|
await _mqttClient.PublishAsync(entry.Key, entry.Value.ToString(CultureInfo.InvariantCulture));
|
2019-11-14 14:45:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void RegisterSensor(ITelemetrySensor sensor)
|
|
|
|
{
|
|
|
|
_registeredSensors.Add(sensor);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void UnregisterSensor(ITelemetrySensor sensor)
|
|
|
|
{
|
|
|
|
_registeredSensors.Remove(sensor);
|
|
|
|
}
|
2019-11-14 17:23:16 +00:00
|
|
|
|
|
|
|
public void SetLogger(ILogger logger)
|
|
|
|
{
|
|
|
|
_logger = logger;
|
|
|
|
}
|
2019-11-14 14:45:32 +00:00
|
|
|
}
|
|
|
|
}
|