improve multi-inverter configurtaion page - add inverter number and fix unit issue
This commit is contained in:
parent
35938e9597
commit
ce7a9d3cf2
|
|
@ -12,7 +12,9 @@ public class Configuration
|
|||
public double? MaximumDischargingCurrent { get; set; }
|
||||
public double? MaximumChargingCurrent { get; set; }
|
||||
public double? OperatingPriority { get; set; }
|
||||
public int? InverterNumber { get; set; }
|
||||
public double? BatteriesCount { get; set; }
|
||||
public List<int>? BatteriesCountPerInverter { get; set; }
|
||||
public double? ClusterNumber { get; set; }
|
||||
public double? PvNumber { get; set; }
|
||||
public bool ControlPermission { get; set; }
|
||||
|
|
@ -25,7 +27,7 @@ public class Configuration
|
|||
return $"MinimumSoC: {MinimumSoC}, GridSetPoint: {GridSetPoint}, CalibrationChargeState: {CalibrationChargeState}, CalibrationChargeDate: {CalibrationChargeDate}, " +
|
||||
$"CalibrationDischargeState: {CalibrationDischargeState}, CalibrationDischargeDate: {CalibrationDischargeDate}, " +
|
||||
$"MaximumDischargingCurrent: {MaximumDischargingCurrent}, MaximumChargingCurrent: {MaximumChargingCurrent}, OperatingPriority: {OperatingPriority}, " +
|
||||
$"BatteriesCount: {BatteriesCount}, ClusterNumber: {ClusterNumber}, PvNumber: {PvNumber}, ControlPermission:{ControlPermission}, "+
|
||||
$"InverterNumber: {InverterNumber}, BatteriesCount: {BatteriesCount}, BatteriesCountPerInverter: [{(BatteriesCountPerInverter != null ? string.Join(", ", BatteriesCountPerInverter) : "")}], ClusterNumber: {ClusterNumber}, PvNumber: {PvNumber}, ControlPermission:{ControlPermission}, "+
|
||||
$"SinexcelTimeChargeandDischargePower: {TimeChargeandDischargePower}, SinexcelStartTimeChargeandDischargeDayandTime: {StartTimeChargeandDischargeDayandTime}, SinexcelStopTimeChargeandDischargeDayandTime: {StopTimeChargeandDischargeDayandTime}";
|
||||
|
||||
}
|
||||
|
|
@ -45,7 +47,7 @@ public class Configuration
|
|||
public string GetConfigurationSodistoreHome()
|
||||
{
|
||||
return $"MinimumSoC: {MinimumSoC}, MaximumDischargingCurrent: {MaximumDischargingCurrent}, MaximumChargingCurrent: {MaximumChargingCurrent}, OperatingPriority: {OperatingPriority}, " +
|
||||
$"BatteriesCount: {BatteriesCount}, ClusterNumber: {ClusterNumber}, PvNumber: {PvNumber}, ControlPermission:{ControlPermission}, "+
|
||||
$"InverterNumber: {InverterNumber}, BatteriesCount: {BatteriesCount}, BatteriesCountPerInverter: [{(BatteriesCountPerInverter != null ? string.Join(", ", BatteriesCountPerInverter) : "")}], ClusterNumber: {ClusterNumber}, PvNumber: {PvNumber}, ControlPermission:{ControlPermission}, "+
|
||||
$"SinexcelTimeChargeandDischargePower: {TimeChargeandDischargePower}, SinexcelStartTimeChargeandDischargeDayandTime: {StartTimeChargeandDischargeDayandTime}, SinexcelStopTimeChargeandDischargeDayandTime: {StopTimeChargeandDischargeDayandTime}";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -329,6 +329,8 @@ export interface JSONRecordData {
|
|||
MaximumDischargingCurrent: number;
|
||||
OperatingPriority: string;
|
||||
BatteriesCount: number;
|
||||
InverterNumber?: number;
|
||||
BatteriesCountPerInverter?: number[];
|
||||
ClusterNumber: number;
|
||||
PvNumber: number;
|
||||
ControlPermission:boolean;
|
||||
|
|
@ -696,6 +698,8 @@ export type ConfigurationValues = {
|
|||
maximumChargingCurrent: number;
|
||||
operatingPriority: number;
|
||||
batteriesCount: number;
|
||||
inverterNumber: number;
|
||||
batteriesCountPerInverter: number[];
|
||||
clusterNumber: number;
|
||||
PvNumber: number;
|
||||
controlPermission:boolean;
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ function SodistoreHomeConfiguration(props: SodistoreHomeConfigurationProps) {
|
|||
}));
|
||||
};
|
||||
const theme = useTheme();
|
||||
const [formDirty, setFormDirty] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
const [updated, setUpdated] = useState(false);
|
||||
|
|
@ -89,27 +90,34 @@ function SodistoreHomeConfiguration(props: SodistoreHomeConfigurationProps) {
|
|||
const pendingConfigKey = `pendingConfig_${props.id}`;
|
||||
|
||||
// Helper to build form values from S3 data
|
||||
const getS3Values = (): Partial<ConfigurationValues> => ({
|
||||
minimumSoC: props.values.Config.MinSoc,
|
||||
maximumDischargingCurrent: props.values.Config.MaximumChargingCurrent,
|
||||
maximumChargingCurrent: props.values.Config.MaximumDischargingCurrent,
|
||||
operatingPriority: resolveOperatingPriorityIndex(
|
||||
props.values.Config.OperatingPriority
|
||||
),
|
||||
batteriesCount: props.values.Config.BatteriesCount,
|
||||
clusterNumber: props.values.Config.ClusterNumber ?? 1,
|
||||
PvNumber: props.values.Config.PvNumber ?? 0,
|
||||
timeChargeandDischargePower: props.values.Config?.TimeChargeandDischargePower ?? 0,
|
||||
startTimeChargeandDischargeDayandTime:
|
||||
props.values.Config?.StartTimeChargeandDischargeDayandTime
|
||||
? dayjs(props.values.Config.StartTimeChargeandDischargeDayandTime).toDate()
|
||||
: null,
|
||||
stopTimeChargeandDischargeDayandTime:
|
||||
props.values.Config?.StopTimeChargeandDischargeDayandTime
|
||||
? dayjs(props.values.Config.StopTimeChargeandDischargeDayandTime).toDate()
|
||||
: null,
|
||||
controlPermission: String(props.values.Config.ControlPermission).toLowerCase() === "true",
|
||||
});
|
||||
const getS3Values = (): Partial<ConfigurationValues> => {
|
||||
const inverterNum = props.values.Config.InverterNumber ?? 1;
|
||||
const batteriesPerInverter: number[] = props.values.Config.BatteriesCountPerInverter
|
||||
?? Array(inverterNum).fill(props.values.Config.BatteriesCount || 1);
|
||||
return {
|
||||
minimumSoC: props.values.Config.MinSoc,
|
||||
maximumDischargingCurrent: props.values.Config.MaximumChargingCurrent,
|
||||
maximumChargingCurrent: props.values.Config.MaximumDischargingCurrent,
|
||||
operatingPriority: resolveOperatingPriorityIndex(
|
||||
props.values.Config.OperatingPriority
|
||||
),
|
||||
inverterNumber: inverterNum,
|
||||
batteriesCountPerInverter: batteriesPerInverter,
|
||||
batteriesCount: props.values.Config.BatteriesCount,
|
||||
clusterNumber: props.values.Config.ClusterNumber || 1,
|
||||
PvNumber: props.values.Config.PvNumber ?? 0,
|
||||
timeChargeandDischargePower: props.values.Config?.TimeChargeandDischargePower ?? 0,
|
||||
startTimeChargeandDischargeDayandTime:
|
||||
props.values.Config?.StartTimeChargeandDischargeDayandTime
|
||||
? dayjs(props.values.Config.StartTimeChargeandDischargeDayandTime).toDate()
|
||||
: null,
|
||||
stopTimeChargeandDischargeDayandTime:
|
||||
props.values.Config?.StopTimeChargeandDischargeDayandTime
|
||||
? dayjs(props.values.Config.StopTimeChargeandDischargeDayandTime).toDate()
|
||||
: null,
|
||||
controlPermission: String(props.values.Config.ControlPermission).toLowerCase() === "true",
|
||||
};
|
||||
};
|
||||
|
||||
// Restore pending config from localStorage, converting date strings back to Date objects.
|
||||
// Returns { values, s3ConfigSnapshot } or null if no pending config.
|
||||
|
|
@ -169,7 +177,10 @@ function SodistoreHomeConfiguration(props: SodistoreHomeConfigurationProps) {
|
|||
// When S3 data updates (polled every 60s), reconcile with any pending localStorage.
|
||||
// Strategy: device is the authority. Once S3 Config changes from the snapshot taken at
|
||||
// submit time, the device has uploaded new data — trust S3 regardless of values.
|
||||
// Skip reset if the user is actively editing (formDirty).
|
||||
useEffect(() => {
|
||||
if (formDirty) return;
|
||||
|
||||
const s3Values = getS3Values();
|
||||
const pending = restorePendingConfig();
|
||||
|
||||
|
|
@ -192,6 +203,7 @@ function SodistoreHomeConfiguration(props: SodistoreHomeConfigurationProps) {
|
|||
}, [props.values]);
|
||||
|
||||
const handleOperatingPriorityChange = (event) => {
|
||||
setFormDirty(true);
|
||||
setFormValues({
|
||||
...formValues,
|
||||
['operatingPriority']: OperatingPriorityOptions.indexOf(
|
||||
|
|
@ -230,7 +242,9 @@ function SodistoreHomeConfiguration(props: SodistoreHomeConfigurationProps) {
|
|||
maximumDischargingCurrent: formValues.maximumDischargingCurrent,
|
||||
maximumChargingCurrent: formValues.maximumChargingCurrent,
|
||||
operatingPriority: formValues.operatingPriority,
|
||||
batteriesCount:formValues.batteriesCount,
|
||||
inverterNumber: formValues.inverterNumber,
|
||||
batteriesCountPerInverter: formValues.batteriesCountPerInverter,
|
||||
batteriesCount: formValues.batteriesCountPerInverter?.[0] ?? formValues.batteriesCount,
|
||||
clusterNumber:formValues.clusterNumber,
|
||||
PvNumber:formValues.PvNumber,
|
||||
timeChargeandDischargePower: formValues.timeChargeandDischargePower,
|
||||
|
|
@ -259,6 +273,7 @@ function SodistoreHomeConfiguration(props: SodistoreHomeConfigurationProps) {
|
|||
if (res) {
|
||||
setUpdated(true);
|
||||
setLoading(false);
|
||||
setFormDirty(false);
|
||||
|
||||
// Save submitted values + S3 snapshot to localStorage for optimistic UI update.
|
||||
// s3ConfigSnapshot = fingerprint of S3 Config at submit time.
|
||||
|
|
@ -273,6 +288,7 @@ function SodistoreHomeConfiguration(props: SodistoreHomeConfigurationProps) {
|
|||
};
|
||||
|
||||
const handleChange = (e) => {
|
||||
setFormDirty(true);
|
||||
const { name, value } = e.target;
|
||||
|
||||
if (name === 'minimumSoC') {
|
||||
|
|
@ -305,6 +321,7 @@ function SodistoreHomeConfiguration(props: SodistoreHomeConfigurationProps) {
|
|||
};
|
||||
|
||||
const handleTimeChargeDischargeChange = (name: string, value: any) => {
|
||||
setFormDirty(true);
|
||||
setFormValues((prev) => ({
|
||||
...prev,
|
||||
[name]: value
|
||||
|
|
@ -384,11 +401,13 @@ function SodistoreHomeConfiguration(props: SodistoreHomeConfigurationProps) {
|
|||
<Switch
|
||||
name="controlPermission"
|
||||
checked={Boolean(formValues.controlPermission)}
|
||||
onChange={(e) =>
|
||||
onChange={(e) => {
|
||||
setFormDirty(true);
|
||||
setFormValues((prev) => ({
|
||||
...prev,
|
||||
controlPermission: e.target.checked,
|
||||
}))
|
||||
}));
|
||||
}
|
||||
}
|
||||
sx={{ transform: "scale(1.4)", marginLeft: "15px" }}
|
||||
/>
|
||||
|
|
@ -405,19 +424,63 @@ function SodistoreHomeConfiguration(props: SodistoreHomeConfigurationProps) {
|
|||
|
||||
<div style={{ marginBottom: '5px' }}>
|
||||
<TextField
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="batteriesCount "
|
||||
defaultMessage="Batteries Count"
|
||||
/>
|
||||
}
|
||||
name="batteriesCount"
|
||||
value={formValues.batteriesCount}
|
||||
onChange={handleChange}
|
||||
label={intl.formatMessage({ id: 'inverterNumber' })}
|
||||
name="inverterNumber"
|
||||
value={formValues.inverterNumber ?? ''}
|
||||
onChange={(e) => {
|
||||
setFormDirty(true);
|
||||
const raw = e.target.value;
|
||||
if (raw === '') {
|
||||
setFormValues((prev) => ({ ...prev, inverterNumber: '' as any }));
|
||||
return;
|
||||
}
|
||||
const parsed = parseInt(raw);
|
||||
if (isNaN(parsed) || parsed < 1) return;
|
||||
const currentArr = formValues.batteriesCountPerInverter || [1];
|
||||
const newArr = Array.from({ length: parsed }, (_, i) => currentArr[i] ?? 1);
|
||||
setFormValues((prev) => ({
|
||||
...prev,
|
||||
inverterNumber: parsed,
|
||||
batteriesCountPerInverter: newArr,
|
||||
}));
|
||||
}}
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
|
||||
{Array.from({ length: formValues.inverterNumber ?? 1 }, (_, i) => (
|
||||
<div key={`battCount_${i}`} style={{ marginBottom: '5px' }}>
|
||||
<TextField
|
||||
label={intl.formatMessage(
|
||||
{ id: 'batteriesCountInInverter' },
|
||||
{ number: i + 1 }
|
||||
)}
|
||||
name={`batteriesCountPerInverter_${i}`}
|
||||
value={formValues.batteriesCountPerInverter?.[i] ?? ''}
|
||||
onChange={(e) => {
|
||||
setFormDirty(true);
|
||||
const raw = e.target.value;
|
||||
if (raw === '') {
|
||||
setFormValues((prev) => {
|
||||
const arr = [...(prev.batteriesCountPerInverter || [1])];
|
||||
arr[i] = '' as any;
|
||||
return { ...prev, batteriesCountPerInverter: arr };
|
||||
});
|
||||
return;
|
||||
}
|
||||
const parsed = parseInt(raw);
|
||||
if (isNaN(parsed) || parsed < 1) return;
|
||||
setFormValues((prev) => {
|
||||
const arr = [...(prev.batteriesCountPerInverter || [1])];
|
||||
arr[i] = parsed;
|
||||
return { ...prev, batteriesCountPerInverter: arr };
|
||||
});
|
||||
}}
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{device === 4 && (
|
||||
<>
|
||||
<div style={{ marginBottom: '5px' }}>
|
||||
|
|
@ -489,12 +552,7 @@ function SodistoreHomeConfiguration(props: SodistoreHomeConfigurationProps) {
|
|||
|
||||
<div style={{ marginBottom: '5px' }}>
|
||||
<TextField
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="maximumChargingCurrent "
|
||||
defaultMessage="Maximum Charging Current"
|
||||
/>
|
||||
}
|
||||
label={intl.formatMessage({ id: 'maximumChargingCurrentPerBattery' })}
|
||||
name="maximumChargingCurrent"
|
||||
value={formValues.maximumChargingCurrent}
|
||||
onChange={handleChange}
|
||||
|
|
@ -504,12 +562,7 @@ function SodistoreHomeConfiguration(props: SodistoreHomeConfigurationProps) {
|
|||
|
||||
<div style={{ marginBottom: '5px' }}>
|
||||
<TextField
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="maximumDischargingCurrent "
|
||||
defaultMessage="Maximum Discharging Current"
|
||||
/>
|
||||
}
|
||||
label={intl.formatMessage({ id: 'maximumDischargingCurrentPerBattery' })}
|
||||
name="maximumDischargingCurrent"
|
||||
value={formValues.maximumDischargingCurrent}
|
||||
onChange={handleChange}
|
||||
|
|
@ -554,13 +607,13 @@ function SodistoreHomeConfiguration(props: SodistoreHomeConfigurationProps) {
|
|||
{/* Power input*/}
|
||||
<div style={{ marginBottom: '5px' }}>
|
||||
<TextField
|
||||
label={intl.formatMessage({ id: 'powerW' })}
|
||||
label={intl.formatMessage({ id: 'powerPerInverterKW' })}
|
||||
name="timeChargeandDischargePower"
|
||||
value={formValues.timeChargeandDischargePower}
|
||||
onChange={(e) =>
|
||||
handleTimeChargeDischargeChange(e.target.name, e.target.value)
|
||||
}
|
||||
helperText={intl.formatMessage({ id: 'enterPowerValue' })}
|
||||
helperText={intl.formatMessage({ id: 'perInverter' })}
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -486,6 +486,11 @@
|
|||
"minimumSocPercent": "Minimaler Ladezustand (%)",
|
||||
"powerW": "Leistung (W)",
|
||||
"enterPowerValue": "Positiven oder negativen Leistungswert eingeben",
|
||||
"inverterNumber": "Anzahl Wechselrichter",
|
||||
"batteriesCountInInverter": "Batterieanzahl in Wechselrichter {number}",
|
||||
"maximumChargingCurrentPerBattery": "Maximaler Ladestrom pro Batterie (A)",
|
||||
"maximumDischargingCurrentPerBattery": "Maximaler Entladestrom pro Batterie (A)",
|
||||
"powerPerInverterKW": "Leistung pro Wechselrichter (kW)",
|
||||
"startDateTime": "Startdatum und -zeit (Startzeit < Stoppzeit)",
|
||||
"stopDateTime": "Stoppdatum und -zeit (Startzeit < Stoppzeit)",
|
||||
"tourLanguageTitle": "Sprache",
|
||||
|
|
|
|||
|
|
@ -234,6 +234,11 @@
|
|||
"minimumSocPercent": "Minimum SoC (%)",
|
||||
"powerW": "Power (W)",
|
||||
"enterPowerValue": "Enter a positive or negative power value",
|
||||
"inverterNumber": "Inverter Number",
|
||||
"batteriesCountInInverter": "Batteries Count in Inverter {number}",
|
||||
"maximumChargingCurrentPerBattery": "Maximum Charging Current per Battery (A)",
|
||||
"maximumDischargingCurrentPerBattery": "Maximum Discharging Current per Battery (A)",
|
||||
"powerPerInverterKW": "Power per Inverter (kW)",
|
||||
"startDateTime": "Start Date and Time (Start Time < Stop Time)",
|
||||
"stopDateTime": "Stop Date and Time (Start Time < Stop Time)",
|
||||
"tourLanguageTitle": "Language",
|
||||
|
|
|
|||
|
|
@ -486,6 +486,11 @@
|
|||
"minimumSocPercent": "SoC minimum (%)",
|
||||
"powerW": "Puissance (W)",
|
||||
"enterPowerValue": "Entrez une valeur de puissance positive ou négative",
|
||||
"inverterNumber": "Nombre d'onduleurs",
|
||||
"batteriesCountInInverter": "Nombre de batteries dans l'onduleur {number}",
|
||||
"maximumChargingCurrentPerBattery": "Courant de charge maximum par batterie (A)",
|
||||
"maximumDischargingCurrentPerBattery": "Courant de décharge maximum par batterie (A)",
|
||||
"powerPerInverterKW": "Puissance par onduleur (kW)",
|
||||
"startDateTime": "Date et heure de début (Début < Fin)",
|
||||
"stopDateTime": "Date et heure de fin (Début < Fin)",
|
||||
"tourLanguageTitle": "Langue",
|
||||
|
|
|
|||
|
|
@ -486,6 +486,11 @@
|
|||
"minimumSocPercent": "SoC minimo (%)",
|
||||
"powerW": "Potenza (W)",
|
||||
"enterPowerValue": "Inserire un valore di potenza positivo o negativo",
|
||||
"inverterNumber": "Numero di inverter",
|
||||
"batteriesCountInInverter": "Numero di batterie nell'inverter {number}",
|
||||
"maximumChargingCurrentPerBattery": "Corrente massima di carica per batteria (A)",
|
||||
"maximumDischargingCurrentPerBattery": "Corrente massima di scarica per batteria (A)",
|
||||
"powerPerInverterKW": "Potenza per inverter (kW)",
|
||||
"startDateTime": "Data e ora di inizio (Inizio < Fine)",
|
||||
"stopDateTime": "Data e ora di fine (Inizio < Fine)",
|
||||
"tourLanguageTitle": "Lingua",
|
||||
|
|
|
|||
Loading…
Reference in New Issue