diff --git a/typescript/frontend-marios2/src/content/dashboards/Configuration/Configuration.tsx b/typescript/frontend-marios2/src/content/dashboards/Configuration/Configuration.tsx
index 449d12308..dbb832c1d 100644
--- a/typescript/frontend-marios2/src/content/dashboards/Configuration/Configuration.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/Configuration/Configuration.tsx
@@ -29,7 +29,6 @@ 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 {
@@ -136,14 +135,6 @@ function Configuration(props: ConfigurationProps) {
.add(localOffset, 'minute')
.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);
@@ -160,21 +151,8 @@ function Configuration(props: ConfigurationProps) {
});
if (res) {
- 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);
- }
+ 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
index 49ffd8a68..86923395e 100644
--- a/typescript/frontend-marios2/src/content/dashboards/History/History.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/History/History.tsx
@@ -122,102 +122,135 @@ function HistoryOfActions(props: HistoryProps) {
- {/*
*/}
- {/* */}
- {/* */}
- {/* */}
- {/*
*/}
+
+
+
+
+
- {history.map((action, index) => (
- <>
-
-
-
-
- {action.user}
-
-
+ {history.map((action, index) => {
+ // Parse the timestamp string to a Date object
+ const date = new Date(action.timestamp);
+ // Extract the date part (e.g., "2023-05-31")
+ const datePart = date.toLocaleDateString();
+
+ // Extract the time part (e.g., "12:34:56")
+ const timePart = date.toLocaleTimeString();
+ return (
+ <>
+
-
- {action.date}
-
-
-
-
+ {action.userName}
+
+
+
+
- {action.time}
-
+
+ {datePart}
+
+
+
+
+ {timePart}
+
+
+
+
+
+ {action.description}
+
+
-
- >
- ))}
+ >
+ );
+ })}
diff --git a/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx b/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx
index d6f4cf84b..23f25107a 100644
--- a/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx
@@ -24,6 +24,7 @@ import Information from '../Information/Information';
import BatteryView from '../BatteryView/BatteryView';
import { UserType } from '../../../interfaces/UserTypes';
import HistoryOfActions from '../History/History';
+import PvView from '../PvView/PvView';
interface singleInstallationProps {
current_installation?: I_Installation;
@@ -124,6 +125,7 @@ function Installation(props: singleInstallationProps) {
useEffect(() => {
if (
currentTab == 'live' ||
+ currentTab == 'pvview' ||
currentTab == 'configuration' ||
location.includes('batteryview')
) {
@@ -131,7 +133,8 @@ function Installation(props: singleInstallationProps) {
if (
currentTab == 'live' ||
- (location.includes('batteryview') && !location.includes('mainstats'))
+ (location.includes('batteryview') && !location.includes('mainstats')) ||
+ currentTab == 'pvview'
) {
fetchDataPeriodically();
interval = setInterval(fetchDataPeriodically, 2000);
@@ -144,6 +147,7 @@ function Installation(props: singleInstallationProps) {
return () => {
if (
currentTab == 'live' ||
+ currentTab == 'pvview' ||
(location.includes('batteryview') && !location.includes('mainstats'))
) {
clearInterval(interval);
@@ -314,13 +318,7 @@ function Installation(props: singleInstallationProps) {
+
}
>
diff --git a/typescript/frontend-marios2/src/content/dashboards/Installations/index.tsx b/typescript/frontend-marios2/src/content/dashboards/Installations/index.tsx
index 34d6e9e21..bd10ab9d4 100644
--- a/typescript/frontend-marios2/src/content/dashboards/Installations/index.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/Installations/index.tsx
@@ -140,15 +140,15 @@ function InstallationTabs() {
/>
)
},
- // {
- // value: 'history',
- // label: (
- //
- // )
- // },
+ {
+ value: 'history',
+ label: (
+
+ )
+ },
{
value: 'pvview',
label:
@@ -271,16 +271,16 @@ function InstallationTabs() {
defaultMessage="Configuration"
/>
)
+ },
+ {
+ value: 'history',
+ label: (
+
+ )
}
- // {
- // value: 'history',
- // label: (
- //
- // )
- // }
]
: currentUser.userType == UserType.partner
? [
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 ec4dad776..9ecb56639 100644
--- a/typescript/frontend-marios2/src/content/dashboards/Log/graph.util.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/Log/graph.util.tsx
@@ -35,7 +35,12 @@ export type ConfigurationValues = {
calibrationChargeDate: Date | null;
};
-export interface Pv {}
+export interface Pv {
+ PvId: number;
+ Power: I_BoxDataValue;
+ Voltage: I_BoxDataValue;
+ Current: I_BoxDataValue;
+}
export interface Battery {
BatteryId: number;
@@ -81,6 +86,8 @@ export interface Battery {
MaxDischargePower: I_BoxDataValue;
}
+const PvKeys = ['PvId', 'Power', 'Voltage', 'Current'];
+
const BatteryKeys = [
'BatteryId',
'FwVersion',
@@ -172,10 +179,15 @@ type TopologyPaths = { [key in keyof TopologyValues]: string[] };
const batteryIds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+const pvIds = [
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 26, 27, 28, 29, 30
+];
+
const PvPaths = [
+ '/PvOnDc/Strings/%id%/Power',
'/PvOnDc/Strings/%id%/Voltage',
- '/PvOnDc/Strings/%id%/Current',
- '/PvOnDc/Strings/%id%/Power'
+ '/PvOnDc/Strings/%id%/Current'
];
const batteryPaths = [
@@ -300,7 +312,7 @@ export const topologyPaths: TopologyPaths = {
batteryPaths.map((path) => path.replace('%id%', id.toString()))
),
- pvView: batteryIds.flatMap((id) =>
+ pvView: pvIds.flatMap((id) =>
PvPaths.map((path) => path.replace('%id%', id.toString()))
),
@@ -331,15 +343,47 @@ export const extractValues = (
timeSeriesData: DataPoint
): TopologyValues | null => {
const extractedValues: TopologyValues = {} as TopologyValues;
-
- // console.log('timeSeriesData=', timeSeriesData);
-
for (const topologyKey of Object.keys(topologyPaths)) {
//Each topologykey may have more than one paths (for example inverter)
const paths = topologyPaths[topologyKey];
let topologyValues: { unit: string; value: string | number }[] = [];
- if (topologyKey === 'batteryView') {
+ if (topologyKey === 'pvView') {
+ extractedValues[topologyKey] = [];
+ let pv_index = 0;
+ let pathIndex = 0;
+
+ while (pathIndex < paths.length) {
+ let pv = {};
+ let existingKeys = 0;
+
+ //We prepare a pv object for each node. We extract the number of nodes from the '/PvOnDc/NbrOfStrings' path.
+ //PvKeys[0] is the pv id.
+ pv[PvKeys[0]] = pv_index;
+ //Then, search all the remaining battery keys
+ for (let i = 1; i < PvKeys.length; i++) {
+ const path = paths[pathIndex];
+ if (timeSeriesData.value.hasOwnProperty(path)) {
+ existingKeys++;
+
+ pv[PvKeys[i]] = {
+ unit: timeSeriesData.value[path].unit.includes('~')
+ ? timeSeriesData.value[path].unit.replace('~', '')
+ : timeSeriesData.value[path].unit,
+ value:
+ typeof timeSeriesData.value[path].value === 'string'
+ ? timeSeriesData.value[path].value
+ : Number(timeSeriesData.value[path].value).toFixed(1)
+ };
+ }
+ pathIndex++;
+ }
+ pv_index++;
+ if (existingKeys > 0) {
+ extractedValues[topologyKey].push(pv as Pv);
+ }
+ }
+ } else if (topologyKey === 'batteryView') {
extractedValues[topologyKey] = [];
const node_ids_from_csv = timeSeriesData.value[
'/Config/Devices/BatteryNodes'
diff --git a/typescript/frontend-marios2/src/content/dashboards/PvView/PvView.tsx b/typescript/frontend-marios2/src/content/dashboards/PvView/PvView.tsx
index e69de29bb..548fb36fc 100644
--- a/typescript/frontend-marios2/src/content/dashboards/PvView/PvView.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/PvView/PvView.tsx
@@ -0,0 +1,187 @@
+import React, { useEffect, useState } from 'react';
+import {
+ Container,
+ Paper,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ Typography
+} from '@mui/material';
+import { TopologyValues } from '../Log/graph.util';
+import { useLocation, useNavigate } from 'react-router-dom';
+import routes from '../../../Resources/routes.json';
+import CircularProgress from '@mui/material/CircularProgress';
+
+interface PvViewProps {
+ values: TopologyValues;
+ connected: boolean;
+}
+
+function PvView(props: PvViewProps) {
+ if (props.values === null && props.connected == true) {
+ return null;
+ }
+ const currentLocation = useLocation();
+ const navigate = useNavigate();
+ const sortedPvView =
+ props.values != null
+ ? [...props.values.pvView].sort((a, b) => a.PvId - b.PvId)
+ : [];
+
+ const [loading, setLoading] = useState(sortedPvView.length == 0);
+
+ const handleMainStatsButton = () => {
+ navigate(routes.mainstats);
+ };
+
+ const findBatteryData = (batteryId: number) => {
+ for (let i = 0; i < props.values.batteryView.length; i++) {
+ if (props.values.batteryView[i].BatteryId == batteryId) {
+ return props.values.batteryView[i];
+ }
+ }
+ };
+
+ useEffect(() => {
+ if (sortedPvView.length == 0) {
+ setLoading(true);
+ } else {
+ setLoading(false);
+ }
+ }, [sortedPvView]);
+
+ return (
+ <>
+ {!props.connected && (
+
+
+
+ Unable to communicate with the installation
+
+
+ Please wait or refresh the page
+
+
+ )}
+ {loading && props.connected && (
+
+
+
+ Battery service is not available at the moment
+
+
+ Please wait or refresh the page
+
+
+ )}
+
+ {!loading && props.connected && (
+
+
+
+
+
+ Pv
+ Power
+ Voltage
+ Current
+
+
+
+ {sortedPvView.map((pv) => (
+
+
+ {'String ' + pv.PvId}
+
+
+
+ {pv.Power.value + ' ' + pv.Power.unit}
+
+
+ {pv.Voltage.value + ' ' + pv.Voltage.unit}
+
+
+ {pv.Current.value + ' ' + pv.Current.unit}
+
+
+ ))}
+
+
+
+
+ )}
+ >
+ );
+}
+
+export default PvView;
diff --git a/typescript/frontend-marios2/src/interfaces/S3Types.tsx b/typescript/frontend-marios2/src/interfaces/S3Types.tsx
index 078a011bc..6d7c56db0 100644
--- a/typescript/frontend-marios2/src/interfaces/S3Types.tsx
+++ b/typescript/frontend-marios2/src/interfaces/S3Types.tsx
@@ -1,5 +1,3 @@
-import { ConfigurationValues } from '../content/dashboards/Log/graph.util';
-
export interface I_S3Credentials {
s3Region: string;
s3Provider: string;
@@ -20,8 +18,9 @@ export interface ErrorMessage {
}
export interface Action {
- configuration: ConfigurationValues;
- date: string;
- time: string;
- user: string;
+ id: number;
+ userName: string;
+ installationId: number;
+ timestamp: string;
+ description: String;
}