diff --git a/NucuCar.Domain/Services/ConnectionStringParser.cs b/NucuCar.Domain/Services/ConnectionStringParser.cs new file mode 100644 index 0000000..4615317 --- /dev/null +++ b/NucuCar.Domain/Services/ConnectionStringParser.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using DotNetty.Common; + +namespace NucuCar.Domain.Services +{ + /// + /// ConnectionStringParser is an utility service to parse and validate connection strings. + /// + public static class ConnectionStringParser + { + /// + /// Parse parses and validates a provided connection string. + /// + /// The connection string to parse + /// A dictionary object containing the values of the connection string. + /// + public static Dictionary Parse(string connectionString) + { + // TODO: Write tests for this. + var items = connectionString.Split(";"); + var parsedConnectionString = new Dictionary(); + foreach (var item in items) + { + var keyValue = item.Split("="); + if (keyValue.Length != 2) + { + throw new ArgumentException( + $"Invalid argument for connection string, expected KEY=VALUE got {item}"); + } + + parsedConnectionString[keyValue[0]] = parsedConnectionString[keyValue[1]]; + } + + return parsedConnectionString; + } + } +} \ No newline at end of file diff --git a/NucuCar.Domain/Telemetry/TelemetryPublisher.cs b/NucuCar.Domain/Telemetry/TelemetryPublisher.cs index 79fdaa9..b333e8f 100644 --- a/NucuCar.Domain/Telemetry/TelemetryPublisher.cs +++ b/NucuCar.Domain/Telemetry/TelemetryPublisher.cs @@ -11,13 +11,6 @@ namespace NucuCar.Domain.Telemetry /// public abstract class TelemetryPublisher : IDisposable { - /// - /// Parameter less constructor, mainly used for testing. - /// - public TelemetryPublisher() - { - } - /// /// Raw connection string that is used to connect to the cloud service. Should be parsed if required. /// @@ -40,15 +33,10 @@ namespace NucuCar.Domain.Telemetry protected readonly ILogger Logger; /// - /// Constructor for . + /// Parameter less constructor, mainly used for testing. /// - /// TelemetryPublisher options, see: - protected TelemetryPublisher(TelemetryPublisherBuilderOptions opts) + public TelemetryPublisher() { - ConnectionString = opts.ConnectionString; - TelemetrySource = opts.TelemetrySource; - Logger = opts.Logger; - RegisteredTelemeters = new List(5); } /// @@ -88,5 +76,47 @@ namespace NucuCar.Domain.Telemetry RegisteredTelemeters.Remove(t); return true; } + + /// + /// Constructor for . + /// + /// TelemetryPublisher options, see: + protected TelemetryPublisher(TelemetryPublisherBuilderOptions opts) + { + ConnectionString = opts.ConnectionString; + TelemetrySource = opts.TelemetrySource; + Logger = opts.Logger; + RegisteredTelemeters = new List(5); + } + + /// + /// Iterates through the registered telemeters and returns the telemetry data as dictionary. + /// It also adds metadata information such as: source and timestamp. + /// + /// A dictionary containing all telemetry data. + protected Dictionary GetTelemetry() + { + var data = new List>(); + foreach (var telemeter in RegisteredTelemeters) + { + var telemetryData = telemeter.GetTelemetryData(); + if (telemetryData == null) + { + Logger?.LogWarning($"Warning! Data for {telemeter.GetIdentifier()} is null!"); + continue; + } + + telemetryData["_id"] = telemeter.GetIdentifier(); + data.Add(telemetryData); + } + + var metadata = new Dictionary + { + ["source"] = TelemetrySource ?? nameof(TelemetryPublisher), + ["timestamp"] = DateTime.Now, + ["data"] = data.ToArray() + }; + return metadata; + } } } \ No newline at end of file diff --git a/NucuCar.Domain/Telemetry/TelemetryPublisherAzure.cs b/NucuCar.Domain/Telemetry/TelemetryPublisherAzure.cs index ab99460..8b16b99 100644 --- a/NucuCar.Domain/Telemetry/TelemetryPublisherAzure.cs +++ b/NucuCar.Domain/Telemetry/TelemetryPublisherAzure.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -25,7 +24,7 @@ namespace NucuCar.Domain.Telemetry throw; } - Logger?.LogDebug("Started the AzureTelemetryPublisher!"); + Logger?.LogDebug("Initialized the AzureTelemetryPublisher!"); } /// @@ -83,31 +82,6 @@ namespace NucuCar.Domain.Telemetry await PublishToCloudAsync(message, cancellationToken); } - private Dictionary GetTelemetry() - { - var data = new List>(); - foreach (var telemeter in RegisteredTelemeters) - { - var telemetryData = telemeter.GetTelemetryData(); - if (telemetryData == null) - { - Logger?.LogWarning($"Warning! Data for {telemeter.GetIdentifier()} is null!"); - continue; - } - - telemetryData["_id"] = telemeter.GetIdentifier(); - data.Add(telemetryData); - } - - var metadata = new Dictionary - { - ["source"] = TelemetrySource ?? nameof(TelemetryPublisherAzure), - ["timestamp"] = DateTime.Now, - ["data"] = data.ToArray() - }; - return metadata; - } - private async Task PublishToCloudAsync(Message message, CancellationToken cancellationToken, int maxRetries = 3) { var retry = 0; diff --git a/NucuCar.Domain/Telemetry/TelemetryPublisherDisk.cs b/NucuCar.Domain/Telemetry/TelemetryPublisherDisk.cs new file mode 100644 index 0000000..2f9a87f --- /dev/null +++ b/NucuCar.Domain/Telemetry/TelemetryPublisherDisk.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using NucuCar.Domain.Services; + +namespace NucuCar.Domain.Telemetry +{ + /// + /// The TelemetryPublisherDisk is used to publish telemetry data to a file residing on the disk. + /// + public class TelemetryPublisherDisk : TelemetryPublisher + { + private readonly FileStream _fileStream; + + /// + /// Constructs an instance of . + /// + /// The connection string must contain the following options: + /// Filename (required) - The path of the filename in which to log telemetry data. + /// BufferSize (optional) - The buffer size for the async writer. Default: 4096 + /// + /// + /// + public TelemetryPublisherDisk(TelemetryPublisherBuilderOptions opts) : base(opts) + { + var connectionStringParams = ConnectionStringParser.Parse(opts.ConnectionString); + var fileName = connectionStringParams.GetValueOrDefault("Filename"); + var bufferSize = connectionStringParams.GetValueOrDefault("BufferSize", "4096"); + + _fileStream = new FileStream(fileName, FileMode.Append, FileAccess.Write, + FileShare.None, int.Parse(bufferSize), true); + Logger?.LogDebug("Initialized the TelemetryPublisherDisk!"); + } + + public override async Task PublishAsync(CancellationToken cancellationToken) + { + var data = GetTelemetry(); + var messageString = JsonConvert.SerializeObject(data); + Logger?.LogDebug($"Telemetry message: {messageString}"); + var encodedText = Encoding.Unicode.GetBytes(messageString); + + await _fileStream.WriteAsync(encodedText, (int) _fileStream.Length, + encodedText.Length, cancellationToken); + } + + public override void Dispose() + { + _fileStream.Close(); + } + } +} \ No newline at end of file