Enabled set date button in front end

Use s3cmd in backend to search the corresponding timestamps
This commit is contained in:
Noe 2024-08-02 16:15:12 +02:00
parent b499d01473
commit cce3a6f9bd
22 changed files with 254 additions and 203 deletions

View File

@ -1,4 +1,6 @@
using System.Diagnostics;
using System.Net; using System.Net;
using System.Text.RegularExpressions;
using InnovEnergy.App.Backend.Database; using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.DataTypes; using InnovEnergy.App.Backend.DataTypes;
using InnovEnergy.App.Backend.DataTypes.Methods; using InnovEnergy.App.Backend.DataTypes.Methods;
@ -145,8 +147,10 @@ public class Controller : ControllerBase
.ToList(); .ToList();
} }
[HttpGet(nameof(GetCsvTimestampsForInstallation))] [HttpGet(nameof(GetCsvTimestampsForInstallation))]
public ActionResult<IEnumerable<CsvTimestamp>> GetCsvTimestampsForInstallation(Int64 id, Int32 start, Int32 end, Token authToken) public ActionResult<IEnumerable<Int64>> GetCsvTimestampsForInstallation(Int64 id, Int32 start, Int32 end, Token authToken)
{ {
var user = Db.GetSession(authToken)?.User; var user = Db.GetSession(authToken)?.User;
if (user == null) if (user == null)
@ -158,21 +162,84 @@ public class Controller : ControllerBase
return Unauthorized(); return Unauthorized();
var sampleSize = 100; var sampleSize = 100;
var allTimestamps = new List<CsvTimestamp>(); var allTimestamps = new List<Int64>();
if (start != 0 && end != 0) static string FindCommonPrefix(string str1, string str2)
{ {
allTimestamps = Db.CsvTimestamps int minLength = Math.Min(str1.Length, str2.Length);
.Where(csvTimestamp => csvTimestamp.InstallationId == id && csvTimestamp.Timestamp > start && csvTimestamp.Timestamp < end) int i = 0;
.OrderBy(csvTimestamp => csvTimestamp.Timestamp).ToList(); while (i < minLength && str1[i] == str2[i])
{
i++;
}
return str1.Substring(0, i);
}
string commonPrefix = FindCommonPrefix(start.ToString(), end.ToString());
string bucketPath = "s3://"+installation.S3BucketId + "-3e5b3069-214a-43ee-8d85-57d72000c19d/"+commonPrefix;
string configPath = "/home/ubuntu/.s3cfg";
try
{
// Set up process start info
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = "s3cmd",
Arguments = $"--config {configPath} ls {bucketPath}",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
// Start the process
Process process = new Process
{
StartInfo = startInfo
};
process.Start();
// Read the output
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
process.WaitForExit();
// Check for errors
if (process.ExitCode != 0)
{
Console.WriteLine("Error executing command:");
Console.WriteLine(error);
} }
else else
{ {
allTimestamps = Db.CsvTimestamps Console.WriteLine("Command output:");
.Where(csvTimestamp => csvTimestamp.InstallationId == id) //Console.WriteLine(output);
.OrderBy(csvTimestamp => csvTimestamp.Timestamp).ToList(); // Define a regex pattern to match the filenames without .csv extension
var pattern = @"/([^/]+)\.csv$";
var regex = new Regex(pattern);
// Process each line of the output
foreach (var line in output.Split('\n'))
{
var match = regex.Match(line);
if (match.Success && long.Parse(match.Groups[1].Value)>=start && long.Parse(match.Groups[1].Value)<=end)
{
allTimestamps.Add(long.Parse(match.Groups[1].Value));
//Console.WriteLine(match.Groups[1].Value);
}
}
} }
}
catch (Exception e)
{
Console.WriteLine($"Exception: {e.Message}");
}
int totalRecords = allTimestamps.Count; int totalRecords = allTimestamps.Count;
if (totalRecords <= sampleSize) if (totalRecords <= sampleSize)
@ -186,7 +253,7 @@ public class Controller : ControllerBase
int interval = totalRecords / sampleSize; int interval = totalRecords / sampleSize;
var sampledTimestamps = new List<CsvTimestamp>(); var sampledTimestamps = new List<Int64>();
for (int i = 0; i < totalRecords; i += interval) for (int i = 0; i < totalRecords; i += interval)
{ {
@ -204,11 +271,6 @@ public class Controller : ControllerBase
Console.WriteLine("SampledTimestamps = " + sampledTimestamps.Count); Console.WriteLine("SampledTimestamps = " + sampledTimestamps.Count);
return sampledTimestamps; return sampledTimestamps;
// return Db.CsvTimestamps
// .Where(csvTimestamp => csvTimestamp.InstallationId == id)
// .OrderByDescending(csvTimestamp => csvTimestamp.Timestamp)
// .ToList();
} }
[HttpGet(nameof(GetUserById))] [HttpGet(nameof(GetUserById))]

View File

@ -1,11 +0,0 @@
using SQLite;
namespace InnovEnergy.App.Backend.DataTypes;
public class CsvTimestamp{
[PrimaryKey, AutoIncrement]
public Int64 Id { get; set; }
public Int64 InstallationId { get; set; }
public Int32 Timestamp { get; set; }
}

View File

@ -105,7 +105,7 @@ public static class InstallationMethods
public static Installation HideWriteKeyIfUserIsNotAdmin(this Installation installation, int userIsAdmin) public static Installation HideWriteKeyIfUserIsNotAdmin(this Installation installation, int userIsAdmin)
{ {
if(userIsAdmin!=2) if(userIsAdmin==2)
return installation; return installation;
installation.S3WriteKey = ""; installation.S3WriteKey = "";

View File

@ -42,11 +42,6 @@ public static partial class Db
return Insert(warning); return Insert(warning);
} }
public static Boolean Create(CsvTimestamp csvTimestamp)
{
return Insert(csvTimestamp);
}
public static Boolean Create(Folder folder) public static Boolean Create(Folder folder)
{ {
return Insert(folder); return Insert(folder);
@ -176,34 +171,4 @@ public static partial class Db
} }
} }
public static void AddCsvTimestamp(CsvTimestamp newCsvTimestamp,int installationId)
{
var maxCSvPerInstallation = 60 * 24;
//Find the total number of warnings for this installation
var totalCsvNames = CsvTimestamps.Count(csvTimestamp => csvTimestamp.InstallationId == installationId);
//If there are 100 warnings, remove the one with the oldest timestamp
if (totalCsvNames == maxCSvPerInstallation)
{
var oldestCSvTimestamp =
CsvTimestamps.Where(csvTimestamp => csvTimestamp.InstallationId == installationId)
.OrderBy(csvTimestamp => csvTimestamp.Timestamp)
.FirstOrDefault();
//Remove the old error
Delete(oldestCSvTimestamp);
//Add the new error
Create(newCsvTimestamp);
}
else
{
//Console.WriteLine("---------------Added the new Csv Timestamp to the database-----------------");
Create(newCsvTimestamp);
}
}
} }

View File

@ -37,7 +37,6 @@ public static partial class Db
fileConnection.CreateTable<Error>(); fileConnection.CreateTable<Error>();
fileConnection.CreateTable<Warning>(); fileConnection.CreateTable<Warning>();
fileConnection.CreateTable<UserAction>(); fileConnection.CreateTable<UserAction>();
fileConnection.CreateTable<CsvTimestamp>();
return fileConnection; return fileConnection;
//return CopyDbToMemory(fileConnection); //return CopyDbToMemory(fileConnection);
@ -58,7 +57,6 @@ public static partial class Db
memoryConnection.CreateTable<Error>(); memoryConnection.CreateTable<Error>();
memoryConnection.CreateTable<Warning>(); memoryConnection.CreateTable<Warning>();
fileConnection.CreateTable<UserAction>(); fileConnection.CreateTable<UserAction>();
fileConnection.CreateTable<CsvTimestamp>();
//Copy all the existing tables from the disk to main memory //Copy all the existing tables from the disk to main memory
fileConnection.Table<Session>().ForEach(memoryConnection.Insert); fileConnection.Table<Session>().ForEach(memoryConnection.Insert);
@ -71,7 +69,6 @@ public static partial class Db
fileConnection.Table<Error>().ForEach(memoryConnection.Insert); fileConnection.Table<Error>().ForEach(memoryConnection.Insert);
fileConnection.Table<Warning>().ForEach(memoryConnection.Insert); fileConnection.Table<Warning>().ForEach(memoryConnection.Insert);
fileConnection.Table<UserAction>().ForEach(memoryConnection.Insert); fileConnection.Table<UserAction>().ForEach(memoryConnection.Insert);
fileConnection.Table<CsvTimestamp>().ForEach(memoryConnection.Insert);
return memoryConnection; return memoryConnection;
} }
@ -92,7 +89,6 @@ public static partial class Db
public static TableQuery<Error> Errors => Connection.Table<Error>(); public static TableQuery<Error> Errors => Connection.Table<Error>();
public static TableQuery<Warning> Warnings => Connection.Table<Warning>(); public static TableQuery<Warning> Warnings => Connection.Table<Warning>();
public static TableQuery<UserAction> UserActions => Connection.Table<UserAction>(); public static TableQuery<UserAction> UserActions => Connection.Table<UserAction>();
public static TableQuery<CsvTimestamp> CsvTimestamps => Connection.Table<CsvTimestamp>();
public static void Init() public static void Init()
{ {
@ -115,7 +111,6 @@ public static partial class Db
Connection.CreateTable<Error>(); Connection.CreateTable<Error>();
Connection.CreateTable<Warning>(); Connection.CreateTable<Warning>();
Connection.CreateTable<UserAction>(); Connection.CreateTable<UserAction>();
Connection.CreateTable<CsvTimestamp>();
}); });
//UpdateKeys(); //UpdateKeys();

View File

@ -90,20 +90,6 @@ public static partial class Db
} }
} }
public static Boolean Delete(CsvTimestamp csvTimestampToDelete)
{
var deleteSuccess = RunTransaction(DeleteCsvTimestampToDelete);
if (deleteSuccess)
Backup();
return deleteSuccess;
Boolean DeleteCsvTimestampToDelete()
{
return CsvTimestamps.Delete(csvTimestamp => csvTimestamp.Id == csvTimestampToDelete.Id) >0;
}
}
public static Boolean Delete(Installation installation) public static Boolean Delete(Installation installation)
{ {
var deleteSuccess = RunTransaction(DeleteInstallationAndItsDependencies); var deleteSuccess = RunTransaction(DeleteInstallationAndItsDependencies);

View File

@ -1,3 +1,4 @@
using System.Diagnostics;
using Flurl.Http; using Flurl.Http;
using Hellang.Middleware.ProblemDetails; using Hellang.Middleware.ProblemDetails;
using InnovEnergy.App.Backend.Database; using InnovEnergy.App.Backend.Database;
@ -22,6 +23,7 @@ public static class Program
RabbitMqManager.StartRabbitMqConsumer(); RabbitMqManager.StartRabbitMqConsumer();
WebsocketManager.MonitorInstallationTable(); WebsocketManager.MonitorInstallationTable();
builder.Services.AddControllers(); builder.Services.AddControllers();
builder.Services.AddProblemDetails(setup => builder.Services.AddProblemDetails(setup =>
{ {

View File

@ -52,8 +52,6 @@ public static class RabbitMqManager
//A message can be an alarm, a warning or a heartbit //A message can be an alarm, a warning or a heartbit
StatusMessage? receivedStatusMessage = JsonSerializer.Deserialize<StatusMessage>(message); StatusMessage? receivedStatusMessage = JsonSerializer.Deserialize<StatusMessage>(message);
lock (WebsocketManager.InstallationConnections) lock (WebsocketManager.InstallationConnections)
{ {
//Consumer received a message //Consumer received a message
@ -69,17 +67,6 @@ public static class RabbitMqManager
if (receivedStatusMessage.Type == MessageType.Heartbit) if (receivedStatusMessage.Type == MessageType.Heartbit)
{ {
//Console.WriteLine("This is a heartbit message from installation: " + installationId + " Name of the file is "+ receivedStatusMessage.Timestamp); //Console.WriteLine("This is a heartbit message from installation: " + installationId + " Name of the file is "+ receivedStatusMessage.Timestamp);
if (receivedStatusMessage.Timestamp != 0)
{
CsvTimestamp newCsvTimestamp = new CsvTimestamp
{
InstallationId = installationId,
Timestamp = receivedStatusMessage.Timestamp
};
Db.AddCsvTimestamp(newCsvTimestamp, installationId);
}
} }
else else
{ {
@ -133,7 +120,7 @@ public static class RabbitMqManager
Seen = false Seen = false
}; };
Console.WriteLine("Add an alarm for installation "+installationId); //Console.WriteLine("Add an alarm for installation "+installationId);
// Send replace battery email to support team if this alarm is "NeedToReplaceBattery" // Send replace battery email to support team if this alarm is "NeedToReplaceBattery"
if (alarm.Description == "2 or more string are disabled") if (alarm.Description == "2 or more string are disabled")
@ -187,9 +174,7 @@ public static class RabbitMqManager
{ {
WebsocketManager.InformWebsocketsForInstallation(installationId); WebsocketManager.InformWebsocketsForInstallation(installationId);
} }
} }
} }
}; };
Channel.BasicConsume(queue: "statusQueue", autoAck: true, consumer: consumer); Channel.BasicConsume(queue: "statusQueue", autoAck: true, consumer: consumer);

View File

@ -19,7 +19,6 @@ public static class WebsocketManager
lock (InstallationConnections){ lock (InstallationConnections){
foreach (var installationConnection in InstallationConnections){ foreach (var installationConnection in InstallationConnections){
if ((DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(1)){ if ((DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(1)){
Console.WriteLine("Installation "+installationConnection.Key+" is offline, latest timestamp was "+installationConnection.Value.Timestamp);
installationConnection.Value.Status = -1; installationConnection.Value.Status = -1;
if (installationConnection.Value.Connections.Count > 0){InformWebsocketsForInstallation(installationConnection.Key);} if (installationConnection.Value.Connections.Count > 0){InformWebsocketsForInstallation(installationConnection.Key);}
} }
@ -94,7 +93,7 @@ public static class WebsocketManager
continue; continue;
} }
Console.WriteLine("Received a new message from websocket"); //Console.WriteLine("Received a new message from websocket");
lock (InstallationConnections) lock (InstallationConnections)
{ {
//Each front-end will send the list of the installations it wants to access //Each front-end will send the list of the installations it wants to access
@ -102,6 +101,7 @@ public static class WebsocketManager
//Then, report the status of each requested installation to the front-end that created the websocket connection //Then, report the status of each requested installation to the front-end that created the websocket connection
foreach (var installationId in installationIds) foreach (var installationId in installationIds)
{ {
//Console.WriteLine("New id is "+installationId);
var installation = Db.GetInstallationById(installationId); var installation = Db.GetInstallationById(installationId);
if (!InstallationConnections.ContainsKey(installationId)) if (!InstallationConnections.ContainsKey(installationId))
{ {

View File

@ -9,5 +9,5 @@ Salimax0005 ie-entwicklung@10.2.4.36 Schreinerei Schönthal (Thu
Salimax0006 ie-entwicklung@10.2.4.35 Steakhouse Mettmenstetten Salimax0006 ie-entwicklung@10.2.4.35 Steakhouse Mettmenstetten
Salimax0007 ie-entwicklung@10.2.4.154 LerchenhofHerr Twannberg Salimax0007 ie-entwicklung@10.2.4.154 LerchenhofHerr Twannberg
Salimax0008 ie-entwicklung@10.2.4.113 Wittmann Kottingbrunn Salimax0008 ie-entwicklung@10.2.4.113 Wittmann Kottingbrunn
Salimax0010 ie-entwicklung@10.2.4.211 Mohatech 1 Salimax0010 ie-entwicklung@10.2.4.211 Mohatech 1 (Beat Moser)
SalidomoServer ig@134.209.238.170 SalidomoServer ig@134.209.238.170

View File

@ -54,6 +54,6 @@ INNOVENERGY_PROTOCOL_VERSION = '48TL200V3'
# S3 Credentials # S3 Credentials
S3BUCKET = "10-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e" S3BUCKET = "35-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e"
S3KEY = "EXOa8cc58d2e51e389fed9ccbfa" S3KEY = "EXO2a70f415b6b387b5628020bd"
S3SECRET = "hofDGMmSSN1OACYXHWRUGdG61mFjBxKC18sF0VpMQgY" S3SECRET = "qdlYoXDZyME8LEB8BFTuCmJoTzkP2Nebr2jVmlXUzgY"

View File

@ -448,6 +448,8 @@ function Configuration(props: ConfigurationProps) {
fullWidth fullWidth
/> />
</div> </div>
{props.values.maximumDischargePower && (
<div style={{ marginBottom: '5px' }}> <div style={{ marginBottom: '5px' }}>
<TextField <TextField
label={ label={
@ -464,6 +466,7 @@ function Configuration(props: ConfigurationProps) {
fullWidth fullWidth
/> />
</div> </div>
)}
<div> <div>
<TextField <TextField
label={ label={

View File

@ -387,6 +387,26 @@ function HistoryOfActions(props: HistoryProps) {
/> />
</Typography> </Typography>
</div> </div>
<div
style={{
flex: 1,
marginTop: '15px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<Typography
variant="body1"
color="dimgrey"
fontWeight="bold"
fontSize="1rem"
gutterBottom
noWrap
>
<FormattedMessage id="edit" defaultMessage="Edit" />
</Typography>
</div>
</div> </div>
<Divider /> <Divider />
<div style={{ maxHeight: '600px', overflowY: 'auto' }}> <div style={{ maxHeight: '600px', overflowY: 'auto' }}>

View File

@ -282,30 +282,10 @@ function Information(props: InformationProps) {
/> />
</div> </div>
{/*<div>*/}
{/* <TextField*/}
{/* label="S3 Write Key"*/}
{/* name="s3writekey"*/}
{/* value={formValues.s3WriteKey}*/}
{/* variant="outlined"*/}
{/* fullWidth*/}
{/* />*/}
{/*</div>*/}
{/*<div>*/}
{/* <TextField*/}
{/* label="S3 Write Secret Key"*/}
{/* name="s3writesecretkey"*/}
{/* value={formValues.s3WriteSecret}*/}
{/* variant="outlined"*/}
{/* fullWidth*/}
{/* />*/}
{/*</div>*/}
<div> <div>
<TextField <TextField
label="S3 Bucket Name" label="S3 Bucket Name"
name="s3writesecretkey" name="s3bucketname"
value={ value={
formValues.s3BucketId + formValues.s3BucketId +
'-3e5b3069-214a-43ee-8d85-57d72000c19d' '-3e5b3069-214a-43ee-8d85-57d72000c19d'
@ -314,6 +294,25 @@ function Information(props: InformationProps) {
fullWidth fullWidth
/> />
</div> </div>
<div>
<TextField
label="S3 Write Key"
name="s3writekey"
value={formValues.s3WriteKey}
variant="outlined"
fullWidth
/>
</div>
<div>
<TextField
label="S3 Write Secret Key"
name="s3writesecretkey"
value={formValues.s3WriteSecret}
variant="outlined"
fullWidth
/>
</div>
</> </>
)} )}

View File

@ -117,11 +117,11 @@ function Installation(props: singleInstallationProps) {
}; };
const fetchDataPeriodically = async () => { const fetchDataPeriodically = async () => {
var timeperiodToSearch = 70; var timeperiodToSearch = 90;
let res; let res;
let timestampToFetch; let timestampToFetch;
for (var i = timeperiodToSearch; i > 0; i -= 2) { for (var i = 0; i < timeperiodToSearch; i += 2) {
if (!continueFetching.current) { if (!continueFetching.current) {
return false; return false;
} }
@ -379,6 +379,7 @@ function Installation(props: singleInstallationProps) {
{loading && {loading &&
currentTab != 'information' && currentTab != 'information' &&
currentTab != 'history' && currentTab != 'history' &&
currentTab != 'overview' &&
currentTab != 'log' && ( currentTab != 'log' && (
<Container <Container
maxWidth="xl" maxWidth="xl"

View File

@ -85,7 +85,14 @@ function Overview(props: OverviewProps) {
const resultPromise: Promise<{ const resultPromise: Promise<{
chartData: chartDataInterface; chartData: chartDataInterface;
chartOverview: overviewInterface; chartOverview: overviewInterface;
}> = transformInputToDailyData(props.s3Credentials, props.id); }> = transformInputToDailyData(
props.s3Credentials,
props.id,
UnixTime.fromTicks(new Date().getTime() / 1000).earlier(
TimeSpan.fromDays(1)
),
UnixTime.fromTicks(new Date().getTime() / 1000)
);
resultPromise resultPromise
.then((result) => { .then((result) => {
@ -169,6 +176,7 @@ function Overview(props: OverviewProps) {
const resultPromise: Promise<{ const resultPromise: Promise<{
chartAggregatedData: chartAggregatedDataInterface; chartAggregatedData: chartAggregatedDataInterface;
chartOverview: overviewInterface; chartOverview: overviewInterface;
dateList: string[];
}> = transformInputToAggregatedData( }> = transformInputToAggregatedData(
props.s3Credentials, props.s3Credentials,
dayjs().subtract(1, 'week'), dayjs().subtract(1, 'week'),
@ -193,7 +201,7 @@ function Overview(props: OverviewProps) {
prevData.concat({ prevData.concat({
chartData: result.chartAggregatedData, chartData: result.chartAggregatedData,
chartOverview: result.chartOverview, chartOverview: result.chartOverview,
datelist: computeLast7Days(), datelist: result.dateList,
netbalance: powerDifference netbalance: powerDifference
}) })
); );
@ -482,7 +490,7 @@ function Overview(props: OverviewProps) {
<FormattedMessage id="lastweek" defaultMessage="Last week" /> <FormattedMessage id="lastweek" defaultMessage="Last week" />
</Button> </Button>
{aggregatedData && ( {/*{aggregatedData && (*/}
<Button <Button
variant="contained" variant="contained"
onClick={handleSetDate} onClick={handleSetDate}
@ -497,7 +505,7 @@ function Overview(props: OverviewProps) {
> >
<FormattedMessage id="set_date" defaultMessage="Set Date" /> <FormattedMessage id="set_date" defaultMessage="Set Date" />
</Button> </Button>
)} {/*)}*/}
</Grid> </Grid>
<Grid <Grid

View File

@ -78,6 +78,7 @@ function SalidomoOverview(props: salidomoOverviewProps) {
const resultPromise: Promise<{ const resultPromise: Promise<{
chartAggregatedData: chartAggregatedDataInterface; chartAggregatedData: chartAggregatedDataInterface;
chartOverview: overviewInterface; chartOverview: overviewInterface;
dateList: string[];
}> = transformInputToAggregatedData( }> = transformInputToAggregatedData(
props.s3Credentials, props.s3Credentials,
dayjs().subtract(1, 'week'), dayjs().subtract(1, 'week'),
@ -102,7 +103,7 @@ function SalidomoOverview(props: salidomoOverviewProps) {
prevData.concat({ prevData.concat({
chartData: result.chartAggregatedData, chartData: result.chartAggregatedData,
chartOverview: result.chartOverview, chartOverview: result.chartOverview,
datelist: computeLast7Days(), datelist: result.dateList,
netbalance: powerDifference netbalance: powerDifference
}) })
); );

View File

@ -18,6 +18,7 @@ import { FormattedMessage } from 'react-intl';
import { useLocation, useNavigate } from 'react-router-dom'; import { useLocation, useNavigate } from 'react-router-dom';
import routes from '../../../Resources/routes.json'; import routes from '../../../Resources/routes.json';
import CancelIcon from '@mui/icons-material/Cancel'; import CancelIcon from '@mui/icons-material/Cancel';
import BuildIcon from '@mui/icons-material/Build';
interface FlatInstallationViewProps { interface FlatInstallationViewProps {
installations: I_Installation[]; installations: I_Installation[];
@ -26,7 +27,7 @@ interface FlatInstallationViewProps {
const FlatInstallationView = (props: FlatInstallationViewProps) => { const FlatInstallationView = (props: FlatInstallationViewProps) => {
const [isRowHovered, setHoveredRow] = useState(-1); const [isRowHovered, setHoveredRow] = useState(-1);
const webSocketContext = useContext(WebSocketContext); const webSocketContext = useContext(WebSocketContext);
const { getStatus } = webSocketContext; const { getStatus, getTestingMode } = webSocketContext;
const navigate = useNavigate(); const navigate = useNavigate();
const [selectedInstallation, setSelectedInstallation] = useState<number>(-1); const [selectedInstallation, setSelectedInstallation] = useState<number>(-1);
const currentLocation = useLocation(); const currentLocation = useLocation();
@ -320,6 +321,19 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
: 'green' : 'green'
}} }}
/> />
{getTestingMode(installation.id) && (
<BuildIcon
style={{
width: '23px',
height: '23px',
color: 'purple',
borderRadius: '50%',
position: 'relative',
zIndex: 1,
marginLeft: '15px'
}}
/>
)}
</div> </div>
</TableCell> </TableCell>
</TableRow> </TableRow>

View File

@ -24,6 +24,9 @@ import BatteryView from '../BatteryView/BatteryView';
import Log from '../Log/Log'; import Log from '../Log/Log';
import CancelIcon from '@mui/icons-material/Cancel'; import CancelIcon from '@mui/icons-material/Cancel';
import SalidomoOverview from '../Overview/salidomoOverview'; import SalidomoOverview from '../Overview/salidomoOverview';
import { UserType } from '../../../interfaces/UserTypes';
import HistoryOfActions from '../History/History';
import BuildIcon from '@mui/icons-material/Build';
interface singleInstallationProps { interface singleInstallationProps {
current_installation?: I_Installation; current_installation?: I_Installation;
@ -36,7 +39,7 @@ function Installation(props: singleInstallationProps) {
const location = useLocation().pathname; const location = useLocation().pathname;
const [errorLoadingS3Data, setErrorLoadingS3Data] = useState(false); const [errorLoadingS3Data, setErrorLoadingS3Data] = useState(false);
const webSocketsContext = useContext(WebSocketContext); const webSocketsContext = useContext(WebSocketContext);
const { getStatus } = webSocketsContext; const { getStatus, getTestingMode } = webSocketsContext;
const [currentTab, setCurrentTab] = useState<string>(undefined); const [currentTab, setCurrentTab] = useState<string>(undefined);
const [values, setValues] = useState<TopologyValues | null>(null); const [values, setValues] = useState<TopologyValues | null>(null);
const status = getStatus(props.current_installation.id); const status = getStatus(props.current_installation.id);
@ -74,11 +77,11 @@ function Installation(props: singleInstallationProps) {
const continueFetching = useRef(false); const continueFetching = useRef(false);
const fetchDataPeriodically = async () => { const fetchDataPeriodically = async () => {
var timeperiodToSearch = 70; var timeperiodToSearch = 90;
let res; let res;
let timestampToFetch; let timestampToFetch;
for (var i = timeperiodToSearch; i > 0; i -= 2) { for (var i = 0; i < timeperiodToSearch; i += 2) {
if (!continueFetching.current) { if (!continueFetching.current) {
return false; return false;
} }
@ -274,11 +277,26 @@ function Installation(props: singleInstallationProps) {
: 'green' : 'green'
}} }}
/> />
{getTestingMode(props.current_installation.id) && (
<BuildIcon
style={{
width: '23px',
height: '23px',
color: 'purple',
borderRadius: '50%',
position: 'relative',
zIndex: 1,
marginLeft: '15px'
}}
/>
)}
</div> </div>
</div> </div>
{loading && {loading &&
currentTab != 'information' && currentTab != 'information' &&
currentTab != 'overview' && currentTab != 'overview' &&
currentTab != 'history' &&
currentTab != 'log' && ( currentTab != 'log' && (
<Container <Container
maxWidth="xl" maxWidth="xl"
@ -354,6 +372,18 @@ function Installation(props: singleInstallationProps) {
} }
></Route> ></Route>
{currentUser.userType == UserType.admin && (
<Route
path={routes.history}
element={
<HistoryOfActions
errorLoadingS3Data={errorLoadingS3Data}
id={props.current_installation.id}
></HistoryOfActions>
}
/>
)}
<Route <Route
path={'*'} path={'*'}
element={<Navigate to={routes.information}></Navigate>} element={<Navigate to={routes.information}></Navigate>}

View File

@ -14,7 +14,7 @@ function SalidomoInstallationTabs() {
const location = useLocation(); const location = useLocation();
const context = useContext(UserContext); const context = useContext(UserContext);
const { currentUser } = context; const { currentUser } = context;
const tabList = ['batteryview', 'information', 'overview', 'log']; const tabList = ['batteryview', 'information', 'overview', 'log', 'history'];
const [currentTab, setCurrentTab] = useState<string>(undefined); const [currentTab, setCurrentTab] = useState<string>(undefined);
const [fetchedInstallations, setFetchedInstallations] = const [fetchedInstallations, setFetchedInstallations] =
@ -106,6 +106,15 @@ function SalidomoInstallationTabs() {
label: ( label: (
<FormattedMessage id="information" defaultMessage="Information" /> <FormattedMessage id="information" defaultMessage="Information" />
) )
},
{
value: 'history',
label: (
<FormattedMessage
id="history"
defaultMessage="History Of Actions"
/>
)
} }
] ]
: [ : [

View File

@ -380,16 +380,15 @@ export const transformInputToDailyData = async (
}; };
}); });
let timestampArray: CsvTimestamp[] = []; let timestampArray: number[] = [];
let adjustedTimestampArray = []; let adjustedTimestampArray = [];
const timestampPromises = []; const timestampPromises = [];
if (start_time && end_time) {
await axiosConfig await axiosConfig
.get( .get(
`/GetCsvTimestampsForInstallation?id=${id}&start=${start_time.ticks}&end=${end_time.ticks}` `/GetCsvTimestampsForInstallation?id=${id}&start=${start_time.ticks}&end=${end_time.ticks}`
) )
.then((res: AxiosResponse<CsvTimestamp[]>) => { .then((res: AxiosResponse<number[]>) => {
timestampArray = res.data; timestampArray = res.data;
}) })
.catch((err: AxiosError) => { .catch((err: AxiosError) => {
@ -398,29 +397,13 @@ export const transformInputToDailyData = async (
//navigate(routes.login); //navigate(routes.login);
} }
}); });
} else {
await axiosConfig
.get(`/GetCsvTimestampsForInstallation?id=${id}&start=${0}&end=${0}`)
.then((res: AxiosResponse<CsvTimestamp[]>) => {
timestampArray = res.data;
})
.catch((err: AxiosError) => {
if (err.response && err.response.status == 401) {
//removeToken();
//navigate(routes.login);
}
});
}
for (var i = 0; i < timestampArray.length; i++) { for (var i = 0; i < timestampArray.length; i++) {
timestampPromises.push( timestampPromises.push(
fetchDataForOneTime( fetchDataForOneTime(UnixTime.fromTicks(timestampArray[i]), s3Credentials)
UnixTime.fromTicks(timestampArray[i].timestamp),
s3Credentials
)
); );
const adjustedTimestamp = new Date(timestampArray[i].timestamp * 1000); const adjustedTimestamp = new Date(timestampArray[i] * 1000);
//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
adjustedTimestamp.setHours( adjustedTimestamp.setHours(
adjustedTimestamp.getHours() - adjustedTimestamp.getTimezoneOffset() / 60 adjustedTimestamp.getHours() - adjustedTimestamp.getTimezoneOffset() / 60
@ -619,7 +602,6 @@ export const transformInputToAggregatedData = async (
if (result[path].value < overviewData[path].min) { if (result[path].value < overviewData[path].min) {
overviewData[path].min = result[path].value; overviewData[path].min = result[path].value;
} }
if (result[path].value > overviewData[path].max) { if (result[path].value > overviewData[path].max) {
overviewData[path].max = result[path].value; overviewData[path].max = result[path].value;
} }