End Broker

This commit is contained in:
2019-04-24 19:27:40 +02:00
parent a1e1d43a6f
commit e3cc6392a7
12 changed files with 518 additions and 23 deletions

49
PlantBox.Broker/Broker.cs Normal file
View File

@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace PlantBox.Broker
{
class Broker
{
public PlantBoxesManager PlantBoxesManager { get; }
public ClientManager ClientManager { get; }
public ServerManager ServerManager { get; }
public bool IsRunning { get; set; }
public Broker(string[] args)
{
Console.WriteLine("Initializing Broker...");
PlantBoxesManager = new PlantBoxesManager();
PlantBoxesManager.Load();
ClientManager = new ClientManager(this);
ServerManager = new ServerManager(this);
}
public void Start()
{
Task.Run(() => ClientManager.Start());
Task.Run(() => ServerManager.Start());
string input;
do
{
input = Console.ReadLine().ToLowerInvariant();
if (input == "save")
{
PlantBoxesManager.Save();
}
} while (input != "exit" && input != "stop" && input != "quit");
Console.WriteLine("Stopping Broker...");
PlantBoxesManager.Save();
}
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace PlantBox.Broker
{
class CaptorsValue
{
public double Humidity { get; set; }
public double Luminosity { get; set; }
public double Temperature { get; set; }
public CaptorsValue(double humidity, double luminosity, double temperature)
{
Humidity = humidity;
Luminosity = luminosity;
Temperature = temperature;
}
public CaptorsValue((double humidity, double luminosity, double temperature) tuple) : this(tuple.humidity, tuple.luminosity, tuple.temperature)
{
}
}
}

View File

@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PlantBox.Shared.Communication;
using PlantBox.Shared.Communication.Commands;
namespace PlantBox.Broker
{
class ClientManager : TcpManager
{
public ClientManager(Broker broker) : base(broker)
{
}
protected override string LogPrefix => "Client";
protected override int ListeningPort => Connection.TCP_CLIENT_PORT;
protected override void CaptorsCommand(CommandStream commandStream, CommandPacket packet)
{
CaptorsRequest captorsRequest = new CaptorsRequest().Deserialize(packet.Arguments);
ulong id = packet.ID;
PlantBox plantBox = Broker.PlantBoxesManager[id];
if (plantBox == null)
{
throw new InvalidOperationException($"This PlantBox (${id}) does not exist");
}
var response = new CaptorsResponse(plantBox.Humidity.Value, plantBox.Luminosity.Value, plantBox.Temperature.Value, plantBox.TankLevel);
commandStream.Send(response.ToCommandPacket(id));
}
protected override void HistoricCommand(CommandStream commandStream, CommandPacket packet)
{
HistoricRequest historicRequest = new HistoricRequest().Deserialize(packet.Arguments);
ulong id = packet.ID;
PlantBox plantBox = Broker.PlantBoxesManager[id];
if (plantBox == null)
{
throw new InvalidOperationException($"This PlantBox (${id}) does not exist");
}
HistoricManager historic = plantBox.HistoricManager;
IReadOnlyList<CaptorsValue> captorsValues;
switch (historicRequest.Interval)
{
case HistoricInterval.Minutely:
captorsValues = historic.MinutesHistoric;
break;
case HistoricInterval.Hourly:
captorsValues = historic.HoursHistoric;
break;
case HistoricInterval.Daily:
captorsValues = historic.DaysHistoric;
break;
case HistoricInterval.Weekly:
captorsValues = historic.WeeksHistoric;
break;
case HistoricInterval.Monthly:
captorsValues = historic.MonthsHistoric;
break;
default:
throw new InvalidOperationException("How did you just got here? Even 『 』can't");
}
var response = new HistoricResponse
(
DateTime.Now - plantBox.LastMeasureDate,
captorsValues.Select(x => x.Humidity).ToArray(),
captorsValues.Select(x => x.Luminosity).ToArray(),
captorsValues.Select(x => x.Temperature).ToArray()
);
commandStream.Send(response.ToCommandPacket(id));
}
protected override void InfoCommand(CommandStream commandStream, CommandPacket packet)
{
throw new NotImplementedException();
}
protected override void PingCommand(CommandStream commandStream, CommandPacket packet)
{
var ping = new PingCommand().Deserialize(packet.Arguments);
commandStream.Send(ping.ToCommandPacket(packet.ID));
}
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace PlantBox.Broker
{
public static class Extensions
{
public static IEnumerable<T> TakeFromEnd<T>(this List<T> list, int count)
{
return list.GetRange(list.Count - count, count);
}
}
}

View File

@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PlantBox.Broker
{
class HistoricManager
{
private List<CaptorsValue> _minutesHistoric;
public IReadOnlyList<CaptorsValue> MinutesHistoric => _minutesHistoric.AsReadOnly();
private List<CaptorsValue> _hoursHistoric;
public IReadOnlyList<CaptorsValue> HoursHistoric => _hoursHistoric.AsReadOnly();
private List<CaptorsValue> _daysHistoric;
public IReadOnlyList<CaptorsValue> DaysHistoric => _daysHistoric.AsReadOnly();
private List<CaptorsValue> _weeksHistoric;
public IReadOnlyList<CaptorsValue> WeeksHistoric => _weeksHistoric.AsReadOnly();
private List<CaptorsValue> _monthsHistoric;
public IReadOnlyList<CaptorsValue> MonthsHistoric => _monthsHistoric.AsReadOnly();
public HistoricManager()
{
_minutesHistoric = new List<CaptorsValue>();
_hoursHistoric = new List<CaptorsValue>();
_daysHistoric = new List<CaptorsValue>();
_weeksHistoric = new List<CaptorsValue>();
_monthsHistoric = new List<CaptorsValue>();
}
public HistoricManager(List<CaptorsValue> minutesHistoric, List<CaptorsValue> hoursHistoric, List<CaptorsValue> daysHistoric, List<CaptorsValue> weeksHistoric, List<CaptorsValue> monthsHistoric)
{
_minutesHistoric = minutesHistoric;
_hoursHistoric = hoursHistoric;
_daysHistoric = daysHistoric;
_weeksHistoric = weeksHistoric;
_monthsHistoric = monthsHistoric;
}
public void Add(CaptorsValue captorsValue)
{
_minutesHistoric.Add(captorsValue);
if (_minutesHistoric.Count % 12 == 0)
{
_hoursHistoric.Add(new CaptorsValue(GetAverage(_minutesHistoric.TakeFromEnd(12))));
}
if (_hoursHistoric.Count % 24 == 0)
{
_daysHistoric.Add(new CaptorsValue(GetAverage(_hoursHistoric.TakeFromEnd(24))));
}
if (_daysHistoric.Count % 7 == 0)
{
_weeksHistoric.Add(new CaptorsValue(GetAverage(_daysHistoric.TakeFromEnd(7))));
}
if (_daysHistoric.Count % 31 == 0)
{
_monthsHistoric.Add(new CaptorsValue(GetAverage(_daysHistoric.TakeFromEnd(31))));
}
}
private (double humidity, double luminosity, double temperature) GetAverage(IEnumerable<CaptorsValue> captorsValues)
{
return
(
captorsValues.Average(x => x.Humidity),
captorsValues.Average(x => x.Luminosity),
captorsValues.Average(x => x.Temperature)
);
}
}
}

View File

@@ -7,6 +7,10 @@
<Description>Broker of the PlantBox project</Description>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PlantBox.Shared\PlantBox.Shared.csproj" />
</ItemGroup>

View File

@@ -0,0 +1,26 @@
using PlantBox.Shared.Communication.Commands;
using System;
using System.Collections.Generic;
using System.Text;
namespace PlantBox.Broker
{
class PlantBox
{
// General Info
public ulong ID { get; }
public string Name { get; set; }
public PlantType Type { get; set; }
public PlantState State { get; set; }
// Captors
public DateTime LastMeasureDate { get; set; }
public double TankLevel { get; set; }
public CaptorValue Humidity { get; set; }
public CaptorValue Luminosity { get; set; }
public CaptorValue Temperature { get; set; }
// Historic
public HistoricManager HistoricManager { get; set; }
}
}

View File

@@ -0,0 +1,53 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace PlantBox.Broker
{
class PlantBoxesManager
{
public string FilePath => Path.Combine(Environment.CurrentDirectory, FileName);
public string FileName => "storage.json";
private Dictionary<ulong, PlantBox> _plantBoxes;
public PlantBoxesManager()
{
}
public PlantBox this[ulong id]
{
get => _plantBoxes.GetValueOrDefault(id);
}
public PlantBox GetPlantBox(ulong id) => this[id];
public void Load()
{
Console.WriteLine("Loading storage...");
if (File.Exists(FilePath))
{
_plantBoxes = JsonConvert.DeserializeObject<Dictionary<ulong, PlantBox>>(File.ReadAllText(FilePath));
}
else
{
_plantBoxes = new Dictionary<ulong, PlantBox>();
}
Console.WriteLine("Storage loaded");
}
public void Save()
{
Console.WriteLine("Saving storage...");
File.WriteAllText(FilePath, JsonConvert.SerializeObject(_plantBoxes));
Console.WriteLine("Storage saved");
}
}
}

View File

@@ -8,30 +8,14 @@ namespace PlantBox.Broker
{
class Program
{
public static Broker Broker { get; private set; }
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var listener = new TcpListener(IPAddress.Any, Connection.TCP_PORT);
listener.Start();
Broker = new Broker(args);
Broker.Start();
while (true)
{
try
{
var client = listener.AcceptTcpClient();
var stream = new CommandStream(client.GetStream());
(var command, var ping) = stream.Receive<PingCommand>();
Console.WriteLine(command);
Console.WriteLine($"Ping: {ping.Message}");
stream.Send(new PingCommand(ping.Message).ToCommandPacket(command.ID));
}
catch (Exception)
{
}
}
Console.WriteLine("Broker stopped");
}
}
}

View File

@@ -0,0 +1,77 @@
using PlantBox.Shared.Communication;
using PlantBox.Shared.Communication.Commands;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace PlantBox.Broker
{
class ServerManager : TcpManager
{
public ServerManager(Broker broker) : base(broker)
{
}
protected override string LogPrefix => "Server";
protected override int ListeningPort => Connection.TCP_SERVER_PORT;
protected override void InfoCommand(CommandStream commandStream, CommandPacket packet)
{
InfoResponse infoResponse = new InfoResponse().Deserialize(packet.Arguments);
ulong id = packet.ID;
PlantBox plantBox = Broker.PlantBoxesManager[id];
if (plantBox == null)
{
plantBox = new PlantBox
{
HistoricManager = new HistoricManager()
};
}
plantBox.Name = infoResponse.Name;
plantBox.Type = infoResponse.Type;
plantBox.State = infoResponse.State;
plantBox.Humidity = new CaptorValue(infoResponse.HumidityMin, infoResponse.HumidityMax, plantBox.Humidity?.Value ?? 0);
plantBox.Luminosity = new CaptorValue(infoResponse.LuminosityMin, infoResponse.LuminosityMax, plantBox.Luminosity?.Value ?? 0);
plantBox.Temperature = new CaptorValue(infoResponse.TemperatureMin, infoResponse.TemperatureMax, plantBox.Temperature?.Value ?? 0);
}
protected override void CaptorsCommand(CommandStream commandStream, CommandPacket packet)
{
CaptorsResponse captorsResponse = new CaptorsResponse().Deserialize(packet.Arguments);
ulong id = packet.ID;
PlantBox plantBox = Broker.PlantBoxesManager[id];
if (plantBox == null)
{
Log($"Received captors info from non-registered PlantBox ({id}), ignoring it");
return;
}
plantBox.LastMeasureDate = DateTime.Now;
plantBox.Humidity.Value = captorsResponse.Humidity;
plantBox.Luminosity.Value = captorsResponse.Luminosity;
plantBox.Temperature.Value = captorsResponse.Temperature;
plantBox.TankLevel = captorsResponse.Tank;
plantBox.HistoricManager.Add(new CaptorsValue(plantBox.Humidity.Value, plantBox.Luminosity.Value, plantBox.Temperature.Value));
}
protected override void HistoricCommand(CommandStream commandStream, CommandPacket packet)
{
throw new NotImplementedException();
}
protected override void PingCommand(CommandStream commandStream, CommandPacket packet)
{
var ping = new PingCommand().Deserialize(packet.Arguments);
commandStream.Send(ping.ToCommandPacket(packet.ID));
}
}
}

View File

@@ -0,0 +1,98 @@
using PlantBox.Shared.Communication;
using PlantBox.Shared.Communication.Commands;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace PlantBox.Broker
{
abstract class TcpManager
{
public Broker Broker { get; }
protected abstract string LogPrefix { get; }
protected abstract int ListeningPort { get; }
private TcpListener _listener;
public TcpManager(Broker broker)
{
Log("Initializing...");
Broker = broker;
_listener = new TcpListener(IPAddress.Any, ListeningPort);
}
public void Start()
{
Log("Starting...");
_listener.Start();
Log("Started");
TcpLoop();
}
private void TcpLoop()
{
while (Broker.IsRunning)
{
Log("Waiting client...");
var client = _listener.AcceptTcpClient();
Log($"Found client: {client.Client.RemoteEndPoint}");
ClientLoop(client);
}
}
private void ClientLoop(TcpClient client)
{
var commandStream = new CommandStream(client.GetStream());
try
{
while (client.Connected)
{
var packet = commandStream.Receive();
Log($"Received command from {client.Client.RemoteEndPoint}");
Log(packet.ToString());
switch (packet.Command)
{
case Command.Captors:
CaptorsCommand(commandStream, packet);
break;
case Command.Historic:
HistoricCommand(commandStream, packet);
break;
case Command.Info:
InfoCommand(commandStream, packet);
break;
case Command.Ping:
PingCommand(commandStream, packet);
break;
}
}
}
catch (Exception ex)
{
Log($"Client disconnected: {ex.Message}");
}
}
protected void Log(string message)
{
Console.WriteLine($"[{LogPrefix}] {message}");
}
protected abstract void CaptorsCommand(CommandStream commandStream, CommandPacket packet);
protected abstract void HistoricCommand(CommandStream commandStream, CommandPacket packet);
protected abstract void InfoCommand(CommandStream commandStream, CommandPacket packet);
protected abstract void PingCommand(CommandStream commandStream, CommandPacket packet);
}
}