Added SodistoreGrid as an empty templated and built S3 bucket creation pipeline

This commit is contained in:
Yinyin Liu 2026-03-04 10:59:08 +01:00
parent 7df4842980
commit 7aacddd761
23 changed files with 239 additions and 53 deletions

View File

@ -200,6 +200,8 @@ public class Controller : ControllerBase
bucketPath = "s3://" + installation.S3BucketId + "-3e5b3069-214a-43ee-8d85-57d72000c19d/" + startTimestamp; bucketPath = "s3://" + installation.S3BucketId + "-3e5b3069-214a-43ee-8d85-57d72000c19d/" + startTimestamp;
else if (installation.Product == (int)ProductType.SodioHome) else if (installation.Product == (int)ProductType.SodioHome)
bucketPath = "s3://" + installation.S3BucketId + "-e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa/" + startTimestamp; bucketPath = "s3://" + installation.S3BucketId + "-e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa/" + startTimestamp;
else if (installation.Product == (int)ProductType.SodistoreGrid)
bucketPath = "s3://" + installation.S3BucketId + "-5109c126-e141-43ab-8658-f3c44c838ae8/" + startTimestamp;
else else
bucketPath = "s3://" + installation.S3BucketId + "-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e/" + startTimestamp; bucketPath = "s3://" + installation.S3BucketId + "-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e/" + startTimestamp;
Console.WriteLine("Fetching data for "+startTimestamp); Console.WriteLine("Fetching data for "+startTimestamp);
@ -546,6 +548,19 @@ public class Controller : ControllerBase
.ToList(); .ToList();
} }
[HttpGet(nameof(GetAllSodistoreGridInstallations))]
public ActionResult<IEnumerable<Installation>> GetAllSodistoreGridInstallations(Token authToken)
{
var user = Db.GetSession(authToken)?.User;
if (user is null)
return Unauthorized();
return user
.AccessibleInstallations(product:(int)ProductType.SodistoreGrid)
.ToList();
}
[HttpGet(nameof(GetAllFolders))] [HttpGet(nameof(GetAllFolders))]
@ -1513,6 +1528,7 @@ public class Controller : ControllerBase
0 => config.GetConfigurationSalimax(), // Salimax 0 => config.GetConfigurationSalimax(), // Salimax
3 => config.GetConfigurationSodistoreMax(), // SodiStoreMax 3 => config.GetConfigurationSodistoreMax(), // SodiStoreMax
2 => config.GetConfigurationSodistoreHome(), // SodiStoreHome 2 => config.GetConfigurationSodistoreHome(), // SodiStoreHome
4 => config.GetConfigurationSodistoreGrid(), // SodistoreGrid
_ => config.GetConfigurationString() // fallback _ => config.GetConfigurationString() // fallback
}; };

View File

@ -48,6 +48,12 @@ public class Configuration
$"BatteriesCount: {BatteriesCount}, ClusterNumber: {ClusterNumber}, PvNumber: {PvNumber}, ControlPermission:{ControlPermission}, "+ $"BatteriesCount: {BatteriesCount}, ClusterNumber: {ClusterNumber}, PvNumber: {PvNumber}, ControlPermission:{ControlPermission}, "+
$"SinexcelTimeChargeandDischargePower: {TimeChargeandDischargePower}, SinexcelStartTimeChargeandDischargeDayandTime: {StartTimeChargeandDischargeDayandTime}, SinexcelStopTimeChargeandDischargeDayandTime: {StopTimeChargeandDischargeDayandTime}"; $"SinexcelTimeChargeandDischargePower: {TimeChargeandDischargePower}, SinexcelStartTimeChargeandDischargeDayandTime: {StartTimeChargeandDischargeDayandTime}, SinexcelStopTimeChargeandDischargeDayandTime: {StopTimeChargeandDischargeDayandTime}";
} }
// TODO: SodistoreGrid — update configuration fields when defined
public string GetConfigurationSodistoreGrid()
{
return "";
}
} }
public enum CalibrationChargeType public enum CalibrationChargeType

View File

@ -7,7 +7,8 @@ public enum ProductType
Salimax = 0, Salimax = 0,
Salidomo = 1, Salidomo = 1,
SodioHome =2, SodioHome =2,
SodiStoreMax=3 SodiStoreMax=3,
SodistoreGrid=4
} }
public enum StatusType public enum StatusType

View File

@ -145,6 +145,7 @@ public static class ExoCmd
const String method = "iam-role"; const String method = "iam-role";
String rolename = installation.Product==(int)ProductType.Salimax?Db.Installations.Count(f => f.Product == (int)ProductType.Salimax) + installation.Name: String rolename = installation.Product==(int)ProductType.Salimax?Db.Installations.Count(f => f.Product == (int)ProductType.Salimax) + installation.Name:
installation.Product==(int)ProductType.SodiStoreMax?Db.Installations.Count(f => f.Product == (int)ProductType.SodiStoreMax) + installation.Name: installation.Product==(int)ProductType.SodiStoreMax?Db.Installations.Count(f => f.Product == (int)ProductType.SodiStoreMax) + installation.Name:
installation.Product==(int)ProductType.SodistoreGrid?Db.Installations.Count(f => f.Product == (int)ProductType.SodistoreGrid) + installation.Name:
Db.Installations.Count(f => f.Product == (int)ProductType.Salidomo) + installation.Name; Db.Installations.Count(f => f.Product == (int)ProductType.Salidomo) + installation.Name;
@ -320,6 +321,7 @@ public static class ExoCmd
const String method = "iam-role"; const String method = "iam-role";
String rolename = installation.Product==(int)ProductType.Salimax?Db.Installations.Count(f => f.Product == (int)ProductType.Salimax) + installation.Name: String rolename = installation.Product==(int)ProductType.Salimax?Db.Installations.Count(f => f.Product == (int)ProductType.Salimax) + installation.Name:
installation.Product==(int)ProductType.SodiStoreMax?Db.Installations.Count(f => f.Product == (int)ProductType.SodiStoreMax) + installation.Name: installation.Product==(int)ProductType.SodiStoreMax?Db.Installations.Count(f => f.Product == (int)ProductType.SodiStoreMax) + installation.Name:
installation.Product==(int)ProductType.SodistoreGrid?Db.Installations.Count(f => f.Product == (int)ProductType.SodistoreGrid) + installation.Name:
Db.Installations.Count(f => f.Product == (int)ProductType.Salidomo) + installation.Name; Db.Installations.Count(f => f.Product == (int)ProductType.Salidomo) + installation.Name;
var contentString = $$""" var contentString = $$"""

View File

@ -10,6 +10,7 @@ public static class InstallationMethods
private static readonly String BucketNameSalt = "3e5b3069-214a-43ee-8d85-57d72000c19d"; private static readonly String BucketNameSalt = "3e5b3069-214a-43ee-8d85-57d72000c19d";
private static readonly String SalidomoBucketNameSalt = "c0436b6a-d276-4cd8-9c44-1eae86cf5d0e"; private static readonly String SalidomoBucketNameSalt = "c0436b6a-d276-4cd8-9c44-1eae86cf5d0e";
private static readonly String SodioHomeBucketNameSalt = "e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa"; private static readonly String SodioHomeBucketNameSalt = "e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa";
private static readonly String SodistoreGridBucketNameSalt = "5109c126-e141-43ab-8658-f3c44c838ae8";
public static String BucketName(this Installation installation) public static String BucketName(this Installation installation)
{ {
@ -23,6 +24,11 @@ public static class InstallationMethods
return $"{installation.S3BucketId}-{SodioHomeBucketNameSalt}"; return $"{installation.S3BucketId}-{SodioHomeBucketNameSalt}";
} }
if (installation.Product == (int)ProductType.SodistoreGrid)
{
return $"{installation.S3BucketId}-{SodistoreGridBucketNameSalt}";
}
return $"{installation.S3BucketId}-{SalidomoBucketNameSalt}"; return $"{installation.S3BucketId}-{SalidomoBucketNameSalt}";
} }

View File

@ -239,7 +239,7 @@ public static class SessionMethods
} }
if (installation.Product == (int)ProductType.SodiStoreMax || installation.Product == (int)ProductType.SodioHome) if (installation.Product == (int)ProductType.SodiStoreMax || installation.Product == (int)ProductType.SodioHome || installation.Product == (int)ProductType.SodistoreGrid)
{ {
return user is not null return user is not null
&& user.UserType != 0 && user.UserType != 0
@ -295,7 +295,7 @@ public static class SessionMethods
.Apply(Db.Update); .Apply(Db.Update);
} }
if (installation.Product == (int)ProductType.SodiStoreMax) if (installation.Product == (int)ProductType.SodiStoreMax || installation.Product == (int)ProductType.SodistoreGrid)
{ {
return user is not null return user is not null

View File

@ -12,7 +12,11 @@ public class DeleteOldDataFromS3
{ {
string configPath = "/home/ubuntu/.s3cfg"; string configPath = "/home/ubuntu/.s3cfg";
string bucketPath = installation.Product ==(int)ProductType.Salidomo ? $"s3://{installation.S3BucketId}-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e/{timestamps_to_delete}*" : $"s3://{installation.S3BucketId}-3e5b3069-214a-43ee-8d85-57d72000c19d/{timestamps_to_delete}*" ; string bucketPath = installation.Product == (int)ProductType.Salidomo
? $"s3://{installation.S3BucketId}-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e/{timestamps_to_delete}*"
: installation.Product == (int)ProductType.SodistoreGrid
? $"s3://{installation.S3BucketId}-5109c126-e141-43ab-8658-f3c44c838ae8/{timestamps_to_delete}*"
: $"s3://{installation.S3BucketId}-3e5b3069-214a-43ee-8d85-57d72000c19d/{timestamps_to_delete}*" ;
//Console.WriteLine($"Deleting old data from {bucketPath}"); //Console.WriteLine($"Deleting old data from {bucketPath}");

View File

@ -16,6 +16,7 @@ public class Session : Relation<String, Int64>
public Boolean AccessToSalidomo { get; set; } = false; public Boolean AccessToSalidomo { get; set; } = false;
public Boolean AccessToSodistoreMax { get; set; } = false; public Boolean AccessToSodistoreMax { get; set; } = false;
public Boolean AccessToSodioHome { get; set; } = false; public Boolean AccessToSodioHome { get; set; } = false;
public Boolean AccessToSodistoreGrid { get; set; } = false;
[Ignore] public Boolean Valid => DateTime.Now - LastSeen <=MaxAge ; [Ignore] public Boolean Valid => DateTime.Now - LastSeen <=MaxAge ;
// Private backing field // Private backing field
@ -49,6 +50,7 @@ public class Session : Relation<String, Int64>
AccessToSalidomo = user.AccessibleInstallations(product: (int)ProductType.Salidomo).ToList().Count > 0; AccessToSalidomo = user.AccessibleInstallations(product: (int)ProductType.Salidomo).ToList().Count > 0;
AccessToSodistoreMax = user.AccessibleInstallations(product: (int)ProductType.SodiStoreMax).ToList().Count > 0; AccessToSodistoreMax = user.AccessibleInstallations(product: (int)ProductType.SodiStoreMax).ToList().Count > 0;
AccessToSodioHome = user.AccessibleInstallations(product: (int)ProductType.SodioHome).ToList().Count > 0; AccessToSodioHome = user.AccessibleInstallations(product: (int)ProductType.SodioHome).ToList().Count > 0;
AccessToSodistoreGrid = user.AccessibleInstallations(product: (int)ProductType.SodistoreGrid).ToList().Count > 0;
Console.WriteLine("salimax" +user.AccessibleInstallations(product: (int)ProductType.Salimax).ToList().Count); Console.WriteLine("salimax" +user.AccessibleInstallations(product: (int)ProductType.Salimax).ToList().Count);
Console.WriteLine("AccessToSodistoreMax" +user.AccessibleInstallations(product: (int)ProductType.SodiStoreMax).ToList().Count); Console.WriteLine("AccessToSodistoreMax" +user.AccessibleInstallations(product: (int)ProductType.SodiStoreMax).ToList().Count);

View File

@ -100,6 +100,11 @@ public static class RabbitMqManager
monitorLink = monitorLink =
$"https://monitor.inesco.energy/sodistore_installations/list/installation/{installation.S3BucketId}/batteryview"; $"https://monitor.inesco.energy/sodistore_installations/list/installation/{installation.S3BucketId}/batteryview";
} }
else if (installation.Product == (int)ProductType.SodistoreGrid)
{
monitorLink =
$"https://monitor.inesco.energy/sodistoregrid_installations/list/installation/{installation.S3BucketId}/batteryview";
}
else else
{ {
monitorLink = monitorLink =

View File

@ -30,7 +30,8 @@ public static class WebsocketManager
if ((installationConnection.Value.Product == (int)ProductType.Salimax && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(2)) || if ((installationConnection.Value.Product == (int)ProductType.Salimax && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(2)) ||
(installationConnection.Value.Product == (int)ProductType.Salidomo && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(60)) || (installationConnection.Value.Product == (int)ProductType.Salidomo && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(60)) ||
(installationConnection.Value.Product == (int)ProductType.SodioHome && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(4)) || (installationConnection.Value.Product == (int)ProductType.SodioHome && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(4)) ||
(installationConnection.Value.Product == (int)ProductType.SodiStoreMax && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(2)) (installationConnection.Value.Product == (int)ProductType.SodiStoreMax && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(2)) ||
(installationConnection.Value.Product == (int)ProductType.SodistoreGrid && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(2))
) )
{ {
Console.WriteLine("Installation ID is " + installationConnection.Key); Console.WriteLine("Installation ID is " + installationConnection.Key);

View File

@ -35,7 +35,8 @@ function App() {
setAccessToSalimax, setAccessToSalimax,
setAccessToSalidomo, setAccessToSalidomo,
setAccessToSodiohome, setAccessToSodiohome,
setAccessToSodistore setAccessToSodistore,
setAccessToSodistoreGrid
} = useContext(ProductIdContext); } = useContext(ProductIdContext);
const [language, setLanguage] = useState<string>( const [language, setLanguage] = useState<string>(
@ -102,12 +103,15 @@ function App() {
setAccessToSalidomo(response.data.accessToSalidomo); setAccessToSalidomo(response.data.accessToSalidomo);
setAccessToSodiohome(response.data.accessToSodioHome); setAccessToSodiohome(response.data.accessToSodioHome);
setAccessToSodistore(response.data.accessToSodistoreMax); setAccessToSodistore(response.data.accessToSodistoreMax);
setAccessToSodistoreGrid(response.data.accessToSodistoreGrid);
if (response.data.accessToSalimax) { if (response.data.accessToSalimax) {
navigate(routes.installations); navigate(routes.installations);
} else if (response.data.accessToSalidomo) { } else if (response.data.accessToSalidomo) {
navigate(routes.salidomo_installations); navigate(routes.salidomo_installations);
} else if (response.data.accessToSodistoreMax) { } else if (response.data.accessToSodistoreMax) {
navigate(routes.sodistore_installations); navigate(routes.sodistore_installations);
} else if (response.data.accessToSodistoreGrid) {
navigate(routes.sodistoregrid_installations);
} else { } else {
navigate(routes.sodiohome_installations); navigate(routes.sodiohome_installations);
} }
@ -215,6 +219,15 @@ function App() {
} }
/> />
<Route
path={routes.sodistoregrid_installations + '*'}
element={
<AccessContextProvider>
<InstallationTabs product={4} />
</AccessContextProvider>
}
/>
<Route path={routes.users + '*'} element={<Users />} /> <Route path={routes.users + '*'} element={<Users />} />
<Route <Route
path={'*'} path={'*'}

View File

@ -4,6 +4,7 @@
"salidomo_installations": "/salidomo_installations/", "salidomo_installations": "/salidomo_installations/",
"sodistore_installations": "/sodistore_installations/", "sodistore_installations": "/sodistore_installations/",
"sodiohome_installations": "/sodiohome_installations/", "sodiohome_installations": "/sodiohome_installations/",
"sodistoregrid_installations": "/sodistoregrid_installations/",
"installation": "installation/", "installation": "installation/",
"login": "/login/", "login": "/login/",
"forgotPassword": "/forgotPassword/", "forgotPassword": "/forgotPassword/",

View File

@ -40,7 +40,8 @@ function Login() {
setAccessToSalimax, setAccessToSalimax,
setAccessToSalidomo, setAccessToSalidomo,
setAccessToSodiohome, setAccessToSodiohome,
setAccessToSodistore setAccessToSodistore,
setAccessToSodistoreGrid
} = useContext(ProductIdContext); } = useContext(ProductIdContext);
const navigate = useNavigate(); const navigate = useNavigate();
@ -84,6 +85,7 @@ function Login() {
setAccessToSalidomo(response.data.accessToSalidomo); setAccessToSalidomo(response.data.accessToSalidomo);
setAccessToSodiohome(response.data.accessToSodioHome); setAccessToSodiohome(response.data.accessToSodioHome);
setAccessToSodistore(response.data.accessToSodistoreMax); setAccessToSodistore(response.data.accessToSodistoreMax);
setAccessToSodistoreGrid(response.data.accessToSodistoreGrid);
if (rememberMe) { if (rememberMe) {
cookies.set('rememberedUsername', username, { path: '/' }); cookies.set('rememberedUsername', username, { path: '/' });
@ -95,6 +97,8 @@ function Login() {
navigate(routes.salidomo_installations); navigate(routes.salidomo_installations);
} else if (response.data.accessToSodistoreMax) { } else if (response.data.accessToSodistoreMax) {
navigate(routes.sodistore_installations); navigate(routes.sodistore_installations);
} else if (response.data.accessToSodistoreGrid) {
navigate(routes.sodistoregrid_installations);
} else { } else {
navigate(routes.sodiohome_installations); navigate(routes.sodiohome_installations);
} }

View File

@ -245,7 +245,7 @@ function BatteryView(props: BatteryViewProps) {
) : ( ) : (
<TableCell align="center">Max Cell Voltage</TableCell> <TableCell align="center">Max Cell Voltage</TableCell>
)} )}
{product === 3 && ( {(product === 3 || product === 4) && (
<TableCell align="center">Voltage Difference</TableCell> <TableCell align="center">Voltage Difference</TableCell>
)} )}
</TableRow> </TableRow>
@ -469,7 +469,7 @@ function BatteryView(props: BatteryViewProps) {
</TableCell> </TableCell>
</> </>
)} )}
{product === 3 && ( {(product === 3 || product === 4) && (
<> <>
{(() => { {(() => {
const cellVoltagesString = const cellVoltagesString =
@ -524,7 +524,7 @@ function BatteryView(props: BatteryViewProps) {
})()} })()}
</> </>
)} )}
{product === 3 && ( {(product === 3 || product === 4) && (
<> <>
{(() => { {(() => {
const cellVoltagesString = const cellVoltagesString =

View File

@ -328,9 +328,12 @@ function Information(props: InformationProps) {
label="S3 Bucket Name" label="S3 Bucket Name"
name="s3bucketname" name="s3bucketname"
value={ value={
product === 0 || product == 3 formValues.product === 0 || formValues.product == 3
? formValues.s3BucketId + ? formValues.s3BucketId +
'-3e5b3069-214a-43ee-8d85-57d72000c19d' '-3e5b3069-214a-43ee-8d85-57d72000c19d'
: formValues.product == 4
? formValues.s3BucketId +
'-5109c126-e141-43ab-8658-f3c44c838ae8'
: formValues.s3BucketId + : formValues.s3BucketId +
'-e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa' '-e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa'
} }

View File

@ -57,9 +57,13 @@ function Installation(props: singleInstallationProps) {
s3BucketId: props.current_installation.s3BucketId s3BucketId: props.current_installation.s3BucketId
}; };
// TODO: SodistoreGrid — uses its own bucket salt
const s3BucketSalt =
props.current_installation.product === 4
? '5109c126-e141-43ab-8658-f3c44c838ae8'
: '3e5b3069-214a-43ee-8d85-57d72000c19d';
const s3Bucket = const s3Bucket =
props.current_installation.s3BucketId.toString() + props.current_installation.s3BucketId.toString() + '-' + s3BucketSalt;
'-3e5b3069-214a-43ee-8d85-57d72000c19d';
const s3Credentials = { s3Bucket, ...S3data }; const s3Credentials = { s3Bucket, ...S3data };
@ -427,12 +431,15 @@ function Installation(props: singleInstallationProps) {
} }
></Route> ></Route>
<Route {/* TODO: SodistoreGrid — PV View excluded, add back when data path is ready */}
path={routes.pvview + '/*'} {props.current_installation.product !== 4 && (
element={ <Route
<PvView values={values} connected={connected}></PvView> path={routes.pvview + '/*'}
} element={
></Route> <PvView values={values} connected={connected}></PvView>
}
></Route>
)}
<Route <Route
path={routes.overview} path={routes.overview}
@ -447,11 +454,28 @@ function Installation(props: singleInstallationProps) {
<Route <Route
path={routes.live} path={routes.live}
element={ element={
<Topology props.current_installation.product === 4 ? (
values={values} // TODO: SodistoreGrid — implement actual topology layout
connected={connected} <Container
loading={loading} maxWidth="xl"
></Topology> sx={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '40vh'
}}
>
<Typography variant="body1" color="text.secondary">
Live view coming soon
</Typography>
</Container>
) : (
<Topology
values={values}
connected={connected}
loading={loading}
></Topology>
)
} }
/> />
@ -470,10 +494,27 @@ function Installation(props: singleInstallationProps) {
<Route <Route
path={routes.configuration} path={routes.configuration}
element={ element={
<Configuration props.current_installation.product === 4 ? (
values={values} // TODO: SodistoreGrid — implement actual configuration
id={props.current_installation.id} <Container
></Configuration> maxWidth="xl"
sx={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '40vh'
}}
>
<Typography variant="body1" color="text.secondary">
Configuration not yet available
</Typography>
</Container>
) : (
<Configuration
values={values}
id={props.current_installation.id}
></Configuration>
)
} }
/> />
)} )}

View File

@ -39,11 +39,18 @@ function InstallationTabs(props: InstallationTabsProps) {
const { const {
salimax_or_sodistore_Installations, salimax_or_sodistore_Installations,
sodistoreGridInstallations,
fetchAllInstallations, fetchAllInstallations,
socket, socket,
openSocket, openSocket,
closeSocket closeSocket
} = useContext(InstallationsContext); } = useContext(InstallationsContext);
// Use the correct installations array based on product
const installations =
props.product === 4
? sodistoreGridInstallations
: salimax_or_sodistore_Installations;
const { product, setProduct } = useContext(ProductIdContext); const { product, setProduct } = useContext(ProductIdContext);
useEffect(() => { useEffect(() => {
@ -93,7 +100,10 @@ function InstallationTabs(props: InstallationTabsProps) {
return ret_path; return ret_path;
}; };
const singleInstallationTabs = // TODO: SodistoreGrid — PV View excluded for product 4, add back when data path is ready
const hidePvView = props.product === 4;
const singleInstallationTabs = (
currentUser.userType == UserType.admin currentUser.userType == UserType.admin
? [ ? [
{ {
@ -204,7 +214,8 @@ function InstallationTabs(props: InstallationTabsProps) {
<FormattedMessage id="information" defaultMessage="Information" /> <FormattedMessage id="information" defaultMessage="Information" />
) )
} }
]; ]
).filter((tab) => !(hidePvView && tab.value === 'pvview'));
const tabs = const tabs =
currentTab != 'list' && currentTab != 'list' &&
@ -372,7 +383,12 @@ function InstallationTabs(props: InstallationTabsProps) {
} }
]; ];
return salimax_or_sodistore_Installations.length > 1 ? ( // Filter out PV View for SodistoreGrid
const filteredTabs = hidePvView
? tabs.filter((tab) => tab.value !== 'pvview')
: tabs;
return installations.length > 1 ? (
<> <>
<Container maxWidth="xl" sx={{ marginTop: '20px' }} className="mainframe"> <Container maxWidth="xl" sx={{ marginTop: '20px' }} className="mainframe">
<TabsContainerWrapper> <TabsContainerWrapper>
@ -384,7 +400,7 @@ function InstallationTabs(props: InstallationTabsProps) {
textColor="primary" textColor="primary"
indicatorColor="primary" indicatorColor="primary"
> >
{tabs.map((tab) => ( {filteredTabs.map((tab) => (
<Tab <Tab
key={tab.value} key={tab.value}
value={tab.value} value={tab.value}
@ -415,7 +431,7 @@ function InstallationTabs(props: InstallationTabsProps) {
<Grid item xs={12}> <Grid item xs={12}>
<Box p={4}> <Box p={4}>
<InstallationSearch <InstallationSearch
installations={salimax_or_sodistore_Installations} installations={installations}
/> />
</Box> </Box>
</Grid> </Grid>
@ -428,6 +444,10 @@ function InstallationTabs(props: InstallationTabsProps) {
element={ element={
props.product === 0 ? ( props.product === 0 ? (
<Navigate to={routes.installations + routes.list} /> <Navigate to={routes.installations + routes.list} />
) : props.product === 4 ? (
<Navigate
to={routes.sodistoregrid_installations + routes.list}
/>
) : ( ) : (
<Navigate <Navigate
to={routes.sodistore_installations + routes.list} to={routes.sodistore_installations + routes.list}
@ -441,7 +461,7 @@ function InstallationTabs(props: InstallationTabsProps) {
</Container> </Container>
<Footer /> <Footer />
</> </>
) : salimax_or_sodistore_Installations.length === 1 ? ( ) : installations.length === 1 ? (
<> <>
<Container maxWidth="xl" sx={{ marginTop: '20px' }} className="mainframe"> <Container maxWidth="xl" sx={{ marginTop: '20px' }} className="mainframe">
<TabsContainerWrapper> <TabsContainerWrapper>
@ -480,7 +500,7 @@ function InstallationTabs(props: InstallationTabsProps) {
<Box p={4}> <Box p={4}>
<Installation <Installation
current_installation={ current_installation={
salimax_or_sodistore_Installations[0] installations[0]
} }
type="installation" type="installation"
></Installation> ></Installation>

View File

@ -65,10 +65,11 @@ function TreeInformation(props: TreeInformationProps) {
setProduct(e.target.value); // Directly update the product state setProduct(e.target.value); // Directly update the product state
}; };
const ProductTypes = ['Salimax', 'Salidomo', 'SodistoreHome', 'SodistoreMax']; const ProductTypes = ['Salimax', 'Salidomo', 'SodistoreHome', 'SodistoreMax', 'SodistoreGrid'];
const ProductDisplayNames: Record<string, string> = { const ProductDisplayNames: Record<string, string> = {
'SodistoreHome': 'Sodistore Home', 'SodistoreHome': 'Sodistore Home',
'SodistoreMax': 'Sodistore Max' 'SodistoreMax': 'Sodistore Max',
'SodistoreGrid': 'Sodistore Grid'
}; };
const isMobile = window.innerWidth <= 1490; const isMobile = window.innerWidth <= 1490;
@ -327,7 +328,8 @@ function TreeInformation(props: TreeInformationProps) {
)} )}
{openModalInstallation && {openModalInstallation &&
(product == 'Salimax' || (product == 'Salimax' ||
product == 'SodistoreMax') && ( product == 'SodistoreMax' ||
product == 'SodistoreGrid') && (
<InstallationForm <InstallationForm
cancel={handleFormCancel} cancel={handleFormCancel}
submit={handleInstallationFormSubmit} submit={handleInstallationFormSubmit}

View File

@ -80,7 +80,7 @@ function InstallationTree() {
key={installation.id} key={installation.id}
path={routes.installation + installation.id + '/*'} path={routes.installation + installation.id + '/*'}
element={ element={
installation.product == 0 || installation.product == 3 ? ( installation.product == 0 || installation.product == 3 || installation.product == 4 ? (
<Installation <Installation
key={installation.id} key={installation.id}
current_installation={installation} current_installation={installation}

View File

@ -31,6 +31,9 @@ const InstallationsContextProvider = ({
const [sodiohomeInstallations, setSodiohomeInstallations] = useState< const [sodiohomeInstallations, setSodiohomeInstallations] = useState<
I_Installation[] I_Installation[]
>([]); >([]);
const [sodistoreGridInstallations, setSodistoreGridInstallations] = useState<
I_Installation[]
>([]);
const [foldersAndInstallations, setFoldersAndInstallations] = useState([]); const [foldersAndInstallations, setFoldersAndInstallations] = useState([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState(false); const [error, setError] = useState(false);
@ -89,16 +92,31 @@ const InstallationsContextProvider = ({
: installation; : installation;
}); });
const updatedSodistoreGrid = sodistoreGridInstallations.map(
(installation) => {
const update = pendingUpdates.current[installation.id];
return update
? {
...installation,
status: update.status,
testingMode: update.testingMode
}
: installation;
}
);
setSalidomoInstallations(updatedSalidomo); setSalidomoInstallations(updatedSalidomo);
setSalimax_Or_Sodistore_Installations(updatedSalimax); setSalimax_Or_Sodistore_Installations(updatedSalimax);
setSodiohomeInstallations(updatedSodiohome); setSodiohomeInstallations(updatedSodiohome);
setSodistoreGridInstallations(updatedSodistoreGrid);
// Clear the pending updates after applying // Clear the pending updates after applying
pendingUpdates.current = {}; pendingUpdates.current = {};
}, [ }, [
salidomoInstallations, salidomoInstallations,
salimax_or_sodistore_Installations, salimax_or_sodistore_Installations,
sodiohomeInstallations sodiohomeInstallations,
sodistoreGridInstallations
]); ]);
useEffect(() => { useEffect(() => {
@ -172,6 +190,8 @@ const InstallationsContextProvider = ({
setSalidomoInstallations(res.data); setSalidomoInstallations(res.data);
} else if (product === 0 || product === 3) { } else if (product === 0 || product === 3) {
setSalimax_Or_Sodistore_Installations(res.data); setSalimax_Or_Sodistore_Installations(res.data);
} else if (product === 4) {
setSodistoreGridInstallations(res.data);
} }
if (open_socket) { if (open_socket) {
@ -390,6 +410,7 @@ const InstallationsContextProvider = ({
salimax_or_sodistore_Installations, salimax_or_sodistore_Installations,
salidomoInstallations, salidomoInstallations,
sodiohomeInstallations, sodiohomeInstallations,
sodistoreGridInstallations,
foldersAndInstallations, foldersAndInstallations,
fetchAllInstallations, fetchAllInstallations,
fetchAllFoldersAndInstallations, fetchAllFoldersAndInstallations,
@ -416,6 +437,7 @@ const InstallationsContextProvider = ({
salimax_or_sodistore_Installations, salimax_or_sodistore_Installations,
salidomoInstallations, salidomoInstallations,
sodiohomeInstallations, sodiohomeInstallations,
sodistoreGridInstallations,
foldersAndInstallations, foldersAndInstallations,
loading, loading,
error, error,

View File

@ -9,10 +9,12 @@ interface ProductIdContextType {
accessToSalidomo: boolean; accessToSalidomo: boolean;
accessToSodiohome: boolean; accessToSodiohome: boolean;
accessToSodistore: boolean; accessToSodistore: boolean;
accessToSodistoreGrid: boolean;
setAccessToSalimax: (access: boolean) => void; setAccessToSalimax: (access: boolean) => void;
setAccessToSalidomo: (access: boolean) => void; setAccessToSalidomo: (access: boolean) => void;
setAccessToSodiohome: (access: boolean) => void; setAccessToSodiohome: (access: boolean) => void;
setAccessToSodistore: (access: boolean) => void; setAccessToSodistore: (access: boolean) => void;
setAccessToSodistoreGrid: (access: boolean) => void;
} }
// Create the context. // Create the context.
@ -43,6 +45,10 @@ export const ProductIdContextProvider = ({
const storedValue = localStorage.getItem('accessToSodistore'); const storedValue = localStorage.getItem('accessToSodistore');
return storedValue === 'true'; return storedValue === 'true';
}); });
const [accessToSodistoreGrid, setAccessToSodistoreGrid] = useState(() => {
const storedValue = localStorage.getItem('accessToSodistoreGrid');
return storedValue === 'true';
});
// const [product, setProduct] = useState(location.includes('salidomo') ? 1 : 0); // const [product, setProduct] = useState(location.includes('salidomo') ? 1 : 0);
// const [product, setProduct] = useState<number>( // const [product, setProduct] = useState<number>(
// productMapping[Object.keys(productMapping).find((key) => location.includes(key)) || ''] || -1 // productMapping[Object.keys(productMapping).find((key) => location.includes(key)) || ''] || -1
@ -52,6 +58,8 @@ export const ProductIdContextProvider = ({
return 1; return 1;
} else if (location.includes('sodiohome')) { } else if (location.includes('sodiohome')) {
return 2; return 2;
} else if (location.includes('sodistoregrid')) {
return 4;
} else { } else {
return 0; return 0;
} }
@ -80,6 +88,10 @@ export const ProductIdContextProvider = ({
setAccessToSodistore(access); setAccessToSodistore(access);
localStorage.setItem('accessToSodistore', JSON.stringify(access)); localStorage.setItem('accessToSodistore', JSON.stringify(access));
}; };
const changeAccessSodistoreGrid = (access: boolean) => {
setAccessToSodistoreGrid(access);
localStorage.setItem('accessToSodistoreGrid', JSON.stringify(access));
};
return ( return (
<ProductIdContext.Provider <ProductIdContext.Provider
@ -90,10 +102,12 @@ export const ProductIdContextProvider = ({
accessToSalidomo, accessToSalidomo,
accessToSodiohome, accessToSodiohome,
accessToSodistore, accessToSodistore,
accessToSodistoreGrid,
setAccessToSalimax: changeAccessSalimax, setAccessToSalimax: changeAccessSalimax,
setAccessToSalidomo: changeAccessSalidomo, setAccessToSalidomo: changeAccessSalidomo,
setAccessToSodiohome: changeAccessSodiohome, setAccessToSodiohome: changeAccessSodiohome,
setAccessToSodistore: changeAccessSodistore setAccessToSodistore: changeAccessSodistore,
setAccessToSodistoreGrid: changeAccessSodistoreGrid
}} }}
> >
{children} {children}

View File

@ -89,8 +89,9 @@ export const transformInputToBatteryViewDataJson = async (
const categories = isSodioHome const categories = isSodioHome
? ['Soc', 'Power', 'Voltage', 'Current', 'Soh'] ? ['Soc', 'Power', 'Voltage', 'Current', 'Soh']
: ['Soc', 'Temperature', 'Power', 'Voltage', 'Current']; : ['Soc', 'Temperature', 'Power', 'Voltage', 'Current'];
// TODO: SodistoreGrid — update data paths when installation data format is finalized
const pathCategories = const pathCategories =
product === 3 product === 3 || product === 4
? [ ? [
'.BatteryDeligreenDataRecord.Soc', '.BatteryDeligreenDataRecord.Soc',
'.BatteryDeligreenDataRecord.TemperaturesList.EnvironmentTemperature', '.BatteryDeligreenDataRecord.TemperaturesList.EnvironmentTemperature',
@ -167,7 +168,7 @@ export const transformInputToBatteryViewDataJson = async (
); );
const adjustedTimestamp = const adjustedTimestamp =
product == 0 || product == 2 || product == 3 product == 0 || product == 2 || product == 3 || product == 4
? new Date(timestampArray[i] * 1000) ? new Date(timestampArray[i] * 1000)
: new Date(timestampArray[i] * 100000); : new Date(timestampArray[i] * 100000);
//Timezone offset is negative, so we convert the timestamp to the current zone by subtracting the corresponding offset //Timezone offset is negative, so we convert the timestamp to the current zone by subtracting the corresponding offset
@ -270,7 +271,7 @@ export const transformInputToBatteryViewDataJson = async (
if (battery_nodes.length > old_length) { if (battery_nodes.length > old_length) {
battery_nodes.forEach((node) => { battery_nodes.forEach((node) => {
const node_number = const node_number =
product == 3 ? Number(node) + 1 : Number(node) - 1; product == 3 || product == 4 ? Number(node) + 1 : Number(node) - 1;
if (!pathsToSave.includes('Node' + node_number)) { if (!pathsToSave.includes('Node' + node_number)) {
pathsToSave.push('Node' + node_number); pathsToSave.push('Node' + node_number);
} }

View File

@ -168,7 +168,8 @@ function SidebarMenu() {
accessToSalimax, accessToSalimax,
accessToSodistore, accessToSodistore,
accessToSalidomo, accessToSalidomo,
accessToSodiohome accessToSodiohome,
accessToSodistoreGrid
} = useContext(ProductIdContext); } = useContext(ProductIdContext);
return ( return (
@ -242,6 +243,27 @@ function SidebarMenu() {
</List> </List>
)} )}
{accessToSodistoreGrid && (
<List component="div">
<ListItem component="div">
<Button
disableRipple
component={RouterLink}
onClick={closeSidebar}
to="/sodistoregrid_installations"
startIcon={<BrightnessLowTwoToneIcon />}
>
<Box sx={{ marginTop: '3px' }}>
<FormattedMessage
id="sodistoregrid"
defaultMessage="Sodistore Grid"
/>
</Box>
</Button>
</ListItem>
</List>
)}
{accessToSodiohome && ( {accessToSodiohome && (
<List component="div"> <List component="div">
<ListItem component="div"> <ListItem component="div">