diff --git a/scripts/memory_test.py b/scripts/memory_test.py new file mode 100755 index 000000000..fa27bea3d --- /dev/null +++ b/scripts/memory_test.py @@ -0,0 +1,168 @@ +# see run_memory_test.sh for usage + +import argparse +import requests +import time +from timeit import default_timer as timer +import platform # For getting the operating system type +import subprocess # For executing a shell command +from termcolor import cprint + + +def print_success(x): return cprint(x, 'green') +def print_fail(x): return cprint(x, 'red') + + +def ping_until_up(ip, text): + print(text + "...", flush=True, end="") + time.sleep(1) + param = '-n' if platform.system().lower() == 'windows' else '-c' + command = ["ping", param, "2", ip] + while True: + if (subprocess.run(args=command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0): + print_success("Connected") + time.sleep(1) + return + print(".", flush=True, end="") + time.sleep(1) + + +def run_test(skip, ip, wait, name, count, token): + BASE_URL = "http://" + str(ip) + + INFO_URL = BASE_URL + "/api/system/info" + RESTART_URL = BASE_URL + "/api/system/restart" + SYSTEMSTATUS_URL = BASE_URL + "/rest/systemStatus" + TEST_URL = BASE_URL + "/api?device=system&cmd=test&data=" + name + + GET_HEADERS = {'Content-Type': 'application/json'} + GET_HEADERS_SECURE = {'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + str(token)} + # BODY = json.dumps({ "value": 22.5 }) + + start = timer() + + # Print welcome message + print() + print("Benchmarking EMS-ESP, memory profiling") + print(" Base URL: " + BASE_URL) + print(" Test Name: " + name) + print() + end = timer() + + using_test = False + + if not skip: + + # check if IP exists + ping_until_up(ip, "(" + str(round(end - start, 1)) + + ")\t1. Checking if EMS-ESP is reachable") + end = timer() + + # check if it has been compiled with test + response = requests.get( + SYSTEMSTATUS_URL, headers=GET_HEADERS, verify=False) + build_flags = response.json()['build_flags'] + # see if build_flags contains "TEST" + if "TEST" not in build_flags: + print_fail("Error! EMS-ESP not compiled with -DTEST flag") + exit(1) + + # Restart EMS-ESP + print("(" + str(round(end - start, 1)) + + ")\t2. Doing a cold restart...", end="") + response = requests.get( + RESTART_URL, headers=GET_HEADERS_SECURE, verify=False) + if (response.status_code != 200): + print_fail("Failed") + return + print_success("Success") + end = timer() + + # Wait for EMS-ESP to come back up and reconnect to WiFi + ping_until_up(ip, "(" + str(round(end - start, 1)) + + ")\t3. Waiting for EMS-ESP to come back online") + end = timer() + + print("(" + str(round(end - start, 1)) + + ")\t4. Getting initial memory stats...", flush=True, end="") + time.sleep(1) + response = requests.get(INFO_URL, headers=GET_HEADERS, verify=False) + uptime_a = response.json()['system']['uptimeSec'] + freemem_a = response.json()['system']['freeMem'] + maxalloc_a = response.json()['system']['maxAlloc'] + print_success("Uptime is " + str(uptime_a) + + " secs, Free mem/Max alloc before=" + str(freemem_a) + "/" + str(maxalloc_a)) + end = timer() + + # run test count times + for i in range(count): + + if using_test: + # run test + print("(" + str(round(end - start, 1)) + + ")\t5. Running test (count #" + str(i+1) + " of " + str(count)+") called '" + name + "'...", end="") + response = requests.get( + TEST_URL, headers=GET_HEADERS, verify=False) + else: + # use URL + response = requests.get( + BASE_URL + "/api/boiler/info", headers=GET_HEADERS, verify=False) + print("Response: ", response.json()) + + if (response.status_code != 200): + print_fail("Test Failed!") + return + print_success("Test ran successfully") + end = timer() + + # wait n seconds + print("(" + str(round(end - start, 1)) + ")\t6. Waiting for " + + str(wait) + " seconds...", flush=True, end="") + time.sleep(wait) + print_success("Done") + end = timer() + + # get latest stats + print("(" + str(round(end - start, 1)) + + ")\t7. Getting latest memory stats...", end="") + response = requests.get(INFO_URL, headers=GET_HEADERS, verify=False) + uptime_b = response.json()['system']['uptimeSec'] + freemem_b = response.json()['system']['freeMem'] + maxalloc_b = response.json()['system']['maxAlloc'] + print_success("Uptime is " + str(uptime_b) + + " secs, Free mem/Max alloc after=" + str(freemem_b) + "/" + str(maxalloc_b)) + print() + + if not skip: + # check if it worked and report back + if (uptime_b <= uptime_a): + print_fail("Error! EMS-ESP crashed and restarted :-(") + else: + print("In the " + str(uptime_b - uptime_a) + + " seconds elapsed, we have Free mem/Max alloc: ", end="") + cprint("before=" + str(freemem_a) + "/" + str(maxalloc_a) + + " after=" + str(freemem_b) + "/" + str(maxalloc_b) + + " diff=" + str(freemem_a - freemem_b) + "/" + str(maxalloc_a - maxalloc_b), "cyan", attrs=["bold"]) + + # finish + print() + + +# main +parser = argparse.ArgumentParser( + description="Benchmark EMS-ESP, memory profiler") +parser.add_argument("-s", "--skip", metavar="SKIP", type=bool, + default=False, help="skip all checks and just run the test") +parser.add_argument("-i", "--ip", metavar="IP", type=str, + default="ems-esp.local", help="IP address of EMS-ESP") +parser.add_argument("-w", "--wait", metavar="WAIT", type=int, + default="10", help="time to wait between test") +parser.add_argument("-n", "--name", metavar="NAME", type=str, + default="memory", help="Name of test to run") +parser.add_argument("-c", "--count", metavar="COUNT", type=int, + default="1", help="number of times to run the test") +parser.add_argument("-t", "--token", metavar="TOKEN", type=str, + default="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOnRydWV9.2bHpWya2C7Q12WjNUBD6_7N3RCD7CMl-EGhyQVzFdDg", help="Bearer Token") +args = parser.parse_args() +run_test(**vars(args)) diff --git a/scripts/run_memory_test.py b/scripts/run_memory_test.py deleted file mode 100755 index 6fdafcba5..000000000 --- a/scripts/run_memory_test.py +++ /dev/null @@ -1,144 +0,0 @@ -# pre-reqs: -# 1a) via python3 standalone - (sudo apt install python3-pip) -# 1b) via PlatformIO's penv - (using the "PlatformIO Core CLI" menu option) -# 2) install termcolor (python3 -m pip install --upgrade termcolor) -# Run with (example): -# % python3 ./scripts/run_memory_test.py -i 10.10.10.20 -w 30 -t eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOnRydWV9.2bHpWya2C7Q12WjNUBD6_7N3RCD7CMl-EGhyQVzFdDg -# Note the bearer token is required for the Restart command - -import argparse -import requests -import time -from timeit import default_timer as timer -import platform # For getting the operating system type -import subprocess # For executing a shell command -from termcolor import cprint - - -def print_success(x): return cprint(x, 'green') -def print_fail(x): return cprint(x, 'red') - - -def ping_until_up(ip, text): - print(text + "...", flush=True, end="") - time.sleep(1) - param = '-n' if platform.system().lower() == 'windows' else '-c' - command = ["ping", param, "2", ip] - while True: - if (subprocess.run(args=command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0): - print_success("Connected") - time.sleep(1) - return - print(".", flush=True, end="") - time.sleep(1) - - -def run_test(ip, wait, name, token): - BASE_URL = "http://" + str(ip) - INFO_URL = BASE_URL + "/api/system/info" - RESTART_URL = BASE_URL + "/api/system/restart" - TEST_URL = BASE_URL + "/api?device=system&cmd=test&data=" + name - GET_HEADERS = {'Content-Type': 'application/json'} - GET_HEADERS_SECURE = {'Content-Type': 'application/json', - 'Authorization': 'Bearer ' + str(token)} - # BODY = json.dumps({ "value": 22.5 }) - - start = timer() - - # Print welcome message - print() - print("Benchmarking EMS-ESP, memory profiling") - print(" Base URL: " + BASE_URL) - print(" Test Name: " + name) - print(" 7 steps will run now:") - print() - end = timer() - - # check if IP exists - ping_until_up(ip, "(" + str(round(end - start, 1)) + - ")\t1. Checking if EMS-ESP is reachable") - end = timer() - - # Restart EMS-ESP - print("(" + str(round(end - start, 1)) + - ")\t2. Doing a cold restart...", end="") - response = requests.get( - RESTART_URL, headers=GET_HEADERS_SECURE, verify=False) - if (response.status_code != 200): - print_fail("Failed") - return - print_success("Success") - end = timer() - - # Wait for EMS-ESP to come back up and reconnect to WiFi - ping_until_up(ip, "(" + str(round(end - start, 1)) + - ")\t3. Waiting for EMS-ESP to come back online") - end = timer() - - print("(" + str(round(end - start, 1)) + - ")\t4. Getting initial memory stats...", flush=True, end="") - time.sleep(1) - response = requests.get(INFO_URL, headers=GET_HEADERS, verify=False) - uptime_a = response.json()['System Info']['uptime (seconds)'] - freemem_a = response.json()['System Info']['free mem'] - maxalloc_a = response.json()['System Info']['max alloc'] - print_success("Uptime is " + str(uptime_a) + - " secs, Free mem/Max alloc before=" + str(freemem_a) + "/" + str(maxalloc_a)) - end = timer() - - # run test - print("(" + str(round(end - start, 1)) + - ")\t5. Running test called '" + name + "'...", end="") - response = requests.get(TEST_URL, headers=GET_HEADERS, verify=False) - test_output = response.json()['message'] - if (test_output != 'OK'): - print_fail("Test Failed!") - return - print_success("Test ran successfully") - end = timer() - - # wait n seconds - print("(" + str(round(end - start, 1)) + ")\t6. Waiting for " + - str(wait) + " seconds...", flush=True, end="") - time.sleep(wait) - print_success("Done") - end = timer() - - # get latest stats - print("(" + str(round(end - start, 1)) + - ")\t7. Getting latest memory stats...", end="") - response = requests.get(INFO_URL, headers=GET_HEADERS, verify=False) - uptime_b = response.json()['System Info']['uptime (seconds)'] - freemem_b = response.json()['System Info']['free mem'] - maxalloc_b = response.json()['System Info']['max alloc'] - print_success("Uptime is " + str(uptime_b) + - " secs, Free mem/Max alloc after=" + str(freemem_b) + "/" + str(maxalloc_b)) - print() - - # check if it worked and report back - if (uptime_b <= uptime_a): - print(" Error! EMS-ESP crashed and restarted :-(") - else: - print("In the " + str(uptime_b - uptime_a) + - " seconds elapsed, we have Free mem/Max alloc: ", end="") - cprint("before=" + str(freemem_a) + "/" + str(maxalloc_a) + - " after=" + str(freemem_b) + "/" + str(maxalloc_b) + - " diff=" + str(freemem_a - freemem_b) + "/" + str(maxalloc_a - maxalloc_b), "cyan", attrs=["bold"]) - - # finish - print() - - -# main -parser = argparse.ArgumentParser( - description="Benchmark EMS-ESP, memory profiler") -parser.add_argument("-i", "--ip", metavar="IP", type=str, - default="ems-esp.local", help="IP address of EMS-ESP") -parser.add_argument("-w", "--wait", metavar="WAIT", type=int, - default="10", help="time to wait between test") -parser.add_argument("-n", "--name", metavar="NAME", type=str, - default="memory", help="Name of test to run") -parser.add_argument("-t", "--token", metavar="TOKEN", - type=str, help="Bearer Token") -args = parser.parse_args() -run_test(**vars(args)) diff --git a/scripts/run_memory_test.sh b/scripts/run_memory_test.sh new file mode 100755 index 000000000..0f457f7a4 --- /dev/null +++ b/scripts/run_memory_test.sh @@ -0,0 +1,18 @@ +# pre-reqs: +# 1a) via python3 standalone - (sudo apt install python3-pip) +# 1b) via PlatformIO's penv - (using the "PlatformIO Core CLI" menu option) +# +# Setup with: +# cd scripts +# python3 -m venv venv +# source ./venv/bin/activate +# pip install -r requirements.txt + +# -s skip all checks, just run the test. default False. +# -i ip address of the device. default ems-esp.local. +# -w wait time in seconds between each test. default 10. +# -c count, the number of tests to run. default 1. +# -t bearer token for the Restart command. optional. default is admin/admin's token. +# -h help + +python3 memory_test.py -s True -i 10.10.10.175 -w 5 -c 10 diff --git a/src/test/test.cpp b/src/test/test.cpp index 8c5eae5e9..83f837801 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -50,7 +50,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) { } if (cmd == "general") { - EMSESP::logger().info("Testing general. Adding a Boiler and Thermostat"); + EMSESP::logger().notice("Testing general. Adding a Boiler and Thermostat"); // System::test_set_all_active(true); // uncomment if we want to show all entities and give them fake values @@ -77,6 +77,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) { return true; } + // // the tests take a lot of memory when built for the ESP32 // so only including the full set in standalone, otherwise a limited selection of basic tests @@ -84,7 +85,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) { #ifdef EMSESP_STANDALONE if (cmd == "heat_exchange") { - EMSESP::logger().info("Testing heating exchange..."); + EMSESP::logger().notice("Testing heating exchange..."); add_device(0x08, 219); // Greenstar HIU/Logamax kompakt WS170 @@ -96,7 +97,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) { } if (cmd == "2thermostats") { - EMSESP::logger().info("Testing with multiple thermostats..."); + EMSESP::logger().notice("Testing with multiple thermostats..."); add_device(0x08, 123); // GB072 add_device(0x10, 158); // RC310 @@ -128,7 +129,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) { } if (cmd == "310") { - EMSESP::logger().info("Adding a GB072/RC310 combo..."); + EMSESP::logger().notice("Adding a GB072/RC310 combo..."); add_device(0x08, 123); // GB072 add_device(0x10, 158); // RC310 @@ -155,7 +156,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) { } if (cmd == "gateway") { - EMSESP::logger().info("Adding a Gateway..."); + EMSESP::logger().notice("Adding a Gateway..."); // add 0x48 KM200, via a version command rx_telegram({0x48, 0x0B, 0x02, 0x00, 0xBD, 0x04, 0x06, 00, 00, 00, 00, 00, 00, 00}); @@ -175,7 +176,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) { } if (cmd == "mixer") { - EMSESP::logger().info("Adding a mixer..."); + EMSESP::logger().notice("Adding a mixer..."); // add controller add_device(0x09, 114); @@ -197,7 +198,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) { } if (cmd == "boiler") { - EMSESP::logger().info("Adding boiler..."); + EMSESP::logger().notice("Adding boiler..."); add_device(0x08, 123); // Nefit Trendline // UBAuptime @@ -214,7 +215,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) { } if (cmd == "thermostat") { - EMSESP::logger().info("Adding thermostat..."); + EMSESP::logger().notice("Adding thermostat..."); add_device(0x10, 192); // FW120 @@ -227,7 +228,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) { } if (cmd == "solar") { - EMSESP::logger().info("Adding solar..."); + EMSESP::logger().notice("Adding solar..."); add_device(0x30, 163); // SM100 @@ -246,7 +247,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) { } if (cmd == "heatpump") { - EMSESP::logger().info("Adding heatpump..."); + EMSESP::logger().notice("Adding heatpump..."); add_device(0x38, 200); // Enviline module add_device(0x10, 192); // FW120 thermostat @@ -263,7 +264,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) { return false; } -// These next tests are run from the Consol via the test command, so inherit the Shell +// These next tests are run from the Console via the test command, so inherit the Shell void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const std::string & id1_s, const std::string & id2_s) { bool ok = false; // default tests fail @@ -323,15 +324,14 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const shell.printfln("Testing adding a boiler, thermostat, all sensors, scheduler and custom entities..."); // setup fake data - EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS + // EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS // add devices test("general"); - EMSESP::webCustomEntityService.test(); // add custom entities - EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS - EMSESP::temperaturesensor_.test(); // add temperature sensors - EMSESP::webSchedulerService.test(); // add scheduler items + // EMSESP::webCustomEntityService.test(); // add custom entities + // EMSESP::temperaturesensor_.test(); // add temperature sensors + // EMSESP::webSchedulerService.test(); // add scheduler items // shell.invoke_command("show devices"); // shell.invoke_command("show values"); @@ -551,7 +551,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const } if (command == "620") { - EMSESP::logger().info("Testing 620..."); + EMSESP::logger().notice("Testing 620..."); // Version Controller uart_telegram({0x09, 0x0B, 0x02, 0x00, 0x5F, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); @@ -2238,8 +2238,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const #endif if (!ok) { - shell.printfln("Unknown test command: %s", command.c_str()); - EMSESP::logger().notice("Unknown test command: %s", command.c_str()); + shell.printfln("Unknown test %s", command.c_str()); + EMSESP::logger().notice("Unknown test %s", command.c_str()); } } diff --git a/test/test_api/api_test.http b/test/test_api/api_test.http index e78f68b58..5243da61d 100755 --- a/test/test_api/api_test.http +++ b/test/test_api/api_test.http @@ -82,14 +82,14 @@ GET {{host}}/api/boiler/commands GET {{host_dev}}/api/system/info -# Run a test. EMS-ESP must be compiled with -DEMSESP_TEST -# Use this to load up a dummy thermostat and boiler with data - ### GET {{host_dev}}/api/system/restart Authorization: Bearer {{token}} +# Run a test. EMS-ESP must be compiled with -DEMSESP_TEST +# Use this to load up a dummy thermostat and boiler with data + ### GET {{host_dev}}/api?device=system&cmd=test&data=general @@ -98,6 +98,10 @@ GET {{host_dev}}/api?device=system&cmd=test&data=general GET {{host_dev}}/api/boiler/info +### + +GET {{host_dev}}/api/boiler/info + ### GET {{host_dev}}/api/boiler/values