Simple working of Imgui, Implot and UDP Socket

vishnumuthu Avatar

About the author


MenuBar

 

Simple working of Imgui, Implot and UDP Socket

The Example ImGUI_Implot_UDPSocket github link, Create a simple working for ImGUI and Implot with VisualStudio 22 to show the working of UDP socket.

* The example shows integrating Imgui with Implot with glfw.

* The example shows UDP socket send and receive through winsock2.

* The example shows the real time plot of value.

Graph output:

ImGui (Immediate Mode GUI) is a lightweight, fast, and easy-to-integrate graphical user interface library designed primarily for real-time applications like game engines, simulations, and tools. Unlike traditional GUIs that store the state of widgets, ImGui works in “immediate mode,” where the UI is fully reconstructed every frame based on the current state. This makes it highly efficient and flexible for dynamic UIs, as developers can modify the interface directly in the render loop. ImGui is widely used for debugging interfaces, editor tools, and quick prototyping because of its simple API, minimal setup, and real-time responsiveness.

A UDP socket is a communication endpoint that uses the User Datagram Protocol (UDP) for transmitting data over a network. Unlike TCP, UDP is a connectionless protocol, meaning it sends messages, called datagrams, without establishing a reliable connection between sender and receiver. This makes UDP lightweight and faster but less reliable, as it doesn’t guarantee delivery, ordering, or error-checking of packets. UDP sockets are commonly used in applications where speed is critical and occasional data loss is acceptable, such as in real-time video streaming, online gaming, or VoIP (Voice over IP).

 

Sending Data Section

Send Network part:

// Network.h header file for C++ UDP

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <system_error>
#include <string>
#include <iostream>
#include <winsock2.h>

#pragma once
#pragma comment (lib, "ws2_32")

class WSASession
{
public:
    WSASession()
    {
        int ret = WSAStartup(MAKEWORD(2, 2), &data);
        if (ret != 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "WSAStartup Failed");
    }
    ~WSASession()
    {
        WSACleanup();
    }

private:
    WSAData data;
};

class UDPSocket
{
public:
    UDPSocket()
    {
        sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        if (sock == INVALID_SOCKET)
            throw std::system_error(WSAGetLastError(), std::system_category(), "Error opening socket");
    }
    ~UDPSocket()
    {
        closesocket(sock);
    }

    void SendTo(const std::string& address, unsigned short port, const char* buffer, int len, int flags = 0)
    {
        sockaddr_in add;
        add.sin_family = AF_INET;
        add.sin_addr.s_addr = inet_addr(address.c_str());
        //add.sin_addr.s_addr = inet_pton(AF_INET, "127.0.0.1", address.c_str());
        add.sin_port = htons(port);
        int ret = sendto(sock, buffer, len, flags, reinterpret_cast<SOCKADDR*>(&add), sizeof(add));
        if (ret < 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "sendto failed");
    }
    void SendTo(sockaddr_in& address, const char* buffer, int len, int flags = 0)
    {
        int ret = sendto(sock, buffer, len, flags, reinterpret_cast<SOCKADDR*>(&address), sizeof(address));
        if (ret < 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "sendto failed");
    }
    sockaddr_in RecvFrom(char* buffer, int len, int flags = 0)
    {
        sockaddr_in from;
        int size = sizeof(from);
        int ret = recvfrom(sock, buffer, len, flags, reinterpret_cast<SOCKADDR*>(&from), &size);
        if (ret < 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "recvfrom failed");

        // make the buffer zero terminated
        buffer[ret] = 0;
        return from;
    }
    void Bind(unsigned short port)
    {
        sockaddr_in add;
        add.sin_family = AF_INET;
        add.sin_addr.s_addr = htonl(INADDR_ANY);
        add.sin_port = htons(port);

        int ret = bind(sock, reinterpret_cast<SOCKADDR*>(&add), sizeof(add));
        if (ret < 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "Bind failed");
    }

private:
    SOCKET sock;
};

Sending Main part:

// C++ UDP Transmitter

#include "Network.h"

#include <cstdlib>
#include <ctime>
#include <iostream>

#pragma once

int main()
{
    std::string IP = "127.0.0.1";
    int PORT = 8888;

    try
    {
        WSASession Session;
        UDPSocket Socket;
        std::string data = "hello world";
        char buffer[100];

        // create a clock and start timer
        clock_t TimeZero = clock(); //Start timer
        std::cout << "Start time: " << TimeZero << std::endl;

        double deltaTime = 0;
        double hz_value = 10;
        double reset_time = 1000 / hz_value;

        // generate random seed using time 
        srand(time(0));

        while (1)
        {

            // get delta time in milliseconds
            deltaTime = (clock() - TimeZero);

            // compare if delta time is 2 or more seconds
            if (deltaTime > reset_time) {

                struct tm newtime;
                time_t now = time(0);
                localtime_s(&newtime, &now);
                //std::cout << "Current local time : " << newtime.tm_hour << ":" << newtime.tm_min << ":" << newtime.tm_sec <<std::endl;

                // generate new random number
                int i = rand() % 100 + 1;
                //std::cout  << ": New random : " << i << "\n";
                data = std::to_string(newtime.tm_hour) +":" + std::to_string(newtime.tm_min) + ":" + std::to_string(newtime.tm_sec) + " " + std::to_string(i);
                std::cout << data << std::endl;
                Socket.SendTo(IP, PORT, data.c_str(), data.size());

                //reset the clock timers
                TimeZero = clock();
            }
        }
    }
    catch (std::exception& ex)
    {
        std::cout << ex.what();
    }
}

Explanation:

Here the UDP network socket is created using winsock2. Then a random value is generated with a time stamp of the system and sent through the created socket.

 

Receive Data Section:

Receive Network part:

// Network.h
#include <iostream>
#include <winsock2.h>
//#include <ws2tcpip.h>

#pragma comment(lib, "ws2_32.lib")

class UDPHandler {
private:
    SOCKET sockfd;
    struct sockaddr_in serverAddr, clientAddr;
    int addrLen;
    char buffer[1024];

public:
    UDPHandler(int port) {
        WSADATA wsaData;
        if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
            std::cerr << "WSAStartup failed" << std::endl;
            exit(EXIT_FAILURE);
        }

        sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (sockfd == INVALID_SOCKET) {
            std::cerr << "Socket creation failed" << std::endl;
            WSACleanup();
            exit(EXIT_FAILURE);
        }

        // Set socket options using setsockopt()
        int timeout = 10; // 5 seconds
        if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)) == SOCKET_ERROR) {
            std::cerr << "setsockopt failed" << std::endl;
            closesocket(sockfd);
            WSACleanup();
            exit(EXIT_FAILURE);
        }

        memset(&serverAddr, 0, sizeof(serverAddr));
        memset(&clientAddr, 0, sizeof(clientAddr));

        serverAddr.sin_family = AF_INET;
        serverAddr.sin_addr.s_addr = INADDR_ANY;
        serverAddr.sin_port = htons(port);

        if (bind(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
            std::cerr << "Bind failed" << std::endl;
            closesocket(sockfd);
            WSACleanup();
            exit(EXIT_FAILURE);
        }

        addrLen = sizeof(clientAddr);
    }

    std::string receiveData() {
        int n = recvfrom(sockfd, buffer, 1024, 0, (struct sockaddr *)&clientAddr, &addrLen);
        if (n == SOCKET_ERROR) {
            std::cerr << "Receive failed" << std::endl;
            return "NILL";
        }
        buffer[n] = '\0';
        return std::string(buffer);
    }

    void sendData(const std::string &data) {
        sendto(sockfd, data.c_str(), data.length(), 0, (struct sockaddr *)&clientAddr, addrLen);
    }

    ~UDPHandler() {
        closesocket(sockfd);
        WSACleanup();
    }
};

Receive Main part:

//main.cpp
std::vector<std::string> split(std::string s, std::string delimiter) {
    size_t pos_start = 0, pos_end, delim_len = delimiter.length();
    std::string token;
    std::vector<std::string> res;

    while ((pos_end = s.find(delimiter, pos_start)) != std::string::npos) {
        token = s.substr(pos_start, pos_end - pos_start);
        pos_start = pos_end + delim_len;
        res.push_back(token);
    }

    res.push_back(s.substr(pos_start));
    return res;
}

    UDPHandler udpHandler(8888);  // Initialize UDPHandler with the desired port

    // assign graph
    static float xs1[10000], ys1[10000];
    int gcount1 = 0;

    // Main loop
#ifdef __EMSCRIPTEN__
    // For an Emscripten build we are disabling file-system access, so let's not attempt to do a fopen() of the imgui.ini file.
    // You may manually call LoadIniSettingsFromMemory() to load settings from your own storage.
    io.IniFilename = nullptr;
    EMSCRIPTEN_MAINLOOP_BEGIN
#else
    while (!glfwWindowShouldClose(window))
#endif
    {
        glfwPollEvents();
        if (glfwGetWindowAttrib(window, GLFW_ICONIFIED) != 0)
        {
            ImGui_ImplGlfw_Sleep(10);
            continue;
        }

        // Start the Dear ImGui frame
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();

        {
            static float f = 0.0f;
            //static int counter = 0;

            ImGui::Begin("Main Window");                          // Create a window called "Hello, world!" and append into it.

            ImGui::Text("List of Graphs");               // Display some text (you can use a format strings too)
            ImGui::Checkbox("show graph", &show_graph);      // Edit bools storing our window open/close state

            ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
            ImGui::End();
        }

        
        if(show_graph)
        { 
            ImGui::Begin("graph");

            // Receive UDP data
            std::string receivedData = udpHandler.receiveData();

            // Process the data (for simplicity, we'll just print it here)
            std::cout << "Received: " << receivedData << std::endl;
            std::string delimiter = " ";
            std::vector<std::string> v = split(receivedData, delimiter);

            for (auto i : v) std::cout << i << std::endl;


            if (v[0] != "NILL")
            {
                xs1[gcount1] = gcount1;
                ys1[gcount1] = std::stof(v[1]);
                gcount1++;

            }
            

            if (ImPlot::BeginPlot("Line Plots")) {
                ImPlot::SetupAxes("x", "y");
                ImPlot::PlotLine("f(x)", xs1, ys1, gcount1);
                ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle);
                //ImPlot::PlotLine("g(x)", xs2, ys2, 20, ImPlotLineFlags_Segments);
                ImPlot::EndPlot();
            }

            ImGui::End();
        }
  

        // Rendering
        ImGui::Render();
        int display_w, display_h;
        glfwGetFramebufferSize(window, &display_w, &display_h);
        glViewport(0, 0, display_w, display_h);
        glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
        glClear(GL_COLOR_BUFFER_BIT);
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

        glfwSwapBuffers(window);
    }
#ifdef __EMSCRIPTEN__
    EMSCRIPTEN_MAINLOOP_END;
#endif

    // Cleanup
    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImPlot::DestroyContext();
    ImGui::DestroyContext();

    glfwDestroyWindow(window);
    glfwTerminate();

    return 0;
}

Explanation:

Here the UDP network socket for receiving is created using winsock2. Then the data is extracted and send to the implot data. Once the data is received the graph is updated accordingly.


One response to “Simple working of Imgui, Implot and UDP Socket”

  1. […] 3) Simple working of Imgui, Implot and UDP Socket […]

Leave a Reply

Your email address will not be published. Required fields are marked *