From 30d6910206f99983fe0adaf564b868e1266fa1c2 Mon Sep 17 00:00:00 2001 From: xLPMG Date: Thu, 11 Jan 2024 23:48:35 +0100 Subject: [PATCH] implemented api --- include/communicator_api.h | 131 +++++++++++++++++++++++++++++++++++++ runServer.sh | 4 ++ src/Server.cpp | 115 +++++++++++++++++--------------- src/constants.h | 91 +------------------------- src/ui/GUI.cpp | 123 +++++++++++++++++++--------------- src/ui/GUI.h | 32 +++++---- 6 files changed, 289 insertions(+), 207 deletions(-) create mode 100644 include/communicator_api.h diff --git a/include/communicator_api.h b/include/communicator_api.h new file mode 100644 index 00000000..41103125 --- /dev/null +++ b/include/communicator_api.h @@ -0,0 +1,131 @@ +/** + * @author Luca-Philipp Grumbach + * + * # Description + * Communicator api. + **/ +#ifndef XLPMG_COMMUNICATOR_API_H +#define XLPMG_COMMUNICATOR_API_H + +#include +#include + +#include + +namespace xlpmg +{ + enum MessagePart + { + MESSAGE_TYPE, + KEY, + ARGS + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(MessagePart, {{MESSAGE_TYPE, "type"}, + {KEY, "key"}, + {ARGS, "arguments"}}); + + enum MessageType + { + SERVER__CALL, + FUNCTION_CALL, + CHECK_CALL + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(MessageType, {{SERVER__CALL, "server_call"}, + {FUNCTION_CALL, "function_call"}, + {CHECK_CALL, "check_call"}}); + + //! should not not induce any functionality and is only used to check if the other side responds + inline const char *KEY_CHECK = "XCHECKX"; + + // SERVER CALLS + + /* + * Tells the server to shutdown. + * + * @MESSAGE_TYPE SERVER__CALL + * @ARGS none + */ + inline const char *KEY_SHUTDOWN_SERVER = "shutdown_server"; + + /* + * Tells the server to restart. + * + * @MESSAGE_TYPE SERVER__CALL + * @ARGS path to config file or "". + */ + inline const char *KEY_START_SIMULATION = "start_simulation"; + + /* + * Server will stop the running simulation. + * + * @MESSAGE_TYPE SERVER__CALL + * @ARGS none. + */ + inline const char *KEY_KILL_SIMULATION = "kill_simulation"; + + /* + * Server will recompile. + * + * @MESSAGE_TYPE SERVER__CALL + * @ARGS as array: + * args[ENV]: environment option for compiler (eg. "CXX=g++-13" or ""). + * args[OPT]: compiler options (eg. "omp=gnu opt=-O2" or "") + */ + inline const char *KEY_RECOMPILE = "recompile"; + + // FUNCTION CALLS: VOID + + /* + * Simulator will write a checkpoint. + * + * @MESSAGE_TYPE FUNCTION_CALL + * @ARGS none. + */ + inline const char *KEY_WRITE_CHECKPOINT = "write_checkpoint"; + + /* + * Simulator will load config from json data. + * + * @MESSAGE_TYPE FUNCTION_CALL + * @ARGS config data in json format. + */ + inline const char *KEY_LOAD_CONFIG_JSON = "load_config_json"; + + /* + * Simulator will load config from .json config file. + * + * @MESSAGE_TYPE FUNCTION_CALL + * @ARGS path to config file. + */ + inline const char *KEY_LOAD_CONFIG_FILE = "load_config_file"; + + /* + * Simulator will toggle file i/o usage. + * + * @MESSAGE_TYPE FUNCTION_CALL + * @ARGS boolean to set file i/o to. + */ + inline const char *KEY_TOGGLE_FILEIO = "FV_TOGGLE_FILEIO"; + + // FUNCTION CALLS: RETURNING + + /* + * Returns the current timestep from the simulator. + * + * @MESSAGE_TYPE FUNCTION_CALL + * @ARGS none. + */ + inline const char *KEY_GET_TIMESTEP = "FR_GET_TIMESTEP"; + + /* + * Returns the max timesteps from the simulator. + * + * @MESSAGE_TYPE FUNCTION_CALL + * @ARGS none. + */ + inline const char *KEY_GET_MAXTIMESTEPS = "FR_GET_MAXTIMESTEPS"; +} + +#endif \ No newline at end of file diff --git a/runServer.sh b/runServer.sh index 2b983063..db91d7d7 100755 --- a/runServer.sh +++ b/runServer.sh @@ -1,5 +1,8 @@ #!/bin/bash +# wait for 2s to make sure the server stopped +sleep 2 + # check if first argument is not empty # the second argument are the compiler options which will be appended in any case if [ -n "$1" ] @@ -11,4 +14,5 @@ scons $2 fi # restart the server +echo STARTING SERVER ./build/tsunami_lab \ No newline at end of file diff --git a/src/Server.cpp b/src/Server.cpp index 5edfccd8..cc161a9a 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -1,13 +1,30 @@ #include "Simulator.h" #include "Communicator.hpp" -#include "constants.h" +#include "communicator_api.h" +#include + #include +#define USEGUI 1 + +using json = nlohmann::json; int PORT = 8080; bool EXIT = false; std::thread simulationThread; bool isSimulationRunning = false; +int execWithOutput(std::string i_cmd, std::string i_outputFile) +{ + std::string commandString = (i_cmd + " > " + i_outputFile + " 2>&1 &").data(); + const char *commandChars = commandString.data(); + return system(commandChars); +} + +int exec(std::string i_cmd) +{ + return system(i_cmd.data()); +} + int main(int i_argc, char *i_argv[]) { int exitCode = 0; @@ -23,75 +40,69 @@ int main(int i_argc, char *i_argv[]) communicator.startServer(PORT); while (!EXIT) { - std::string data = communicator.receiveFromClient(); - // SERVER FUNCTION CALLS - if (data[0] == 'S') + std::string rawData = communicator.receiveFromClient(); + // check if client sent valid json, go back to reading if not + if (!json::accept(rawData)) + { + communicator.sendToClient("FAIL"); + continue; + } + + json parsedData = json::parse(rawData); + json type = parsedData.at(xlpmg::MESSAGE_TYPE); + json key = parsedData.at(xlpmg::KEY); + // json data = parsedData.value(xlpmg::DATA, ""); + + if (type.get() == xlpmg::SERVER__CALL) { - if (strcmp(data.c_str(), tsunami_lab::KEY_SHUTDOWN_SERVER) == 0) + if (key == xlpmg::KEY_SHUTDOWN_SERVER) { EXIT = true; - } - else if (strcmp(data.c_str(), tsunami_lab::KEY_KILL_SIMULATION) == 0) - { + communicator.stopServer(); if (simulationThread.joinable()) { simulationThread.std::thread::~thread(); isSimulationRunning = false; } } - } - // FUNCTION CALLS - else if (data[0] == 'F' && simulator != nullptr) - { - // VOIDS - if (data[1] == 'V') + else if (key == xlpmg::KEY_START_SIMULATION) { - if (strcmp(data.c_str(), tsunami_lab::KEY_WRITE_CHECKPOINT) == 0) - { - simulator->writeCheckpoint(); - } - else if (strcmp(data.c_str(), tsunami_lab::KEY_START_SIMULATION) == 0) - { - std::string config = communicator.receiveFromClient(); - if (!isSimulationRunning) - { - simulationThread = std::thread(&tsunami_lab::Simulator::start, simulator, config); - isSimulationRunning = true; - } - } - else if (strcmp(data.c_str(), tsunami_lab::KEY_LOAD_CONFIG_JSON) == 0) + std::string config = parsedData.at(xlpmg::ARGS); + if (!isSimulationRunning) { - std::string config = communicator.receiveFromClient(); - simulator->loadConfigDataJsonString(config); + simulationThread = std::thread(&tsunami_lab::Simulator::start, simulator, config); + isSimulationRunning = true; } - else if (strcmp(data.c_str(), tsunami_lab::KEY_LOAD_CONFIG_FILE) == 0) - { - std::string configFile = communicator.receiveFromClient(); - simulator->loadConfigDataFromFile(configFile); - } - else if (strcmp(data.c_str(), tsunami_lab::KEY_TOGGLE_FILEIO) == 0) - { - std::string toggle = communicator.receiveFromClient(); - if (strcmp(toggle.c_str(), "true") == 0) - { - simulator->toggleFileIO(true); - } - else - { - simulator->toggleFileIO(false); - } - } - else if (strcmp(data.c_str(), tsunami_lab::KEY_GET_TIMESTEP) == 0) + } + else if (key == xlpmg::KEY_KILL_SIMULATION) + { + if (simulationThread.joinable()) { - std::cout << simulator->getTimeStep() << std::endl; - communicator.sendToClient(std::to_string(simulator->getTimeStep())); + simulationThread.std::thread::~thread(); + isSimulationRunning = false; } } - // CLIENT WILL WAIT FOR RETURN MESSAGE - else if (data[1] == 'R' && simulator != nullptr) + else if (key == xlpmg::KEY_RECOMPILE) { + //Shutdown server + EXIT = true; + communicator.stopServer(); + if (simulationThread.joinable()) + { + simulationThread.std::thread::~thread(); + isSimulationRunning = false; + } + //execute recompilation + std::string env = parsedData.at(xlpmg::ARGS).value("ENV", ""); // environment var + std::string opt = parsedData.at(xlpmg::ARGS).value("OPT", ""); //compiler opt + exec("chmod +x runServer.sh"); + exec("./runServer.sh \""+env+"\" \""+opt+"\" &"); } } + else if (type.get() == xlpmg::FUNCTION_CALL) + { + std::cout << "function call" << std::endl; + } } if (simulationThread.joinable()) diff --git a/src/constants.h b/src/constants.h index 726326f0..e8d747a1 100644 --- a/src/constants.h +++ b/src/constants.h @@ -1,5 +1,6 @@ /** - * @author Alexander Breuer (alex.breuer AT uni-jena.de) + * @author Luca-Philipp Grumbach + * @author Richard Hofmann * * # Description * Constants / typedefs used throughout the code. @@ -17,94 +18,6 @@ namespace tsunami_lab //! floating point type typedef float t_real; - - //------------------------------------------// - //-------------Communicator API-------------// - //------------------------------------------// - - /** - * To ensure faster decoding, the commands need to follow some rules: - * 1. Commands need to be unique. - * 2. Commands regarding the server start with an 'S' - * 3. Commands that call a function within the simulator, start with an 'F' - * 3.1 Void functions start with 'FV': the client will not expect a reply - * 3.2 Non-void functions start with 'FR': the client will expect a reply - * - * */ - - - //! should not not induce any functionality and is only used to check if the other side responds - inline const char* KEY_CHECK = "XCHECKX"; - - // SERVER CALLS - - //! tells the server to shutdown - inline const char* KEY_SHUTDOWN_SERVER = "Sshutdown"; - - //! tells the server to restart - inline const char* KEY_RESTART_SERVER = "Srestart"; - - //! launcher will start its main loop - inline const char* KEY_KILL_SIMULATION = "Skill_simulation"; - - /* - * Server will recompile with the options that are sent immediately after. - * - * Protocol: - * 1. Send the command. - * 2. Send environment compiler option (eg. "CXX=icpc" or ""). - * 3. Send the compiler parameters (eg. "opt=-O2 omp=gnu"). - */ - inline const char* KEY_RECOMPILE = "Srecompile"; - - // FUNCTION CALLS: VOID - - /* - * Simulator will start its main loop. - * - * Protocol: - * 1. Send the command. - * 2. Send the path to a config file (located on the server) or "". - */ - inline const char* KEY_START_SIMULATION = "FV_START"; - - //! simulator will write a checkpoint - inline const char* KEY_WRITE_CHECKPOINT = "FV_WRITE_CHECKPOINT"; - - /* - * Simulator will load config from json data. - * - * Protocol: - * 1. Send the command. - * 2. Send the config data in json format. - */ - inline const char* KEY_LOAD_CONFIG_JSON = "FV_LOAD_CONFIG_JSON"; - - /* - * Simulator will load config from .json config file. - * - * Protocol: - * 1. Send the command. - * 2. Send the path to a config file (located on the server). - */ - inline const char* KEY_LOAD_CONFIG_FILE = "FV_LOAD_CONFIG_FILE"; - - /* - * Simulator will toggle file i/o usage to the boolean which is sent immediately after. - * - * Protocol: - * 1. Send the command. - * 2. Send the path to a config file (located on the server). - */ - inline const char* KEY_TOGGLE_FILEIO = "FV_TOGGLE_FILEIO"; - - // FUNCTION CALLS: RETURNING - - //! gets the current timestep from the simulator - inline const char* KEY_GET_TIMESTEP = "FR_GET_TIMESTEP"; - - //! gets the max timesteps from the simulator - inline const char* KEY_GET_MAXTIMESTEPS = "FR_GET_MAXTIMESTEPS"; } #endif \ No newline at end of file diff --git a/src/ui/GUI.cpp b/src/ui/GUI.cpp index ac845cd9..c26302f0 100644 --- a/src/ui/GUI.cpp +++ b/src/ui/GUI.cpp @@ -27,30 +27,36 @@ // c headers #include #include -#include "../constants.h" +#include "communicator_api.h" #include #include +#include #include "Communicator.hpp" // ui components #include "RTCustWindow.h" -const unsigned int WINDOW_WIDTH = 1500; -const unsigned int WINDOW_HEIGHT = 1000; -const char *LOG_FILE = "log.txt"; - static void glfw_error_callback(int error, const char *description) { fprintf(stderr, "GLFW Error %d: %s\n", error, description); } -int exec(std::string i_cmd, std::string i_outputFile) +int tsunami_lab::ui::GUI::exec(std::string i_cmd, std::string i_outputFile) { std::string commandString = (i_cmd + " > " + i_outputFile + " 2>&1 &").data(); const char *commandChars = commandString.data(); return system(commandChars); } +void tsunami_lab::ui::GUI::updateData() +{ + if (std::chrono::system_clock::now() - lastDataUpdate >= std::chrono::duration(dataUpdateFrequency)) + { + //update + lastDataUpdate = std::chrono::system_clock::now(); + } +} + static void HelpMarker(const char *desc) { ImGui::TextDisabled("(?)"); @@ -180,14 +186,8 @@ int tsunami_lab::ui::GUI::launch(int i_PORT) bool disableConfigs = false; - - ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); - - - - unsigned char *pixels = new unsigned char[100 * 100 * 3]; for (int y = 0; y < 100; ++y) { @@ -198,10 +198,6 @@ int tsunami_lab::ui::GUI::launch(int i_PORT) pixels[y * 100 * 3 + x * 3 + 2] = 0x00; // B } } - - - - // Main loop while (!glfwWindowShouldClose(window)) { @@ -222,6 +218,8 @@ int tsunami_lab::ui::GUI::launch(int i_PORT) ImGui::NewFrame(); + updateData(); + if (show_demo_window) ImGui::ShowDemoWindow(&show_demo_window); @@ -288,7 +286,10 @@ int tsunami_lab::ui::GUI::launch(int i_PORT) if (ImGui::Button("Check")) { - if (m_communicator.sendToServer(tsunami_lab::KEY_CHECK) != 0) + json checkMsg; + checkMsg[xlpmg::MESSAGE_TYPE] = xlpmg::CHECK_CALL; + checkMsg[xlpmg::KEY] = xlpmg::KEY_CHECK; + if (m_communicator.sendToServer(checkMsg.dump()) != 0) { btnConnectDisabled = false; btnDisonnectDisabled = true; @@ -300,36 +301,49 @@ int tsunami_lab::ui::GUI::launch(int i_PORT) if (ImGui::Button("Run")) { - if (m_communicator.sendToServer(tsunami_lab::KEY_START_SIMULATION) == 0) + json runMsg; + runMsg[xlpmg::MESSAGE_TYPE] = xlpmg::SERVER__CALL; + runMsg[xlpmg::KEY] = xlpmg::KEY_START_SIMULATION; + runMsg[xlpmg::ARGS] = "configs/chile5000.json"; + if (m_communicator.sendToServer(runMsg.dump()) == 0) { - //json config = createJson(); - usleep(1000); - //m_communicator.sendToServer(config); - m_communicator.sendToServer("configs/chile5000.json"); - disableConfigs = true; } } ImGui::SameLine(); if (ImGui::Button("Kill")) { - m_communicator.sendToServer(tsunami_lab::KEY_KILL_SIMULATION); + json killMsg; + killMsg[xlpmg::MESSAGE_TYPE] = xlpmg::SERVER__CALL; + killMsg[xlpmg::KEY] = xlpmg::KEY_KILL_SIMULATION; + m_communicator.sendToServer(killMsg.dump()); } - if (ImGui::Button("Shutdown server")) { - m_communicator.sendToServer(tsunami_lab::KEY_SHUTDOWN_SERVER); + json shutdownMsg; + shutdownMsg[xlpmg::MESSAGE_TYPE] = xlpmg::SERVER__CALL; + shutdownMsg[xlpmg::KEY] = xlpmg::KEY_SHUTDOWN_SERVER; + m_communicator.sendToServer(shutdownMsg.dump()); + } + if (ImGui::Button("Recompile")) + { + json recompileMsg; + recompileMsg[xlpmg::MESSAGE_TYPE] = xlpmg::SERVER__CALL; + recompileMsg[xlpmg::KEY] = xlpmg::KEY_RECOMPILE; + recompileMsg[xlpmg::ARGS]["ENV"] = ""; // eg. CXX=g++-13 + recompileMsg[xlpmg::ARGS]["OPT"] = ""; // eg. omp=gnu opt=-O2 + m_communicator.sendToServer(recompileMsg.dump()); } if (ImGui::Button("Get time step")) { - m_communicator.sendToServer(tsunami_lab::KEY_GET_TIMESTEP); + m_communicator.sendToServer(xlpmg::KEY_GET_TIMESTEP); std::cout << m_communicator.receiveFromServer() << std::endl; } if (ImGui::Button("file io true")) { - if (m_communicator.sendToServer(tsunami_lab::KEY_TOGGLE_FILEIO) == 0) + if (m_communicator.sendToServer(xlpmg::KEY_TOGGLE_FILEIO) == 0) { usleep(1000); m_communicator.sendToServer("true"); @@ -338,7 +352,7 @@ int tsunami_lab::ui::GUI::launch(int i_PORT) if (ImGui::Button("file io false")) { - if (m_communicator.sendToServer(tsunami_lab::KEY_TOGGLE_FILEIO) == 0) + if (m_communicator.sendToServer(xlpmg::KEY_TOGGLE_FILEIO) == 0) { usleep(1000); m_communicator.sendToServer("false"); @@ -411,7 +425,7 @@ int tsunami_lab::ui::GUI::launch(int i_PORT) if (ImGui::Button("Recompile")) { - if (m_communicator.sendToServer(tsunami_lab::KEY_WRITE_CHECKPOINT) == 0) + if (m_communicator.sendToServer(xlpmg::KEY_WRITE_CHECKPOINT) == 0) { usleep(1000); } @@ -426,35 +440,36 @@ int tsunami_lab::ui::GUI::launch(int i_PORT) // Simulation parameters if (showSimulationParameterWindow) { - if(disableConfigs == false){ - ImGui::Begin("Simulation parameters"); + if (disableConfigs == false) + { + ImGui::Begin("Simulation parameters"); - ImGui::InputInt("Cells X", &l_nx, 1, 100); - ImGui::InputInt("Cells Y", &l_ny, 1, 100); - l_nx = abs(l_nx); - l_ny = abs(l_ny); + ImGui::InputInt("Cells X", &l_nx, 1, 100); + ImGui::InputInt("Cells Y", &l_ny, 1, 100); + l_nx = abs(l_nx); + l_ny = abs(l_ny); - const char *events[] = {"Tohoku", "Chile", "Custom"}; - ImGui::Combo("Tsunami Event", &event_current, events, IM_ARRAYSIZE(events)); + const char *events[] = {"Tohoku", "Chile", "Custom"}; + ImGui::Combo("Tsunami Event", &event_current, events, IM_ARRAYSIZE(events)); - ImGui::InputFloat("Simulation size X", &l_simulationSizeX); - ImGui::InputFloat("Simulation size Y", &l_simulationSizeY); - l_simulationSizeX = abs(l_simulationSizeX); - l_simulationSizeY = abs(l_simulationSizeY); + ImGui::InputFloat("Simulation size X", &l_simulationSizeX); + ImGui::InputFloat("Simulation size Y", &l_simulationSizeY); + l_simulationSizeX = abs(l_simulationSizeX); + l_simulationSizeY = abs(l_simulationSizeY); - ImGui::InputFloat("Endtime", &endTime); - ImGui::SeparatorText("Sliders"); - { - ImGui::SliderInt("Writingfrequency", &writingFrequency, 10, 1000); - ImGui::SameLine(); - HelpMarker("CTRL+click to input value."); - } - endTime = abs(endTime); - writingFrequency = abs(writingFrequency); + ImGui::InputFloat("Endtime", &endTime); + ImGui::SeparatorText("Sliders"); + { + ImGui::SliderInt("Writingfrequency", &writingFrequency, 10, 1000); + ImGui::SameLine(); + HelpMarker("CTRL+click to input value."); + } + endTime = abs(endTime); + writingFrequency = abs(writingFrequency); - if (ImGui::Button("Close")) - showSimulationParameterWindow = false; - ImGui::End(); + if (ImGui::Button("Close")) + showSimulationParameterWindow = false; + ImGui::End(); } } // Rendering diff --git a/src/ui/GUI.h b/src/ui/GUI.h index ad594cad..d08c0a39 100644 --- a/src/ui/GUI.h +++ b/src/ui/GUI.h @@ -2,7 +2,7 @@ * @author Luca-Philipp Grumbach * @author Richard Hofmann * - * # Description + * # Description * Entry point of the GUI. **/ @@ -10,11 +10,11 @@ #define TSUNAMI_LAB_UI_GUI #include "Communicator.hpp" +#include #include using json = nlohmann::json; - namespace tsunami_lab { namespace ui @@ -26,26 +26,34 @@ namespace tsunami_lab class tsunami_lab::ui::GUI { private: + const unsigned int WINDOW_WIDTH = 1500; + const unsigned int WINDOW_HEIGHT = 1000; + const char *LOG_FILE = "log.txt"; + xlpmg::Communicator m_communicator; std::string m_clientLog; int PORT = 0; char IPADDRESS[16] = "127.0.0.1"; char inputBuffer[256] = {0}; + std::chrono::time_point lastDataUpdate; + float dataUpdateFrequency = 1; + + int exec(std::string i_cmd, std::string i_outputFile); + void updateData(); + public: int launch(int PORT); json createJson(); - - int event_current = 0; - int l_nx = 1; - int l_ny = 1; - float l_simulationSizeX = 0; - float l_simulationSizeY = 0; - float endTime = 1000; - int writingFrequency = 100; // todo: change int to t_idx - int flag_current = 0; - + int event_current = 0; + int l_nx = 1; + int l_ny = 1; + float l_simulationSizeX = 0; + float l_simulationSizeY = 0; + float endTime = 1000; + int writingFrequency = 100; // todo: change int to t_idx + int flag_current = 0; }; #endif \ No newline at end of file