From 6477b4cce12c1c1c6b7b0830781111ff43258c7c Mon Sep 17 00:00:00 2001 From: Nadav Mordechai <46380330+nadav26740@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:29:51 +0300 Subject: [PATCH 01/15] Added GitActions To run CICD The reason is because I want to the test the CICD --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 5aa510a..9cd522e 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -5,7 +5,7 @@ name: Build backend Windows NET on: push: - branches: [ "master" ] + branches: [ "master", "GitActions" ] pull_request: branches: [ "master" ] From 5230a610d12988b676858703f141b226afb74f8c Mon Sep 17 00:00:00 2001 From: Nadav Mordechai <46380330+nadav26740@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:31:32 +0300 Subject: [PATCH 02/15] Added Ubuntu test build --- .github/workflows/dotnet.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 9cd522e..56af047 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -11,8 +11,7 @@ on: jobs: build: - - runs-on: windows-latest + runs-on: [ windows-latest, ubuntu-latest ] defaults: run: From 7e3a65141f476e0a1f711dbe193fa098b62108e2 Mon Sep 17 00:00:00 2001 From: Nadav Mordechai <46380330+nadav26740@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:33:54 +0300 Subject: [PATCH 03/15] Ubuntu Build CICD separated from windows build --- .github/workflows/dotnet.yml | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 56af047..f21a30c 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -10,8 +10,8 @@ on: branches: [ "master" ] jobs: - build: - runs-on: [ windows-latest, ubuntu-latest ] + build-windows: + runs-on: windows-latest defaults: run: @@ -29,3 +29,24 @@ jobs: run: dotnet build --no-restore # - name: Test # run: dotnet test --no-build --verbosity normal + + build-ubuntu: + runs-on: ubuntu-latest + + defaults: + run: + working-directory: ./backend/HomeServer-Backend-win + + steps: + - uses: actions/checkout@v4 + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + # - name: Test + # run: dotnet test --no-build --verbosity normal + From ef99ec11f60ec49a9cca10c60db0526285d73c69 Mon Sep 17 00:00:00 2001 From: Nadav Mordechai <46380330+nadav26740@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:37:06 +0300 Subject: [PATCH 04/15] Added Action build badge to readme --- Readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Readme.md b/Readme.md index d23a82b..d0a1a4b 100644 --- a/Readme.md +++ b/Readme.md @@ -4,6 +4,8 @@ **A powerful, extensible backend for Windows process management,
automation, and monitoring — accessible via a modern TCP API.** +[![Build backend Windows NET badge](https://github.com/nadav26740/HomeServer/actions/workflows/dotnet.yml/badge.svg)](https://github.com/nadav26740/HomeServer/actions/workflows/dotnet.yml) + --- From a8391d027c76efa77a9209d4206682629e13ed6a Mon Sep 17 00:00:00 2001 From: Nadav Mordechai Date: Tue, 19 Aug 2025 18:21:25 +0300 Subject: [PATCH 05/15] tests moved to tests folder --- backend/{API-Gateway/Tester.py => Tests/API_Test.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename backend/{API-Gateway/Tester.py => Tests/API_Test.py} (100%) diff --git a/backend/API-Gateway/Tester.py b/backend/Tests/API_Test.py similarity index 100% rename from backend/API-Gateway/Tester.py rename to backend/Tests/API_Test.py From f009d7b1ab5d5c3c5ece72d4f3357eac31a06c0d Mon Sep 17 00:00:00 2001 From: Nadav Mordechai Date: Wed, 20 Aug 2025 19:56:13 +0300 Subject: [PATCH 06/15] Automated test run added* --- .github/workflows/dotnet.yml | 17 ++++++++++++++++- backend/HomeServer-Backend-win/Program.cs | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index f21a30c..94ddc86 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -12,7 +12,7 @@ on: jobs: build-windows: runs-on: windows-latest - + defaults: run: working-directory: ./backend/HomeServer-Backend-win @@ -32,6 +32,9 @@ jobs: build-ubuntu: runs-on: ubuntu-latest + strategy: + matrix: + python_ver: ["3.12"] defaults: run: @@ -47,6 +50,18 @@ jobs: run: dotnet restore - name: Build run: dotnet build --no-restore + + - name: PreTests Set up Python 3.13 # Example name for the step + uses: actions/setup-python@v5 # Uses the setup-python action + with: + python-version: ${{ matrix.python_ver }} # Specifies the desired Python version + + - name: Run And Test + run: | + dotnet run --no-build --verbosity normal + API_PID = $! + python ../Tests/API_Test.py + kill $API_PID # - name: Test # run: dotnet test --no-build --verbosity normal diff --git a/backend/HomeServer-Backend-win/Program.cs b/backend/HomeServer-Backend-win/Program.cs index a11d8fc..fdb199a 100644 --- a/backend/HomeServer-Backend-win/Program.cs +++ b/backend/HomeServer-Backend-win/Program.cs @@ -120,7 +120,7 @@ private static async Task Main(string[] args) { Console.WriteLine(GetSplashMessage()); Console.WriteLine("Press any key to start test"); - Console.ReadKey(); + //Console.ReadKey(); Console.WriteLine("Home Server test Starting..."); await using ServerCore Core = new ServerCore(); From b07a24a3d56db72abcd2f820afba523f970748f5 Mon Sep 17 00:00:00 2001 From: Nadav Mordechai Date: Wed, 20 Aug 2025 20:00:15 +0300 Subject: [PATCH 07/15] Fixed bug where crashing if Processes.json not found --- backend/HomeServer-Backend-win/Core/ServerCore.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/backend/HomeServer-Backend-win/Core/ServerCore.cs b/backend/HomeServer-Backend-win/Core/ServerCore.cs index 0de5a53..933160e 100644 --- a/backend/HomeServer-Backend-win/Core/ServerCore.cs +++ b/backend/HomeServer-Backend-win/Core/ServerCore.cs @@ -77,10 +77,20 @@ public void LoadData() // TODO: // Load Config.. - ProcessSlaveArgs[]? procSlaves = ProcessConfigSave.ReadSlaveProcessData("processes.json"); + ProcessSlaveArgs[]? procSlaves = null; + try + { + procSlaves = ProcessConfigSave.ReadSlaveProcessData("processes.json"); + } + catch (Exception ex) + { + Logger.LogError($"Failed to read process configuration: {ex.Message}"); + } + if (procSlaves == null || procSlaves.Length == 0) { Logger.LogWarn("No process slaves found in the config file. Please check your configuration."); + return; } else { From 921ccc8d5f29c5da394d229a0f174e587fc173af Mon Sep 17 00:00:00 2001 From: Nadav Mordechai Date: Wed, 20 Aug 2025 20:08:43 +0300 Subject: [PATCH 08/15] Server core start seperated to async and sync to allow headless run in sync mod without wait for console --- .../Communication/SimpleTcpServer.cs | 3 +++ .../HomeServer-Backend-win/Core/ServerCore.cs | 16 +++++++++++++++- backend/HomeServer-Backend-win/Program.cs | 5 ----- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/backend/HomeServer-Backend-win/Communication/SimpleTcpServer.cs b/backend/HomeServer-Backend-win/Communication/SimpleTcpServer.cs index 4603ed9..76bed41 100644 --- a/backend/HomeServer-Backend-win/Communication/SimpleTcpServer.cs +++ b/backend/HomeServer-Backend-win/Communication/SimpleTcpServer.cs @@ -81,6 +81,8 @@ public async Task StartAsync() public void Start() { + Running = true; + _listener.Start(); Logger.LogInfo($"Server started. Listening on {_ipAddress}:{_port}"); @@ -98,6 +100,7 @@ public void Start() } finally { + Logger.LogWarn("Server Listener Stopped"); _listener.Stop(); } } diff --git a/backend/HomeServer-Backend-win/Core/ServerCore.cs b/backend/HomeServer-Backend-win/Core/ServerCore.cs index 933160e..2063a5a 100644 --- a/backend/HomeServer-Backend-win/Core/ServerCore.cs +++ b/backend/HomeServer-Backend-win/Core/ServerCore.cs @@ -105,14 +105,28 @@ public void LoadData() // Load all data from the config file } + /// + /// Running server synced for headless + /// public void Start() { Logger.LogInfo("Server Core Start has been called"); - server_task = m_TcpServer.StartAsync(); + m_Manager.ForceStart(); Discovery_task = m_DiscoveryListener.StartAsyncListening(); + m_TcpServer.Start(); + } + + /// + /// Running Server core async for console + /// + public void AsyncStart() + { + Logger.LogInfo("Server Core Start has been called"); m_Manager.ForceStart(); + Discovery_task = m_DiscoveryListener.StartAsyncListening(); + server_task = m_TcpServer.StartAsync(); } public void Shutdown() diff --git a/backend/HomeServer-Backend-win/Program.cs b/backend/HomeServer-Backend-win/Program.cs index fdb199a..3cb8060 100644 --- a/backend/HomeServer-Backend-win/Program.cs +++ b/backend/HomeServer-Backend-win/Program.cs @@ -130,17 +130,12 @@ private static async Task Main(string[] args) Logger.LogInfo("Starting server..."); Core.Start(); - Logger.LogInfo("Server started successfully!"); - - Console.WriteLine("========================== Press any key to stop =========================="); - Console.ReadKey(); Logger.LogInfo("Stopping server..."); // Core.Shutdown(); Logger.LogInfo("Server stopped successfully!"); Console.WriteLine("Home Server test completed. Press any key to exit."); - Console.ReadKey(); } } } From 003dd832cbf3b9455dbae281b702b59ed6afe905 Mon Sep 17 00:00:00 2001 From: Nadav Mordechai Date: Wed, 20 Aug 2025 20:20:58 +0300 Subject: [PATCH 09/15] Trying to run action server in background --- .github/workflows/dotnet.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 94ddc86..6363f5b 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -1,7 +1,7 @@ # This workflow will build a .NET project # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net -name: Build backend Windows NET +name: Build backend NET on: push: @@ -58,7 +58,7 @@ jobs: - name: Run And Test run: | - dotnet run --no-build --verbosity normal + dotnet run --no-build --verbosity normal & API_PID = $! python ../Tests/API_Test.py kill $API_PID From 91b87125f2261985a999ef82d6f1f76e3bbbc0e4 Mon Sep 17 00:00:00 2001 From: Nadav Mordechai Date: Wed, 20 Aug 2025 20:22:33 +0300 Subject: [PATCH 10/15] delay time added between actions --- .github/workflows/dotnet.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 6363f5b..98a1dba 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -59,8 +59,13 @@ jobs: - name: Run And Test run: | dotnet run --no-build --verbosity normal & + sleep 5 # Wating for the API to start + + # Run the Python test script API_PID = $! python ../Tests/API_Test.py + + sleep 10 # Allow some time for the API to finish processing kill $API_PID # - name: Test # run: dotnet test --no-build --verbosity normal From cfc80de66ba42069c086792b903376ab859d7ba8 Mon Sep 17 00:00:00 2001 From: Nadav Mordechai Date: Wed, 20 Aug 2025 20:26:18 +0300 Subject: [PATCH 11/15] Messages and code fixing --- .github/workflows/dotnet.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 98a1dba..4a9014f 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -58,14 +58,17 @@ jobs: - name: Run And Test run: | + echo "Starting the API..." dotnet run --no-build --verbosity normal & + API_PID = $! sleep 5 # Wating for the API to start # Run the Python test script - API_PID = $! + echo "Running API tests..." python ../Tests/API_Test.py sleep 10 # Allow some time for the API to finish processing + echo "Killing the API..." kill $API_PID # - name: Test # run: dotnet test --no-build --verbosity normal From 1e2615e9c9c832617c86fafdd136d7f1d97f7ae6 Mon Sep 17 00:00:00 2001 From: Nadav Mordechai Date: Wed, 20 Aug 2025 20:36:13 +0300 Subject: [PATCH 12/15] Action rearanged for better understanding of what happening --- .github/workflows/dotnet.yml | 64 ++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 4a9014f..9be4150 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -32,44 +32,52 @@ jobs: build-ubuntu: runs-on: ubuntu-latest - strategy: - matrix: - python_ver: ["3.12"] - - defaults: - run: - working-directory: ./backend/HomeServer-Backend-win + # strategy: + # matrix: + # python_ver: ["3.12"] steps: + - uses: actions/checkout@v4 + + # Setup .NET - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: 8.0.x + - name: Restore dependencies run: dotnet restore - - name: Build - run: dotnet build --no-restore + + # Build project + - name: Build backend + working-directory: ./backend/HomeServer-Backend-win + run: dotnet build --configuration Release - - name: PreTests Set up Python 3.13 # Example name for the step - uses: actions/setup-python@v5 # Uses the setup-python action + # Run backend in background + - name: Start backend + working-directory: ./backend/HomeServer-Backend-win + run: | + dotnet run --configuration Release & + echo $! > backend_pid.txt + + # Wait before running tests + - name: Wait for server to start + run: sleep 5 + + # Setup Python + - name: Setup Python + uses: actions/setup-python@v5 with: - python-version: ${{ matrix.python_ver }} # Specifies the desired Python version + python-version: '3.x' + + # Run Python test script + - name: Run API tests + working-directory: ./backend/Tests + run: python3 API_Test.py - - name: Run And Test + # Stop backend process after tests + - name: Stop backend + if: always() run: | - echo "Starting the API..." - dotnet run --no-build --verbosity normal & - API_PID = $! - sleep 5 # Wating for the API to start - - # Run the Python test script - echo "Running API tests..." - python ../Tests/API_Test.py - - sleep 10 # Allow some time for the API to finish processing - echo "Killing the API..." - kill $API_PID - # - name: Test - # run: dotnet test --no-build --verbosity normal - + kill $(cat backend/HomeServer-Backend-win/backend_pid.txt) || true From 9377d4d0b91a7cf236ca5b0d79b42f94e667edf1 Mon Sep 17 00:00:00 2001 From: Nadav Mordechai Date: Wed, 20 Aug 2025 20:38:12 +0300 Subject: [PATCH 13/15] Fixed not restoring libs --- .github/workflows/dotnet.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 9be4150..d4351f8 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -45,14 +45,13 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: 8.0.x - - - name: Restore dependencies - run: dotnet restore - + # Build project - name: Build backend working-directory: ./backend/HomeServer-Backend-win - run: dotnet build --configuration Release + run: | + dotnet restore + dotnet build --configuration Release # Run backend in background - name: Start backend From cc6a5d1dd85a84ae8875b090d147a2f4eea02e0c Mon Sep 17 00:00:00 2001 From: Nadav Mordechai Date: Wed, 20 Aug 2025 20:45:16 +0300 Subject: [PATCH 14/15] Tests seperated to diffrenet files --- .github/workflows/dotnet.yml | 5 +++ backend/Tests/API_Test.py | 48 --------------------- backend/Tests/Discovery_Test.py | 74 +++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 48 deletions(-) create mode 100644 backend/Tests/Discovery_Test.py diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index d4351f8..2f37b95 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -70,6 +70,11 @@ jobs: with: python-version: '3.x' + # Run Python Discovery Test + - name: Run Discovery tests + working-directory: ./backend/Tests + run: python3 Discovery_Test.py + # Run Python test script - name: Run API tests working-directory: ./backend/Tests diff --git a/backend/Tests/API_Test.py b/backend/Tests/API_Test.py index 9e8e2a7..b10abf9 100644 --- a/backend/Tests/API_Test.py +++ b/backend/Tests/API_Test.py @@ -1,6 +1,5 @@ import socket import time -import json IP = "127.0.0.1" PORT = 3391 @@ -27,12 +26,6 @@ def main(): global totalRunTime print("Searching for server") - if discover_server(): - print(f"{bcolors.OKGREEN} Server found {IP}:{str(PORT)} {bcolors.ENDC}") - - else: - print(f"{bcolors.FAIL} Failed to find server! {bcolors.ENDC}") - return -1 print("=====================Starting API Tests=======================") TestAPI('{"Path":"/api/process/start", "Type":"UPDATE", "Data":"Minecraft Server"}') @@ -74,47 +67,6 @@ def TestAPI(message): print("\n\n") -def discover_server(timeout=5): - global IP - global PORT - global totalRunTime - - # Create a UDP socket - Succeeded = False - client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - client_socket.settimeout(timeout) # timeout in seconds - - # Broadcast address - broadcast_address = ('192.168.50.255', DISCOVERY_PORT) - - try: - # Send discovery message - client_socket.sendto(DISCOVERY_MESSAGE.encode('utf-8'), broadcast_address) - print(f"Broadcasted discovery request to {broadcast_address}") - - # Wait for response from server - data, server_addr = client_socket.recvfrom(BUFFER_SIZE) - - StartTime = time.perf_counter() - response = data.decode('utf-8') - elapsed_time = time.perf_counter() - StartTime - totalRunTime += elapsed_time - - print(f"Received response: '{response}' from {server_addr[0]}:{server_addr[1]}") - print(f"{bcolors.OKCYAN}{bcolors.BOLD} Answer time: {(elapsed_time * 1000):.0f} ms{bcolors.ENDC}") - - PORT = json.loads(response)["Port"] - IP = str(server_addr[0]) - - Succeeded = True - - except socket.timeout: - print("No server response received within timeout.") - finally: - client_socket.close() - return Succeeded - if __name__ == "__main__": main() \ No newline at end of file diff --git a/backend/Tests/Discovery_Test.py b/backend/Tests/Discovery_Test.py new file mode 100644 index 0000000..496c681 --- /dev/null +++ b/backend/Tests/Discovery_Test.py @@ -0,0 +1,74 @@ +import socket +import time + +IP = "127.0.0.1" +PORT = 3391 + +BROADCAST_ADDRESS = ('255.255.255.255', 50130) +BUFFER_SIZE = 1024 +DISCOVERY_MESSAGE = "DISCOVER_LOCAL_HOMESERVER" + +class bcolors: + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKCYAN = '\033[96m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + + +def main(): + if discover_server(): + print(f"{bcolors.OKGREEN} Server found {IP} {bcolors.ENDC}") + + else: + print(f"{bcolors.FAIL} Failed to find server! {bcolors.ENDC}") + exit(1) + + +def discover_server(timeout=5): + global IP + global PORT + global totalRunTime + + # Create a UDP socket + Succeeded = False + client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + client_socket.settimeout(timeout) # timeout in seconds + + # Broadcast address + broadcast_address = BROADCAST_ADDRESS + + try: + # Send discovery message + client_socket.sendto(DISCOVERY_MESSAGE.encode('utf-8'), broadcast_address) + print(f"Broadcasted discovery request to {broadcast_address}") + + # Wait for response from server + data, server_addr = client_socket.recvfrom(BUFFER_SIZE) + + StartTime = time.perf_counter() + response = data.decode('utf-8') + elapsed_time = time.perf_counter() - StartTime + totalRunTime += elapsed_time + + print(f"Received response: '{response}' from {server_addr[0]}:{server_addr[1]}") + print(f"{bcolors.OKCYAN}{bcolors.BOLD} Answer time: {(elapsed_time * 1000):.0f} ms{bcolors.ENDC}") + + IP = str(server_addr[0]) + + Succeeded = True + + except socket.timeout: + print("No server response received within timeout.") + finally: + client_socket.close() + return Succeeded + + +if __name__ == "__main__": + main() \ No newline at end of file From 6fc6652645040f0eaf4445df64550e7a7e8bf5c8 Mon Sep 17 00:00:00 2001 From: Nadav Mordechai Date: Wed, 20 Aug 2025 20:47:25 +0300 Subject: [PATCH 15/15] Fixed Discovery trying to add to elapsed var --- backend/Tests/Discovery_Test.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/Tests/Discovery_Test.py b/backend/Tests/Discovery_Test.py index 496c681..652befd 100644 --- a/backend/Tests/Discovery_Test.py +++ b/backend/Tests/Discovery_Test.py @@ -41,7 +41,7 @@ def discover_server(timeout=5): client_socket.settimeout(timeout) # timeout in seconds # Broadcast address - broadcast_address = BROADCAST_ADDRESS + broadcast_address = BROADCAST_ADDRESS try: # Send discovery message @@ -54,7 +54,6 @@ def discover_server(timeout=5): StartTime = time.perf_counter() response = data.decode('utf-8') elapsed_time = time.perf_counter() - StartTime - totalRunTime += elapsed_time print(f"Received response: '{response}' from {server_addr[0]}:{server_addr[1]}") print(f"{bcolors.OKCYAN}{bcolors.BOLD} Answer time: {(elapsed_time * 1000):.0f} ms{bcolors.ENDC}")