Merge branch 'main' of 91.92.155.224:Innovenergy/Innovenergy_trunk
This commit is contained in:
commit
9526ed0739
|
@ -109,7 +109,7 @@ public static partial class Db
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine("---------------Added the new Error to the database-----------------");
|
Console.WriteLine("---------------Added the new Warning to the database-----------------");
|
||||||
Create(newWarning);
|
Create(newWarning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,4 +8,4 @@ public class AlarmOrWarning
|
||||||
public required String Time { get; set; }
|
public required String Time { get; set; }
|
||||||
public required String Description { get; set; }
|
public required String Description { get; set; }
|
||||||
public required String CreatedBy { get; set; }
|
public required String CreatedBy { get; set; }
|
||||||
}
|
}
|
|
@ -50,6 +50,8 @@ public static class RabbitMqManager
|
||||||
var message = Encoding.UTF8.GetString(body);
|
var message = Encoding.UTF8.GetString(body);
|
||||||
//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)
|
||||||
{
|
{
|
||||||
|
@ -57,9 +59,9 @@ public static class RabbitMqManager
|
||||||
if (receivedStatusMessage != null)
|
if (receivedStatusMessage != null)
|
||||||
{
|
{
|
||||||
Console.WriteLine("----------------------------------------------");
|
Console.WriteLine("----------------------------------------------");
|
||||||
Console.WriteLine("Received a message from installation: " + receivedStatusMessage.InstallationId + " and status is: " + receivedStatusMessage.Status);
|
|
||||||
int installationId = (int)Db.Installations.Where(f => f.Product == 0 && f.S3BucketId == receivedStatusMessage.InstallationId).Select(f => f.Id).FirstOrDefault();
|
|
||||||
|
|
||||||
|
int installationId = (int)Db.Installations.Where(f => f.Product == receivedStatusMessage.Product && f.S3BucketId == receivedStatusMessage.InstallationId).Select(f => f.Id).FirstOrDefault();
|
||||||
|
Console.WriteLine("Received a message from installation: " + installationId + " , product is: "+receivedStatusMessage.Product+ " and status is: " + receivedStatusMessage.Status);
|
||||||
|
|
||||||
//This is a heartbit message, just update the timestamp for this installation.
|
//This is a heartbit message, just update the timestamp for this installation.
|
||||||
//There is no need to notify the corresponding front-ends.
|
//There is no need to notify the corresponding front-ends.
|
||||||
|
@ -73,11 +75,12 @@ public static class RabbitMqManager
|
||||||
//Traverse the Warnings list, and store each of them to the database
|
//Traverse the Warnings list, and store each of them to the database
|
||||||
if (receivedStatusMessage.Warnings != null)
|
if (receivedStatusMessage.Warnings != null)
|
||||||
{
|
{
|
||||||
|
|
||||||
foreach (var warning in receivedStatusMessage.Warnings)
|
foreach (var warning in receivedStatusMessage.Warnings)
|
||||||
{
|
{
|
||||||
Warning newWarning = new Warning
|
Warning newWarning = new Warning
|
||||||
{
|
{
|
||||||
InstallationId = receivedStatusMessage.InstallationId,
|
InstallationId = installationId,
|
||||||
Description = warning.Description,
|
Description = warning.Description,
|
||||||
Date = warning.Date,
|
Date = warning.Date,
|
||||||
Time = warning.Time,
|
Time = warning.Time,
|
||||||
|
@ -85,7 +88,8 @@ public static class RabbitMqManager
|
||||||
Seen = false
|
Seen = false
|
||||||
};
|
};
|
||||||
//Create a new warning and add it to the database
|
//Create a new warning and add it to the database
|
||||||
Db.HandleWarning(newWarning, receivedStatusMessage.InstallationId);
|
Console.WriteLine("Add a warning for installation "+installationId);
|
||||||
|
Db.HandleWarning(newWarning, installationId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,20 +97,20 @@ public static class RabbitMqManager
|
||||||
//Traverse the Alarm list, and store each of them to the database
|
//Traverse the Alarm list, and store each of them to the database
|
||||||
if (receivedStatusMessage.Alarms != null)
|
if (receivedStatusMessage.Alarms != null)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Add an alarm for installation "+receivedStatusMessage.InstallationId);
|
|
||||||
foreach (var alarm in receivedStatusMessage.Alarms)
|
foreach (var alarm in receivedStatusMessage.Alarms)
|
||||||
{
|
{
|
||||||
Error newError = new Error
|
Error newError = new Error
|
||||||
{
|
{
|
||||||
InstallationId = receivedStatusMessage.InstallationId,
|
InstallationId = installationId,
|
||||||
Description = alarm.Description,
|
Description = alarm.Description,
|
||||||
Date = alarm.Date,
|
Date = alarm.Date,
|
||||||
Time = alarm.Time,
|
Time = alarm.Time,
|
||||||
DeviceCreatedTheMessage = alarm.CreatedBy,
|
DeviceCreatedTheMessage = alarm.CreatedBy,
|
||||||
Seen = false
|
Seen = false
|
||||||
};
|
}; Console.WriteLine("Add an alarm for installation "+installationId);
|
||||||
//Create a new error and add it to the database
|
//Create a new error and add it to the database
|
||||||
Db.HandleError(newError, receivedStatusMessage.InstallationId);
|
Db.HandleError(newError, installationId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,6 +143,7 @@ public static class RabbitMqManager
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Channel.BasicConsume(queue: "statusQueue", autoAck: true, consumer: consumer);
|
Channel.BasicConsume(queue: "statusQueue", autoAck: true, consumer: consumer);
|
||||||
|
|
|
@ -4,7 +4,8 @@ namespace InnovEnergy.App.Backend.Websockets;
|
||||||
public class StatusMessage
|
public class StatusMessage
|
||||||
{
|
{
|
||||||
public required Int32 InstallationId { get; set; }
|
public required Int32 InstallationId { get; set; }
|
||||||
public required int Status { get; set; }
|
public required Int32 Product { get; set; }
|
||||||
|
public required Int32 Status { get; set; }
|
||||||
public required MessageType Type { get; set; }
|
public required MessageType Type { get; set; }
|
||||||
public List<AlarmOrWarning>? Warnings { get; set; }
|
public List<AlarmOrWarning>? Warnings { get; set; }
|
||||||
public List<AlarmOrWarning>? Alarms { get; set; }
|
public List<AlarmOrWarning>? Alarms { get; set; }
|
||||||
|
|
|
@ -16,8 +16,10 @@ dotnet publish \
|
||||||
-r linux-x64
|
-r linux-x64
|
||||||
|
|
||||||
echo -e "\n============================ Deploy ============================\n"
|
echo -e "\n============================ Deploy ============================\n"
|
||||||
ip_addresses=("10.2.3.115" "10.2.3.104" "10.2.4.33" "10.2.4.32" "10.2.4.36" "10.2.4.35" "10.2.4.154" "10.2.4.113" "10.2.4.29")
|
#ip_addresses=("10.2.3.115" "10.2.3.104" "10.2.4.33" "10.2.4.32" "10.2.4.36" "10.2.4.35" "10.2.4.154" "10.2.4.113" "10.2.4.29")
|
||||||
#ip_addresses=("10.2.4.154" "10.2.4.29")
|
#ip_addresses=("10.2.4.154" "10.2.4.29")
|
||||||
|
ip_addresses=("10.2.3.115" "10.2.3.104" "10.2.4.33" "10.2.4.32" "10.2.4.36" "10.2.4.35" "10.2.4.154" "10.2.4.29")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for ip_address in "${ip_addresses[@]}"; do
|
for ip_address in "${ip_addresses[@]}"; do
|
||||||
|
|
|
@ -5,6 +5,7 @@ namespace InnovEnergy.App.SaliMax.DataTypes;
|
||||||
public class StatusMessage
|
public class StatusMessage
|
||||||
{
|
{
|
||||||
public required Int32 InstallationId { get; set; }
|
public required Int32 InstallationId { get; set; }
|
||||||
|
public required Int32 Product { get; set; }
|
||||||
public required SalimaxAlarmState Status { get; set; }
|
public required SalimaxAlarmState Status { get; set; }
|
||||||
public required MessageType Type { get; set; }
|
public required MessageType Type { get; set; }
|
||||||
public List<AlarmOrWarning>? Warnings { get; set; }
|
public List<AlarmOrWarning>? Warnings { get; set; }
|
||||||
|
|
|
@ -486,6 +486,7 @@ internal static class Program
|
||||||
var returnedStatus = new StatusMessage
|
var returnedStatus = new StatusMessage
|
||||||
{
|
{
|
||||||
InstallationId = installationId,
|
InstallationId = installationId,
|
||||||
|
Product = 0,
|
||||||
Status = salimaxAlarmsState,
|
Status = salimaxAlarmsState,
|
||||||
Type = MessageType.AlarmOrWarning,
|
Type = MessageType.AlarmOrWarning,
|
||||||
Alarms = alarmList,
|
Alarms = alarmList,
|
||||||
|
@ -675,6 +676,10 @@ internal static class Program
|
||||||
var s3Path = timeStamp.ToUnixTime() + ".csv";
|
var s3Path = timeStamp.ToUnixTime() + ".csv";
|
||||||
var request = s3Config.CreatePutRequest(s3Path);
|
var request = s3Config.CreatePutRequest(s3Path);
|
||||||
|
|
||||||
|
// This is temporary for Wittman, but now it's for all Instalattion
|
||||||
|
await File.WriteAllTextAsync("/var/www/html/status.csv", csv.SplitLines().Where(l => !l.Contains("Secret")).JoinLines());
|
||||||
|
|
||||||
|
|
||||||
// Compress CSV data to a byte array
|
// Compress CSV data to a byte array
|
||||||
byte[] compressedBytes;
|
byte[] compressedBytes;
|
||||||
using (var memoryStream = new MemoryStream())
|
using (var memoryStream = new MemoryStream())
|
||||||
|
|
Binary file not shown.
|
@ -114,6 +114,16 @@ function MainStats(props: MainStatsProps) {
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const [isZooming, setIsZooming] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isZooming) {
|
||||||
|
setLoading(true);
|
||||||
|
} else if (!isZooming && batteryViewDataArray.length > 0) {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, [isZooming, batteryViewDataArray]);
|
||||||
|
|
||||||
function generateSeries(chartData, category, color) {
|
function generateSeries(chartData, category, color) {
|
||||||
const series = [];
|
const series = [];
|
||||||
const pathsToSearch = [
|
const pathsToSearch = [
|
||||||
|
@ -223,11 +233,14 @@ function MainStats(props: MainStatsProps) {
|
||||||
setErrorDateModalOpen(false);
|
setErrorDateModalOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const startZoom = () => {
|
||||||
|
setIsZooming(true);
|
||||||
|
};
|
||||||
|
|
||||||
const handleBeforeZoom = (chartContext, { xaxis }) => {
|
const handleBeforeZoom = (chartContext, { xaxis }) => {
|
||||||
const startX = parseInt(xaxis.min) / 1000;
|
const startX = parseInt(xaxis.min) / 1000;
|
||||||
const endX = parseInt(xaxis.max) / 1000;
|
const endX = parseInt(xaxis.max) / 1000;
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
const resultPromise: Promise<{
|
const resultPromise: Promise<{
|
||||||
chartData: BatteryDataInterface;
|
chartData: BatteryDataInterface;
|
||||||
chartOverview: BatteryOverviewInterface;
|
chartOverview: BatteryOverviewInterface;
|
||||||
|
@ -246,7 +259,7 @@ function MainStats(props: MainStatsProps) {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
setLoading(false);
|
setIsZooming(false);
|
||||||
setChartState(batteryViewDataArray.length);
|
setChartState(batteryViewDataArray.length);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
@ -509,7 +522,10 @@ function MainStats(props: MainStatsProps) {
|
||||||
),
|
),
|
||||||
chart: {
|
chart: {
|
||||||
events: {
|
events: {
|
||||||
beforeZoom: handleBeforeZoom
|
beforeZoom: (chartContext, options) => {
|
||||||
|
startZoom();
|
||||||
|
handleBeforeZoom(chartContext, options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
@ -568,7 +584,10 @@ function MainStats(props: MainStatsProps) {
|
||||||
),
|
),
|
||||||
chart: {
|
chart: {
|
||||||
events: {
|
events: {
|
||||||
beforeZoom: handleBeforeZoom
|
beforeZoom: (chartContext, options) => {
|
||||||
|
startZoom();
|
||||||
|
handleBeforeZoom(chartContext, options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
@ -626,7 +645,10 @@ function MainStats(props: MainStatsProps) {
|
||||||
),
|
),
|
||||||
chart: {
|
chart: {
|
||||||
events: {
|
events: {
|
||||||
beforeZoom: handleBeforeZoom
|
beforeZoom: (chartContext, options) => {
|
||||||
|
startZoom();
|
||||||
|
handleBeforeZoom(chartContext, options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
@ -684,7 +706,10 @@ function MainStats(props: MainStatsProps) {
|
||||||
),
|
),
|
||||||
chart: {
|
chart: {
|
||||||
events: {
|
events: {
|
||||||
beforeZoom: handleBeforeZoom
|
beforeZoom: (chartContext, options) => {
|
||||||
|
startZoom();
|
||||||
|
handleBeforeZoom(chartContext, options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
@ -742,7 +767,10 @@ function MainStats(props: MainStatsProps) {
|
||||||
),
|
),
|
||||||
chart: {
|
chart: {
|
||||||
events: {
|
events: {
|
||||||
beforeZoom: handleBeforeZoom
|
beforeZoom: (chartContext, options) => {
|
||||||
|
startZoom();
|
||||||
|
handleBeforeZoom(chartContext, options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -288,6 +288,26 @@ function InformationSalidomo(props: InformationSalidomoProps) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<TextField
|
||||||
|
label="S3 Write Key"
|
||||||
|
name="s3writesecretkey"
|
||||||
|
value={formValues.s3WriteKey}
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<TextField
|
||||||
|
label="S3 Write Key"
|
||||||
|
name="s3writesecretkey"
|
||||||
|
value={formValues.s3WriteKey}
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
|
|
@ -25,9 +25,29 @@ export const fetchDailyData = (
|
||||||
if (r.status === 404) {
|
if (r.status === 404) {
|
||||||
return Promise.resolve(FetchResult.notAvailable);
|
return Promise.resolve(FetchResult.notAvailable);
|
||||||
} else if (r.status === 200) {
|
} else if (r.status === 200) {
|
||||||
const text = await r.text();
|
// const text = await r.text();
|
||||||
|
const csvtext = await r.text(); // Assuming the server returns the Base64 encoded ZIP file as text
|
||||||
|
|
||||||
|
//const response = await fetch(url); // Fetch the resource from the server
|
||||||
|
const contentEncoding = r.headers.get('content-type');
|
||||||
|
|
||||||
|
if (contentEncoding != 'application/base64; charset=utf-8') {
|
||||||
|
return parseCsv(csvtext);
|
||||||
|
}
|
||||||
|
|
||||||
|
const byteArray = Uint8Array.from(atob(csvtext), (c) =>
|
||||||
|
c.charCodeAt(0)
|
||||||
|
);
|
||||||
|
|
||||||
|
//Decompress the byte array using JSZip
|
||||||
|
const zip = await JSZip.loadAsync(byteArray);
|
||||||
|
// Assuming the CSV file is named "data.csv" inside the ZIP archive
|
||||||
|
const csvContent = await zip.file('data.csv').async('text');
|
||||||
|
|
||||||
|
return parseCsv(csvContent);
|
||||||
|
|
||||||
//console.log(parseCsv(text));
|
//console.log(parseCsv(text));
|
||||||
return parseCsv(text);
|
//return parseCsv(text);
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve(FetchResult.notAvailable);
|
return Promise.resolve(FetchResult.notAvailable);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ function InstallationTabs() {
|
||||||
useContext(InstallationsContext);
|
useContext(InstallationsContext);
|
||||||
|
|
||||||
const webSocketsContext = useContext(WebSocketContext);
|
const webSocketsContext = useContext(WebSocketContext);
|
||||||
const { socket, openSocket } = webSocketsContext;
|
const { socket, openSocket, closeSocket } = webSocketsContext;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let path = location.pathname.split('/');
|
let path = location.pathname.split('/');
|
||||||
|
@ -50,7 +50,11 @@ function InstallationTabs() {
|
||||||
}, [location]);
|
}, [location]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!socket && salimaxInstallations.length > 0) {
|
if (salimaxInstallations && salimaxInstallations.length > 0) {
|
||||||
|
if (socket) {
|
||||||
|
closeSocket();
|
||||||
|
}
|
||||||
|
|
||||||
openSocket(salimaxInstallations);
|
openSocket(salimaxInstallations);
|
||||||
}
|
}
|
||||||
}, [salimaxInstallations]);
|
}, [salimaxInstallations]);
|
||||||
|
@ -135,6 +139,33 @@ function InstallationTabs() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
: currentUser.userType == UserType.partner
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
value: 'live',
|
||||||
|
label: <FormattedMessage id="live" defaultMessage="Live" />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'overview',
|
||||||
|
label: <FormattedMessage id="overview" defaultMessage="Overview" />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'batteryview',
|
||||||
|
label: (
|
||||||
|
<FormattedMessage
|
||||||
|
id="batteryview"
|
||||||
|
defaultMessage="Battery View"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
value: 'information',
|
||||||
|
label: (
|
||||||
|
<FormattedMessage id="information" defaultMessage="Information" />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
value: 'live',
|
value: 'live',
|
||||||
|
@ -219,7 +250,8 @@ function InstallationTabs() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
: [
|
: currentUser.userType == UserType.partner
|
||||||
|
? [
|
||||||
{
|
{
|
||||||
value: 'list',
|
value: 'list',
|
||||||
icon: <ListIcon id="mode-toggle-button-list-icon" />
|
icon: <ListIcon id="mode-toggle-button-list-icon" />
|
||||||
|
@ -249,6 +281,37 @@ function InstallationTabs() {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
value: 'information',
|
||||||
|
label: (
|
||||||
|
<FormattedMessage
|
||||||
|
id="information"
|
||||||
|
defaultMessage="Information"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
value: 'list',
|
||||||
|
icon: <ListIcon id="mode-toggle-button-list-icon" />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'tree',
|
||||||
|
icon: <AccountTreeIcon id="mode-toggle-button-tree-icon" />
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
value: 'live',
|
||||||
|
label: <FormattedMessage id="live" defaultMessage="Live" />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'overview',
|
||||||
|
label: (
|
||||||
|
<FormattedMessage id="overview" defaultMessage="Overview" />
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
value: 'information',
|
value: 'information',
|
||||||
label: (
|
label: (
|
||||||
|
|
|
@ -71,6 +71,15 @@ function Overview(props: OverviewProps) {
|
||||||
|
|
||||||
const [startDate, setStartDate] = useState(dayjs().add(-1, 'day'));
|
const [startDate, setStartDate] = useState(dayjs().add(-1, 'day'));
|
||||||
const [endDate, setEndDate] = useState(dayjs());
|
const [endDate, setEndDate] = useState(dayjs());
|
||||||
|
const [isZooming, setIsZooming] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isZooming) {
|
||||||
|
setLoading(true);
|
||||||
|
} else if (!isZooming && dailyDataArray.length > 0) {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, [isZooming, dailyDataArray]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const resultPromise: Promise<{
|
const resultPromise: Promise<{
|
||||||
|
@ -97,11 +106,14 @@ function Overview(props: OverviewProps) {
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const startZoom = () => {
|
||||||
|
setIsZooming(true);
|
||||||
|
};
|
||||||
|
|
||||||
const handleBeforeZoom = (chartContext, { xaxis }) => {
|
const handleBeforeZoom = (chartContext, { xaxis }) => {
|
||||||
const startX = parseInt(xaxis.min) / 1000;
|
const startX = parseInt(xaxis.min) / 1000;
|
||||||
const endX = parseInt(xaxis.max) / 1000;
|
const endX = parseInt(xaxis.max) / 1000;
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
const resultPromise: Promise<{
|
const resultPromise: Promise<{
|
||||||
chartData: chartDataInterface;
|
chartData: chartDataInterface;
|
||||||
chartOverview: overviewInterface;
|
chartOverview: overviewInterface;
|
||||||
|
@ -111,21 +123,31 @@ function Overview(props: OverviewProps) {
|
||||||
UnixTime.fromTicks(endX)
|
UnixTime.fromTicks(endX)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let isComponentMounted = true;
|
||||||
|
|
||||||
resultPromise
|
resultPromise
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
setDailyDataArray((prevData) =>
|
if (isComponentMounted) {
|
||||||
prevData.concat({
|
setDailyDataArray((prevData) =>
|
||||||
chartData: result.chartData,
|
prevData.concat({
|
||||||
chartOverview: result.chartOverview
|
chartData: result.chartData,
|
||||||
})
|
chartOverview: result.chartOverview
|
||||||
);
|
})
|
||||||
|
);
|
||||||
setLoading(false);
|
setIsZooming(false);
|
||||||
setChartState(dailyDataArray.length);
|
setChartState(dailyDataArray.length);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('Error:', error);
|
if (isComponentMounted) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
setLoading(false); // Ensure loading is turned off even if there is an error
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
isComponentMounted = false;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const handle24HourData = () => {
|
const handle24HourData = () => {
|
||||||
|
@ -595,7 +617,10 @@ function Overview(props: OverviewProps) {
|
||||||
),
|
),
|
||||||
chart: {
|
chart: {
|
||||||
events: {
|
events: {
|
||||||
beforeZoom: handleBeforeZoom
|
beforeZoom: (chartContext, options) => {
|
||||||
|
startZoom();
|
||||||
|
handleBeforeZoom(chartContext, options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
@ -761,7 +786,10 @@ function Overview(props: OverviewProps) {
|
||||||
),
|
),
|
||||||
chart: {
|
chart: {
|
||||||
events: {
|
events: {
|
||||||
beforeZoom: handleBeforeZoom
|
beforeZoom: (chartContext, options) => {
|
||||||
|
startZoom();
|
||||||
|
handleBeforeZoom(chartContext, options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
@ -851,7 +879,10 @@ function Overview(props: OverviewProps) {
|
||||||
),
|
),
|
||||||
chart: {
|
chart: {
|
||||||
events: {
|
events: {
|
||||||
beforeZoom: handleBeforeZoom
|
beforeZoom: (chartContext, options) => {
|
||||||
|
startZoom();
|
||||||
|
handleBeforeZoom(chartContext, options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
@ -982,7 +1013,10 @@ function Overview(props: OverviewProps) {
|
||||||
),
|
),
|
||||||
chart: {
|
chart: {
|
||||||
events: {
|
events: {
|
||||||
beforeZoom: handleBeforeZoom
|
beforeZoom: (chartContext, options) => {
|
||||||
|
startZoom();
|
||||||
|
handleBeforeZoom(chartContext, options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
@ -1040,7 +1074,10 @@ function Overview(props: OverviewProps) {
|
||||||
),
|
),
|
||||||
chart: {
|
chart: {
|
||||||
events: {
|
events: {
|
||||||
beforeZoom: handleBeforeZoom
|
beforeZoom: (chartContext, options) => {
|
||||||
|
startZoom();
|
||||||
|
handleBeforeZoom(chartContext, options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
@ -1111,7 +1148,10 @@ function Overview(props: OverviewProps) {
|
||||||
),
|
),
|
||||||
chart: {
|
chart: {
|
||||||
events: {
|
events: {
|
||||||
beforeZoom: handleBeforeZoom
|
beforeZoom: (chartContext, options) => {
|
||||||
|
startZoom();
|
||||||
|
handleBeforeZoom(chartContext, options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
@ -1196,7 +1236,10 @@ function Overview(props: OverviewProps) {
|
||||||
),
|
),
|
||||||
chart: {
|
chart: {
|
||||||
events: {
|
events: {
|
||||||
beforeZoom: handleBeforeZoom
|
beforeZoom: (chartContext, options) => {
|
||||||
|
startZoom();
|
||||||
|
handleBeforeZoom(chartContext, options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { useContext, useState } from 'react';
|
import React, { useContext, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
|
CircularProgress,
|
||||||
Grid,
|
Grid,
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
|
@ -16,6 +17,7 @@ import { WebSocketContext } from 'src/contexts/WebSocketContextProvider';
|
||||||
import { FormattedMessage } from 'react-intl';
|
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';
|
||||||
|
|
||||||
interface FlatInstallationViewProps {
|
interface FlatInstallationViewProps {
|
||||||
installations: I_Installation[];
|
installations: I_Installation[];
|
||||||
|
@ -29,6 +31,21 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
const [selectedInstallation, setSelectedInstallation] = useState<number>(-1);
|
const [selectedInstallation, setSelectedInstallation] = useState<number>(-1);
|
||||||
const currentLocation = useLocation();
|
const currentLocation = useLocation();
|
||||||
|
|
||||||
|
const sortedInstallations = [...props.installations].sort((a, b) => {
|
||||||
|
// Compare the status field of each installation and sort them based on the status.
|
||||||
|
//Installations with alarms go first
|
||||||
|
let a_status = getStatus(a.id);
|
||||||
|
let b_status = getStatus(b.id);
|
||||||
|
|
||||||
|
if (a_status > b_status) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (a_status < b_status) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
const handleSelectOneInstallation = (installationID: number): void => {
|
const handleSelectOneInstallation = (installationID: number): void => {
|
||||||
if (selectedInstallation != installationID) {
|
if (selectedInstallation != installationID) {
|
||||||
setSelectedInstallation(installationID);
|
setSelectedInstallation(installationID);
|
||||||
|
@ -95,10 +112,13 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<FormattedMessage id="VRM Link" defaultMessage="VRM Link" />
|
<FormattedMessage id="VRM Link" defaultMessage="VRM Link" />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<FormattedMessage id="status" defaultMessage="Status" />
|
||||||
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{props.installations.map((installation) => {
|
{sortedInstallations.map((installation) => {
|
||||||
const isInstallationSelected =
|
const isInstallationSelected =
|
||||||
installation.s3BucketId === selectedInstallation;
|
installation.s3BucketId === selectedInstallation;
|
||||||
|
|
||||||
|
@ -200,6 +220,57 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
</a>
|
</a>
|
||||||
</Typography>
|
</Typography>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginLeft: '15px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{status === -1 ? (
|
||||||
|
<CancelIcon
|
||||||
|
style={{
|
||||||
|
width: '23px',
|
||||||
|
height: '23px',
|
||||||
|
color: 'red',
|
||||||
|
borderRadius: '50%'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
|
||||||
|
{status === -2 ? (
|
||||||
|
<CircularProgress
|
||||||
|
size={20}
|
||||||
|
sx={{
|
||||||
|
color: '#f7b34d'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '20px',
|
||||||
|
height: '20px',
|
||||||
|
marginLeft: '2px',
|
||||||
|
borderRadius: '50%',
|
||||||
|
backgroundColor:
|
||||||
|
status === 2
|
||||||
|
? 'red'
|
||||||
|
: status === 1
|
||||||
|
? 'orange'
|
||||||
|
: status === -1 || status === -2
|
||||||
|
? 'transparent'
|
||||||
|
: 'green'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import { Card, Grid, Typography } from '@mui/material';
|
import { Card, CircularProgress, Grid, Typography } from '@mui/material';
|
||||||
import { I_Installation } from 'src/interfaces/InstallationTypes';
|
import { I_Installation } from 'src/interfaces/InstallationTypes';
|
||||||
import { UserContext } from 'src/contexts/userContext';
|
import { UserContext } from 'src/contexts/userContext';
|
||||||
import { TimeSpan, UnixTime } from 'src/dataCache/time';
|
import { TimeSpan, UnixTime } from 'src/dataCache/time';
|
||||||
|
@ -15,6 +15,8 @@ import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
|
||||||
import routes from '../../../Resources/routes.json';
|
import routes from '../../../Resources/routes.json';
|
||||||
import InformationSalidomo from '../Information/InformationSalidomo';
|
import InformationSalidomo from '../Information/InformationSalidomo';
|
||||||
import BatteryView from '../BatteryView/BatteryView';
|
import BatteryView from '../BatteryView/BatteryView';
|
||||||
|
import Log from '../Log/Log';
|
||||||
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
|
|
||||||
interface singleInstallationProps {
|
interface singleInstallationProps {
|
||||||
current_installation?: I_Installation;
|
current_installation?: I_Installation;
|
||||||
|
@ -174,6 +176,69 @@ function Installation(props: singleInstallationProps) {
|
||||||
{props.current_installation.installationName}
|
{props.current_installation.installationName}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||||
|
<Typography
|
||||||
|
fontWeight="bold"
|
||||||
|
color="text.primary"
|
||||||
|
noWrap
|
||||||
|
sx={{
|
||||||
|
marginTop: '0px',
|
||||||
|
marginBottom: '10px',
|
||||||
|
fontSize: '14px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Status:
|
||||||
|
</Typography>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginLeft: '75px',
|
||||||
|
marginTop: '-10px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{status === -1 ? (
|
||||||
|
<CancelIcon
|
||||||
|
style={{
|
||||||
|
width: '23px',
|
||||||
|
height: '23px',
|
||||||
|
color: 'red',
|
||||||
|
borderRadius: '50%'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
|
||||||
|
{status === -2 ? (
|
||||||
|
<CircularProgress
|
||||||
|
size={20}
|
||||||
|
sx={{
|
||||||
|
color: '#f7b34d'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '20px',
|
||||||
|
height: '20px',
|
||||||
|
marginLeft: '2px',
|
||||||
|
borderRadius: '50%',
|
||||||
|
backgroundColor:
|
||||||
|
status === 2
|
||||||
|
? 'red'
|
||||||
|
: status === 1
|
||||||
|
? 'orange'
|
||||||
|
: status === -1 || status === -2
|
||||||
|
? 'transparent'
|
||||||
|
: 'green'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Card variant="outlined">
|
<Card variant="outlined">
|
||||||
<Grid
|
<Grid
|
||||||
|
@ -195,6 +260,16 @@ function Installation(props: singleInstallationProps) {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path={routes.log}
|
||||||
|
element={
|
||||||
|
<Log
|
||||||
|
errorLoadingS3Data={errorLoadingS3Data}
|
||||||
|
id={props.current_installation.id}
|
||||||
|
></Log>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path={routes.batteryview + '*'}
|
path={routes.batteryview + '*'}
|
||||||
element={
|
element={
|
||||||
|
|
|
@ -23,7 +23,7 @@ function SalidomoInstallationTabs() {
|
||||||
useContext(InstallationsContext);
|
useContext(InstallationsContext);
|
||||||
|
|
||||||
const webSocketsContext = useContext(WebSocketContext);
|
const webSocketsContext = useContext(WebSocketContext);
|
||||||
const { socket, openSocket } = webSocketsContext;
|
const { socket, openSocket, closeSocket } = webSocketsContext;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let path = location.pathname.split('/');
|
let path = location.pathname.split('/');
|
||||||
|
@ -43,6 +43,15 @@ function SalidomoInstallationTabs() {
|
||||||
}
|
}
|
||||||
}, [salidomoInstallations]);
|
}, [salidomoInstallations]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (salidomoInstallations && salidomoInstallations.length > 0) {
|
||||||
|
if (socket) {
|
||||||
|
closeSocket();
|
||||||
|
}
|
||||||
|
openSocket(salidomoInstallations);
|
||||||
|
}
|
||||||
|
}, [salidomoInstallations]);
|
||||||
|
|
||||||
const handleTabsChange = (_event: ChangeEvent<{}>, value: string): void => {
|
const handleTabsChange = (_event: ChangeEvent<{}>, value: string): void => {
|
||||||
setCurrentTab(value);
|
setCurrentTab(value);
|
||||||
};
|
};
|
||||||
|
@ -83,6 +92,10 @@ function SalidomoInstallationTabs() {
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: 'log',
|
||||||
|
label: <FormattedMessage id="log" defaultMessage="Log" />
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
value: 'information',
|
value: 'information',
|
||||||
|
|
|
@ -9,11 +9,30 @@ import { InstallationsContext } from 'src/contexts/InstallationsContextProvider'
|
||||||
import { Route, Routes } from 'react-router-dom';
|
import { Route, Routes } from 'react-router-dom';
|
||||||
import routes from '../../../Resources/routes.json';
|
import routes from '../../../Resources/routes.json';
|
||||||
import Folder from './Folder';
|
import Folder from './Folder';
|
||||||
|
import { WebSocketContext } from '../../../contexts/WebSocketContextProvider';
|
||||||
|
|
||||||
function InstallationTree() {
|
function InstallationTree() {
|
||||||
const { foldersAndInstallations, fetchAllFoldersAndInstallations } =
|
const { foldersAndInstallations, fetchAllFoldersAndInstallations } =
|
||||||
useContext(InstallationsContext);
|
useContext(InstallationsContext);
|
||||||
|
|
||||||
|
const webSocketContext = useContext(WebSocketContext);
|
||||||
|
const { getStatus } = webSocketContext;
|
||||||
|
|
||||||
|
const sortedInstallations = [...foldersAndInstallations].sort((a, b) => {
|
||||||
|
// Compare the status field of each installation and sort them based on the status.
|
||||||
|
//Installations with alarms go first
|
||||||
|
let a_status = getStatus(a.id);
|
||||||
|
let b_status = getStatus(b.id);
|
||||||
|
|
||||||
|
if (a_status > b_status) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (a_status < b_status) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchAllFoldersAndInstallations();
|
fetchAllFoldersAndInstallations();
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -23,7 +42,7 @@ function InstallationTree() {
|
||||||
return (
|
return (
|
||||||
node.parentId == parent_id && (
|
node.parentId == parent_id && (
|
||||||
<CustomTreeItem node={node} parent_id={parent_id}>
|
<CustomTreeItem node={node} parent_id={parent_id}>
|
||||||
{foldersAndInstallations.map((subnode) => {
|
{sortedInstallations.map((subnode) => {
|
||||||
return (
|
return (
|
||||||
subnode != node &&
|
subnode != node &&
|
||||||
subnode.parentId == node.id && (
|
subnode.parentId == node.id && (
|
||||||
|
|
Loading…
Reference in New Issue