update daily data aggregator
This commit is contained in:
parent
c36ebeeb6a
commit
85b248dc6f
|
@ -54,7 +54,6 @@ public static class Aggregator
|
||||||
// Output the time until the next rounded hour
|
// Output the time until the next rounded hour
|
||||||
Console.WriteLine("Waiting for " + timeUntilNextDay.TotalHours + " hours...");
|
Console.WriteLine("Waiting for " + timeUntilNextDay.TotalHours + " hours...");
|
||||||
Console.WriteLine("-----------------------------------------------------------------------------------------------------------------");
|
Console.WriteLine("-----------------------------------------------------------------------------------------------------------------");
|
||||||
|
|
||||||
|
|
||||||
// Wait until the next rounded hour
|
// Wait until the next rounded hour
|
||||||
await Task.Delay(timeUntilNextDay);
|
await Task.Delay(timeUntilNextDay);
|
||||||
|
@ -63,8 +62,15 @@ public static class Aggregator
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
AggregatedData dailyAggregatedData = CreateAverage("HourlyData",DateTime.Now.AddDays(-1).ToUnixTime(),DateTime.Now.ToUnixTime());
|
var currentTime = DateTime.Now;
|
||||||
|
AggregatedData dailyAggregatedData = CreateAverage("HourlyData",currentTime.AddDays(-1).ToUnixTime(),currentTime.ToUnixTime());
|
||||||
dailyAggregatedData.Save("DailyData");
|
dailyAggregatedData.Save("DailyData");
|
||||||
|
if (await dailyAggregatedData.PushToS3())
|
||||||
|
{
|
||||||
|
DeleteHourlyData("HourlyData",currentTime.ToUnixTime());
|
||||||
|
dailyAggregatedData.DeleteDailyData("DailyData");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -73,6 +79,21 @@ public static class Aggregator
|
||||||
await Task.Delay(TimeSpan.FromDays(1));
|
await Task.Delay(TimeSpan.FromDays(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void DeleteHourlyData(String myDirectory, Int64 beforeTimestamp)
|
||||||
|
{
|
||||||
|
var csvFiles = Directory.GetFiles(myDirectory, "*.csv");
|
||||||
|
Console.WriteLine("Delete data before"+beforeTimestamp);
|
||||||
|
foreach (var csvFile in csvFiles)
|
||||||
|
{
|
||||||
|
if (IsFileWithinTimeRange(csvFile, 0, beforeTimestamp))
|
||||||
|
{
|
||||||
|
File.Delete(csvFile);
|
||||||
|
Console.WriteLine($"Deleted hourly data file: {csvFile}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static AggregatedData CreateAverage(String myDirectory, Int64 afterTimestamp, Int64 beforeTimestamp)
|
private static AggregatedData CreateAverage(String myDirectory, Int64 afterTimestamp, Int64 beforeTimestamp)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using Flurl.Http;
|
||||||
using InnovEnergy.App.SaliMax.Devices;
|
using InnovEnergy.App.SaliMax.Devices;
|
||||||
|
using InnovEnergy.App.SaliMax.SystemConfig;
|
||||||
using InnovEnergy.Lib.Units;
|
using InnovEnergy.Lib.Units;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using static System.Text.Json.JsonSerializer;
|
using static System.Text.Json.JsonSerializer;
|
||||||
|
@ -13,6 +15,8 @@ public class AggregatedData
|
||||||
public required Double AvgSoc { get; set; }
|
public required Double AvgSoc { get; set; }
|
||||||
public required Double AvgPvPower { get; set; }
|
public required Double AvgPvPower { get; set; }
|
||||||
public required Double BatteryPowerAverage { get; set; }
|
public required Double BatteryPowerAverage { get; set; }
|
||||||
|
|
||||||
|
private readonly S3Config? _S3Config = Config.Load().S3;
|
||||||
|
|
||||||
public void Save(String directory)
|
public void Save(String directory)
|
||||||
{
|
{
|
||||||
|
@ -39,6 +43,38 @@ public class AggregatedData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void DeleteDailyData(String directory)
|
||||||
|
{
|
||||||
|
|
||||||
|
var csvFiles = Directory.GetFiles(directory, "*.csv");
|
||||||
|
foreach (var csvFile in csvFiles)
|
||||||
|
{
|
||||||
|
File.Delete(csvFile);
|
||||||
|
Console.WriteLine($"Deleted daily data file: {csvFile}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Boolean> PushToS3()
|
||||||
|
{
|
||||||
|
var csv = this.ToCsv();
|
||||||
|
if (_S3Config is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var s3Path = DateTime.Now.ToString("yyyy-MM-dd") + ".csv";
|
||||||
|
var request = _S3Config.CreatePutRequest(s3Path);
|
||||||
|
var response = await request.PutAsync(new StringContent(csv));
|
||||||
|
|
||||||
|
if (response.StatusCode != 200)
|
||||||
|
{
|
||||||
|
Console.WriteLine("ERROR: PUT");
|
||||||
|
var error = await response.GetStringAsync();
|
||||||
|
Console.WriteLine(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// public static HourlyData? Load(String dataFilePath)
|
// public static HourlyData? Load(String dataFilePath)
|
||||||
// {
|
// {
|
||||||
// try
|
// try
|
||||||
|
|
|
@ -19,7 +19,6 @@ using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc.Control;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Channels;
|
using InnovEnergy.Lib.Protocols.Modbus.Channels;
|
||||||
using InnovEnergy.Lib.Units;
|
using InnovEnergy.Lib.Units;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using System.Text.Json;
|
|
||||||
using InnovEnergy.App.SaliMax.AggregationService;
|
using InnovEnergy.App.SaliMax.AggregationService;
|
||||||
using InnovEnergy.App.SaliMax.DataTypes;
|
using InnovEnergy.App.SaliMax.DataTypes;
|
||||||
using static InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes.SystemConfig;
|
using static InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes.SystemConfig;
|
||||||
|
@ -33,56 +32,56 @@ namespace InnovEnergy.App.SaliMax;
|
||||||
internal static class Program
|
internal static class Program
|
||||||
{
|
{
|
||||||
private static readonly TimeSpan UpdateInterval = TimeSpan.FromSeconds(2);
|
private static readonly TimeSpan UpdateInterval = TimeSpan.FromSeconds(2);
|
||||||
|
|
||||||
private static readonly IReadOnlyList<Byte> BatteryNodes;
|
private static readonly IReadOnlyList<Byte> BatteryNodes;
|
||||||
|
|
||||||
private static readonly Channel TruConvertAcChannel ;
|
private static readonly Channel TruConvertAcChannel;
|
||||||
private static readonly Channel TruConvertDcChannel ;
|
private static readonly Channel TruConvertDcChannel;
|
||||||
private static readonly Channel GridMeterChannel ;
|
private static readonly Channel GridMeterChannel;
|
||||||
private static readonly Channel IslandBusLoadChannel;
|
private static readonly Channel IslandBusLoadChannel;
|
||||||
private static readonly Channel PvOnDc ;
|
private static readonly Channel PvOnDc;
|
||||||
private static readonly Channel PvOnAcGrid ;
|
private static readonly Channel PvOnAcGrid;
|
||||||
private static readonly Channel PvOnAcIsland ;
|
private static readonly Channel PvOnAcIsland;
|
||||||
private static readonly Channel RelaysChannel ;
|
private static readonly Channel RelaysChannel;
|
||||||
private static readonly Channel BatteriesChannel ;
|
private static readonly Channel BatteriesChannel;
|
||||||
|
|
||||||
private const String VpnServerIp = "10.2.0.11";
|
private const String VpnServerIp = "10.2.0.11";
|
||||||
private static Boolean _subscribedToQueue = false;
|
private static Boolean _subscribedToQueue = false;
|
||||||
private static Boolean _subscribeToQueueForTheFirstTime = false;
|
private static Boolean _subscribeToQueueForTheFirstTime = false;
|
||||||
private static SalimaxAlarmState _prevSalimaxState = SalimaxAlarmState.Green;
|
private static SalimaxAlarmState _prevSalimaxState = SalimaxAlarmState.Green;
|
||||||
private static Int32 _heartBitInterval = 0;
|
private static Int32 _heartBitInterval = 0;
|
||||||
|
|
||||||
static Program()
|
static Program()
|
||||||
{
|
{
|
||||||
var config = Config.Load();
|
var config = Config.Load();
|
||||||
var d = config.Devices;
|
var d = config.Devices;
|
||||||
|
|
||||||
Channel CreateChannel(SalimaxDevice device) => device.DeviceState == DeviceState.Disabled
|
Channel CreateChannel(SalimaxDevice device) => device.DeviceState == DeviceState.Disabled
|
||||||
? new NullChannel()
|
? new NullChannel()
|
||||||
: new TcpChannel(device);
|
: new TcpChannel(device);
|
||||||
|
|
||||||
TruConvertAcChannel = CreateChannel(d.TruConvertAcIp);
|
TruConvertAcChannel = CreateChannel(d.TruConvertAcIp);
|
||||||
TruConvertDcChannel = CreateChannel(d.TruConvertDcIp);
|
TruConvertDcChannel = CreateChannel(d.TruConvertDcIp);
|
||||||
GridMeterChannel = CreateChannel(d.GridMeterIp);
|
GridMeterChannel = CreateChannel(d.GridMeterIp);
|
||||||
IslandBusLoadChannel = CreateChannel(d.IslandBusLoadMeterIp);
|
IslandBusLoadChannel = CreateChannel(d.IslandBusLoadMeterIp);
|
||||||
PvOnDc = CreateChannel(d.PvOnDc);
|
PvOnDc = CreateChannel(d.PvOnDc);
|
||||||
PvOnAcGrid = CreateChannel(d.PvOnAcGrid);
|
PvOnAcGrid = CreateChannel(d.PvOnAcGrid);
|
||||||
PvOnAcIsland = CreateChannel(d.PvOnAcIsland);
|
PvOnAcIsland = CreateChannel(d.PvOnAcIsland);
|
||||||
RelaysChannel = CreateChannel(d.RelaysIp);
|
RelaysChannel = CreateChannel(d.RelaysIp);
|
||||||
BatteriesChannel = CreateChannel(d.BatteryIp);
|
BatteriesChannel = CreateChannel(d.BatteryIp);
|
||||||
|
|
||||||
BatteryNodes = config
|
BatteryNodes = config
|
||||||
.Devices
|
.Devices
|
||||||
.BatteryNodes
|
.BatteryNodes
|
||||||
.Select(n => n.ConvertTo<Byte>())
|
.Select(n => n.ConvertTo<Byte>())
|
||||||
.ToArray(config.Devices.BatteryNodes.Length);
|
.ToArray(config.Devices.BatteryNodes.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task Main(String[] args)
|
public static async Task Main(String[] args)
|
||||||
{
|
{
|
||||||
//Do not await
|
//Do not await
|
||||||
Aggregator.HourlyDataAggregationManager().ContinueWith(t=>t.Exception.WriteLine(), TaskContinuationOptions.OnlyOnFaulted).SupressAwaitWarning();
|
Aggregator.HourlyDataAggregationManager().ContinueWith(t => t.Exception.WriteLine(), TaskContinuationOptions.OnlyOnFaulted).SupressAwaitWarning();
|
||||||
Aggregator.DailyDataAggregationManager().ContinueWith(t=>t.Exception.WriteLine(), TaskContinuationOptions.OnlyOnFaulted).SupressAwaitWarning();
|
Aggregator.DailyDataAggregationManager().ContinueWith(t => t.Exception.WriteLine(), TaskContinuationOptions.OnlyOnFaulted).SupressAwaitWarning();
|
||||||
MiddlewareAgent.InitializeCommunicationToMiddleware();
|
MiddlewareAgent.InitializeCommunicationToMiddleware();
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
@ -93,81 +92,80 @@ internal static class Program
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
e.LogError();
|
e.LogError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static async Task Run()
|
private static async Task Run()
|
||||||
{
|
{
|
||||||
"Starting SaliMax".LogInfo();
|
"Starting SaliMax".LogInfo();
|
||||||
|
|
||||||
Watchdog.NotifyReady();
|
Watchdog.NotifyReady();
|
||||||
|
|
||||||
var battery48TlDevices = BatteryNodes
|
var battery48TlDevices = BatteryNodes
|
||||||
.Select(n => new Battery48TlDevice(BatteriesChannel, n))
|
.Select(n => new Battery48TlDevice(BatteriesChannel, n))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var batteryDevices = new Battery48TlDevices(battery48TlDevices);
|
var batteryDevices = new Battery48TlDevices(battery48TlDevices);
|
||||||
var acDcDevices = new TruConvertAcDcDevices(TruConvertAcChannel);
|
var acDcDevices = new TruConvertAcDcDevices(TruConvertAcChannel);
|
||||||
var dcDcDevices = new TruConvertDcDcDevices(TruConvertDcChannel);
|
var dcDcDevices = new TruConvertDcDcDevices(TruConvertDcChannel);
|
||||||
var gridMeterDevice = new EmuMeterDevice(GridMeterChannel);
|
var gridMeterDevice = new EmuMeterDevice(GridMeterChannel);
|
||||||
var acIslandLoadMeter = new EmuMeterDevice(IslandBusLoadChannel);
|
var acIslandLoadMeter = new EmuMeterDevice(IslandBusLoadChannel);
|
||||||
var pvOnDcDevice = new AmptDevices(PvOnDc);
|
var pvOnDcDevice = new AmptDevices(PvOnDc);
|
||||||
var pvOnAcGridDevice = new AmptDevices(PvOnAcGrid);
|
var pvOnAcGridDevice = new AmptDevices(PvOnAcGrid);
|
||||||
var pvOnAcIslandDevice = new AmptDevices(PvOnAcIsland);
|
var pvOnAcIslandDevice = new AmptDevices(PvOnAcIsland);
|
||||||
var saliMaxRelaysDevice = new RelaysDevice(RelaysChannel);
|
var saliMaxRelaysDevice = new RelaysDevice(RelaysChannel);
|
||||||
|
|
||||||
StatusRecord ReadStatus()
|
StatusRecord ReadStatus()
|
||||||
{
|
{
|
||||||
var config = Config.Load();
|
var config = Config.Load();
|
||||||
var devices = config.Devices;
|
var devices = config.Devices;
|
||||||
var acDc = acDcDevices.Read();
|
var acDc = acDcDevices.Read();
|
||||||
var dcDc = dcDcDevices.Read();
|
var dcDc = dcDcDevices.Read();
|
||||||
var relays = saliMaxRelaysDevice.Read();
|
var relays = saliMaxRelaysDevice.Read();
|
||||||
var loadOnAcIsland = acIslandLoadMeter.Read();
|
var loadOnAcIsland = acIslandLoadMeter.Read();
|
||||||
var gridMeter = gridMeterDevice.Read();
|
var gridMeter = gridMeterDevice.Read();
|
||||||
var pvOnDc = pvOnDcDevice.Read();
|
var pvOnDc = pvOnDcDevice.Read();
|
||||||
var battery = batteryDevices.Read();
|
var battery = batteryDevices.Read();
|
||||||
|
|
||||||
var pvOnAcGrid = pvOnAcGridDevice.Read();
|
var pvOnAcGrid = pvOnAcGridDevice.Read();
|
||||||
var pvOnAcIsland = pvOnAcIslandDevice.Read();
|
var pvOnAcIsland = pvOnAcIslandDevice.Read();
|
||||||
|
|
||||||
var gridBusToIslandBus = Topology.CalculateGridBusToIslandBusPower(pvOnAcIsland, loadOnAcIsland, acDc);
|
var gridBusToIslandBus = Topology.CalculateGridBusToIslandBusPower(pvOnAcIsland, loadOnAcIsland, acDc);
|
||||||
|
|
||||||
var gridBusLoad = devices.LoadOnAcGrid.DeviceState == DeviceState.Disabled
|
var gridBusLoad = devices.LoadOnAcGrid.DeviceState == DeviceState.Disabled
|
||||||
? new AcPowerDevice { Power = 0 }
|
? new AcPowerDevice { Power = 0 }
|
||||||
: Topology.CalculateGridBusLoad(gridMeter, pvOnAcGrid, gridBusToIslandBus);
|
: Topology.CalculateGridBusLoad(gridMeter, pvOnAcGrid, gridBusToIslandBus);
|
||||||
|
|
||||||
var dcLoad = devices.LoadOnDc.DeviceState == DeviceState.Disabled
|
var dcLoad = devices.LoadOnDc.DeviceState == DeviceState.Disabled
|
||||||
? new DcPowerDevice { Power = 0 }
|
? new DcPowerDevice { Power = 0 }
|
||||||
: Topology.CalculateDcLoad(acDc, pvOnDc, dcDc);
|
: Topology.CalculateDcLoad(acDc, pvOnDc, dcDc);
|
||||||
|
|
||||||
var acDcToDcLink = devices.LoadOnDc.DeviceState == DeviceState.Disabled ?
|
var acDcToDcLink = devices.LoadOnDc.DeviceState == DeviceState.Disabled
|
||||||
Topology.CalculateAcDcToDcLink(pvOnDc, dcDc, acDc)
|
? Topology.CalculateAcDcToDcLink(pvOnDc, dcDc, acDc)
|
||||||
: new DcPowerDevice{ Power = acDc.Dc.Power};
|
: new DcPowerDevice { Power = acDc.Dc.Power };
|
||||||
|
|
||||||
return new StatusRecord
|
return new StatusRecord
|
||||||
{
|
{
|
||||||
AcDc = acDc,
|
AcDc = acDc,
|
||||||
DcDc = dcDc,
|
DcDc = dcDc,
|
||||||
Battery = battery,
|
Battery = battery,
|
||||||
Relays = relays,
|
Relays = relays,
|
||||||
GridMeter = gridMeter,
|
GridMeter = gridMeter,
|
||||||
PvOnAcGrid = pvOnAcGrid,
|
PvOnAcGrid = pvOnAcGrid,
|
||||||
PvOnAcIsland = pvOnAcIsland,
|
PvOnAcIsland = pvOnAcIsland,
|
||||||
PvOnDc = pvOnDc,
|
PvOnDc = pvOnDc,
|
||||||
AcGridToAcIsland = gridBusToIslandBus,
|
AcGridToAcIsland = gridBusToIslandBus,
|
||||||
AcDcToDcLink = acDcToDcLink,
|
AcDcToDcLink = acDcToDcLink,
|
||||||
LoadOnAcGrid = gridBusLoad,
|
LoadOnAcGrid = gridBusLoad,
|
||||||
LoadOnAcIsland = loadOnAcIsland,
|
LoadOnAcIsland = loadOnAcIsland,
|
||||||
LoadOnDc = dcLoad,
|
LoadOnDc = dcLoad,
|
||||||
|
|
||||||
StateMachine = StateMachine.Default,
|
StateMachine = StateMachine.Default,
|
||||||
EssControl = EssControl.Default,
|
EssControl = EssControl.Default,
|
||||||
Log = new SystemLog { SalimaxAlarmState = SalimaxAlarmState.Green, Message = null }, //TODO: Put real stuff
|
Log = new SystemLog { SalimaxAlarmState = SalimaxAlarmState.Green, Message = null }, //TODO: Put real stuff
|
||||||
Config = config // load from disk every iteration, so config can be changed while running
|
Config = config // load from disk every iteration, so config can be changed while running
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,23 +183,23 @@ internal static class Program
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
await Observable
|
await Observable
|
||||||
.Interval(UpdateInterval)
|
.Interval(UpdateInterval)
|
||||||
.Select(_ => RunIteration())
|
.Select(_ => RunIteration())
|
||||||
.SelectMany(r => UploadCsv(r, DateTime.Now.Round(UpdateInterval)))
|
.SelectMany(r => UploadCsv(r, DateTime.Now.Round(UpdateInterval)))
|
||||||
.SelectError()
|
.SelectError()
|
||||||
.ToTask();
|
.ToTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
StatusRecord RunIteration()
|
StatusRecord RunIteration()
|
||||||
{
|
{
|
||||||
Watchdog.NotifyAlive();
|
Watchdog.NotifyAlive();
|
||||||
|
|
||||||
var record = ReadStatus();
|
var record = ReadStatus();
|
||||||
|
|
||||||
var currentSalimaxState = GetSalimaxStateAlarm(record);
|
var currentSalimaxState = GetSalimaxStateAlarm(record);
|
||||||
|
|
||||||
SendSalimaxStateAlarm(currentSalimaxState,record);
|
SendSalimaxStateAlarm(currentSalimaxState, record);
|
||||||
|
|
||||||
record.ControlConstants();
|
record.ControlConstants();
|
||||||
record.ControlSystemState();
|
record.ControlSystemState();
|
||||||
|
@ -219,16 +217,16 @@ internal static class Program
|
||||||
|
|
||||||
$"{DateTime.Now.Round(UpdateInterval).ToUnixTime()} : {record.StateMachine.State}: {record.StateMachine.Message}".WriteLine()
|
$"{DateTime.Now.Round(UpdateInterval).ToUnixTime()} : {record.StateMachine.State}: {record.StateMachine.Message}".WriteLine()
|
||||||
.LogInfo();
|
.LogInfo();
|
||||||
|
|
||||||
record.CreateTopologyTextBlock().WriteLine();
|
record.CreateTopologyTextBlock().WriteLine();
|
||||||
|
|
||||||
(record.Relays is null ? "No relay Data available" : record.Relays.FiWarning ? "Alert: Fi Warning Detected" : "No Fi Warning Detected").WriteLine();
|
(record.Relays is null ? "No relay Data available" : record.Relays.FiWarning ? "Alert: Fi Warning Detected" : "No Fi Warning Detected").WriteLine();
|
||||||
(record.Relays is null ? "No relay Data available" : record.Relays.FiError ? "Alert: Fi Error Detected" : "No Fi Error Detected") .WriteLine();
|
(record.Relays is null ? "No relay Data available" : record.Relays.FiError ? "Alert: Fi Error Detected" : "No Fi Error Detected").WriteLine();
|
||||||
|
|
||||||
//record.ApplyConfigFile(minSoc:22, gridSetPoint:1);
|
//record.ApplyConfigFile(minSoc:22, gridSetPoint:1);
|
||||||
|
|
||||||
record.Config.Save();
|
record.Config.Save();
|
||||||
|
|
||||||
"===========================================".LogInfo();
|
"===========================================".LogInfo();
|
||||||
|
|
||||||
return record;
|
return record;
|
||||||
|
@ -236,20 +234,21 @@ internal static class Program
|
||||||
|
|
||||||
// ReSharper disable once FunctionNeverReturns
|
// ReSharper disable once FunctionNeverReturns
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SendSalimaxStateAlarm(StatusMessage currentSalimaxState, StatusRecord record)
|
public static void SendSalimaxStateAlarm(StatusMessage currentSalimaxState, StatusRecord record)
|
||||||
{
|
{
|
||||||
var s3Bucket = Config.Load().S3?.Bucket;
|
var s3Bucket = Config.Load().S3?.Bucket;
|
||||||
|
|
||||||
//Every 15 iterations(30 seconds), the installation sends a heartbit message to the queue
|
//Every 15 iterations(30 seconds), the installation sends a heartbit message to the queue
|
||||||
_heartBitInterval++;
|
_heartBitInterval++;
|
||||||
|
|
||||||
//When the controller boots, it tries to subscribe to the queue
|
//When the controller boots, it tries to subscribe to the queue
|
||||||
if (_subscribeToQueueForTheFirstTime==false)
|
if (_subscribeToQueueForTheFirstTime == false)
|
||||||
{
|
{
|
||||||
_subscribeToQueueForTheFirstTime = true;
|
_subscribeToQueueForTheFirstTime = true;
|
||||||
_subscribedToQueue = RabbitMqManager.SubscribeToQueue(currentSalimaxState, s3Bucket, VpnServerIp);
|
_subscribedToQueue = RabbitMqManager.SubscribeToQueue(currentSalimaxState, s3Bucket, VpnServerIp);
|
||||||
}
|
}
|
||||||
|
|
||||||
//If already subscribed to the queue and the status has been changed, update the queue
|
//If already subscribed to the queue and the status has been changed, update the queue
|
||||||
if (_subscribedToQueue && currentSalimaxState.Status != _prevSalimaxState)
|
if (_subscribedToQueue && currentSalimaxState.Status != _prevSalimaxState)
|
||||||
{
|
{
|
||||||
|
@ -257,13 +256,13 @@ internal static class Program
|
||||||
if (s3Bucket != null)
|
if (s3Bucket != null)
|
||||||
RabbitMqManager.InformMiddleware(currentSalimaxState);
|
RabbitMqManager.InformMiddleware(currentSalimaxState);
|
||||||
}
|
}
|
||||||
else if (_subscribedToQueue && _heartBitInterval>=15)
|
else if (_subscribedToQueue && _heartBitInterval >= 15)
|
||||||
{
|
{
|
||||||
//Send a heartbit to the backend
|
//Send a heartbit to the backend
|
||||||
Console.WriteLine("----------------------------------------Sending Heartbit----------------------------------------");
|
Console.WriteLine("----------------------------------------Sending Heartbit----------------------------------------");
|
||||||
_heartBitInterval = 0;
|
_heartBitInterval = 0;
|
||||||
currentSalimaxState.Type = MessageType.Heartbit;
|
currentSalimaxState.Type = MessageType.Heartbit;
|
||||||
|
|
||||||
if (s3Bucket != null)
|
if (s3Bucket != null)
|
||||||
RabbitMqManager.InformMiddleware(currentSalimaxState);
|
RabbitMqManager.InformMiddleware(currentSalimaxState);
|
||||||
}
|
}
|
||||||
|
@ -274,16 +273,15 @@ internal static class Program
|
||||||
{
|
{
|
||||||
record.ApplyConfigFile(config);
|
record.ApplyConfigFile(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static StatusMessage GetSalimaxStateAlarm(StatusRecord record)
|
private static StatusMessage GetSalimaxStateAlarm(StatusRecord record)
|
||||||
{
|
{
|
||||||
var alarmCondition = record.DetectAlarmStates();
|
var alarmCondition = record.DetectAlarmStates();
|
||||||
var s3Bucket = Config.Load().S3?.Bucket;
|
var s3Bucket = Config.Load().S3?.Bucket;
|
||||||
|
|
||||||
var alarmList = new List<AlarmOrWarning>();
|
var alarmList = new List<AlarmOrWarning>();
|
||||||
var warningList = new List<AlarmOrWarning>();
|
var warningList = new List<AlarmOrWarning>();
|
||||||
|
|
||||||
if (alarmCondition is not null)
|
if (alarmCondition is not null)
|
||||||
{
|
{
|
||||||
|
@ -297,148 +295,147 @@ internal static class Program
|
||||||
Description = alarmCondition
|
Description = alarmCondition
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var alarm in record.AcDc.Alarms)
|
|
||||||
{
|
|
||||||
alarmList.Add(new AlarmOrWarning
|
|
||||||
{
|
|
||||||
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
|
||||||
Time = DateTime.Now.ToString("HH:mm:ss"),
|
|
||||||
CreatedBy = "AcDc",
|
|
||||||
Description = alarm.ToString()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var alarm in record.DcDc.Alarms)
|
|
||||||
{
|
|
||||||
alarmList.Add(new AlarmOrWarning
|
|
||||||
{
|
|
||||||
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
|
||||||
Time = DateTime.Now.ToString("HH:mm:ss"),
|
|
||||||
CreatedBy = "DcDc",
|
|
||||||
Description = alarm.ToString()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (record.Battery != null)
|
foreach (var alarm in record.AcDc.Alarms)
|
||||||
{
|
{
|
||||||
var i = 0;
|
alarmList.Add(new AlarmOrWarning
|
||||||
|
{
|
||||||
foreach (var battery in record.Battery.Devices)
|
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
||||||
{
|
Time = DateTime.Now.ToString("HH:mm:ss"),
|
||||||
i++;
|
CreatedBy = "AcDc",
|
||||||
foreach (var alarm in battery.Alarms)
|
Description = alarm.ToString()
|
||||||
{
|
});
|
||||||
alarmList.Add(new AlarmOrWarning
|
}
|
||||||
{
|
|
||||||
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
|
||||||
Time = DateTime.Now.ToString("HH:mm:ss"),
|
|
||||||
CreatedBy = "Battery" + i,
|
|
||||||
Description = alarm
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var warning in record.AcDc.Warnings)
|
|
||||||
{
|
|
||||||
warningList.Add(new AlarmOrWarning
|
|
||||||
{
|
|
||||||
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
|
||||||
Time = DateTime.Now.ToString("HH:mm:ss"),
|
|
||||||
CreatedBy = "AcDc",
|
|
||||||
Description = warning.ToString()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var warning in record.DcDc.Warnings)
|
|
||||||
{
|
|
||||||
warningList.Add(new AlarmOrWarning
|
|
||||||
{
|
|
||||||
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
|
||||||
Time = DateTime.Now.ToString("HH:mm:ss"),
|
|
||||||
CreatedBy = "DcDc",
|
|
||||||
Description = warning.ToString()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (record.Battery != null)
|
foreach (var alarm in record.DcDc.Alarms)
|
||||||
{
|
{
|
||||||
foreach (var warning in record.Battery.Warnings)
|
alarmList.Add(new AlarmOrWarning
|
||||||
{
|
{
|
||||||
warningList.Add(new AlarmOrWarning
|
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
||||||
{
|
Time = DateTime.Now.ToString("HH:mm:ss"),
|
||||||
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
CreatedBy = "DcDc",
|
||||||
Time = DateTime.Now.ToString("HH:mm:ss"),
|
Description = alarm.ToString()
|
||||||
CreatedBy = "Battery",
|
});
|
||||||
Description = warning
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var salimaxAlarmsState = (record.Battery is not null && record.Battery.Warnings.Any())
|
if (record.Battery != null)
|
||||||
| record.AcDc.Warnings.Any()
|
{
|
||||||
| record.AcDc.SystemControl.Warnings.Any()
|
var i = 0;
|
||||||
| record.DcDc.Warnings.Any()
|
|
||||||
? SalimaxAlarmState.Orange
|
|
||||||
: SalimaxAlarmState.Green; // this will be replaced by LedState
|
|
||||||
|
|
||||||
salimaxAlarmsState = (record.Battery is not null && record.Battery.Alarms.Any())
|
foreach (var battery in record.Battery.Devices)
|
||||||
| record.AcDc.Alarms.Any()
|
{
|
||||||
| record.AcDc.SystemControl.Alarms.Any()
|
i++;
|
||||||
| record.DcDc.Alarms.Any()
|
foreach (var alarm in battery.Alarms)
|
||||||
| alarmCondition is not null
|
{
|
||||||
? SalimaxAlarmState.Red
|
alarmList.Add(new AlarmOrWarning
|
||||||
: salimaxAlarmsState; // this will be replaced by LedState
|
{
|
||||||
|
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
||||||
|
Time = DateTime.Now.ToString("HH:mm:ss"),
|
||||||
|
CreatedBy = "Battery" + i,
|
||||||
|
Description = alarm
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int.TryParse(s3Bucket?.Split("-")[0], out var installationId);
|
foreach (var warning in record.AcDc.Warnings)
|
||||||
|
{
|
||||||
|
warningList.Add(new AlarmOrWarning
|
||||||
|
{
|
||||||
|
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
||||||
|
Time = DateTime.Now.ToString("HH:mm:ss"),
|
||||||
|
CreatedBy = "AcDc",
|
||||||
|
Description = warning.ToString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var returnedStatus = new StatusMessage
|
foreach (var warning in record.DcDc.Warnings)
|
||||||
{
|
{
|
||||||
InstallationId = installationId,
|
warningList.Add(new AlarmOrWarning
|
||||||
Status = salimaxAlarmsState,
|
{
|
||||||
Type = MessageType.AlarmOrWarning,
|
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
||||||
Alarms = alarmList,
|
Time = DateTime.Now.ToString("HH:mm:ss"),
|
||||||
Warnings = warningList
|
CreatedBy = "DcDc",
|
||||||
};
|
Description = warning.ToString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return returnedStatus;
|
if (record.Battery != null)
|
||||||
|
{
|
||||||
|
foreach (var warning in record.Battery.Warnings)
|
||||||
|
{
|
||||||
|
warningList.Add(new AlarmOrWarning
|
||||||
|
{
|
||||||
|
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
||||||
|
Time = DateTime.Now.ToString("HH:mm:ss"),
|
||||||
|
CreatedBy = "Battery",
|
||||||
|
Description = warning
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var salimaxAlarmsState = (record.Battery is not null && record.Battery.Warnings.Any())
|
||||||
|
| record.AcDc.Warnings.Any()
|
||||||
|
| record.AcDc.SystemControl.Warnings.Any()
|
||||||
|
| record.DcDc.Warnings.Any()
|
||||||
|
? SalimaxAlarmState.Orange
|
||||||
|
: SalimaxAlarmState.Green; // this will be replaced by LedState
|
||||||
|
|
||||||
|
salimaxAlarmsState = (record.Battery is not null && record.Battery.Alarms.Any())
|
||||||
|
| record.AcDc.Alarms.Any()
|
||||||
|
| record.AcDc.SystemControl.Alarms.Any()
|
||||||
|
| record.DcDc.Alarms.Any()
|
||||||
|
| alarmCondition is not null
|
||||||
|
? SalimaxAlarmState.Red
|
||||||
|
: salimaxAlarmsState; // this will be replaced by LedState
|
||||||
|
|
||||||
|
int.TryParse(s3Bucket?.Split("-")[0], out var installationId);
|
||||||
|
|
||||||
|
var returnedStatus = new StatusMessage
|
||||||
|
{
|
||||||
|
InstallationId = installationId,
|
||||||
|
Status = salimaxAlarmsState,
|
||||||
|
Type = MessageType.AlarmOrWarning,
|
||||||
|
Alarms = alarmList,
|
||||||
|
Warnings = warningList
|
||||||
|
};
|
||||||
|
|
||||||
|
return returnedStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String? DetectAlarmStates(this StatusRecord r) => r.Relays switch
|
private static String? DetectAlarmStates(this StatusRecord r) => r.Relays switch
|
||||||
{
|
{
|
||||||
{ K2ConnectIslandBusToGridBus: false, K2IslandBusIsConnectedToGridBus: true } => " Contradiction: R0 is opening the K2 but the K2 is still close ",
|
{ K2ConnectIslandBusToGridBus: false, K2IslandBusIsConnectedToGridBus: true } => " Contradiction: R0 is opening the K2 but the K2 is still close ",
|
||||||
{ K1GridBusIsConnectedToGrid : false, K2IslandBusIsConnectedToGridBus: true } => " Contradiction: K1 is open but the K2 is still close ",
|
{ K1GridBusIsConnectedToGrid : false, K2IslandBusIsConnectedToGridBus: true } => " Contradiction: K1 is open but the K2 is still close ",
|
||||||
{ FiError: true, K2IslandBusIsConnectedToGridBus: true } => " Contradiction: Fi error occured but the K2 is still close ",
|
{ FiError: true, K2IslandBusIsConnectedToGridBus: true } => " Contradiction: Fi error occured but the K2 is still close ",
|
||||||
_ => null
|
_ => null
|
||||||
};
|
};
|
||||||
|
|
||||||
private static void ControlConstants(this StatusRecord r)
|
private static void ControlConstants(this StatusRecord r)
|
||||||
{
|
{
|
||||||
var inverters = r.AcDc.Devices;
|
var inverters = r.AcDc.Devices;
|
||||||
var dcDevices = r.DcDc.Devices;
|
var dcDevices = r.DcDc.Devices;
|
||||||
var configFile = r.Config;
|
var configFile = r.Config;
|
||||||
|
|
||||||
var devicesConfig = r.AcDc.Devices.All(d => d.Control.Ac.GridType == GridType.GridTied400V50Hz) ? configFile.GridTie : configFile.IslandMode; // TODO if any of the grid tie mode
|
var devicesConfig = r.AcDc.Devices.All(d => d.Control.Ac.GridType == GridType.GridTied400V50Hz) ? configFile.GridTie : configFile.IslandMode; // TODO if any of the grid tie mode
|
||||||
|
|
||||||
inverters.ForEach(d => d.Control.Dc.MaxVoltage = devicesConfig.AcDc.MaxDcLinkVoltage);
|
|
||||||
inverters.ForEach(d => d.Control.Dc.MinVoltage = devicesConfig.AcDc.MinDcLinkVoltage);
|
|
||||||
inverters.ForEach(d => d.Control.Dc.ReferenceVoltage = devicesConfig.AcDc.ReferenceDcLinkVoltage);
|
|
||||||
|
|
||||||
inverters.ForEach(d => d.Control.Dc.PrechargeConfig = DcPrechargeConfig.PrechargeDcWithInternal);
|
|
||||||
|
|
||||||
dcDevices.ForEach(d => d.Control.DroopControl.UpperVoltage = devicesConfig.DcDc.UpperDcLinkVoltage);
|
|
||||||
dcDevices.ForEach(d => d.Control.DroopControl.LowerVoltage = devicesConfig.DcDc.LowerDcLinkVoltage);
|
|
||||||
dcDevices.ForEach(d => d.Control.DroopControl.ReferenceVoltage = devicesConfig.DcDc.ReferenceDcLinkVoltage);
|
|
||||||
|
|
||||||
dcDevices.ForEach(d => d.Control.CurrentControl.MaxBatteryChargingCurrent = configFile.MaxBatteryChargingCurrent);
|
|
||||||
dcDevices.ForEach(d => d.Control.CurrentControl.MaxBatteryDischargingCurrent = configFile.MaxBatteryDischargingCurrent);
|
|
||||||
dcDevices.ForEach(d => d.Control.MaxDcPower = configFile.MaxDcPower);
|
|
||||||
|
|
||||||
dcDevices.ForEach(d => d.Control.VoltageLimits.MaxBatteryVoltage = configFile.MaxChargeBatteryVoltage);
|
inverters.ForEach(d => d.Control.Dc.MaxVoltage = devicesConfig.AcDc.MaxDcLinkVoltage);
|
||||||
dcDevices.ForEach(d => d.Control.VoltageLimits.MinBatteryVoltage = configFile.MinDischargeBatteryVoltage);
|
inverters.ForEach(d => d.Control.Dc.MinVoltage = devicesConfig.AcDc.MinDcLinkVoltage);
|
||||||
dcDevices.ForEach(d => d.Control.ControlMode = DcControlMode.VoltageDroop);
|
inverters.ForEach(d => d.Control.Dc.ReferenceVoltage = devicesConfig.AcDc.ReferenceDcLinkVoltage);
|
||||||
|
|
||||||
|
inverters.ForEach(d => d.Control.Dc.PrechargeConfig = DcPrechargeConfig.PrechargeDcWithInternal);
|
||||||
|
|
||||||
|
dcDevices.ForEach(d => d.Control.DroopControl.UpperVoltage = devicesConfig.DcDc.UpperDcLinkVoltage);
|
||||||
|
dcDevices.ForEach(d => d.Control.DroopControl.LowerVoltage = devicesConfig.DcDc.LowerDcLinkVoltage);
|
||||||
|
dcDevices.ForEach(d => d.Control.DroopControl.ReferenceVoltage = devicesConfig.DcDc.ReferenceDcLinkVoltage);
|
||||||
|
|
||||||
|
dcDevices.ForEach(d => d.Control.CurrentControl.MaxBatteryChargingCurrent = configFile.MaxBatteryChargingCurrent);
|
||||||
|
dcDevices.ForEach(d => d.Control.CurrentControl.MaxBatteryDischargingCurrent = configFile.MaxBatteryDischargingCurrent);
|
||||||
|
dcDevices.ForEach(d => d.Control.MaxDcPower = configFile.MaxDcPower);
|
||||||
|
|
||||||
|
dcDevices.ForEach(d => d.Control.VoltageLimits.MaxBatteryVoltage = configFile.MaxChargeBatteryVoltage);
|
||||||
|
dcDevices.ForEach(d => d.Control.VoltageLimits.MinBatteryVoltage = configFile.MinDischargeBatteryVoltage);
|
||||||
|
dcDevices.ForEach(d => d.Control.ControlMode = DcControlMode.VoltageDroop);
|
||||||
|
|
||||||
r.DcDc.ResetAlarms();
|
r.DcDc.ResetAlarms();
|
||||||
r.AcDc.ResetAlarms();
|
r.AcDc.ResetAlarms();
|
||||||
|
@ -449,11 +446,11 @@ internal static class Program
|
||||||
private static void DistributePower(StatusRecord record, EssControl essControl)
|
private static void DistributePower(StatusRecord record, EssControl essControl)
|
||||||
{
|
{
|
||||||
var nInverters = record.AcDc.Devices.Count;
|
var nInverters = record.AcDc.Devices.Count;
|
||||||
|
|
||||||
var powerPerInverterPhase = nInverters > 0
|
var powerPerInverterPhase = nInverters > 0
|
||||||
? essControl.PowerSetpoint / nInverters / 3
|
? essControl.PowerSetpoint / nInverters / 3
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
record.AcDc.Devices.ForEach(d =>
|
record.AcDc.Devices.ForEach(d =>
|
||||||
{
|
{
|
||||||
d.Control.Ac.PhaseControl = PhaseControl.Asymmetric;
|
d.Control.Ac.PhaseControl = PhaseControl.Asymmetric;
|
||||||
|
@ -467,74 +464,71 @@ internal static class Program
|
||||||
{
|
{
|
||||||
if (sc is null)
|
if (sc is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sc.ReferenceFrame = ReferenceFrame.Consumer;
|
|
||||||
sc.SystemConfig = AcDcAndDcDc;
|
|
||||||
|
|
||||||
#if DEBUG
|
sc.ReferenceFrame = ReferenceFrame.Consumer;
|
||||||
sc.CommunicationTimeout = TimeSpan.FromMinutes(2);
|
sc.SystemConfig = AcDcAndDcDc;
|
||||||
#else
|
|
||||||
sc.CommunicationTimeout = TimeSpan.FromSeconds(20);
|
#if DEBUG
|
||||||
#endif
|
sc.CommunicationTimeout = TimeSpan.FromMinutes(2);
|
||||||
|
#else
|
||||||
|
sc.CommunicationTimeout = TimeSpan.FromSeconds(20);
|
||||||
|
#endif
|
||||||
|
|
||||||
sc.PowerSetPointActivation = PowerSetPointActivation.Immediate;
|
sc.PowerSetPointActivation = PowerSetPointActivation.Immediate;
|
||||||
sc.UseSlaveIdForAddressing = true;
|
sc.UseSlaveIdForAddressing = true;
|
||||||
sc.SlaveErrorHandling = SlaveErrorHandling.Relaxed;
|
sc.SlaveErrorHandling = SlaveErrorHandling.Relaxed;
|
||||||
sc.SubSlaveErrorHandling = SubSlaveErrorHandling.Off;
|
sc.SubSlaveErrorHandling = SubSlaveErrorHandling.Off;
|
||||||
|
|
||||||
sc.ResetAlarmsAndWarnings = true;
|
sc.ResetAlarmsAndWarnings = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ApplyDcDcDefaultSettings(this SystemControlRegisters? sc)
|
private static void ApplyDcDcDefaultSettings(this SystemControlRegisters? sc)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (sc is null)
|
if (sc is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sc.SystemConfig = DcDcOnly;
|
sc.SystemConfig = DcDcOnly;
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
sc.CommunicationTimeout = TimeSpan.FromMinutes(2);
|
sc.CommunicationTimeout = TimeSpan.FromMinutes(2);
|
||||||
#else
|
#else
|
||||||
sc.CommunicationTimeout = TimeSpan.FromSeconds(20);
|
sc.CommunicationTimeout = TimeSpan.FromSeconds(20);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
sc.PowerSetPointActivation = PowerSetPointActivation.Immediate;
|
sc.PowerSetPointActivation = PowerSetPointActivation.Immediate;
|
||||||
sc.UseSlaveIdForAddressing = true;
|
sc.UseSlaveIdForAddressing = true;
|
||||||
sc.SlaveErrorHandling = SlaveErrorHandling.Relaxed;
|
sc.SlaveErrorHandling = SlaveErrorHandling.Relaxed;
|
||||||
sc.SubSlaveErrorHandling = SubSlaveErrorHandling.Off;
|
sc.SubSlaveErrorHandling = SubSlaveErrorHandling.Off;
|
||||||
|
sc.ResetAlarmsAndWarnings = true;
|
||||||
sc.ResetAlarmsAndWarnings = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<Boolean> UploadCsv(StatusRecord status, DateTime timeStamp)
|
private static async Task<Boolean> UploadCsv(StatusRecord status, DateTime timeStamp)
|
||||||
{
|
{
|
||||||
var s3Config = status.Config.S3;
|
var s3Config = status.Config.S3;
|
||||||
var csv = status.ToCsv().LogInfo();
|
var csv = status.ToCsv().LogInfo();
|
||||||
if (s3Config is null)
|
if (s3Config is null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var s3Path = timeStamp.ToUnixTime() + ".csv";
|
var s3Path = timeStamp.ToUnixTime() + ".csv";
|
||||||
var request = s3Config.CreatePutRequest(s3Path);
|
var request = s3Config.CreatePutRequest(s3Path);
|
||||||
var response = await request.PutAsync(new StringContent(csv));
|
var response = await request.PutAsync(new StringContent(csv));
|
||||||
|
|
||||||
// This is temporary for Wittman
|
// This is temporary for Wittman
|
||||||
//await File.WriteAllTextAsync("/var/www/html/status.csv", csv.SplitLines().Where(l => !l.Contains("Secret")).JoinLines());
|
//await File.WriteAllTextAsync("/var/www/html/status.csv", csv.SplitLines().Where(l => !l.Contains("Secret")).JoinLines());
|
||||||
|
|
||||||
if (response.StatusCode != 200)
|
if (response.StatusCode != 200)
|
||||||
{
|
{
|
||||||
Console.WriteLine("ERROR: PUT");
|
Console.WriteLine("ERROR: PUT");
|
||||||
var error = await response.GetStringAsync();
|
var error = await response.GetStringAsync();
|
||||||
Console.WriteLine(error);
|
Console.WriteLine(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ApplyConfigFile(this StatusRecord status, Configuration? config)
|
private static void ApplyConfigFile(this StatusRecord status, Configuration? config)
|
||||||
{
|
{
|
||||||
status.Config.MinSoc = config.MinimumSoC;
|
status.Config.MinSoc = config.MinimumSoC;
|
||||||
status.Config.GridSetPoint = config.GridSetPoint * 1000; // converted from kW to W
|
status.Config.GridSetPoint = config.GridSetPoint * 1000; // converted from kW to W
|
||||||
status.Config.ForceCalibrationCharge = config.ForceCalibrationCharge;
|
status.Config.ForceCalibrationCharge = config.ForceCalibrationCharge;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -3,125 +3,230 @@
|
||||||
import { ApexOptions } from 'apexcharts';
|
import { ApexOptions } from 'apexcharts';
|
||||||
import { chartInfoInterface } from 'src/interfaces/Chart';
|
import { chartInfoInterface } from 'src/interfaces/Chart';
|
||||||
import { findPower, formatPowerForGraph } from 'src/Resources/formatPower';
|
import { findPower, formatPowerForGraph } from 'src/Resources/formatPower';
|
||||||
import { addHours, format } from 'date-fns';
|
|
||||||
|
|
||||||
export const getChartOptions = (chartInfo: chartInfoInterface): ApexOptions => {
|
export const getChartOptions = (
|
||||||
// Custom datetime formatter for GMT+2
|
chartInfo: chartInfoInterface,
|
||||||
const customDatetimeFormatter = (timestamp, options) => {
|
type: string
|
||||||
const gmtDate = new Date(timestamp); // Convert Unix timestamp to milliseconds
|
): ApexOptions => {
|
||||||
const gmtPlus2Date = addHours(gmtDate, 4); // Add 2 hours to convert to GMT+2
|
const chartOptions: ApexOptions =
|
||||||
|
type == 'daily'
|
||||||
|
? {
|
||||||
|
chart: {
|
||||||
|
id: 'area-datetime',
|
||||||
|
toolbar: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
type: 'area',
|
||||||
|
height: 350,
|
||||||
|
zoom: {
|
||||||
|
autoScaleYaxis: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Use the specified options to format the date and time
|
dataLabels: {
|
||||||
const year = format(gmtDate, 'yyyy');
|
enabled: false
|
||||||
const month = format(gmtDate, "MMM 'yy");
|
},
|
||||||
const day = format(gmtDate, 'dd MMM');
|
|
||||||
const hour = format(gmtDate, 'HH:mm');
|
|
||||||
const minute = format(gmtDate, 'mm');
|
|
||||||
|
|
||||||
// Return the formatted date and time based on the provided options
|
fill: {
|
||||||
return ` ${hour}:${minute}`;
|
type: 'gradient',
|
||||||
};
|
gradient: {
|
||||||
|
shadeIntensity: 1,
|
||||||
|
opacityFrom: 0.7,
|
||||||
|
opacityTo: 0.9,
|
||||||
|
stops: [0, 100]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//colors: ['#FF5733', '#3498db'],
|
||||||
|
colors: ['#3498db', '#2ecc71'],
|
||||||
|
//colors: ['#1abc9c', '#e91e63'],
|
||||||
|
xaxis: {
|
||||||
|
type: 'datetime',
|
||||||
|
labels: {
|
||||||
|
datetimeFormatter: {
|
||||||
|
year: 'yyyy',
|
||||||
|
month: "MMM 'yy",
|
||||||
|
day: 'dd MMM',
|
||||||
|
hour: 'HH:mm',
|
||||||
|
minute: 'mm'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
stroke: {
|
||||||
|
curve: 'smooth',
|
||||||
|
width: 2
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
min:
|
||||||
|
chartInfo.min >= 0
|
||||||
|
? 0
|
||||||
|
: chartInfo.max <= 0
|
||||||
|
? Math.ceil(chartInfo.min / findPower(chartInfo.min).value) *
|
||||||
|
findPower(chartInfo.min).value
|
||||||
|
: undefined,
|
||||||
|
max:
|
||||||
|
chartInfo.min >= 0
|
||||||
|
? Math.ceil(chartInfo.max / findPower(chartInfo.max).value) *
|
||||||
|
findPower(chartInfo.max).value
|
||||||
|
: chartInfo.max <= 0
|
||||||
|
? 0
|
||||||
|
: undefined,
|
||||||
|
title: {
|
||||||
|
text: chartInfo.unit,
|
||||||
|
style: {
|
||||||
|
fontSize: '12px'
|
||||||
|
},
|
||||||
|
offsetY: -160,
|
||||||
|
offsetX: 25,
|
||||||
|
rotate: 0
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
formatter: function (value: number) {
|
||||||
|
return formatPowerForGraph(
|
||||||
|
value,
|
||||||
|
Math.max(Math.abs(chartInfo.max), Math.abs(chartInfo.min))
|
||||||
|
).value.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
const chartOptions: ApexOptions = {
|
tooltip: {
|
||||||
chart: {
|
x: {
|
||||||
id: 'area-datetime',
|
format: 'dd MMM HH:mm:ss'
|
||||||
toolbar: {
|
},
|
||||||
show: false
|
y: {
|
||||||
},
|
formatter: function (val, opts) {
|
||||||
type: 'area',
|
return (
|
||||||
height: 350,
|
formatPowerForGraph(
|
||||||
zoom: {
|
val,
|
||||||
autoScaleYaxis: false
|
Math.max(Math.abs(chartInfo.max), Math.abs(chartInfo.min))
|
||||||
}
|
).value.toFixed(2) +
|
||||||
},
|
' ' +
|
||||||
// markers: {
|
chartInfo.unit
|
||||||
// size: 1,
|
);
|
||||||
// strokeColors: 'black'
|
}
|
||||||
// },
|
}
|
||||||
dataLabels: {
|
}
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
|
|
||||||
fill: {
|
|
||||||
type: 'gradient',
|
|
||||||
gradient: {
|
|
||||||
shadeIntensity: 1,
|
|
||||||
opacityFrom: 0.7,
|
|
||||||
opacityTo: 0.9,
|
|
||||||
stops: [0, 100]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
//colors: ['#FF5733', '#3498db'],
|
|
||||||
colors: ['#3498db', '#2ecc71'],
|
|
||||||
//colors: ['#1abc9c', '#e91e63'],
|
|
||||||
xaxis: {
|
|
||||||
type: 'datetime',
|
|
||||||
labels: {
|
|
||||||
datetimeFormatter: {
|
|
||||||
year: 'yyyy',
|
|
||||||
month: "MMM 'yy",
|
|
||||||
day: 'dd MMM',
|
|
||||||
hour: 'HH:mm',
|
|
||||||
minute: 'mm'
|
|
||||||
}
|
}
|
||||||
}
|
: type == 'monthly'
|
||||||
},
|
? {
|
||||||
stroke: {
|
chart: {
|
||||||
curve: 'smooth',
|
height: 350,
|
||||||
width: 2
|
type: 'bar'
|
||||||
},
|
},
|
||||||
yaxis: {
|
plotOptions: {
|
||||||
min:
|
bar: {
|
||||||
chartInfo.min >= 0
|
borderRadius: 10,
|
||||||
? 0
|
dataLabels: {
|
||||||
: chartInfo.max <= 0
|
position: 'top' // top, center, bottom
|
||||||
? Math.ceil(chartInfo.min / findPower(chartInfo.min).value) *
|
}
|
||||||
findPower(chartInfo.min).value
|
}
|
||||||
: undefined,
|
},
|
||||||
max:
|
dataLabels: {
|
||||||
chartInfo.min >= 0
|
enabled: true,
|
||||||
? Math.ceil(chartInfo.max / findPower(chartInfo.max).value) *
|
formatter: function (val) {
|
||||||
findPower(chartInfo.max).value
|
return val + '%';
|
||||||
: chartInfo.max <= 0
|
},
|
||||||
? 0
|
offsetY: -20,
|
||||||
: undefined,
|
style: {
|
||||||
title: {
|
fontSize: '12px',
|
||||||
text: chartInfo.unit,
|
colors: ['#304758']
|
||||||
style: {
|
}
|
||||||
fontSize: '12px'
|
},
|
||||||
},
|
|
||||||
offsetY: -160,
|
|
||||||
offsetX: 25,
|
|
||||||
rotate: 0
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
formatter: function (value: number) {
|
|
||||||
return formatPowerForGraph(
|
|
||||||
value,
|
|
||||||
Math.max(Math.abs(chartInfo.max), Math.abs(chartInfo.min))
|
|
||||||
).value.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
tooltip: {
|
xaxis: {
|
||||||
x: {
|
categories: [
|
||||||
format: 'dd MMM HH:mm:ss'
|
'Jan',
|
||||||
},
|
'Feb',
|
||||||
y: {
|
'Mar',
|
||||||
formatter: function (val, opts) {
|
'Apr',
|
||||||
return (
|
'May',
|
||||||
formatPowerForGraph(
|
'Jun',
|
||||||
val,
|
'Jul',
|
||||||
Math.max(Math.abs(chartInfo.max), Math.abs(chartInfo.min))
|
'Aug',
|
||||||
).value.toFixed(2) +
|
'Sep',
|
||||||
' ' +
|
'Oct',
|
||||||
chartInfo.unit
|
'Nov',
|
||||||
);
|
'Dec'
|
||||||
|
],
|
||||||
|
position: 'bottom',
|
||||||
|
axisBorder: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisTicks: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
axisBorder: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisTicks: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
show: false,
|
||||||
|
formatter: function (val) {
|
||||||
|
return val + '%';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
: {
|
||||||
}
|
chart: {
|
||||||
};
|
height: 350,
|
||||||
|
type: 'bar'
|
||||||
|
},
|
||||||
|
plotOptions: {
|
||||||
|
bar: {
|
||||||
|
borderRadius: 10,
|
||||||
|
dataLabels: {
|
||||||
|
position: 'top' // top, center, bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dataLabels: {
|
||||||
|
enabled: true,
|
||||||
|
formatter: function (val) {
|
||||||
|
return val + '%';
|
||||||
|
},
|
||||||
|
offsetY: -20,
|
||||||
|
style: {
|
||||||
|
fontSize: '12px',
|
||||||
|
colors: ['#304758']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
xaxis: {
|
||||||
|
categories: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
||||||
|
position: 'bottom',
|
||||||
|
axisBorder: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisTicks: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
axisBorder: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisTicks: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
show: false,
|
||||||
|
formatter: function (val) {
|
||||||
|
return val + '%';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return chartOptions;
|
return chartOptions;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,11 +1,4 @@
|
||||||
import {
|
import { Box, Card, Container, Grid, Typography } from '@mui/material';
|
||||||
Box,
|
|
||||||
Card,
|
|
||||||
Container,
|
|
||||||
Grid,
|
|
||||||
Typography,
|
|
||||||
useTheme
|
|
||||||
} from '@mui/material';
|
|
||||||
import ReactApexChart from 'react-apexcharts';
|
import ReactApexChart from 'react-apexcharts';
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import DataCache from 'src/dataCache/dataCache';
|
import DataCache from 'src/dataCache/dataCache';
|
||||||
|
@ -25,7 +18,6 @@ import { chartDataInterface, overviewInterface } from 'src/interfaces/Chart';
|
||||||
import { fetchData } from 'src/content/dashboards/Installations/fetchData';
|
import { fetchData } from 'src/content/dashboards/Installations/fetchData';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import { ApexOptions } from 'apexcharts';
|
|
||||||
|
|
||||||
const prefixes = ['', 'k', 'M', 'G', 'T'];
|
const prefixes = ['', 'k', 'M', 'G', 'T'];
|
||||||
const MAX_NUMBER = 9999999;
|
const MAX_NUMBER = 9999999;
|
||||||
|
@ -35,7 +27,6 @@ interface OverviewProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function Overview(props: OverviewProps) {
|
function Overview(props: OverviewProps) {
|
||||||
const theme = useTheme();
|
|
||||||
const numOfPointsToFetch = 100;
|
const numOfPointsToFetch = 100;
|
||||||
const [timeRange, setTimeRange] = useState(
|
const [timeRange, setTimeRange] = useState(
|
||||||
createTimes(
|
createTimes(
|
||||||
|
@ -80,6 +71,9 @@ function Overview(props: OverviewProps) {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const times$ = useMemo(() => new BehaviorSubject(timeRange), []);
|
const times$ = useMemo(() => new BehaviorSubject(timeRange), []);
|
||||||
|
const [dailyData, setDailyData] = useState(true);
|
||||||
|
const [weeklyData, setWeeklyData] = useState(false);
|
||||||
|
const [monthlyData, setMonthlyData] = useState(false);
|
||||||
|
|
||||||
const transformToGraphData = (
|
const transformToGraphData = (
|
||||||
input: RecordSeries
|
input: RecordSeries
|
||||||
|
@ -157,12 +151,10 @@ function Overview(props: OverviewProps) {
|
||||||
Math.abs(overviewData[path].max),
|
Math.abs(overviewData[path].max),
|
||||||
Math.abs(overviewData[path].min)
|
Math.abs(overviewData[path].min)
|
||||||
);
|
);
|
||||||
let negative = false;
|
|
||||||
let magnitude = 0;
|
let magnitude = 0;
|
||||||
|
|
||||||
if (value < 0) {
|
if (value < 0) {
|
||||||
value = -value;
|
value = -value;
|
||||||
negative = true;
|
|
||||||
}
|
}
|
||||||
while (value >= 1000) {
|
while (value >= 1000) {
|
||||||
value /= 1000;
|
value /= 1000;
|
||||||
|
@ -289,6 +281,9 @@ function Overview(props: OverviewProps) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handle24HourData = () => {
|
const handle24HourData = () => {
|
||||||
|
setDailyData(true);
|
||||||
|
setWeeklyData(false);
|
||||||
|
setMonthlyData(false);
|
||||||
const times = createTimes(
|
const times = createTimes(
|
||||||
UnixTime.now().rangeBefore(TimeSpan.fromDays(1)),
|
UnixTime.now().rangeBefore(TimeSpan.fromDays(1)),
|
||||||
numOfPointsToFetch
|
numOfPointsToFetch
|
||||||
|
@ -298,21 +293,28 @@ function Overview(props: OverviewProps) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleWeekData = () => {
|
const handleWeekData = () => {
|
||||||
const times = createTimes(
|
setDailyData(false);
|
||||||
UnixTime.now().rangeBefore(TimeSpan.fromWeeks(1)),
|
setWeeklyData(true);
|
||||||
numOfPointsToFetch
|
setMonthlyData(false);
|
||||||
);
|
//fetchData(12312,props.s3Credentials);
|
||||||
cache.getSeries(times);
|
// const times = createTimes(
|
||||||
times$.next(times);
|
// UnixTime.now().rangeBefore(TimeSpan.fromWeeks(1)),
|
||||||
|
// numOfPointsToFetch
|
||||||
|
// );
|
||||||
|
// cache.getSeries(times);
|
||||||
|
// times$.next(times);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMonthData = () => {
|
const handleMonthData = () => {
|
||||||
const times = createTimes(
|
setDailyData(false);
|
||||||
UnixTime.now().rangeBefore(TimeSpan.fromWeeks(4)),
|
setWeeklyData(false);
|
||||||
numOfPointsToFetch
|
setMonthlyData(true);
|
||||||
);
|
// const times = createTimes(
|
||||||
cache.getSeries(times);
|
// UnixTime.now().rangeBefore(TimeSpan.fromWeeks(4)),
|
||||||
times$.next(times);
|
// numOfPointsToFetch
|
||||||
|
// );
|
||||||
|
// cache.getSeries(times);
|
||||||
|
// times$.next(times);
|
||||||
};
|
};
|
||||||
|
|
||||||
const series = [
|
const series = [
|
||||||
|
@ -322,73 +324,6 @@ function Overview(props: OverviewProps) {
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const state: ApexOptions = {
|
|
||||||
chart: {
|
|
||||||
height: 350,
|
|
||||||
type: 'bar'
|
|
||||||
},
|
|
||||||
plotOptions: {
|
|
||||||
bar: {
|
|
||||||
borderRadius: 10,
|
|
||||||
dataLabels: {
|
|
||||||
position: 'top' // top, center, bottom
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dataLabels: {
|
|
||||||
enabled: true,
|
|
||||||
formatter: function (val) {
|
|
||||||
return val + '%';
|
|
||||||
},
|
|
||||||
offsetY: -20,
|
|
||||||
style: {
|
|
||||||
fontSize: '12px',
|
|
||||||
colors: ['#304758']
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
xaxis: {
|
|
||||||
categories: [
|
|
||||||
'Jan',
|
|
||||||
'Feb',
|
|
||||||
'Mar',
|
|
||||||
'Apr',
|
|
||||||
'May',
|
|
||||||
'Jun',
|
|
||||||
'Jul',
|
|
||||||
'Aug',
|
|
||||||
'Sep',
|
|
||||||
'Oct',
|
|
||||||
'Nov',
|
|
||||||
'Dec'
|
|
||||||
],
|
|
||||||
position: 'bottom',
|
|
||||||
axisBorder: {
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
axisTicks: {
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
enabled: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
yaxis: {
|
|
||||||
axisBorder: {
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
axisTicks: {
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
show: false,
|
|
||||||
formatter: function (val) {
|
|
||||||
return val + '%';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderGraphs = () => {
|
const renderGraphs = () => {
|
||||||
return (
|
return (
|
||||||
<Container maxWidth="xl">
|
<Container maxWidth="xl">
|
||||||
|
@ -475,26 +410,43 @@ function Overview(props: OverviewProps) {
|
||||||
></Box>
|
></Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<ReactApexChart
|
{dailyData && (
|
||||||
options={state}
|
<ReactApexChart
|
||||||
series={series}
|
options={{
|
||||||
type="bar"
|
...getChartOptions(chartOverview.soc, 'daily'),
|
||||||
height={350}
|
chart: {
|
||||||
/>
|
events: {
|
||||||
|
beforeZoom: handleBeforeZoom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
series={chartData.soc}
|
||||||
|
type="area"
|
||||||
|
height={350}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{/*<ReactApexChart*/}
|
{weeklyData && (
|
||||||
{/* options={{*/}
|
<ReactApexChart
|
||||||
{/* ...getChartOptions(chartOverview.soc),*/}
|
options={{
|
||||||
{/* chart: {*/}
|
...getChartOptions(chartOverview.soc, 'weekly')
|
||||||
{/* events: {*/}
|
}}
|
||||||
{/* beforeZoom: handleBeforeZoom*/}
|
series={series}
|
||||||
{/* }*/}
|
type="bar"
|
||||||
{/* }*/}
|
height={350}
|
||||||
{/* }}*/}
|
/>
|
||||||
{/* series={chartData.soc}*/}
|
)}
|
||||||
{/* type="area"*/}
|
|
||||||
{/* height={350}*/}
|
{monthlyData && (
|
||||||
{/*/>*/}
|
<ReactApexChart
|
||||||
|
options={{
|
||||||
|
...getChartOptions(chartOverview.soc, 'monthly')
|
||||||
|
}}
|
||||||
|
series={series}
|
||||||
|
type="bar"
|
||||||
|
height={350}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item md={6} xs={12}>
|
<Grid item md={6} xs={12}>
|
||||||
|
@ -532,7 +484,7 @@ function Overview(props: OverviewProps) {
|
||||||
<div onDoubleClick={handleDoubleClick}>
|
<div onDoubleClick={handleDoubleClick}>
|
||||||
<ReactApexChart
|
<ReactApexChart
|
||||||
options={{
|
options={{
|
||||||
...getChartOptions(chartOverview.temperature),
|
...getChartOptions(chartOverview.temperature, 'daily'),
|
||||||
chart: {
|
chart: {
|
||||||
events: {
|
events: {
|
||||||
beforeZoom: handleBeforeZoom
|
beforeZoom: handleBeforeZoom
|
||||||
|
@ -590,7 +542,7 @@ function Overview(props: OverviewProps) {
|
||||||
<div onDoubleClick={handleDoubleClick}>
|
<div onDoubleClick={handleDoubleClick}>
|
||||||
<ReactApexChart
|
<ReactApexChart
|
||||||
options={{
|
options={{
|
||||||
...getChartOptions(chartOverview.pvProduction),
|
...getChartOptions(chartOverview.pvProduction, 'daily'),
|
||||||
chart: {
|
chart: {
|
||||||
events: {
|
events: {
|
||||||
beforeZoom: handleBeforeZoom
|
beforeZoom: handleBeforeZoom
|
||||||
|
@ -639,7 +591,7 @@ function Overview(props: OverviewProps) {
|
||||||
<div onDoubleClick={handleDoubleClick}>
|
<div onDoubleClick={handleDoubleClick}>
|
||||||
<ReactApexChart
|
<ReactApexChart
|
||||||
options={{
|
options={{
|
||||||
...getChartOptions(chartOverview.gridPower),
|
...getChartOptions(chartOverview.gridPower, 'daily'),
|
||||||
chart: {
|
chart: {
|
||||||
events: {
|
events: {
|
||||||
beforeZoom: handleBeforeZoom
|
beforeZoom: handleBeforeZoom
|
||||||
|
@ -696,7 +648,7 @@ function Overview(props: OverviewProps) {
|
||||||
<div onDoubleClick={handleDoubleClick}>
|
<div onDoubleClick={handleDoubleClick}>
|
||||||
<ReactApexChart
|
<ReactApexChart
|
||||||
options={{
|
options={{
|
||||||
...getChartOptions(chartOverview.dcPower),
|
...getChartOptions(chartOverview.dcPower, 'daily'),
|
||||||
chart: {
|
chart: {
|
||||||
events: {
|
events: {
|
||||||
beforeZoom: handleBeforeZoom
|
beforeZoom: handleBeforeZoom
|
||||||
|
@ -745,7 +697,7 @@ function Overview(props: OverviewProps) {
|
||||||
<div onDoubleClick={handleDoubleClick}>
|
<div onDoubleClick={handleDoubleClick}>
|
||||||
<ReactApexChart
|
<ReactApexChart
|
||||||
options={{
|
options={{
|
||||||
...getChartOptions(chartOverview.dcBusVoltage),
|
...getChartOptions(chartOverview.dcBusVoltage, 'daily'),
|
||||||
chart: {
|
chart: {
|
||||||
events: {
|
events: {
|
||||||
beforeZoom: handleBeforeZoom
|
beforeZoom: handleBeforeZoom
|
||||||
|
|
Loading…
Reference in New Issue