Fixed edit mode in the conflict of RabbitMQ(status message updates all entries) and WebSocket

This commit is contained in:
Yinyin Liu 2026-03-11 12:22:19 +01:00
parent 1306ae6b9f
commit 591e273bc7
5 changed files with 48 additions and 11 deletions

View File

@ -56,6 +56,19 @@ public static partial class Db
} }
} }
/// <summary>
/// Updates ONLY the Status column for an installation.
/// This avoids a full-row overwrite that can race with TestingMode changes.
/// </summary>
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 // Ticket system
public static Boolean Update(Ticket ticket) => Update(obj: ticket); public static Boolean Update(Ticket ticket) => Update(obj: ticket);
public static Boolean Update(TicketAiDiagnosis diagnosis) => Update(obj: diagnosis); public static Boolean Update(TicketAiDiagnosis diagnosis) => Update(obj: diagnosis);

View File

@ -179,8 +179,7 @@ public static class RabbitMqManager
Console.WriteLine("RECEIVED A HEARTBIT FROM prototype, time is "+ WebsocketManager.InstallationConnections[installationId].Timestamp); Console.WriteLine("RECEIVED A HEARTBIT FROM prototype, time is "+ WebsocketManager.InstallationConnections[installationId].Timestamp);
} }
installation.Status = receivedStatusMessage.Status; Db.UpdateInstallationStatus(installationId, receivedStatusMessage.Status);
installation.Apply(Db.Update);
//Console.WriteLine("----------------------------------------------"); //Console.WriteLine("----------------------------------------------");
//If the status has changed, update all the connected front-ends regarding this installation //If the status has changed, update all the connected front-ends regarding this installation

View File

@ -38,9 +38,7 @@ public static class WebsocketManager
Console.WriteLine("installationConnection.Value.Timestamp is " + installationConnection.Value.Timestamp); Console.WriteLine("installationConnection.Value.Timestamp is " + installationConnection.Value.Timestamp);
installationConnection.Value.Status = (int)StatusType.Offline; installationConnection.Value.Status = (int)StatusType.Offline;
Installation installation = Db.Installations.FirstOrDefault(f => f.Product == installationConnection.Value.Product && f.Id == installationConnection.Key); Db.UpdateInstallationStatus(installationConnection.Key, (int)StatusType.Offline);
installation.Status = (int)StatusType.Offline;
installation.Apply(Db.Update);
if (installationConnection.Value.Connections.Count > 0) if (installationConnection.Value.Connections.Count > 0)
{ {
idsToInform.Add(installationConnection.Key); idsToInform.Add(installationConnection.Key);
@ -61,17 +59,31 @@ public static class WebsocketManager
public static async Task InformWebsocketsForInstallation(Int64 installationId) public static async Task InformWebsocketsForInstallation(Int64 installationId)
{ {
var installation = Db.GetInstallationById(installationId); var installation = Db.GetInstallationById(installationId);
if (installation is null) return;
byte[] dataToSend; byte[] dataToSend;
List<WebSocket> connections; List<WebSocket> connections;
lock (InstallationConnections) lock (InstallationConnections)
{ {
var installationConnection = InstallationConnections[installationId]; if (!InstallationConnections.ContainsKey(installationId))
Console.WriteLine("Update all the connected websockets for installation " + installation.Name); {
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); 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 var jsonObject = new
{ {
id = installationId, id = installationId,

View File

@ -30,6 +30,8 @@ import timezone from 'dayjs/plugin/timezone';
import EditIcon from '@mui/icons-material/Edit'; import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/Delete'; import DeleteIcon from '@mui/icons-material/Delete';
import { UserContext } from '../../../contexts/userContext'; import { UserContext } from '../../../contexts/userContext';
import { InstallationsContext } from '../../../contexts/InstallationsContextProvider';
import { ProductIdContext } from '../../../contexts/ProductIdContextProvider';
dayjs.extend(utc); dayjs.extend(utc);
dayjs.extend(timezone); dayjs.extend(timezone);
@ -58,6 +60,8 @@ function HistoryOfActions(props: HistoryProps) {
}); });
const context = useContext(UserContext); const context = useContext(UserContext);
const { currentUser, setUser } = context; const { currentUser, setUser } = context;
const { fetchAllInstallations } = useContext(InstallationsContext);
const { product } = useContext(ProductIdContext);
const [isRowHovered, setHoveredRow] = useState(-1); const [isRowHovered, setHoveredRow] = useState(-1);
const [selectedAction, setSelectedAction] = useState<number>(-1); const [selectedAction, setSelectedAction] = useState<number>(-1);
const [editMode, setEditMode] = useState(false); const [editMode, setEditMode] = useState(false);
@ -109,6 +113,7 @@ function HistoryOfActions(props: HistoryProps) {
if (res) { if (res) {
getHistory(); getHistory();
fetchAllInstallations(product, false);
setOpenModalAddAction(false); setOpenModalAddAction(false);
setEditMode(false); setEditMode(false);
} }
@ -129,6 +134,7 @@ function HistoryOfActions(props: HistoryProps) {
if (res) { if (res) {
getHistory(); getHistory();
fetchAllInstallations(product, false);
} }
}; };

View File

@ -122,7 +122,7 @@ const InstallationsContextProvider = ({
useEffect(() => { useEffect(() => {
const timer = setInterval(() => { const timer = setInterval(() => {
applyBatchUpdates(); applyBatchUpdates();
}, 60000); }, 2000);
return () => clearInterval(timer); // Cleanup timer on component unmount return () => clearInterval(timer); // Cleanup timer on component unmount
}, [applyBatchUpdates]); }, [applyBatchUpdates]);
@ -156,8 +156,15 @@ const InstallationsContextProvider = ({
new_socket.addEventListener('message', (event) => { new_socket.addEventListener('message', (event) => {
const message = JSON.parse(event.data); // Parse the JSON data 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( updateInstallationStatus(
message.id, message.id,
message.status, message.status,