Compare commits
No commits in common. "242969306be2d0fba3acebe952ca7cb6f77e0e5c" and "183f8cfc2a48a92bf29f79159f21e3dc9b77f4cc" have entirely different histories.
242969306b
...
183f8cfc2a
|
@ -4,9 +4,9 @@ namespace InnovEnergy.App.SaliMax.DataTypes;
|
||||||
|
|
||||||
public class Configuration
|
public class Configuration
|
||||||
{
|
{
|
||||||
public Double MinimumSoC { get; set; }
|
public Double MinimumSoC { get; set; }
|
||||||
public Double GridSetPoint { get; set; }
|
public Double GridSetPoint { get; set; }
|
||||||
public CalibrationChargeType ForceCalibrationChargeState { get; set; }
|
public CalibrationChargeType ForceCalibrationCharge { get; set; }
|
||||||
public DateTime CalibrationChargeDate { get; set; }
|
public DateTime CalibrationChargeDate { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace InnovEnergy.App.SaliMax.Ess;
|
||||||
public static class Controller
|
public static class Controller
|
||||||
{
|
{
|
||||||
private static readonly Double BatteryHeatingPower = 200.0; // TODO: move to config
|
private static readonly Double BatteryHeatingPower = 200.0; // TODO: move to config
|
||||||
private static Boolean _hasCalibrationChargeChecked = false;
|
private static Boolean _hasOriginalNextDayAt10AmBeenChecked = false;
|
||||||
private static DateTime _nextDayAt10Am = DateTime.Now;
|
private static DateTime _nextDayAt10Am = DateTime.Now;
|
||||||
|
|
||||||
public static EssMode SelectControlMode(this StatusRecord s)
|
public static EssMode SelectControlMode(this StatusRecord s)
|
||||||
|
@ -187,49 +187,74 @@ public static class Controller
|
||||||
|
|
||||||
private static Boolean MustDoCalibrationCharge(this StatusRecord statusRecord)
|
private static Boolean MustDoCalibrationCharge(this StatusRecord statusRecord)
|
||||||
{
|
{
|
||||||
var calibrationChargeForced = statusRecord.Config.ForceCalibrationChargeState;
|
var calibrationChargeForced = statusRecord.Config.ForceCalibrationCharge;
|
||||||
var batteryCalibrationChargeRequested = statusRecord.Battery is { CalibrationChargeRequested: true };//BatteryCalibrationChargeRequested(statusRecord.Battery?.CalibrationChargeRequested?? false) ;
|
var batteryCalibrationChargeRequested = statusRecord.Battery is { CalibrationChargeRequested: true };//BatteryCalibrationChargeRequested(statusRecord.Battery?.CalibrationChargeRequested?? false) ;
|
||||||
|
|
||||||
var mustDoCalibrationCharge = calibrationChargeForced == CalibrationChargeType.ChargePermanently ||
|
var mustDoCalibrationCharge = batteryCalibrationChargeRequested || calibrationChargeForced == CalibrationChargeType.ChargePermanently || calibrationChargeForced == CalibrationChargeType.AdditionallyOnce ;
|
||||||
(calibrationChargeForced == CalibrationChargeType.AdditionallyOnce
|
|
||||||
&& CalibrationDateHasBeenPassed(statusRecord.Config.DayAndTimeForAdditionalCalibration)) ||
|
|
||||||
(calibrationChargeForced == CalibrationChargeType.RepetitivelyEvery &&
|
|
||||||
CalibrationDateHasBeenPassed(statusRecord.Config.DayAndTimeForRepetitiveCalibration));
|
|
||||||
|
|
||||||
if (statusRecord.Battery is not null)
|
if (statusRecord.Battery is not null)
|
||||||
{
|
{
|
||||||
if (calibrationChargeForced == CalibrationChargeType.AdditionallyOnce && statusRecord.Battery.Eoc )
|
if (calibrationChargeForced == CalibrationChargeType.AdditionallyOnce && statusRecord.Battery.Eoc )
|
||||||
{
|
{
|
||||||
statusRecord.Config.ForceCalibrationChargeState = CalibrationChargeType.RepetitivelyEvery;
|
statusRecord.Config.ForceCalibrationCharge = CalibrationChargeType.RepetitivelyEvery;
|
||||||
_hasCalibrationChargeChecked = false;
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (calibrationChargeForced == CalibrationChargeType.RepetitivelyEvery && statusRecord.Battery.Eoc)
|
|
||||||
{
|
|
||||||
statusRecord.Config.DayAndTimeForRepetitiveCalibration = statusRecord.Config.DayAndTimeForRepetitiveCalibration.AddDays(7);
|
|
||||||
_hasCalibrationChargeChecked = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mustDoCalibrationCharge;
|
return mustDoCalibrationCharge;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Boolean CalibrationDateHasBeenPassed(DateTime calibrationChargeDate)
|
private static Boolean BatteryCalibrationChargeRequested(Boolean calibrationChargeRequested)
|
||||||
{
|
{
|
||||||
|
if (calibrationChargeRequested)
|
||||||
if (!_hasCalibrationChargeChecked)
|
{
|
||||||
{
|
if (!_hasOriginalNextDayAt10AmBeenChecked)
|
||||||
if (DateTime.Now >= calibrationChargeDate )
|
{
|
||||||
|
_nextDayAt10Am = CalculateNextCalibrationTime();
|
||||||
|
_hasOriginalNextDayAt10AmBeenChecked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CheckNextCalibrationTime(_nextDayAt10Am))
|
||||||
{
|
{
|
||||||
_hasCalibrationChargeChecked = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return true;
|
else
|
||||||
|
{
|
||||||
|
_hasOriginalNextDayAt10AmBeenChecked = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Boolean CheckNextCalibrationTime(DateTime nextCalibrationTime)
|
||||||
|
{
|
||||||
|
var currentDateAndTime = DateTime.Now;
|
||||||
|
Console.WriteLine(" next X am is: " + nextCalibrationTime);
|
||||||
|
Console.WriteLine(" currentDateAndTime: " + currentDateAndTime);
|
||||||
|
|
||||||
|
// Check if the current time is greater than or equal to the original nextCalibrationTime
|
||||||
|
var x = currentDateAndTime >= nextCalibrationTime;
|
||||||
|
Console.WriteLine(" we are checking next X am : " + x);
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DateTime CalculateNextCalibrationTime()
|
||||||
|
{
|
||||||
|
var currentDateAndTime = DateTime.Now;
|
||||||
|
|
||||||
|
// Calculate the next 10 AM within the same day
|
||||||
|
var nextCalibrationTime = new DateTime(currentDateAndTime.Year, currentDateAndTime.Month, currentDateAndTime.Day, 9, 0, 0); // next 9am
|
||||||
|
|
||||||
|
// If the current time is already past 9 AM, move to the next day
|
||||||
|
if (currentDateAndTime.Hour >= 9)
|
||||||
|
{
|
||||||
|
nextCalibrationTime = nextCalibrationTime.AddDays(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextCalibrationTime;
|
||||||
|
}
|
||||||
|
|
||||||
private static Double ControlGridPower(this StatusRecord status, Double targetPower)
|
private static Double ControlGridPower(this StatusRecord status, Double targetPower)
|
||||||
{
|
{
|
||||||
return ControlPower
|
return ControlPower
|
||||||
|
|
|
@ -64,7 +64,7 @@ public static class MiddlewareAgent
|
||||||
|
|
||||||
Configuration? config = JsonSerializer.Deserialize<Configuration>(message);
|
Configuration? config = JsonSerializer.Deserialize<Configuration>(message);
|
||||||
|
|
||||||
Console.WriteLine($"Received a configuration message: GridSetPoint is " + config.GridSetPoint + ", MinimumSoC is " + config.MinimumSoC + " and ForceCalibrationCharge is " + config.ForceCalibrationChargeState+ " and CalibrationChargeDate is " + config.CalibrationChargeDate);
|
Console.WriteLine($"Received a configuration message: GridSetPoint is " + config.GridSetPoint + ", MinimumSoC is " + config.MinimumSoC + " and ForceCalibrationCharge is " + config.ForceCalibrationCharge+ " and CalibrationChargeDate is " + config.CalibrationChargeDate);
|
||||||
|
|
||||||
// Send the reply to the sender's endpoint
|
// Send the reply to the sender's endpoint
|
||||||
UdpListener.Send(replyData, replyData.Length, serverEndpoint);
|
UdpListener.Send(replyData, replyData.Length, serverEndpoint);
|
||||||
|
|
|
@ -690,17 +690,7 @@ internal static class Program
|
||||||
{
|
{
|
||||||
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.ForceCalibrationChargeState = config.ForceCalibrationChargeState;
|
status.Config.ForceCalibrationCharge = config.ForceCalibrationCharge;
|
||||||
|
|
||||||
if (config.ForceCalibrationChargeState == CalibrationChargeType.RepetitivelyEvery)
|
|
||||||
{
|
|
||||||
status.Config.DayAndTimeForRepetitiveCalibration = config.CalibrationChargeDate;
|
|
||||||
}
|
|
||||||
else if (config.ForceCalibrationChargeState == CalibrationChargeType.AdditionallyOnce)
|
|
||||||
{
|
|
||||||
status.Config.DayAndTimeForAdditionalCalibration = config.CalibrationChargeDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -15,27 +15,26 @@ public class Config //TODO: let IE choose from config files (Json) and connect t
|
||||||
|
|
||||||
private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true };
|
private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true };
|
||||||
|
|
||||||
public required Double MinSoc { get; set; }
|
public required Double MinSoc { get; set; }
|
||||||
public required CalibrationChargeType ForceCalibrationChargeState { get; set; }
|
public required CalibrationChargeType ForceCalibrationCharge { get; set; }
|
||||||
public required DateTime DayAndTimeForRepetitiveCalibration { get; set; }
|
public required DateTime DayAndTimeForCalibration { get; set; }
|
||||||
public required DateTime DayAndTimeForAdditionalCalibration { get; set; }
|
public required Boolean DisplayIndividualBatteries { get; set; }
|
||||||
public required Boolean DisplayIndividualBatteries { get; set; }
|
public required Double PConstant { get; set; }
|
||||||
public required Double PConstant { get; set; }
|
public required Double GridSetPoint { get; set; }
|
||||||
public required Double GridSetPoint { get; set; }
|
public required Double BatterySelfDischargePower { get; set; }
|
||||||
public required Double BatterySelfDischargePower { get; set; }
|
public required Double HoldSocZone { get; set; }
|
||||||
public required Double HoldSocZone { get; set; }
|
public required DevicesConfig IslandMode { get; set; }
|
||||||
public required DevicesConfig IslandMode { get; set; }
|
public required DevicesConfig GridTie { get; set; }
|
||||||
public required DevicesConfig GridTie { get; set; }
|
|
||||||
|
|
||||||
public required Double MaxBatteryChargingCurrent { get; set; }
|
public required Double MaxBatteryChargingCurrent { get; set; }
|
||||||
public required Double MaxBatteryDischargingCurrent { get; set; }
|
public required Double MaxBatteryDischargingCurrent { get; set; }
|
||||||
public required Double MaxDcPower { get; set; }
|
public required Double MaxDcPower { get; set; }
|
||||||
|
|
||||||
public required Double MaxChargeBatteryVoltage { get; set; }
|
public required Double MaxChargeBatteryVoltage { get; set; }
|
||||||
public required Double MinDischargeBatteryVoltage { get; set; }
|
public required Double MinDischargeBatteryVoltage { get; set; }
|
||||||
|
|
||||||
public required DeviceConfig Devices { get; set; }
|
public required DeviceConfig Devices { get; set; }
|
||||||
public required S3Config? S3 { get; set; }
|
public required S3Config? S3 { get; set; }
|
||||||
|
|
||||||
private static String? LastSavedData { get; set; }
|
private static String? LastSavedData { get; set; }
|
||||||
|
|
||||||
|
@ -43,9 +42,8 @@ public class Config //TODO: let IE choose from config files (Json) and connect t
|
||||||
public static Config Default => new()
|
public static Config Default => new()
|
||||||
{
|
{
|
||||||
MinSoc = 20,
|
MinSoc = 20,
|
||||||
ForceCalibrationChargeState = CalibrationChargeType.RepetitivelyEvery,
|
ForceCalibrationCharge = CalibrationChargeType.RepetitivelyEvery,
|
||||||
DayAndTimeForRepetitiveCalibration = DefaultDatetime,
|
DayAndTimeForCalibration = DefaultDatetime,
|
||||||
DayAndTimeForAdditionalCalibration = DefaultDatetime,
|
|
||||||
DisplayIndividualBatteries = false,
|
DisplayIndividualBatteries = false,
|
||||||
PConstant = .5,
|
PConstant = .5,
|
||||||
GridSetPoint = 0,
|
GridSetPoint = 0,
|
||||||
|
@ -122,9 +120,8 @@ public class Config //TODO: let IE choose from config files (Json) and connect t
|
||||||
public static Config Default => new()
|
public static Config Default => new()
|
||||||
{
|
{
|
||||||
MinSoc = 20,
|
MinSoc = 20,
|
||||||
ForceCalibrationChargeState = CalibrationChargeType.RepetitivelyEvery,
|
ForceCalibrationCharge = CalibrationChargeType.RepetitivelyEvery,
|
||||||
DayAndTimeForRepetitiveCalibration = DefaultDatetime,
|
DayAndTimeForCalibration = DefaultDatetime,
|
||||||
DayAndTimeForAdditionalCalibration = DefaultDatetime,
|
|
||||||
DisplayIndividualBatteries = false,
|
DisplayIndividualBatteries = false,
|
||||||
PConstant = .5,
|
PConstant = .5,
|
||||||
GridSetPoint = 0,
|
GridSetPoint = 0,
|
||||||
|
|
|
@ -242,7 +242,7 @@ function BatteryView(props: BatteryViewProps) {
|
||||||
>
|
>
|
||||||
{battery.Warnings.value === '' ? (
|
{battery.Warnings.value === '' ? (
|
||||||
'None'
|
'None'
|
||||||
) : battery.Warnings.value.toString().split('-').length >
|
) : battery.Warnings.value.toString().split(';').length >
|
||||||
1 ? (
|
1 ? (
|
||||||
<Link
|
<Link
|
||||||
style={{ color: 'black' }}
|
style={{ color: 'black' }}
|
||||||
|
@ -274,7 +274,7 @@ function BatteryView(props: BatteryViewProps) {
|
||||||
>
|
>
|
||||||
{battery.Alarms.value === '' ? (
|
{battery.Alarms.value === '' ? (
|
||||||
'None'
|
'None'
|
||||||
) : battery.Alarms.value.toString().split('-').length >
|
) : battery.Alarms.value.toString().split(';').length >
|
||||||
1 ? (
|
1 ? (
|
||||||
<Link
|
<Link
|
||||||
style={{ color: 'black' }}
|
style={{ color: 'black' }}
|
||||||
|
|
|
@ -84,7 +84,8 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
height: '150px'
|
height: '150px',
|
||||||
|
marginTop: '30px'
|
||||||
};
|
};
|
||||||
|
|
||||||
const batteryStringStyle = {
|
const batteryStringStyle = {
|
||||||
|
@ -131,17 +132,6 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item md={4.9} xs={4.9}></Grid>
|
<Grid item md={4.9} xs={4.9}></Grid>
|
||||||
<Grid item md={2.2} xs={2.2}>
|
<Grid item md={2.2} xs={2.2}>
|
||||||
<Typography
|
|
||||||
variant="h6"
|
|
||||||
component="div"
|
|
||||||
sx={{
|
|
||||||
marginLeft: '120px',
|
|
||||||
fontWeight: 'bold'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{'Node ' + props.batteryData.BatteryId}
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<div style={batteryStyle}>
|
<div style={batteryStyle}>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|
|
@ -108,19 +108,20 @@ function MainStats(props: MainStatsProps) {
|
||||||
function generateSeries(chartData, category, color) {
|
function generateSeries(chartData, category, color) {
|
||||||
const series = [];
|
const series = [];
|
||||||
const pathsToSearch = [
|
const pathsToSearch = [
|
||||||
'Node2',
|
'Battery1',
|
||||||
'Node3',
|
'Battery2',
|
||||||
'Node4',
|
'Battery3',
|
||||||
'Node5',
|
'Battery4',
|
||||||
'Node6',
|
'Battery5',
|
||||||
'Node7',
|
'Battery6',
|
||||||
'Node8',
|
'Battery7',
|
||||||
'Node9',
|
'Battery8',
|
||||||
'Node10',
|
'Battery9',
|
||||||
'Node11'
|
'Battery10'
|
||||||
];
|
];
|
||||||
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
// Assuming the chartData.Soc.data structure
|
||||||
pathsToSearch.forEach((devicePath) => {
|
pathsToSearch.forEach((devicePath) => {
|
||||||
if (
|
if (
|
||||||
Object.hasOwnProperty.call(chartData[category].data, devicePath) &&
|
Object.hasOwnProperty.call(chartData[category].data, devicePath) &&
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
|
import { DateField } from '@mui/x-date-pickers/DateField';
|
||||||
import axiosConfig from '../../../Resources/axiosConfig';
|
import axiosConfig from '../../../Resources/axiosConfig';
|
||||||
import { Close as CloseIcon } from '@mui/icons-material';
|
import { Close as CloseIcon } from '@mui/icons-material';
|
||||||
import MenuItem from '@mui/material/MenuItem';
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
|
@ -56,8 +57,6 @@ function Configuration(props: ConfigurationProps) {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState(false);
|
||||||
const [updated, setUpdated] = useState(false);
|
const [updated, setUpdated] = useState(false);
|
||||||
const [dateSelectionError, setDateSelectionError] = useState('');
|
|
||||||
const [isErrorDateModalOpen, setErrorDateModalOpen] = useState(false);
|
|
||||||
const [openForcedCalibrationCharge, setOpenForcedCalibrationCharge] =
|
const [openForcedCalibrationCharge, setOpenForcedCalibrationCharge] =
|
||||||
useState(false);
|
useState(false);
|
||||||
const [
|
const [
|
||||||
|
@ -66,13 +65,19 @@ function Configuration(props: ConfigurationProps) {
|
||||||
] = useState<string>(
|
] = useState<string>(
|
||||||
props.values.calibrationChargeForced[0].value.toString()
|
props.values.calibrationChargeForced[0].value.toString()
|
||||||
);
|
);
|
||||||
|
const [isDateModalOpen, setIsDateModalOpen] = useState(false);
|
||||||
|
const [calibrationChargeDate, setCalibrationChargeDate] = useState();
|
||||||
|
const [isErrorDateModalOpen, setErrorDateModalOpen] = useState(false);
|
||||||
|
const [dateSelectionError, setDateSelectionError] = useState('');
|
||||||
|
const [dateFieldOpen, setDateFieldOpen] = useState(false);
|
||||||
|
|
||||||
const [formValues, setFormValues] = useState<ConfigurationValues>({
|
const [formValues, setFormValues] = useState<ConfigurationValues>({
|
||||||
minimumSoC: props.values.minimumSoC[0].value,
|
minimumSoC: props.values.minimumSoC[0].value,
|
||||||
gridSetPoint: (props.values.gridSetPoint[0].value as number) / 1000,
|
gridSetPoint: (props.values.gridSetPoint[0].value as number) / 1000,
|
||||||
forceCalibrationCharge: forcedCalibrationChargeOptions.indexOf(
|
forceCalibrationCharge: forcedCalibrationChargeOptions.indexOf(
|
||||||
props.values.calibrationChargeForced[0].value.toString()
|
props.values.calibrationChargeForced[0].value.toString()
|
||||||
),
|
),
|
||||||
calibrationChargeDate: null
|
calibrationChargeDate: Date(props.values.calibrationChargeDate[0].value)
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
|
@ -92,24 +97,30 @@ function Configuration(props: ConfigurationProps) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
setIsDateModalOpen(false);
|
||||||
|
};
|
||||||
const handleOkOnErrorDateModal = () => {
|
const handleOkOnErrorDateModal = () => {
|
||||||
setErrorDateModalOpen(false);
|
setErrorDateModalOpen(false);
|
||||||
|
setIsDateModalOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleConfirm = (newDate) => {
|
const handleConfirm = () => {
|
||||||
if (newDate.isBefore(dayjs())) {
|
setIsDateModalOpen(false);
|
||||||
setDateSelectionError('You must specify a future date');
|
|
||||||
|
if (calibrationChargeDate.isBefore(dayjs())) {
|
||||||
|
setDateSelectionError('You must use a future date');
|
||||||
setErrorDateModalOpen(true);
|
setErrorDateModalOpen(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFormValues({
|
setDateFieldOpen(true);
|
||||||
...formValues,
|
|
||||||
['calibrationChargeDate']: newDate.toDate()
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectedCalibrationChargeChange = (event) => {
|
const handleSelectedCalibrationChargeChange = (event) => {
|
||||||
|
if (event.target.value != 'ChargePermanently') {
|
||||||
|
setIsDateModalOpen(true);
|
||||||
|
}
|
||||||
setSelectedForcedCalibrationChargeOption(event.target.value);
|
setSelectedForcedCalibrationChargeOption(event.target.value);
|
||||||
|
|
||||||
setFormValues({
|
setFormValues({
|
||||||
|
@ -170,47 +181,6 @@ function Configuration(props: ConfigurationProps) {
|
||||||
alignItems="stretch"
|
alignItems="stretch"
|
||||||
spacing={3}
|
spacing={3}
|
||||||
>
|
>
|
||||||
{isErrorDateModalOpen && (
|
|
||||||
<Modal open={isErrorDateModalOpen} onClose={() => {}}>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: '50%',
|
|
||||||
left: '50%',
|
|
||||||
transform: 'translate(-50%, -50%)',
|
|
||||||
width: 450,
|
|
||||||
bgcolor: 'background.paper',
|
|
||||||
borderRadius: 4,
|
|
||||||
boxShadow: 24,
|
|
||||||
p: 4,
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography
|
|
||||||
variant="body1"
|
|
||||||
gutterBottom
|
|
||||||
sx={{ fontWeight: 'bold' }}
|
|
||||||
>
|
|
||||||
{dateSelectionError}
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
sx={{
|
|
||||||
marginTop: 2,
|
|
||||||
textTransform: 'none',
|
|
||||||
bgcolor: '#ffc04d',
|
|
||||||
color: '#111111',
|
|
||||||
'&:hover': { bgcolor: '#f7b34d' }
|
|
||||||
}}
|
|
||||||
onClick={handleOkOnErrorDateModal}
|
|
||||||
>
|
|
||||||
Ok
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Modal>
|
|
||||||
)}
|
|
||||||
<Grid item xs={12} md={12}>
|
<Grid item xs={12} md={12}>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Box
|
<Box
|
||||||
|
@ -276,31 +246,128 @@ function Configuration(props: ConfigurationProps) {
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</div>
|
</div>
|
||||||
{formValues.forceCalibrationCharge != 2 && (
|
{(dateFieldOpen || formValues.forceCalibrationCharge != 2) && (
|
||||||
<div>
|
<div>
|
||||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||||
<DateTimePicker
|
<DateField
|
||||||
label="Select Next Calibration Charge Date"
|
label="Calibration Charge Date"
|
||||||
value={
|
value={calibrationChargeDate}
|
||||||
formValues.forceCalibrationCharge == 0
|
|
||||||
? dayjs(
|
|
||||||
props.values.repetitiveCalibrationChargeDate[0]
|
|
||||||
.value
|
|
||||||
)
|
|
||||||
: dayjs(
|
|
||||||
props.values.additionalCalibrationChargeDate[0]
|
|
||||||
.value
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onChange={handleConfirm}
|
|
||||||
sx={{
|
|
||||||
marginTop: 2
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</LocalizationProvider>
|
</LocalizationProvider>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{isErrorDateModalOpen && (
|
||||||
|
<Modal open={isErrorDateModalOpen} onClose={() => {}}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
width: 450,
|
||||||
|
bgcolor: 'background.paper',
|
||||||
|
borderRadius: 4,
|
||||||
|
boxShadow: 24,
|
||||||
|
p: 4,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
gutterBottom
|
||||||
|
sx={{ fontWeight: 'bold' }}
|
||||||
|
>
|
||||||
|
{dateSelectionError}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
sx={{
|
||||||
|
marginTop: 2,
|
||||||
|
textTransform: 'none',
|
||||||
|
bgcolor: '#ffc04d',
|
||||||
|
color: '#111111',
|
||||||
|
'&:hover': { bgcolor: '#f7b34d' }
|
||||||
|
}}
|
||||||
|
onClick={handleOkOnErrorDateModal}
|
||||||
|
>
|
||||||
|
Ok
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isDateModalOpen && (
|
||||||
|
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||||
|
<Modal open={isDateModalOpen} onClose={() => {}}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
width: 450,
|
||||||
|
bgcolor: 'background.paper',
|
||||||
|
borderRadius: 4,
|
||||||
|
boxShadow: 24,
|
||||||
|
p: 4,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DateTimePicker
|
||||||
|
label="Select Calibration Charge Date"
|
||||||
|
value={calibrationChargeDate}
|
||||||
|
onChange={(newDate) =>
|
||||||
|
setCalibrationChargeDate(newDate)
|
||||||
|
}
|
||||||
|
sx={{
|
||||||
|
marginTop: 2
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginTop: 10
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
sx={{
|
||||||
|
marginTop: 2,
|
||||||
|
textTransform: 'none',
|
||||||
|
bgcolor: '#ffc04d',
|
||||||
|
color: '#111111',
|
||||||
|
'&:hover': { bgcolor: '#f7b34d' }
|
||||||
|
}}
|
||||||
|
onClick={handleConfirm}
|
||||||
|
>
|
||||||
|
Confirm
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
sx={{
|
||||||
|
marginTop: 2,
|
||||||
|
marginLeft: 2,
|
||||||
|
textTransform: 'none',
|
||||||
|
bgcolor: '#ffc04d',
|
||||||
|
color: '#111111',
|
||||||
|
'&:hover': { bgcolor: '#f7b34d' }
|
||||||
|
}}
|
||||||
|
onClick={handleCancel}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
</LocalizationProvider>
|
||||||
|
)}
|
||||||
|
|
||||||
<div style={{ marginBottom: '5px' }}>
|
<div style={{ marginBottom: '5px' }}>
|
||||||
<TextField
|
<TextField
|
||||||
label={
|
label={
|
||||||
|
@ -394,7 +461,26 @@ function Configuration(props: ConfigurationProps) {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{error && (
|
||||||
|
<Alert
|
||||||
|
severity="error"
|
||||||
|
sx={{
|
||||||
|
ml: 1,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
An error has occurred
|
||||||
|
<IconButton
|
||||||
|
color="inherit"
|
||||||
|
size="small"
|
||||||
|
onClick={() => setError(false)} // Set error state to false on click
|
||||||
|
sx={{ marginLeft: '4px' }}
|
||||||
|
>
|
||||||
|
<CloseIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
{updated && (
|
{updated && (
|
||||||
<Alert
|
<Alert
|
||||||
severity="success"
|
severity="success"
|
||||||
|
|
|
@ -159,8 +159,7 @@ export type TopologyValues = {
|
||||||
DcDcNum: I_BoxDataValue[];
|
DcDcNum: I_BoxDataValue[];
|
||||||
calibrationChargeForced: I_BoxDataValue[];
|
calibrationChargeForced: I_BoxDataValue[];
|
||||||
mode: I_BoxDataValue[];
|
mode: I_BoxDataValue[];
|
||||||
repetitiveCalibrationChargeDate: I_BoxDataValue[];
|
calibrationChargeDate: I_BoxDataValue[];
|
||||||
additionalCalibrationChargeDate: I_BoxDataValue[];
|
|
||||||
|
|
||||||
batteryView: Battery[];
|
batteryView: Battery[];
|
||||||
};
|
};
|
||||||
|
@ -174,7 +173,6 @@ const batteryPaths = [
|
||||||
'/Battery/Devices/%id%/Dc/Voltage',
|
'/Battery/Devices/%id%/Dc/Voltage',
|
||||||
'/Battery/Devices/%id%/Soc',
|
'/Battery/Devices/%id%/Soc',
|
||||||
'/Battery/Devices/%id%/Temperatures/Cells/Average',
|
'/Battery/Devices/%id%/Temperatures/Cells/Average',
|
||||||
//'/Log/SalimaxWarnings/Battery/%id%',
|
|
||||||
'/Battery/Devices/%id%/Warnings',
|
'/Battery/Devices/%id%/Warnings',
|
||||||
'/Battery/Devices/%id%/Alarms',
|
'/Battery/Devices/%id%/Alarms',
|
||||||
|
|
||||||
|
@ -296,37 +294,23 @@ export const topologyPaths: TopologyPaths = {
|
||||||
gridSetPoint: ['/Config/GridSetPoint'],
|
gridSetPoint: ['/Config/GridSetPoint'],
|
||||||
maximumDischargePower: ['/Config/MaxBatteryDischargingCurrent'],
|
maximumDischargePower: ['/Config/MaxBatteryDischargingCurrent'],
|
||||||
DcDcNum: ['/DcDc/SystemControl/NumberOfConnectedSlaves'],
|
DcDcNum: ['/DcDc/SystemControl/NumberOfConnectedSlaves'],
|
||||||
calibrationChargeForced: ['/Config/ForceCalibrationChargeState'],
|
calibrationChargeForced: ['/Config/ForceCalibrationCharge'],
|
||||||
mode: ['/EssControl/Mode'],
|
mode: ['/EssControl/Mode'],
|
||||||
repetitiveCalibrationChargeDate: [
|
calibrationChargeDate: ['/EssControl/Date']
|
||||||
'/Config/DayAndTimeForRepetitiveCalibration'
|
|
||||||
],
|
|
||||||
additionalCalibrationChargeDate: [
|
|
||||||
'/Config/DayAndTimeForAdditionalCalibration'
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//We are using the function every time we fetch the data from S3 (every 2 seconds).
|
|
||||||
//The data is of the following form: TopologyValues
|
|
||||||
//key: I_BoxDataValue[] ==> key: [{unit:'',value:''},{unit:'',value:''},...]
|
|
||||||
//battery_view: [ {"Battery_id": 2,'FwVersion': {'unit':,'value':}},
|
|
||||||
// {"Battery_id": 4,'FwVersion': {'unit':,'value':}}
|
|
||||||
//]
|
|
||||||
//For batteries, we follow a different approach. We define a key battery_view that is of type Battery[]
|
|
||||||
|
|
||||||
export const extractValues = (
|
export const extractValues = (
|
||||||
timeSeriesData: DataPoint
|
timeSeriesData: DataPoint
|
||||||
): TopologyValues | null => {
|
): TopologyValues | null => {
|
||||||
const extractedValues: TopologyValues = {} as TopologyValues;
|
const extractedValues: TopologyValues = {} as TopologyValues;
|
||||||
|
|
||||||
for (const topologyKey of Object.keys(topologyPaths)) {
|
for (const topologyKey of Object.keys(topologyPaths)) {
|
||||||
//Each topologykey may have more than one paths (for example inverter)
|
|
||||||
const paths = topologyPaths[topologyKey];
|
const paths = topologyPaths[topologyKey];
|
||||||
let topologyValues: { unit: string; value: string | number }[] = [];
|
let topologyValues: { unit: string; value: string | number }[] = [];
|
||||||
|
|
||||||
if (topologyKey === 'batteryView') {
|
if (topologyKey === 'batteryView') {
|
||||||
extractedValues[topologyKey] = [];
|
extractedValues[topologyKey] = [];
|
||||||
const node_ids_from_csv = timeSeriesData.value[
|
const battery_ids_from_csv = timeSeriesData.value[
|
||||||
'/Config/Devices/BatteryNodes'
|
'/Config/Devices/BatteryNodes'
|
||||||
].value
|
].value
|
||||||
.toString()
|
.toString()
|
||||||
|
@ -338,11 +322,7 @@ export const extractValues = (
|
||||||
while (pathIndex < paths.length) {
|
while (pathIndex < paths.length) {
|
||||||
let battery = {};
|
let battery = {};
|
||||||
let existingKeys = 0;
|
let existingKeys = 0;
|
||||||
|
battery[BatteryKeys[0]] = battery_ids_from_csv[battery_index];
|
||||||
//We prepare a battery object for each node. We extract the nodes from the '/Config/Devices/BatteryNodes' path. For example, nodes can be [2,4,5,6] (one is missing)
|
|
||||||
//BatteryKeys[0] is the battery id. We set the battery id to the corresponding node id.
|
|
||||||
battery[BatteryKeys[0]] = node_ids_from_csv[battery_index];
|
|
||||||
//Then, search all the remaining battery keys
|
|
||||||
for (let i = 1; i < BatteryKeys.length; i++) {
|
for (let i = 1; i < BatteryKeys.length; i++) {
|
||||||
const path = paths[pathIndex];
|
const path = paths[pathIndex];
|
||||||
if (timeSeriesData.value.hasOwnProperty(path)) {
|
if (timeSeriesData.value.hasOwnProperty(path)) {
|
||||||
|
|
|
@ -61,17 +61,6 @@ export interface BatteryOverviewInterface {
|
||||||
Current: chartInfoInterface;
|
Current: chartInfoInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use this function in order to retrieve data for main stats.
|
|
||||||
//The data is of the following form:
|
|
||||||
//'Soc' : {name:'Soc',data:[
|
|
||||||
// 'Node1': {name:'Node1', data: [[timestamp,value],[timestamp,value]]},
|
|
||||||
// 'Node2': {name:'Node2', data: [[timestamp,value],[timestamp,value]]},
|
|
||||||
// ]},
|
|
||||||
//'Temperature' : {name:'Temperature',data:[
|
|
||||||
// 'Node1': {name:'Node1', data: [[timestamp,value],[timestamp,value]]},
|
|
||||||
// 'Node2': {name:'Node2', data: [[timestamp,value],[timestamp,value]]},
|
|
||||||
// ]}
|
|
||||||
|
|
||||||
export const transformInputToBatteryViewData = async (
|
export const transformInputToBatteryViewData = async (
|
||||||
s3Credentials: I_S3Credentials,
|
s3Credentials: I_S3Credentials,
|
||||||
startTimestamp: UnixTime,
|
startTimestamp: UnixTime,
|
||||||
|
@ -105,7 +94,18 @@ export const transformInputToBatteryViewData = async (
|
||||||
'/Battery/Devices/10/'
|
'/Battery/Devices/10/'
|
||||||
];
|
];
|
||||||
|
|
||||||
const pathsToSave = [];
|
const pathsToSave = [
|
||||||
|
'Battery1',
|
||||||
|
'Battery2',
|
||||||
|
'Battery3',
|
||||||
|
'Battery4',
|
||||||
|
'Battery5',
|
||||||
|
'Battery6',
|
||||||
|
'Battery7',
|
||||||
|
'Battery8',
|
||||||
|
'Battery9',
|
||||||
|
'Battery10'
|
||||||
|
];
|
||||||
|
|
||||||
const chartData: BatteryDataInterface = {
|
const chartData: BatteryDataInterface = {
|
||||||
Soc: { name: 'State Of Charge', data: [] },
|
Soc: { name: 'State Of Charge', data: [] },
|
||||||
|
@ -123,7 +123,20 @@ export const transformInputToBatteryViewData = async (
|
||||||
Current: { magnitude: 0, unit: '', min: 0, max: 0 }
|
Current: { magnitude: 0, unit: '', min: 0, max: 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
let initialiation = true;
|
categories.forEach((category) => {
|
||||||
|
chartData[category].data = [];
|
||||||
|
pathsToSave.forEach((path) => {
|
||||||
|
chartData[category].data[path] = { name: path, data: [] };
|
||||||
|
});
|
||||||
|
|
||||||
|
chartOverview[category] = {
|
||||||
|
magnitude: 0,
|
||||||
|
unit: '',
|
||||||
|
min: MAX_NUMBER,
|
||||||
|
max: -MAX_NUMBER
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
let adjustedTimestampArray = [];
|
let adjustedTimestampArray = [];
|
||||||
|
|
||||||
let startTimestampToNum = Number(startTimestamp);
|
let startTimestampToNum = Number(startTimestamp);
|
||||||
|
@ -147,7 +160,6 @@ export const transformInputToBatteryViewData = async (
|
||||||
adjustedTimestampArray.push(adjustedTimestamp);
|
adjustedTimestampArray.push(adjustedTimestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Wait until fetching all the data
|
|
||||||
const results = await Promise.all(timestampPromises);
|
const results = await Promise.all(timestampPromises);
|
||||||
|
|
||||||
for (let i = 0; i < results.length; i++) {
|
for (let i = 0; i < results.length; i++) {
|
||||||
|
@ -158,32 +170,6 @@ export const transformInputToBatteryViewData = async (
|
||||||
) {
|
) {
|
||||||
// Handle not available or try later case
|
// Handle not available or try later case
|
||||||
} else {
|
} else {
|
||||||
const battery_nodes = result['/Config/Devices/BatteryNodes'].value
|
|
||||||
.toString()
|
|
||||||
.split(',');
|
|
||||||
|
|
||||||
//Initialize the chartData structure based on the node names extracted from the first result
|
|
||||||
if (initialiation) {
|
|
||||||
initialiation = false;
|
|
||||||
|
|
||||||
battery_nodes.forEach((node) => {
|
|
||||||
pathsToSave.push('Node' + node);
|
|
||||||
});
|
|
||||||
categories.forEach((category) => {
|
|
||||||
chartData[category].data = [];
|
|
||||||
pathsToSave.forEach((path) => {
|
|
||||||
chartData[category].data[path] = { name: path, data: [] };
|
|
||||||
});
|
|
||||||
|
|
||||||
chartOverview[category] = {
|
|
||||||
magnitude: 0,
|
|
||||||
unit: '',
|
|
||||||
min: MAX_NUMBER,
|
|
||||||
max: -MAX_NUMBER
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (
|
for (
|
||||||
let category_index = 0;
|
let category_index = 0;
|
||||||
category_index < pathCategories.length;
|
category_index < pathCategories.length;
|
||||||
|
|
Loading…
Reference in New Issue