diff --git a/csharp/App/Backend/Database/Update.cs b/csharp/App/Backend/Database/Update.cs
index 5ee1d011a..ed5b2cf6a 100644
--- a/csharp/App/Backend/Database/Update.cs
+++ b/csharp/App/Backend/Database/Update.cs
@@ -56,6 +56,19 @@ public static partial class Db
}
}
+ ///
+ /// Updates ONLY the Status column for an installation.
+ /// This avoids a full-row overwrite that can race with TestingMode changes.
+ ///
+ public static Boolean UpdateInstallationStatus(Int64 installationId, int status)
+ {
+ var rows = Connection.Execute(
+ "UPDATE Installation SET Status = ? WHERE Id = ?",
+ status, installationId);
+ if (rows > 0) Backup();
+ return rows > 0;
+ }
+
// Ticket system
public static Boolean Update(Ticket ticket) => Update(obj: ticket);
public static Boolean Update(TicketAiDiagnosis diagnosis) => Update(obj: diagnosis);
diff --git a/csharp/App/Backend/Websockets/RabbitMQManager.cs b/csharp/App/Backend/Websockets/RabbitMQManager.cs
index f3d810920..2c8c65521 100644
--- a/csharp/App/Backend/Websockets/RabbitMQManager.cs
+++ b/csharp/App/Backend/Websockets/RabbitMQManager.cs
@@ -179,8 +179,7 @@ public static class RabbitMqManager
Console.WriteLine("RECEIVED A HEARTBIT FROM prototype, time is "+ WebsocketManager.InstallationConnections[installationId].Timestamp);
}
- installation.Status = receivedStatusMessage.Status;
- installation.Apply(Db.Update);
+ Db.UpdateInstallationStatus(installationId, receivedStatusMessage.Status);
//Console.WriteLine("----------------------------------------------");
//If the status has changed, update all the connected front-ends regarding this installation
diff --git a/csharp/App/Backend/Websockets/WebsockerManager.cs b/csharp/App/Backend/Websockets/WebsockerManager.cs
index 0a8533f94..8d054b8b0 100644
--- a/csharp/App/Backend/Websockets/WebsockerManager.cs
+++ b/csharp/App/Backend/Websockets/WebsockerManager.cs
@@ -38,9 +38,7 @@ public static class WebsocketManager
Console.WriteLine("installationConnection.Value.Timestamp is " + installationConnection.Value.Timestamp);
installationConnection.Value.Status = (int)StatusType.Offline;
- Installation installation = Db.Installations.FirstOrDefault(f => f.Product == installationConnection.Value.Product && f.Id == installationConnection.Key);
- installation.Status = (int)StatusType.Offline;
- installation.Apply(Db.Update);
+ Db.UpdateInstallationStatus(installationConnection.Key, (int)StatusType.Offline);
if (installationConnection.Value.Connections.Count > 0)
{
idsToInform.Add(installationConnection.Key);
@@ -61,17 +59,31 @@ public static class WebsocketManager
public static async Task InformWebsocketsForInstallation(Int64 installationId)
{
var installation = Db.GetInstallationById(installationId);
+ if (installation is null) return;
+
byte[] dataToSend;
List connections;
lock (InstallationConnections)
{
- var installationConnection = InstallationConnections[installationId];
- Console.WriteLine("Update all the connected websockets for installation " + installation.Name);
+ if (!InstallationConnections.ContainsKey(installationId))
+ {
+ Console.WriteLine($"InformWebsocketsForInstallation: No entry for installation {installationId}, skipping");
+ return;
+ }
- // Prune dead/closed connections before sending
+ var installationConnection = InstallationConnections[installationId];
+
+ // Prune dead/closed connections BEFORE checking count
installationConnection.Connections.RemoveAll(c => c.State != WebSocketState.Open);
+ if (installationConnection.Connections.Count == 0)
+ {
+ Console.WriteLine($"InformWebsocketsForInstallation: No open connections for installation {installationId}, skipping");
+ return;
+ }
+ Console.WriteLine("Update all the connected websockets for installation " + installation.Name);
+
var jsonObject = new
{
id = installationId,
diff --git a/typescript/frontend-marios2/src/content/dashboards/History/History.tsx b/typescript/frontend-marios2/src/content/dashboards/History/History.tsx
index 36e2693c0..268ae85d6 100644
--- a/typescript/frontend-marios2/src/content/dashboards/History/History.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/History/History.tsx
@@ -30,6 +30,8 @@ import timezone from 'dayjs/plugin/timezone';
import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/Delete';
import { UserContext } from '../../../contexts/userContext';
+import { InstallationsContext } from '../../../contexts/InstallationsContextProvider';
+import { ProductIdContext } from '../../../contexts/ProductIdContextProvider';
dayjs.extend(utc);
dayjs.extend(timezone);
@@ -58,6 +60,8 @@ function HistoryOfActions(props: HistoryProps) {
});
const context = useContext(UserContext);
const { currentUser, setUser } = context;
+ const { fetchAllInstallations } = useContext(InstallationsContext);
+ const { product } = useContext(ProductIdContext);
const [isRowHovered, setHoveredRow] = useState(-1);
const [selectedAction, setSelectedAction] = useState(-1);
const [editMode, setEditMode] = useState(false);
@@ -109,6 +113,7 @@ function HistoryOfActions(props: HistoryProps) {
if (res) {
getHistory();
+ fetchAllInstallations(product, false);
setOpenModalAddAction(false);
setEditMode(false);
}
@@ -129,6 +134,7 @@ function HistoryOfActions(props: HistoryProps) {
if (res) {
getHistory();
+ fetchAllInstallations(product, false);
}
};
diff --git a/typescript/frontend-marios2/src/contexts/InstallationsContextProvider.tsx b/typescript/frontend-marios2/src/contexts/InstallationsContextProvider.tsx
index 02bed5596..a663f4ae3 100644
--- a/typescript/frontend-marios2/src/contexts/InstallationsContextProvider.tsx
+++ b/typescript/frontend-marios2/src/contexts/InstallationsContextProvider.tsx
@@ -122,7 +122,7 @@ const InstallationsContextProvider = ({
useEffect(() => {
const timer = setInterval(() => {
applyBatchUpdates();
- }, 60000);
+ }, 2000);
return () => clearInterval(timer); // Cleanup timer on component unmount
}, [applyBatchUpdates]);
@@ -156,8 +156,15 @@ const InstallationsContextProvider = ({
new_socket.addEventListener('message', (event) => {
const message = JSON.parse(event.data); // Parse the JSON data
- if (message.id !== -1) {
- //For each received message (except the first one which is a batch, call the updateInstallationStatus function in order to import the message to the pendingUpdates list
+
+ // Initial batch from backend is an array, subsequent updates are single objects
+ if (Array.isArray(message)) {
+ message.forEach((msg) => {
+ if (msg.id !== -1) {
+ updateInstallationStatus(msg.id, msg.status, msg.testingMode);
+ }
+ });
+ } else if (message.id !== -1) {
updateInstallationStatus(
message.id,
message.status,