End Broker
This commit is contained in:
49
PlantBox.Broker/Broker.cs
Normal file
49
PlantBox.Broker/Broker.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
24
PlantBox.Broker/CaptorsValue.cs
Normal file
24
PlantBox.Broker/CaptorsValue.cs
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
92
PlantBox.Broker/ClientManager.cs
Normal file
92
PlantBox.Broker/ClientManager.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
14
PlantBox.Broker/Extensions.cs
Normal file
14
PlantBox.Broker/Extensions.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
74
PlantBox.Broker/HistoricManager.cs
Normal file
74
PlantBox.Broker/HistoricManager.cs
Normal 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
26
PlantBox.Broker/PlantBox.cs
Normal file
26
PlantBox.Broker/PlantBox.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
53
PlantBox.Broker/PlantBoxesManager.cs
Normal file
53
PlantBox.Broker/PlantBoxesManager.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
77
PlantBox.Broker/ServerManager.cs
Normal file
77
PlantBox.Broker/ServerManager.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
98
PlantBox.Broker/TcpManager.cs
Normal file
98
PlantBox.Broker/TcpManager.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ namespace PlantBox.Shared.Communication.Commands
|
||||
throw new ArgumentException($"Excepted 4 arguments, got {arguments.Length}");
|
||||
}
|
||||
|
||||
Time = TimeSpan.FromMinutes(arguments[0].ToDouble());
|
||||
Time = TimeSpan.FromSeconds(arguments[0].ToDouble());
|
||||
Humidities = GetArray(arguments[1]);
|
||||
Luminosities = GetArray(arguments[2]);
|
||||
Temperatures = GetArray(arguments[3]);
|
||||
@@ -63,7 +63,7 @@ namespace PlantBox.Shared.Communication.Commands
|
||||
|
||||
return new[]
|
||||
{
|
||||
Time.TotalMinutes.ToArgument(),
|
||||
Time.TotalSeconds.ToArgument(),
|
||||
Join(Humidities),
|
||||
Join(Luminosities),
|
||||
Join(Temperatures)
|
||||
|
||||
Reference in New Issue
Block a user