diff --git a/csharp/App/Backend/Controller.cs b/csharp/App/Backend/Controller.cs index 85311d24b..d486c06d3 100644 --- a/csharp/App/Backend/Controller.cs +++ b/csharp/App/Backend/Controller.cs @@ -793,7 +793,7 @@ public class Controller : ControllerBase public async Task>> InsertNewAction([FromBody] UserAction action, Token authToken) { var session = Db.GetSession(authToken); - var actionSuccess = await session.RecordUserAction(action); + var actionSuccess = await session.InsertUserAction(action); return actionSuccess ? Ok() : Unauthorized(); } @@ -835,7 +835,7 @@ public class Controller : ControllerBase Description = config.GetConfigurationString() }; - var actionSuccess = await session.RecordUserAction(action); + var actionSuccess = await session.InsertUserAction(action); return actionSuccess?Ok():Unauthorized(); } diff --git a/csharp/App/Backend/DataTypes/Installation.cs b/csharp/App/Backend/DataTypes/Installation.cs index ed52fc1dc..bcbe2368c 100644 --- a/csharp/App/Backend/DataTypes/Installation.cs +++ b/csharp/App/Backend/DataTypes/Installation.cs @@ -19,6 +19,7 @@ public class Installation : TreeNode public int S3BucketId { get; set; } = 0; public String ReadRoleId { get; set; } = ""; public String WriteRoleId { get; set; } = ""; + public Boolean TestingMode { get; set; } = false; public int Product { get; set; } = 0; public int Device { get; set; } = 0; diff --git a/csharp/App/Backend/DataTypes/Methods/Session.cs b/csharp/App/Backend/DataTypes/Methods/Session.cs index 417fc690f..1e751bb85 100644 --- a/csharp/App/Backend/DataTypes/Methods/Session.cs +++ b/csharp/App/Backend/DataTypes/Methods/Session.cs @@ -1,6 +1,7 @@ using System.Diagnostics; using InnovEnergy.App.Backend.Database; using InnovEnergy.App.Backend.Relations; +using InnovEnergy.App.Backend.Websockets; using InnovEnergy.Lib.Utils; using Org.BouncyCastle.Asn1.X509; @@ -131,7 +132,7 @@ public static class SessionMethods && await installation.SendConfig(configuration); } - public static async Task RecordUserAction(this Session? session, UserAction action) + public static async Task InsertUserAction(this Session? session, UserAction action) { var user = session?.User; @@ -140,6 +141,11 @@ public static class SessionMethods action.UserName = user.Name; + var installation = Db.GetInstallationById(action.InstallationId); + installation.TestingMode = action.TestingMode; + installation.Apply(Db.Update); + WebsocketManager.InformWebsocketsForInstallation(action.InstallationId); + // Save the configuration change to the database Db.HandleAction(action); return true; @@ -152,6 +158,14 @@ public static class SessionMethods if (user is null || user.UserType == 0) return false; + var installation = Db.GetInstallationById(action.InstallationId); + if (installation.TestingMode != action.TestingMode) + { + installation.TestingMode = action.TestingMode; + installation.Apply(Db.Update); + WebsocketManager.InformWebsocketsForInstallation(action.InstallationId); + } + Db.UpdateAction(action); return true; } @@ -164,7 +178,13 @@ public static class SessionMethods if (user is null || user.UserType == 0) return false; var action = Db.GetActionById(actionId); - + if (action.TestingMode) + { + var installation = Db.GetInstallationById(action.InstallationId); + installation.TestingMode = false; + installation.Apply(Db.Update); + WebsocketManager.InformWebsocketsForInstallation(action.InstallationId); + } Db.Delete(action); Console.WriteLine("---------------Deleted the Action in the database-----------------"); diff --git a/csharp/App/Backend/DataTypes/UserAction.cs b/csharp/App/Backend/DataTypes/UserAction.cs index 9a20cff26..c546ced32 100644 --- a/csharp/App/Backend/DataTypes/UserAction.cs +++ b/csharp/App/Backend/DataTypes/UserAction.cs @@ -11,6 +11,8 @@ public class UserAction public String UserName { get; set; } = null!;// User Name who made the configuration change public Int64 InstallationId { get; set; } // Installation ID where the configuration change is made + + public Boolean TestingMode { get; set; } public DateTime Timestamp { get; set; } // Timestamp of the configuration change diff --git a/csharp/App/Backend/Database/Create.cs b/csharp/App/Backend/Database/Create.cs index c2f8c6665..981e13be6 100644 --- a/csharp/App/Backend/Database/Create.cs +++ b/csharp/App/Backend/Database/Create.cs @@ -111,11 +111,14 @@ public static partial class Db public static void UpdateAction(UserAction updatedAction) { var existingAction = UserActions.FirstOrDefault(action => action.Id == updatedAction.Id); + + if (existingAction != null) { - existingAction.Description = updatedAction.Description; - existingAction.Timestamp = updatedAction.Timestamp; - Update(existingAction); + //existingAction.Description = updatedAction.Description; + //existingAction.Timestamp = updatedAction.Timestamp; + //existingAction.TestingMode = updatedAction.TestingMode; + Update(updatedAction); Console.WriteLine("---------------Updated the Action in the database-----------------"); } } diff --git a/csharp/App/Backend/Websockets/RabbitMQManager.cs b/csharp/App/Backend/Websockets/RabbitMQManager.cs index fcca8a036..617f2eeae 100644 --- a/csharp/App/Backend/Websockets/RabbitMQManager.cs +++ b/csharp/App/Backend/Websockets/RabbitMQManager.cs @@ -68,7 +68,7 @@ public static class RabbitMqManager //Every 15 iterations(30 seconds), the installation sends a heartbit message to the queue 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) { @@ -167,7 +167,7 @@ public static class RabbitMqManager if (!WebsocketManager.InstallationConnections.ContainsKey(installationId)) { prevStatus = -2; - Console.WriteLine("Create new empty list for installation: " + installationId); + //Console.WriteLine("Create new empty list for installation: " + installationId); WebsocketManager.InstallationConnections[installationId] = new InstallationInfo { Status = receivedStatusMessage.Status, diff --git a/csharp/App/Backend/Websockets/WebsockerManager.cs b/csharp/App/Backend/Websockets/WebsockerManager.cs index 257ce3f76..2a1a29aad 100644 --- a/csharp/App/Backend/Websockets/WebsockerManager.cs +++ b/csharp/App/Backend/Websockets/WebsockerManager.cs @@ -9,7 +9,7 @@ namespace InnovEnergy.App.Backend.Websockets; public static class WebsocketManager { - public static Dictionary InstallationConnections = new Dictionary(); + public static Dictionary InstallationConnections = new Dictionary(); //Every 2 minutes, check the timestamp of the latest received message for every installation. //If the difference between the two timestamps is more than two minutes, we consider this installation unavailable. @@ -30,15 +30,18 @@ public static class WebsocketManager } //Inform all the connected websockets regarding installation "installationId" - public static void InformWebsocketsForInstallation(int installationId) + public static void InformWebsocketsForInstallation(Int64 installationId) { + var installation = Db.GetInstallationById(installationId); var installationConnection = InstallationConnections[installationId]; Console.WriteLine("Update all the connected websockets for installation " + installationId); + var jsonObject = new { id = installationId, - status = installationConnection.Status + status = installationConnection.Status, + testingMode = installation.TestingMode }; string jsonString = JsonSerializer.Serialize(jsonObject); @@ -99,9 +102,10 @@ public static class WebsocketManager //Then, report the status of each requested installation to the front-end that created the websocket connection foreach (var installationId in installationIds) { + var installation = Db.GetInstallationById(installationId); if (!InstallationConnections.ContainsKey(installationId)) { - Console.WriteLine("Create new empty list for installation id " + installationId); + //Console.WriteLine("Create new empty list for installation id " + installationId); InstallationConnections[installationId] = new InstallationInfo { Status = -1 @@ -113,7 +117,8 @@ public static class WebsocketManager var jsonObject = new { id = installationId, - status = InstallationConnections[installationId].Status + status = InstallationConnections[installationId].Status, + testingMode = installation.TestingMode }; var jsonString = JsonSerializer.Serialize(jsonObject); diff --git a/typescript/frontend-marios2/src/App.tsx b/typescript/frontend-marios2/src/App.tsx index dcbe7ae4a..6790e1cf3 100644 --- a/typescript/frontend-marios2/src/App.tsx +++ b/typescript/frontend-marios2/src/App.tsx @@ -11,8 +11,6 @@ import fr from './lang/fr.json'; import SuspenseLoader from './components/SuspenseLoader'; import SidebarLayout from './layouts/SidebarLayout'; import { TokenContext } from './contexts/tokenContext'; -import { TestModeProvider } from './contexts/TestModeContext'; -import ResetPassword from './components/ResetPassword'; import InstallationTabs from './content/dashboards/Installations/index'; import routes from 'src/Resources/routes.json'; import './App.css'; @@ -143,9 +141,7 @@ function App() { element={ - - } diff --git a/typescript/frontend-marios2/src/content/dashboards/History/History.tsx b/typescript/frontend-marios2/src/content/dashboards/History/History.tsx index c4c6ad5d0..8f1a22e11 100644 --- a/typescript/frontend-marios2/src/content/dashboards/History/History.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/History/History.tsx @@ -20,7 +20,6 @@ import { AxiosError, AxiosResponse } from 'axios/index'; import routes from '../../../Resources/routes.json'; import { useNavigate } from 'react-router-dom'; import { TokenContext } from '../../../contexts/tokenContext'; -import { useTestMode } from '../../../contexts/TestModeContext'; import { Action } from '../../../interfaces/S3Types'; import Button from '@mui/material/Button'; import { DateTimePicker, LocalizationProvider } from '@mui/x-date-pickers'; @@ -49,19 +48,14 @@ function HistoryOfActions(props: HistoryProps) { const [newAction, setNewAction] = useState>({ installationId: props.id, timestamp: actionDate.toDate(), - description: '' + description: '', + testingMode: false }); - const { testModeMap, setTestMode } = useTestMode(); - const isTestMode = testModeMap[props.id] || false; const context = useContext(UserContext); const { currentUser, setUser } = context; const [isRowHovered, setHoveredRow] = useState(-1); const [selectedAction, setSelectedAction] = useState(-1); const [editMode, setEditMode] = useState(false); - const handleTestModeToggle = () => { - setTestMode(props.id, !isTestMode); - }; - const handleDateChange = (newdate) => { setActionDate(newdate); setNewAction({ @@ -71,10 +65,10 @@ function HistoryOfActions(props: HistoryProps) { }; const handleChange = (e) => { - const { name, value } = e.target; + const { name, type, checked, value } = e.target; setNewAction({ ...newAction, - [name]: value + [name]: type === 'checkbox' ? checked : value }); }; @@ -83,7 +77,8 @@ function HistoryOfActions(props: HistoryProps) { setActionDate(dayjs()); setNewAction({ ...newAction, - ['description']: '' + ['description']: '', + ['timestamp']: dayjs().toDate() }); setEditMode(false); }; @@ -233,8 +228,9 @@ function HistoryOfActions(props: HistoryProps) { } label="Test Mode" diff --git a/typescript/frontend-marios2/src/content/dashboards/Installations/FlatInstallationView.tsx b/typescript/frontend-marios2/src/content/dashboards/Installations/FlatInstallationView.tsx index 2929386bb..c94883687 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Installations/FlatInstallationView.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Installations/FlatInstallationView.tsx @@ -16,7 +16,6 @@ import { I_Installation } from 'src/interfaces/InstallationTypes'; import CancelIcon from '@mui/icons-material/Cancel'; import BuildIcon from '@mui/icons-material/Build'; import { WebSocketContext } from 'src/contexts/WebSocketContextProvider'; -import { useTestMode } from 'src/contexts/TestModeContext'; import { FormattedMessage } from 'react-intl'; import { useLocation, useNavigate } from 'react-router-dom'; import routes from '../../../Resources/routes.json'; @@ -28,11 +27,10 @@ interface FlatInstallationViewProps { const FlatInstallationView = (props: FlatInstallationViewProps) => { const [isRowHovered, setHoveredRow] = useState(-1); const webSocketContext = useContext(WebSocketContext); - const { getStatus } = webSocketContext; + const { getStatus, getTestingMode } = webSocketContext; const navigate = useNavigate(); const [selectedInstallation, setSelectedInstallation] = useState(-1); const currentLocation = useLocation(); - const { testModeMap } = useTestMode(); const sortedInstallations = [...props.installations].sort((a, b) => { // Compare the status field of each installation and sort them based on the status. @@ -246,7 +244,7 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => { }} /> - {testModeMap[installation.id] && ( + {getTestingMode(installation.id) && ( { borderRadius: '50%', position: 'relative', zIndex: 1, - marginLeft: status === -1 || status === -2 ? '-23px' : '2px', + marginLeft: '15px' }} /> )} diff --git a/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx b/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx index eeefc86ee..3be2f9ec9 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx @@ -32,7 +32,6 @@ import BatteryView from '../BatteryView/BatteryView'; import { UserType } from '../../../interfaces/UserTypes'; import HistoryOfActions from '../History/History'; import PvView from '../PvView/PvView'; -import { useTestMode } from '../../../contexts/TestModeContext'; interface singleInstallationProps { current_installation?: I_Installation; @@ -46,13 +45,12 @@ function Installation(props: singleInstallationProps) { const [fetchFunctionCalled, setFetchFunctionCalled] = useState(false); const [errorLoadingS3Data, setErrorLoadingS3Data] = useState(false); const webSocketsContext = useContext(WebSocketContext); - const { getStatus } = webSocketsContext; + const { getStatus, getTestingMode } = webSocketsContext; const [currentTab, setCurrentTab] = useState(undefined); const [values, setValues] = useState(null); const status = getStatus(props.current_installation.id); const [connected, setConnected] = useState(true); const [loading, setLoading] = useState(true); - const { testModeMap } = useTestMode(); if (props.current_installation == undefined) { return null; @@ -362,7 +360,7 @@ function Installation(props: singleInstallationProps) { }} /> - {testModeMap[props.current_installation.id] && ( + {getTestingMode(props.current_installation.id) && ( )} - {loading && currentTab != 'information' && currentTab != 'history' && ( - - - - Connecting to the device... - - - )} + + + Connecting to the device... + + + )} void; -} - -const TestModeContext = createContext(undefined); - -export const useTestMode = () => { - const context = useContext(TestModeContext); - if (!context) { - throw new Error('useTestMode must be used within a TestModeProvider'); - } - return context; -}; - -export const TestModeProvider: React.FC<{ children: ReactNode }> = ({ children }) => { - const [testModeMap, setTestModeMap] = useState<{ [key: number]: boolean }>({}); - - const setTestMode = (id: number, mode: boolean) => { - setTestModeMap(prev => ({ ...prev, [id]: mode })); - }; - - return ( - - {children} - - ); -}; diff --git a/typescript/frontend-marios2/src/contexts/WebSocketContextProvider.tsx b/typescript/frontend-marios2/src/contexts/WebSocketContextProvider.tsx index d649011d1..8a339e80f 100644 --- a/typescript/frontend-marios2/src/contexts/WebSocketContextProvider.tsx +++ b/typescript/frontend-marios2/src/contexts/WebSocketContextProvider.tsx @@ -7,6 +7,7 @@ interface WebSocketContextProviderProps { openSocket: (installations: I_Installation[]) => void; closeSocket: () => void; getStatus: (installationId: number) => number; + getTestingMode: (installationId: number) => boolean; } // Create the context. @@ -18,7 +19,10 @@ const WebSocketContextProvider = ({ children }: { children: ReactNode }) => { const [socket, setSocket] = useState(null); const [installations, setInstallations] = useState(null); const [installationStatus, setInstallationStatus] = useState< - Record + Record + >({}); + const [installationMode, setInstallatioMode] = useState< + Record >({}); const BUFFER_LENGTH = 5; @@ -52,23 +56,19 @@ const WebSocketContextProvider = ({ children }: { children: ReactNode }) => { if (message.id != -1) { const installation_id = message.id; - const status = message.status; //console.log('Message from server ', installation_id, status); + setInstallatioMode((prevMode) => { + // Create a new object by spreading the previous state + const newMode = { ...prevMode }; + newMode[installation_id] = message.testingMode; + return newMode; + }); setInstallationStatus((prevStatus) => { // Create a new object by spreading the previous state const newStatus = { ...prevStatus }; - - if (!newStatus.hasOwnProperty(installation_id)) { - newStatus[installation_id] = []; - } - newStatus[installation_id].unshift(status); - newStatus[installation_id] = newStatus[installation_id].slice( - 0, - BUFFER_LENGTH - ); - + newStatus[installation_id] = message.status; return newStatus; }); } @@ -90,18 +90,23 @@ const WebSocketContextProvider = ({ children }: { children: ReactNode }) => { if (!installationStatus.hasOwnProperty(installationId)) { status = -2; } else { - status = installationStatus[installationId][0]; + status = installationStatus[installationId]; } return status; }; + const getTestingMode = (installationId: number) => { + return installationMode[installationId]; + }; + return ( {children} diff --git a/typescript/frontend-marios2/src/interfaces/InstallationTypes.tsx b/typescript/frontend-marios2/src/interfaces/InstallationTypes.tsx index 12974e165..7c5d841fc 100644 --- a/typescript/frontend-marios2/src/interfaces/InstallationTypes.tsx +++ b/typescript/frontend-marios2/src/interfaces/InstallationTypes.tsx @@ -18,6 +18,7 @@ export interface I_Installation extends I_S3Credentials { s3WriteSecret: string; product: number; device: number; + testingMode: boolean; } export interface I_Folder { diff --git a/typescript/frontend-marios2/src/interfaces/S3Types.tsx b/typescript/frontend-marios2/src/interfaces/S3Types.tsx index 708ee443d..7d9b60efe 100644 --- a/typescript/frontend-marios2/src/interfaces/S3Types.tsx +++ b/typescript/frontend-marios2/src/interfaces/S3Types.tsx @@ -29,4 +29,5 @@ export interface Action { installationId: number; timestamp: Date; description: string; + testingMode: boolean; }