diff --git a/csharp/App/Backend/DataTypes/Methods/Session.cs b/csharp/App/Backend/DataTypes/Methods/Session.cs index bc829bdc8..7fe7a23fa 100644 --- a/csharp/App/Backend/DataTypes/Methods/Session.cs +++ b/csharp/App/Backend/DataTypes/Methods/Session.cs @@ -75,14 +75,14 @@ public static class SessionMethods await Task.Run(() => { - Process process = new Process(); + var process = new Process(); process.StartInfo.FileName = "/bin/bash"; process.StartInfo.Arguments = $"{scriptPath} {vpnIp} {batteryNode} {version}"; process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = true; process.Start(); - string output = process.StandardOutput.ReadToEnd(); + var output = process.StandardOutput.ReadToEnd(); process.WaitForExit(); Console.WriteLine(output); }); @@ -143,7 +143,7 @@ public static class SessionMethods //Salimax installation - if (installation.Product==0) + if (installation.Product == 0) { return user is not null && user.UserType != 0 @@ -156,7 +156,7 @@ public static class SessionMethods } - if (installation.Product==1) + if (installation.Product == 1) { return user is not null && user.UserType != 0 @@ -175,7 +175,7 @@ public static class SessionMethods var original = Db.GetInstallationById(installation?.Id); //Salimax installation - if (installation.Product==0) + if (installation.Product == 0) { return user is not null diff --git a/csharp/App/SaliMax/src/AggregationService/Aggregator.cs b/csharp/App/SaliMax/src/AggregationService/Aggregator.cs index bd37c99d8..8a6a0ea70 100644 --- a/csharp/App/SaliMax/src/AggregationService/Aggregator.cs +++ b/csharp/App/SaliMax/src/AggregationService/Aggregator.cs @@ -227,8 +227,8 @@ public static class Aggregator Console.WriteLine($"Max SOC: {aggregatedData.MaxSoc}"); Console.WriteLine($"Min SOC: {aggregatedData.MinSoc}"); - Console.WriteLine($"ChargingBatteryPower: {aggregatedData.DischargingBatteryPower}"); - Console.WriteLine($"DischargingBatteryBattery: {aggregatedData.ChargingBatteryPower}"); + Console.WriteLine($"DischargingBatteryBattery: {aggregatedData.DischargingBatteryPower}"); + Console.WriteLine($"ChargingBatteryPower: {aggregatedData.ChargingBatteryPower}"); Console.WriteLine($"SumGridExportPower: {aggregatedData.GridExportPower}"); Console.WriteLine($"SumGridImportPower: {aggregatedData.GridImportPower}"); diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs index ee0a76355..e54ddb987 100644 --- a/csharp/App/SaliMax/src/Program.cs +++ b/csharp/App/SaliMax/src/Program.cs @@ -678,35 +678,36 @@ internal static class Program // This is temporary for Wittman, but now it's for all Instalattion await File.WriteAllTextAsync("/var/www/html/status.csv", csv.SplitLines().Where(l => !l.Contains("Secret")).JoinLines()); - + + var response = await request.PutAsync(new StringContent(csv)); // Compress CSV data to a byte array - byte[] compressedBytes; - using (var memoryStream = new MemoryStream()) - { - //Create a zip directory and put the compressed file inside - using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) - { - var entry = archive.CreateEntry("data.csv", CompressionLevel.SmallestSize); // Add CSV data to the ZIP archive - using (var entryStream = entry.Open()) - using (var writer = new StreamWriter(entryStream)) - { - writer.Write(csv); - } - } - - compressedBytes = memoryStream.ToArray(); - } - - // Encode the compressed byte array as a Base64 string - string base64String = Convert.ToBase64String(compressedBytes); - - // Create StringContent from Base64 string - var stringContent = new StringContent(base64String, Encoding.UTF8, "application/base64"); - - // Upload the compressed data (ZIP archive) to S3 - var response = await request.PutAsync(stringContent); - + // byte[] compressedBytes; + // using (var memoryStream = new MemoryStream()) + // { + // //Create a zip directory and put the compressed file inside + // using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) + // { + // var entry = archive.CreateEntry("data.csv", CompressionLevel.SmallestSize); // Add CSV data to the ZIP archive + // using (var entryStream = entry.Open()) + // using (var writer = new StreamWriter(entryStream)) + // { + // writer.Write(csv); + // } + // } + // + // compressedBytes = memoryStream.ToArray(); + // } + // + // // Encode the compressed byte array as a Base64 string + // string base64String = Convert.ToBase64String(compressedBytes); + // + // // Create StringContent from Base64 string + // var stringContent = new StringContent(base64String, Encoding.UTF8, "application/base64"); + // + // // Upload the compressed data (ZIP archive) to S3 + // var response = await request.PutAsync(stringContent); + // if (response.StatusCode != 200) { Console.WriteLine("ERROR: PUT"); diff --git a/csharp/App/SaliMax/src/S3Config.cs b/csharp/App/SaliMax/src/S3Config.cs index 741a5c565..cbf2a92f9 100644 --- a/csharp/App/SaliMax/src/S3Config.cs +++ b/csharp/App/SaliMax/src/S3Config.cs @@ -70,7 +70,8 @@ public record S3Config // CanonicalizedResource; - contentType = "application/base64; charset=utf-8"; + //contentType = "application/base64; charset=utf-8"; + contentType = "text/plain; charset=utf-8"; var payload = $"{method}\n{md5Hash}\n{contentType}\n{date}\n/{bucket.Trim('/')}/{s3Path.Trim('/')}"; using var hmacSha1 = new HMACSHA1(UTF8.GetBytes(s3Secret)); diff --git a/csharp/Lib/Devices/AMPT/AmptDevices.cs b/csharp/Lib/Devices/AMPT/AmptDevices.cs index b1d6d3ec2..003e09b20 100644 --- a/csharp/Lib/Devices/AMPT/AmptDevices.cs +++ b/csharp/Lib/Devices/AMPT/AmptDevices.cs @@ -77,12 +77,13 @@ public class AmptDevices Current = busCurrent }; - // flatten the 2 strings of each SO into one array - var strings = soStati.SelectMany(GetStrings).ToArray(nStrings); + // flatten the output strings of each SO into one array + var strings = soStati.SelectMany(GetDc).ToArray(nStrings); return new AmptStatus { Dc = dc, + NbrOfStrings = nStringOptimizers, Strings = strings, DcWh = dailyOutputEnergy }; @@ -106,6 +107,16 @@ public class AmptDevices }; } + private static IEnumerable GetDc(StringOptimizerRegisters r) + { + // hardcoded: every SO has 2 strings (produced like this by AMPT) + + yield return new() + { + Voltage = r.Voltage, + Current = r.Current, + }; + } private static IEnumerable> StringOptimizers(ModbusClient modbusClient) { diff --git a/csharp/Lib/Devices/AMPT/AmptStatus.cs b/csharp/Lib/Devices/AMPT/AmptStatus.cs index bbaa5fd05..259bc86f5 100644 --- a/csharp/Lib/Devices/AMPT/AmptStatus.cs +++ b/csharp/Lib/Devices/AMPT/AmptStatus.cs @@ -5,8 +5,9 @@ namespace InnovEnergy.Lib.Devices.AMPT; public class AmptStatus : IMppt { - public required DcBus Dc { get; init; } - public required IReadOnlyList Strings { get; init; } - public required Double DcWh { get; init; } //Daily integrated string output energy in Wh + public required DcBus Dc { get; init; } + public required UInt16 NbrOfStrings { get; init; } + public required IReadOnlyList Strings { get; init; } + public required Double DcWh { get; init; } //Daily integrated string output energy in Wh } diff --git a/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeterDevice.cs b/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeterDevice.cs index 7a1106c5f..863a30c9f 100644 --- a/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeterDevice.cs +++ b/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeterDevice.cs @@ -1,4 +1,4 @@ -/*using InnovEnergy.Lib.Protocols.Modbus.Channels; +using InnovEnergy.Lib.Protocols.Modbus.Channels; using InnovEnergy.Lib.Protocols.Modbus.Clients; using InnovEnergy.Lib.Protocols.Modbus.Slaves; using InnovEnergy.Lib.Utils; @@ -49,83 +49,4 @@ public class Iem3KGridMeterDevice: ModbusDevice } -}*/ - -using InnovEnergy.Lib.Protocols.Modbus.Channels; -using InnovEnergy.Lib.Protocols.Modbus.Clients; -using InnovEnergy.Lib.Protocols.Modbus.Slaves; -using InnovEnergy.Lib.Utils; -using System; - -namespace InnovEnergy.Lib.Devices.IEM3kGridMeter -{ - public class Iem3KGridMeterDevice : ModbusDevice - { - private readonly string _hostname; - private readonly ushort _port; - private readonly byte _slaveId; - - public Iem3KGridMeterDevice(string hostname, ushort port = 502, byte slaveId = 1) - : this(new TcpChannel(hostname, port), slaveId) - { - _hostname = hostname ?? throw new ArgumentNullException(nameof(hostname)); - _port = port; - _slaveId = slaveId; - } - - private Iem3KGridMeterDevice(TcpChannel channel, byte slaveId = 1) - : base(new ModbusTcpClient(channel, slaveId)) - { - _hostname = channel.Host; - _port = channel.Port; - _slaveId = slaveId; - Console.WriteLine($"Initializing Iem3KGridMeterDevice with channel: {channel.Host}:{channel.Port}"); - } - - public Iem3KGridMeterDevice(ModbusClient client) - : base(client) - { - if (client is ModbusTcpClient tcpClient) - { - _hostname = tcpClient.Channel.Host; - _port = tcpClient.Channel.Port; - _slaveId = tcpClient.SlaveId; - } - else - { - throw new ArgumentException("Invalid client type", nameof(client)); - } - Console.WriteLine("Initializing Iem3KGridMeterDevice with ModbusClient"); - } - - public new Iem3KGridMeterRegisters? Read() - { - try - { - Console.WriteLine($"Attempting to read data from {_hostname}:{_port} with slaveId {_slaveId}"); - return base.Read(); - } - catch (Exception ex) - { - Console.WriteLine($"Failed to read data from {nameof(Iem3KGridMeterDevice)}: {ex.Message}"); - return null; - } - } - - public new void Write(Iem3KGridMeterRegisters registers) - { - try - { - base.Write(registers); - } - catch (Exception ex) - { - Console.WriteLine($"Failed to write data to {nameof(Iem3KGridMeterDevice)}: {ex.Message}"); - } - } - } -} - - - - +} \ No newline at end of file diff --git a/csharp/Lib/Protocols/Modbus/Channels/TcpChannel.cs b/csharp/Lib/Protocols/Modbus/Channels/TcpChannel.cs index f1f39a09a..22a20d018 100644 --- a/csharp/Lib/Protocols/Modbus/Channels/TcpChannel.cs +++ b/csharp/Lib/Protocols/Modbus/Channels/TcpChannel.cs @@ -2,7 +2,7 @@ using System.Net.Sockets; using InnovEnergy.Lib.Protocols.Modbus.Protocol; using InnovEnergy.Lib.Utils.Net; -/*namespace InnovEnergy.Lib.Protocols.Modbus.Channels; +namespace InnovEnergy.Lib.Protocols.Modbus.Channels; public class TcpChannel : ConnectionChannel { @@ -82,104 +82,4 @@ public class TcpChannel : ConnectionChannel var array = data.ToArray(); tcpClient.GetStream().Write(array, 0, array.Length); } -}*/ - -using System; -using System.Net.Sockets; - -namespace InnovEnergy.Lib.Protocols.Modbus.Channels -{ - public class TcpChannel : Channel, IDisposable - { - public string Host { get; } - public ushort Port { get; } - - private const int TimeoutMs = 500; // TODO: parametrize - private Socket? Socket { get; set; } - private byte[] Buffer { get; } - - public TcpChannel(string hostname, ushort port) - { - Host = hostname ?? throw new ArgumentNullException(nameof(hostname)); - Port = port; - Buffer = new byte[8192]; // Buffer size can be adjusted - } - - public override IReadOnlyList Read(int nBytes) - { - if (Socket == null) - throw new InvalidOperationException("Socket is not connected."); - - var buffer = new byte[nBytes]; - int bytesRead = 0; - - while (bytesRead < nBytes) - { - var read = Socket.Receive(buffer, bytesRead, nBytes - bytesRead, SocketFlags.None); - if (read == 0) - throw new Exception("Socket closed."); - - bytesRead += read; - } - - return buffer; - } - - public override void Write(IReadOnlyList bytes) - { - if (Socket == null) - throw new InvalidOperationException("Socket is not connected."); - - Socket.Send(bytes.ToArray(), SocketFlags.None); - } - - public void Connect() - { - if (Socket != null) - return; - - Socket = new Socket(SocketType.Stream, ProtocolType.Tcp) - { - Blocking = true, - NoDelay = true, - LingerState = new LingerOption(false, 0), - ReceiveTimeout = TimeoutMs, - SendTimeout = TimeoutMs - }; - - var cts = new CancellationTokenSource(); - cts.CancelAfter(TimeoutMs); - - try - { - Socket.ConnectAsync(Host, Port).Wait(TimeoutMs); - } - catch - { - Socket = null; - throw; - } - } - - public void Disconnect() - { - if (Socket == null) - return; - - try - { - Socket.Close(); - } - finally - { - Socket = null; - } - } - - public void Dispose() - { - Disconnect(); - } - } -} - +} \ No newline at end of file diff --git a/csharp/Lib/Protocols/Modbus/Clients/ModbusTcpClient.cs b/csharp/Lib/Protocols/Modbus/Clients/ModbusTcpClient.cs index 7966bacb4..41baf7fcb 100644 --- a/csharp/Lib/Protocols/Modbus/Clients/ModbusTcpClient.cs +++ b/csharp/Lib/Protocols/Modbus/Clients/ModbusTcpClient.cs @@ -12,7 +12,7 @@ namespace InnovEnergy.Lib.Protocols.Modbus.Clients; using UInt16s = IReadOnlyCollection; using Booleans = IReadOnlyCollection; -/*public class ModbusTcpClient : ModbusClient +public class ModbusTcpClient : ModbusClient { public const UInt16 DefaultPort = 502; private UInt16 _Id; @@ -184,171 +184,4 @@ using Booleans = IReadOnlyCollection; return new MbData(rxFrm.RegistersRead.RawData, readAddress, Endian); } -}*/ - -public class ModbusTcpClient : ModbusClient -{ - public const ushort DefaultPort = 502; - private ushort _Id; - public TcpChannel Channel { get; } - - public ModbusTcpClient(TcpChannel channel, byte slaveId) : base(channel, slaveId) - { - Channel = channel; - Channel.Connect(); - } - - private ushort NextId() => unchecked(++_Id); - - public override MbData ReadCoils(ushort readAddress, ushort nValues) - { - var id = NextId(); // TODO: check response id - - var cmd = new ReadCoilsCommandFrame(SlaveId, readAddress, nValues); - var hdr = new MbapHeader(id, cmd.Data.Count); - var frm = new ModbusTcpFrame(hdr, cmd); - - Channel.Write(frm.Data); - - var hData = Channel.Read(MbapHeader.Size).ToArray(); - var rxHdr = new MbapHeader(hData); - - var rxFrm = Channel - .Read(rxHdr.FrameLength) - .ToArray() - .Apply(ReadCoilsResponseFrame.Parse) - .Apply(cmd.VerifyResponse); - - return new MbData(rxFrm.Coils.RawData, readAddress, Endian); - } - - public override MbData ReadDiscreteInputs(ushort readAddress, ushort nValues) - { - var id = NextId(); // TODO: check response id - - var cmd = new ReadDiscreteInputsCommandFrame(SlaveId, readAddress, nValues); - var hdr = new MbapHeader(id, cmd.Data.Count); - var frm = new ModbusTcpFrame(hdr, cmd); - - Channel.Write(frm.Data); - - var hData = Channel.Read(MbapHeader.Size).ToArray(); - var rxHdr = new MbapHeader(hData); - - var rxFrm = Channel - .Read(rxHdr.FrameLength) - .ToArray() - .Apply(ReadDiscreteInputsResponseFrame.Parse) - .Apply(cmd.VerifyResponse); - - return new MbData(rxFrm.Inputs.RawData, readAddress, Endian); - } - - public override MbData ReadInputRegisters(ushort readAddress, ushort nValues) - { - var id = NextId(); // TODO: check response id - - var cmd = new ReadInputRegistersCommandFrame(SlaveId, readAddress, nValues); - var hdr = new MbapHeader(id, cmd.Data.Count); - var frm = new ModbusTcpFrame(hdr, cmd); - - Channel.Write(frm.Data); - - var hData = Channel.Read(MbapHeader.Size).ToArray(); - var rxHdr = new MbapHeader(hData); - - var rxFrm = Channel - .Read(rxHdr.FrameLength) - .ToArray() - .Apply(ReadInputRegistersResponseFrame.Parse) - .Apply(cmd.VerifyResponse); - - return new MbData(rxFrm.RegistersRead.RawData, readAddress, Endian); - } - - public override MbData ReadHoldingRegisters(ushort readAddress, ushort nValues) - { - var id = NextId(); // TODO: check response id - - var cmd = new ReadHoldingRegistersCommandFrame(SlaveId, readAddress, nValues); - var hdr = new MbapHeader(id, cmd.Data.Count); - var frm = new ModbusTcpFrame(hdr, cmd); - - Channel.Write(frm.Data); - - var hData = Channel.Read(MbapHeader.Size).ToArray(); - var rxHdr = new MbapHeader(hData); - - var rxFrm = Channel - .Read(rxHdr.FrameLength) - .ToArray() - .Apply(ReadHoldingRegistersResponseFrame.Parse) - .Apply(cmd.VerifyResponse); - - return new MbData(rxFrm.RegistersRead.RawData, readAddress, Endian); - } - - public override ushort WriteCoils(ushort writeAddress, Booleans coils) - { - var id = NextId(); // TODO: check response id - var cmd = new WriteCoilsCommandFrame(SlaveId, writeAddress, coils); - var hdr = new MbapHeader(id, cmd.Data.Count); - var frm = new ModbusTcpFrame(hdr, cmd); - - Channel.Write(frm.Data); - - var hData = Channel.Read(MbapHeader.Size).ToArray(); - var rxHdr = new MbapHeader(hData); - - var rxFrm = Channel - .Read(rxHdr.FrameLength) - .ToArray() - .Apply(WriteCoilsResponseFrame.Parse) - .Apply(cmd.VerifyResponse); - - return rxFrm.NbWritten; - } - - public override ushort WriteRegisters(ushort writeAddress, UInt16s values) - { - var id = NextId(); // TODO: check response id - var cmd = new WriteRegistersCommandFrame(SlaveId, writeAddress, values); - var hdr = new MbapHeader(id, cmd.Data.Count); - var frm = new ModbusTcpFrame(hdr, cmd); - - Channel.Write(frm.Data); - - var hData = Channel.Read(MbapHeader.Size).ToArray(); - var rxHdr = new MbapHeader(hData); - - var rxFrm = Channel - .Read(rxHdr.FrameLength) - .ToArray() - .Apply(WriteRegistersResponseFrame.Parse) - .Apply(cmd.VerifyResponse); - - return rxFrm.NbWritten; - } - - public override MbData ReadWriteRegisters(ushort readAddress, ushort nbToRead, ushort writeAddress, UInt16s registersToWrite) - { - var id = NextId(); // TODO: check response id - - var cmd = new ReadWriteRegistersCommandFrame(SlaveId, readAddress, nbToRead, writeAddress, registersToWrite); - - var hdr = new MbapHeader(id, cmd.Data.Count); - var frm = new ModbusTcpFrame(hdr, cmd); - - Channel.Write(frm.Data); - - var hData = Enumerable.ToArray(Channel.Read(MbapHeader.Size)); - var rxHdr = new MbapHeader(hData); - - var fData = Enumerable.ToArray(Channel.Read(rxHdr.FrameLength)); - var rxFrm = fData - .Apply(ReadWriteRegistersResponseFrame.Parse) - .Apply(cmd.VerifyResponse); - - return new MbData(rxFrm.RegistersRead.RawData, readAddress, Endian); - } -} \ No newline at end of file +} diff --git a/typescript/frontend-marios2/src/Resources/routes.json b/typescript/frontend-marios2/src/Resources/routes.json index f86097f4b..88f219a2a 100644 --- a/typescript/frontend-marios2/src/Resources/routes.json +++ b/typescript/frontend-marios2/src/Resources/routes.json @@ -11,10 +11,12 @@ "overview": "overview", "manage": "manage", "batteryview": "batteryview", + "pvview": "pvview", "log": "log", "live": "live", "information": "information", "configuration": "configuration", + "history": "history", "mainstats": "mainstats", "detailed_view": "detailed_view/" } diff --git a/typescript/frontend-marios2/src/content/dashboards/BatteryView/MainStats.tsx b/typescript/frontend-marios2/src/content/dashboards/BatteryView/MainStats.tsx index 7861a57c8..63ca5b839 100644 --- a/typescript/frontend-marios2/src/content/dashboards/BatteryView/MainStats.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/BatteryView/MainStats.tsx @@ -246,8 +246,8 @@ function MainStats(props: MainStatsProps) { chartOverview: BatteryOverviewInterface; }> = transformInputToBatteryViewData( props.s3Credentials, - UnixTime.fromTicks(startX), - UnixTime.fromTicks(endX) + UnixTime.fromTicks(startX).earlier(TimeSpan.fromHours(2)), + UnixTime.fromTicks(endX).earlier(TimeSpan.fromHours(2)) ); resultPromise diff --git a/typescript/frontend-marios2/src/content/dashboards/Configuration/Configuration.tsx b/typescript/frontend-marios2/src/content/dashboards/Configuration/Configuration.tsx index 66b682adf..449d12308 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Configuration/Configuration.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Configuration/Configuration.tsx @@ -15,7 +15,7 @@ import { Typography, useTheme } from '@mui/material'; -import React, { useState } from 'react'; +import React, { useContext, useState } from 'react'; import { FormattedMessage } from 'react-intl'; import Button from '@mui/material/Button'; import { Close as CloseIcon } from '@mui/icons-material'; @@ -29,6 +29,8 @@ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import dayjs from 'dayjs'; import axiosConfig from '../../../Resources/axiosConfig'; import utc from 'dayjs/plugin/utc'; +import { Action } from '../../../interfaces/S3Types'; +import { UserContext } from '../../../contexts/userContext'; interface ConfigurationProps { values: TopologyValues; @@ -82,6 +84,8 @@ function Configuration(props: ConfigurationProps) { const [updated, setUpdated] = useState(false); const [dateSelectionError, setDateSelectionError] = useState(''); const [isErrorDateModalOpen, setErrorDateModalOpen] = useState(false); + const context = useContext(UserContext); + const { currentUser, setUser } = context; const [formValues, setFormValues] = useState({ minimumSoC: props.values.minimumSoC[0].value, @@ -133,6 +137,13 @@ function Configuration(props: ConfigurationProps) { .toDate() }; + const historyAction: Action = { + configuration: configurationToSend, + date: new Date().toISOString().split('T')[0], // Gets the current date in YYYY-MM-DD format + time: new Date().toISOString().split('T')[1].split('.')[0], // Gets the current time in HH:MM:SS format + user: currentUser.name + }; + // console.log('will send ', dayjs(formValues.calibrationChargeDate)); setLoading(true); @@ -149,8 +160,21 @@ function Configuration(props: ConfigurationProps) { }); if (res) { - setUpdated(true); - setLoading(false); + const historyRes = await axiosConfig + .post( + `/UpdateActionHistory?installationId=${props.id}`, + historyAction + ) + .catch((err) => { + if (err.response) { + setError(true); + setLoading(false); + } + }); + if (historyRes) { + setUpdated(true); + setLoading(false); + } } } }; diff --git a/typescript/frontend-marios2/src/content/dashboards/History/History.tsx b/typescript/frontend-marios2/src/content/dashboards/History/History.tsx new file mode 100644 index 000000000..49ffd8a68 --- /dev/null +++ b/typescript/frontend-marios2/src/content/dashboards/History/History.tsx @@ -0,0 +1,275 @@ +import React, { useContext, useEffect, useState } from 'react'; +import { + Alert, + Card, + Container, + Divider, + Grid, + IconButton, + useTheme +} from '@mui/material'; +import Typography from '@mui/material/Typography'; +import { FormattedMessage } from 'react-intl'; +import axiosConfig from '../../../Resources/axiosConfig'; +import { AxiosError, AxiosResponse } from 'axios/index'; +import routes from '../../../Resources/routes.json'; +import { useNavigate } from 'react-router-dom'; +import { TokenContext } from '../../../contexts/tokenContext'; +import { Action } from '../../../interfaces/S3Types'; + +interface HistoryProps { + errorLoadingS3Data: boolean; + id: number; +} + +function HistoryOfActions(props: HistoryProps) { + const theme = useTheme(); + const searchParams = new URLSearchParams(location.search); + + const [history, setHistory] = useState([]); + const navigate = useNavigate(); + const tokencontext = useContext(TokenContext); + const { removeToken } = tokencontext; + + useEffect(() => { + axiosConfig + .get(`/GetHistoryForInstallation?id=${props.id}`) + .then((res: AxiosResponse) => { + setHistory(res.data); + }) + .catch((err: AxiosError) => { + if (err.response && err.response.status == 401) { + removeToken(); + navigate(routes.login); + } + }); + }, []); + + return ( + + + + {history.length > 0 && ( + + +
+
+
+ + + +
+ +
+ + + +
+
+ + + +
+ {/**/} + {/* */} + {/* */} + {/* */} + {/*
*/} +
+ +
+ {history.map((action, index) => ( + <> + +
+
+ + {action.user} + +
+ +
+ + {action.date} + +
+
+ + {action.time} + +
+
+ + ))} +
+ +
+ )} + + {!props.errorLoadingS3Data && history.length == 0 && ( + + + + + )} +
+
+ + + {props.errorLoadingS3Data && ( + + + + + )} + +
+ ); +} + +export default HistoryOfActions; diff --git a/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx b/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx index e812e91fa..d6f4cf84b 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx @@ -23,6 +23,7 @@ import routes from '../../../Resources/routes.json'; import Information from '../Information/Information'; import BatteryView from '../BatteryView/BatteryView'; import { UserType } from '../../../interfaces/UserTypes'; +import HistoryOfActions from '../History/History'; interface singleInstallationProps { current_installation?: I_Installation; @@ -309,6 +310,20 @@ function Installation(props: singleInstallationProps) { > } > + + + } + > + } @@ -342,6 +357,18 @@ function Installation(props: singleInstallationProps) { } /> )} + {currentUser.userType == UserType.admin && ( + + } + /> + )} + {currentUser.userType == UserType.admin && ( (undefined); @@ -137,6 +139,19 @@ function InstallationTabs() { defaultMessage="Configuration" /> ) + }, + // { + // value: 'history', + // label: ( + // + // ) + // }, + { + value: 'pvview', + label: } ] : currentUser.userType == UserType.partner @@ -158,6 +173,10 @@ function InstallationTabs() { /> ) }, + { + value: 'pvview', + label: + }, { value: 'information', @@ -217,6 +236,10 @@ function InstallationTabs() { /> ) }, + { + value: 'pvview', + label: + }, { value: 'manage', label: ( @@ -249,6 +272,15 @@ function InstallationTabs() { /> ) } + // { + // value: 'history', + // label: ( + // + // ) + // } ] : currentUser.userType == UserType.partner ? [ @@ -280,6 +312,10 @@ function InstallationTabs() { /> ) }, + { + value: 'pvview', + label: + }, { value: 'information', diff --git a/typescript/frontend-marios2/src/content/dashboards/Log/graph.util.tsx b/typescript/frontend-marios2/src/content/dashboards/Log/graph.util.tsx index 79237a1e5..ec4dad776 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Log/graph.util.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Log/graph.util.tsx @@ -35,6 +35,8 @@ export type ConfigurationValues = { calibrationChargeDate: Date | null; }; +export interface Pv {} + export interface Battery { BatteryId: number; FwVersion: I_BoxDataValue; @@ -163,11 +165,19 @@ export type TopologyValues = { additionalCalibrationChargeDate: I_BoxDataValue[]; batteryView: Battery[]; + + pvView: Pv[]; }; type TopologyPaths = { [key in keyof TopologyValues]: string[] }; const batteryIds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; +const PvPaths = [ + '/PvOnDc/Strings/%id%/Voltage', + '/PvOnDc/Strings/%id%/Current', + '/PvOnDc/Strings/%id%/Power' +]; + const batteryPaths = [ '/Battery/Devices/%id%/FwVersion', '/Battery/Devices/%id%/Dc/Power', @@ -290,6 +300,10 @@ export const topologyPaths: TopologyPaths = { batteryPaths.map((path) => path.replace('%id%', id.toString())) ), + pvView: batteryIds.flatMap((id) => + PvPaths.map((path) => path.replace('%id%', id.toString())) + ), + minimumSoC: ['/Config/MinSoc'], installedDcDcPower: ['/DcDc/SystemControl/NumberOfConnectedSlaves'], gridSetPoint: ['/Config/GridSetPoint'], diff --git a/typescript/frontend-marios2/src/content/dashboards/Overview/overview.tsx b/typescript/frontend-marios2/src/content/dashboards/Overview/overview.tsx index 8881c7f1e..1bd5daa80 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Overview/overview.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Overview/overview.tsx @@ -119,8 +119,8 @@ function Overview(props: OverviewProps) { chartOverview: overviewInterface; }> = transformInputToDailyData( props.s3Credentials, - UnixTime.fromTicks(startX), - UnixTime.fromTicks(endX) + UnixTime.fromTicks(startX).earlier(TimeSpan.fromHours(2)), + UnixTime.fromTicks(endX).earlier(TimeSpan.fromHours(2)) ); let isComponentMounted = true; diff --git a/typescript/frontend-marios2/src/content/dashboards/PvView/PvView.tsx b/typescript/frontend-marios2/src/content/dashboards/PvView/PvView.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/Installation.tsx b/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/Installation.tsx index b673f0d31..6e11a2a10 100644 --- a/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/Installation.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/Installation.tsx @@ -58,6 +58,28 @@ function Installation(props: singleInstallationProps) { const s3Credentials = { s3Bucket, ...S3data }; + const fetchDataOnlyOneTime = async () => { + var timeperiodToSearch = 70; + + for (var i = timeperiodToSearch; i > 0; i -= 2) { + const now = UnixTime.now().earlier(TimeSpan.fromSeconds(i)); + + const res = await fetchData(now, s3Credentials); + + if (res != FetchResult.notAvailable && res != FetchResult.tryLater) { + setConnected(true); + setFailedToCommunicateWithInstallation(0); + setValues( + extractValues({ + time: now, + value: res + }) + ); + return now; + } + } + }; + const fetchDataPeriodically = async () => { const now = UnixTime.now().earlier(TimeSpan.fromSeconds(20)); @@ -76,7 +98,7 @@ function Installation(props: singleInstallationProps) { return true; } else { setFailedToCommunicateWithInstallation((prevCount) => { - if (prevCount + 1 >= 3) { + if (prevCount + 1 >= 20) { setConnected(false); } return prevCount + 1; @@ -87,19 +109,6 @@ function Installation(props: singleInstallationProps) { } }; - const fetchDataOnlyOneTime = async () => { - let success = false; - const max_retransmissions = 3; - - for (let i = 0; i < max_retransmissions; i++) { - success = await fetchDataPeriodically(); - await new Promise((resolve) => setTimeout(resolve, 1000)); - if (success) { - break; - } - } - }; - useEffect(() => { let path = location.split('/'); @@ -118,7 +127,7 @@ function Installation(props: singleInstallationProps) { currentTab == 'live' || (location.includes('batteryview') && !location.includes('mainstats')) ) { - fetchDataPeriodically(); + fetchDataOnlyOneTime(); interval = setInterval(fetchDataPeriodically, 2000); } if (currentTab == 'configuration' || location.includes('mainstats')) { diff --git a/typescript/frontend-marios2/src/content/dashboards/Topology/Topology.tsx b/typescript/frontend-marios2/src/content/dashboards/Topology/Topology.tsx index 17a73e4b2..e8c4b8260 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Topology/Topology.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Topology/Topology.tsx @@ -222,6 +222,7 @@ function Topology(props: TopologyProps) { }} bottomBox={{ title: 'AC Loads', + data: props.values.islandBusToLoadOnIslandBusConnection, connected: props.values.loadOnIslandBusBox[0].value.toString() != diff --git a/typescript/frontend-marios2/src/interfaces/S3Types.tsx b/typescript/frontend-marios2/src/interfaces/S3Types.tsx index 7309b1cb5..078a011bc 100644 --- a/typescript/frontend-marios2/src/interfaces/S3Types.tsx +++ b/typescript/frontend-marios2/src/interfaces/S3Types.tsx @@ -1,3 +1,5 @@ +import { ConfigurationValues } from '../content/dashboards/Log/graph.util'; + export interface I_S3Credentials { s3Region: string; s3Provider: string; @@ -16,3 +18,10 @@ export interface ErrorMessage { deviceCreatedTheMessage: string; seen: boolean; } + +export interface Action { + configuration: ConfigurationValues; + date: string; + time: string; + user: string; +}