From 4420f7373b4ce34f8057f08a1194ae02453bf358 Mon Sep 17 00:00:00 2001 From: Noe Date: Thu, 25 Sep 2025 15:06:48 +0200 Subject: [PATCH] Added Configuration for SodistoreHome devices in the frontend --- .../BatteryView/BatteryViewSodioHome.tsx | 1 - .../Configuration/Configuration.tsx | 4 +- .../src/content/dashboards/Log/graph.util.tsx | 12 + .../SodiohomeInstallations/Installation.tsx | 58 ++- .../SodistoreHomeConfiguration.tsx | 386 ++++++++++++++++++ .../SodiohomeInstallations/index.tsx | 22 +- 6 files changed, 475 insertions(+), 8 deletions(-) create mode 100644 typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/SodistoreHomeConfiguration.tsx diff --git a/typescript/frontend-marios2/src/content/dashboards/BatteryView/BatteryViewSodioHome.tsx b/typescript/frontend-marios2/src/content/dashboards/BatteryView/BatteryViewSodioHome.tsx index 8793d5437..494da9f42 100644 --- a/typescript/frontend-marios2/src/content/dashboards/BatteryView/BatteryViewSodioHome.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/BatteryView/BatteryViewSodioHome.tsx @@ -44,7 +44,6 @@ function BatteryViewSodioHome(props: BatteryViewSodioHomeProps) { .sort((a, b) => parseInt(b.BatteryId) - parseInt(a.BatteryId)) : []; - console.log('battery view', sortedBatteryView); const [loading, setLoading] = useState(sortedBatteryView.length == 0); const handleMainStatsButton = () => { navigate(routes.mainstats); diff --git a/typescript/frontend-marios2/src/content/dashboards/Configuration/Configuration.tsx b/typescript/frontend-marios2/src/content/dashboards/Configuration/Configuration.tsx index 229c07552..26184efc4 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Configuration/Configuration.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Configuration/Configuration.tsx @@ -93,7 +93,7 @@ function Configuration(props: ConfigurationProps) { const { currentUser, setUser } = context; const { product, setProduct } = useContext(ProductIdContext); - const [formValues, setFormValues] = useState({ + const [formValues, setFormValues] = useState>({ minimumSoC: props.values.Config.MinSoc, gridSetPoint: (props.values.Config.GridSetPoint as number) / 1000, calibrationChargeState: CalibrationChargeOptionsController.indexOf( @@ -151,7 +151,7 @@ function Configuration(props: ConfigurationProps) { return; } else { // console.log('asked for', dayjs(formValues.calibrationChargeDate)); - const configurationToSend: ConfigurationValues = { + const configurationToSend: Partial = { minimumSoC: formValues.minimumSoC, gridSetPoint: formValues.gridSetPoint, calibrationChargeState: formValues.calibrationChargeState, 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 acf98244f..75c15bcdf 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Log/graph.util.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Log/graph.util.tsx @@ -323,6 +323,12 @@ export interface JSONRecordData { TruConvertDcIp: { DeviceState: string }; TsRelaysIp: { DeviceState: string }; }; + + //For SodistoerHome + MaximumChargingCurrent: number; + MaximumDischargingCurrent: number; + OperatingPriority: string; + BatteriesCount: number; }; DcDc: { @@ -602,6 +608,12 @@ export type ConfigurationValues = { calibrationChargeDate: Date | null; calibrationDischargeState: number; calibrationDischargeDate: Date | null; + + //For sodistoreHome + maximumDischargingCurrent: number; + maximumChargingCurrent: number; + operatingPriority: number; + batteriesCount: number; }; // // export interface Pv { diff --git a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/Installation.tsx b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/Installation.tsx index 9fa696a25..5202d5e7b 100644 --- a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/Installation.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/Installation.tsx @@ -24,6 +24,7 @@ import { TimeSpan, UnixTime } from '../../../dataCache/time'; import { fetchDataJson } from '../Installations/fetchData'; import { FetchResult } from '../../../dataCache/dataCache'; import BatteryViewSodioHome from '../BatteryView/BatteryViewSodioHome'; +import SodistoreHomeConfiguration from './SodistoreHomeConfiguration'; interface singleInstallationProps { current_installation?: I_Installation; @@ -179,6 +180,43 @@ function SodioHomeInstallation(props: singleInstallationProps) { } }; + const fetchDataForOneTime = async () => { + var timeperiodToSearch = 200; + let res; + let timestampToFetch; + + for (var i = timeperiodToSearch; i > 0; i -= 2) { + timestampToFetch = UnixTime.now().earlier(TimeSpan.fromSeconds(i)); + try { + res = await fetchDataJson(timestampToFetch, s3Credentials, false); + if (res !== FetchResult.notAvailable && res !== FetchResult.tryLater) { + break; + } + } catch (err) { + console.error('Error fetching data:', err); + return false; + } + } + + if (i <= 0) { + setConnected(false); + setLoading(false); + return false; + } + setConnected(true); + setLoading(false); + + const timestamp = Object.keys(res)[Object.keys(res).length - 1]; + setValues(res[timestamp]); + // setValues( + // extractValues({ + // time: UnixTime.fromTicks(parseInt(timestamp, 10)), + // value: res[timestamp] + // }) + // ); + return true; + }; + useEffect(() => { let path = location.split('/'); setCurrentTab(path[path.length - 1]); @@ -212,10 +250,10 @@ function SodioHomeInstallation(props: singleInstallationProps) { } } } - //Fetch only one time in configuration tab - // if (currentTab == 'configuration') { - // fetchDataForOneTime(); - // } + // Fetch only one time in configuration tab + if (currentTab == 'configuration') { + fetchDataForOneTime(); + } return () => { continueFetching.current = false; @@ -429,6 +467,18 @@ function SodioHomeInstallation(props: singleInstallationProps) { /> )} + {currentUser.userType == UserType.admin && ( + + } + /> + )} + {currentUser.userType == UserType.admin && ( { + setErrors((prevErrors) => ({ + ...prevErrors, + [field_name]: state + })); + }; + const theme = useTheme(); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(false); + const [updated, setUpdated] = useState(false); + const [dateSelectionError, setDateSelectionError] = useState(''); + const [isErrorDateModalOpen, setErrorDateModalOpen] = useState(false); + const context = useContext(UserContext); + const { currentUser, setUser } = context; + const { product, setProduct } = useContext(ProductIdContext); + + const [formValues, setFormValues] = useState>({ + minimumSoC: props.values.Config.MinSoc, + maximumDischargingCurrent: props.values.Config.MaximumChargingCurrent, + maximumChargingCurrent: props.values.Config.MaximumDischargingCurrent, + operatingPriority: OperatingPriorityOptions.indexOf( + props.values.Config.OperatingPriority + ), + batteriesCount: props.values.Config.BatteriesCount + }); + + const handleOperatingPriorityChange = (event) => { + setFormValues({ + ...formValues, + ['operatingPriority']: OperatingPriorityOptions.indexOf( + event.target.value + ) + }); + }; + + const handleSubmit = async (e) => { + // console.log('asked for', dayjs(formValues.calibrationChargeDate)); + const configurationToSend: Partial = { + minimumSoC: formValues.minimumSoC, + maximumDischargingCurrent: formValues.maximumDischargingCurrent, + maximumChargingCurrent: formValues.maximumChargingCurrent, + operatingPriority: formValues.operatingPriority + }; + + setLoading(true); + const res = await axiosConfig + .post( + `/EditInstallationConfig?installationId=${props.id}`, + configurationToSend + ) + .catch((err) => { + if (err.response) { + setError(true); + setLoading(false); + } + }); + + if (res) { + setUpdated(true); + setLoading(false); + } + }; + + const handleChange = (e) => { + const { name, value } = e.target; + + switch (name) { + case 'minimumSoC': + if ( + /[^0-9.]/.test(value) || + isNaN(parseFloat(value)) || + parseFloat(value) > 100 + ) { + SetErrorForField(name, true); + } else { + SetErrorForField(name, false); + } + break; + case 'gridSetPoint': + if (/[^0-9.]/.test(value) || isNaN(parseFloat(value))) { + SetErrorForField(name, true); + } else { + SetErrorForField(name, false); + } + break; + + default: + break; + } + + setFormValues({ + ...formValues, + [name]: value + }); + }; + + const handleOkOnErrorDateModal = () => { + setErrorDateModalOpen(false); + }; + + return ( + + + {isErrorDateModalOpen && ( + {}}> + + + {dateSelectionError} + + + + + + )} + + + + + <> +
+ + } + name="batteriesCount" + value={formValues.batteriesCount} + onChange={handleChange} + fullWidth + /> +
+ +
+ + } + name="minimumSoC" + value={formValues.minimumSoC} + onChange={handleChange} + helperText={ + errors.minimumSoC ? ( + + Value should be between 0-100% + + ) : ( + '' + ) + } + fullWidth + /> +
+ +
+ + } + name="maximumChargingCurrent" + value={formValues.maximumChargingCurrent} + onChange={handleChange} + fullWidth + /> +
+ +
+ + } + name="maximumDischargingCurrent" + value={formValues.maximumDischargingCurrent} + onChange={handleChange} + fullWidth + /> +
+ +
+ + + + + + +
+ + +
+ + {loading && ( + + )} + + {updated && ( + + Successfully applied configuration file + setUpdated(false)} + sx={{ marginLeft: '4px' }} + > + + + + )} + {error && ( + + An error has occurred + setError(false)} + sx={{ marginLeft: '4px' }} + > + + + + )} +
+
+
+
+
+
+ ); +} + +export default SodistoreHomeConfiguration; diff --git a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/index.tsx b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/index.tsx index 8dbf8cf4c..c5d69ba82 100644 --- a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/index.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/index.tsx @@ -29,7 +29,8 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) { 'manage', 'overview', 'log', - 'history' + 'history', + 'configuration' ]; const [currentTab, setCurrentTab] = useState(undefined); @@ -133,6 +134,16 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) { ) }, + + { + value: 'configuration', + label: ( + + ) + }, { value: 'history', label: ( @@ -213,6 +224,15 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) { ) }, + { + value: 'configuration', + label: ( + + ) + }, { value: 'history', label: (