From 427aab95ea884ce33e11180dfbfcbd3fd722ec46 Mon Sep 17 00:00:00 2001 From: xad1 Date: Fri, 17 Mar 2023 21:43:32 +0000 Subject: [PATCH] structs, better error checking, number conversion --- .vscode/settings.json | 52 ++++++++++++ include/global.h | 4 +- include/json/retrieve.h | 39 ++++++++- include/json/utils.h | 8 +- include/menu.h | 6 +- src/json/retrieve.cpp | 96 +++++++++++++++------- src/json/utils.cpp | 55 +++++++++---- src/main.cpp | 28 +++++-- src/menu.cpp | 75 +++++++++++++---- src/statistics.cpp | 174 +++++++++++++++++++--------------------- 10 files changed, 370 insertions(+), 167 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..318838f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,52 @@ +{ + "files.associations": { + "stdexcept": "cpp", + "array": "cpp", + "atomic": "cpp", + "*.tcc": "cpp", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "map": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "string": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "ostream": "cpp", + "sstream": "cpp", + "streambuf": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp" + } +} \ No newline at end of file diff --git a/include/global.h b/include/global.h index 12ebf70..64cf9ae 100644 --- a/include/global.h +++ b/include/global.h @@ -4,6 +4,8 @@ extern String selectedNode; extern int selectedItem; - +extern int selectedLXC; +extern int selectedVM; +// TODO remove later #endif \ No newline at end of file diff --git a/include/json/retrieve.h b/include/json/retrieve.h index 386f325..db4ad3e 100644 --- a/include/json/retrieve.h +++ b/include/json/retrieve.h @@ -2,8 +2,41 @@ #define JSON_RETRIEVE_H #include -JsonArray getNodeInfo(); -JsonArray getContainerInfo(String node); -JsonArray getVMInfo(String node); +typedef struct { + String name; + int cpu; + int threads; + String onlineStatus; + long long mem; + long long maxmem; + long long disk; + long long maxdisk; + long long uptime; +} Node; + +typedef struct { + String name; + int id; + String onlineStatus; + long long maxmem; + long long maxdisk; + long long uptime; +} Container; + +typedef struct { + String name; + int id; + String onlineStatus; + long long maxmem; + long long maxdisk; + long long uptime; + long long netin; + long long netout; +} VM; + +Node *getNodeInfo(int *numNodes); +Container *getContainerInfo(int *numContainers, String node); +VM *getVMInfo(int *numVMs, String node); + #endif /* JSON_RETRIEVE_H */ diff --git a/include/json/utils.h b/include/json/utils.h index 6414eeb..a88712f 100644 --- a/include/json/utils.h +++ b/include/json/utils.h @@ -1,8 +1,10 @@ #ifndef JSON_UTILS_H #define JSON_UTILS_H #include - -JsonObject getNode(String name, JsonArray nodes); -JsonObject getContainer(int id, JsonArray containers); +#include +//JsonObject getNode(String name, JsonArray nodes); +Node getNode(String name); +Container getContainer(int id, String node); +VM getVM(int id, String node); #endif /* JSON_UTILS_H */ diff --git a/include/menu.h b/include/menu.h index 6d9381b..dcd013b 100644 --- a/include/menu.h +++ b/include/menu.h @@ -2,14 +2,16 @@ #define MENU_H_ #include #include +#include // Struct for static menu items such as on the main menu. Has the name of the menu item and a function to run when it is selected. typedef struct { String name; void (*function)(); } MenuItem; -void listNodes(JsonArray nodes); -int listContainers(JsonArray containers); +void listNodes(Node* nodes, int numItems); +void listContainers(Container* containers, int numItems); +void listVMs(VM* vms, int numItems); void mainMenu(); extern int selectedItem; diff --git a/src/json/retrieve.cpp b/src/json/retrieve.cpp index 140024d..38cef4e 100644 --- a/src/json/retrieve.cpp +++ b/src/json/retrieve.cpp @@ -2,14 +2,11 @@ #include #include #include +#include +#include -// todo make the bottom part of these into one function -// ###################################### -// ###################################### -// ###################################### -// ###################################### -// ###################################### -JsonArray getNodeInfo() { +Node *getNodeInfo(int *numNodes) +{ HTTPClient http; String apiAddress = PROXMOX_ADDRESS + "/api2/json/nodes/"; String auth = "PVEAPIToken=" + PROXMOX_TOKEN_USER + "!" + PROXMOX_TOKEN_NAME + "=" + PROXMOX_TOKEN_SECRET; @@ -19,19 +16,36 @@ JsonArray getNodeInfo() { http.setAuthorization(authc); int httpCode = http.GET(); - if (httpCode > 0) { + if (httpCode > 0) + { String json = http.getString(); - DynamicJsonDocument doc(1024); + DynamicJsonDocument doc(6144); deserializeJson(doc, json); + JsonArray nodes = doc["data"].as(); - return nodes; - } + Node *nodeArray = new Node[nodes.size()]; + + for (int i = 0; i < nodes.size(); i++) + { + nodeArray[i].name = nodes[i]["node"].as(); + nodeArray[i].cpu = nodes[i]["cpu"].as(); + nodeArray[i].threads = nodes[i]["maxcpu"].as(); + nodeArray[i].onlineStatus = nodes[i]["status"].as(); + nodeArray[i].mem = nodes[i]["mem"].as(); + nodeArray[i].maxmem = nodes[i]["maxmem"].as(); + nodeArray[i].disk = nodes[i]["disk"].as(); + nodeArray[i].maxdisk = nodes[i]["maxdisk"].as(); + nodeArray[i].uptime = nodes[i]["uptime"].as(); + } - displayError("Error getting data."); - return getNodeInfo(); + *numNodes = nodes.size(); + return nodeArray; + } + throw std::runtime_error("Can't get node info: HTTP request failed with code: " + httpCode); } -JsonArray getContainerInfo(String node) { +Container *getContainerInfo(int *numContainers, String node) +{ HTTPClient http; String apiAddress = PROXMOX_ADDRESS + "/api2/json/nodes/" + node + "/lxc"; String auth = "PVEAPIToken=" + PROXMOX_TOKEN_USER + "!" + PROXMOX_TOKEN_NAME + "=" + PROXMOX_TOKEN_SECRET; @@ -41,19 +55,33 @@ JsonArray getContainerInfo(String node) { http.setAuthorization(authc); int httpCode = http.GET(); - if (httpCode > 0) { + if (httpCode > 0) + { String json = http.getString(); DynamicJsonDocument doc(1024); deserializeJson(doc, json); - JsonArray nodes = doc["data"].as(); - return nodes; + JsonArray containers = doc["data"].as(); + Container *containerArray = new Container[containers.size()]; + + for (int i = 0; i < containers.size(); i++) + { + containerArray[i].name = containers[i]["name"].as(); + containerArray[i].id = containers[i]["vmid"].as(); + containerArray[i].onlineStatus = containers[i]["status"].as(); + containerArray[i].maxmem = containers[i]["maxmem"].as(); + containerArray[i].maxdisk = containers[i]["maxdisk"].as(); + containerArray[i].uptime = containers[i]["uptime"].as(); + } + + *numContainers = containers.size(); + return containerArray; } - displayError("Error getting data."); - return getNodeInfo(); + throw std::runtime_error("Can't get container info: HTTP request failed with code: " + httpCode); } -JsonArray getVMInfo(String node) { +VM *getVMInfo(int *numVMs, String node) +{ HTTPClient http; String apiAddress = PROXMOX_ADDRESS + "/api2/json/nodes/" + node + "/qemu"; String auth = "PVEAPIToken=" + PROXMOX_TOKEN_USER + "!" + PROXMOX_TOKEN_NAME + "=" + PROXMOX_TOKEN_SECRET; @@ -63,14 +91,28 @@ JsonArray getVMInfo(String node) { http.setAuthorization(authc); int httpCode = http.GET(); - if (httpCode > 0) { + if (httpCode > 0) + { String json = http.getString(); DynamicJsonDocument doc(1024); deserializeJson(doc, json); - JsonArray nodes = doc["data"].as(); - return nodes; - } + JsonArray vms = doc["data"].as(); + VM *vmArray = new VM[vms.size()]; - displayError("Error getting data."); - return getNodeInfo(); -} + for (int i = 0; i < vms.size(); i++) + { + vmArray[i].name = vms[i]["name"].as(); + vmArray[i].id = vms[i]["vmid"].as(); + vmArray[i].onlineStatus = vms[i]["status"].as(); + vmArray[i].maxmem = vms[i]["maxmem"].as(); + vmArray[i].maxdisk = vms[i]["maxdisk"].as(); + vmArray[i].uptime = vms[i]["uptime"].as(); + vmArray[i].netin = vms[i]["netin"].as(); + vmArray[i].netout = vms[i]["netout"].as(); + } + + *numVMs = vms.size(); + return vmArray; + } + throw std::runtime_error("Can't get VM info: HTTP request failed with code: " + httpCode); +} \ No newline at end of file diff --git a/src/json/utils.cpp b/src/json/utils.cpp index 86ecb81..661e8b3 100644 --- a/src/json/utils.cpp +++ b/src/json/utils.cpp @@ -1,27 +1,52 @@ #include +#include #include +#include -/** - Get a specific node from an array of nodes -*/ -JsonObject getNode(String name, JsonArray nodes) { - for (JsonObject node : nodes) { - if (node["node"] == name) { - return node; +Node getNode(String name) +{ + int numNodes; + + Node *nodes = getNodeInfo(&numNodes); + for (int i = 0; i < numNodes; i++) + { + if (nodes[i].name == name) + { + return nodes[i]; + } + } + + throw std::runtime_error("Can't find the requested node in the array"); +} + +Container getContainer(int id, String node) +{ + + int numContainers; + Container *containers = getContainerInfo(&numContainers, node); + for (int i = 0; i < numContainers; i++) + { + if (containers[i].id == id) + { + return containers[i]; } } - displayError("Node not found!"); + throw std::runtime_error("Can't find the requested container in the array"); } -JsonObject getContainer(int id, JsonArray containers) { - for (JsonObject container : containers) { - Serial.println(id); - Serial.println(container["vmid"].as()); - if (container["vmid"].as() == id) { - return container; +VM getVM(int id, String node) +{ + int numVMs; + + VM *vms = getVMInfo(&numVMs, node); + for (int i = 0; i < numVMs; i++) + { + if (vms[i].id == id) + { + return vms[i]; } } - displayError("Machine not found!"); + throw std::runtime_error("Can't find the requested vm in the array"); } diff --git a/src/main.cpp b/src/main.cpp index 34ebe8e..e2cb79d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,22 +7,34 @@ #include #include -void setup() { +void setup() +{ Serial.begin(115200); GO.begin(); connectWiFi(); } +void loop() +{ -void loop() { - - if (WiFi.status() != WL_CONNECTED) { + if (WiFi.status() != WL_CONNECTED) + { displayError("WiFi Connection Lost"); connectWiFi(); } - listNodes(getNodeInfo()); - selectedItem = 0; - mainMenu(); - + try + { + int numNodes; + Node *nodes = getNodeInfo(&numNodes); + listNodes(nodes, numNodes); + selectedItem = 0; + mainMenu(); + } + catch (const std::exception &e) + { + Serial.println(e.what()); + displayError(e.what()); + } + } \ No newline at end of file diff --git a/src/menu.cpp b/src/menu.cpp index de68396..dc73dfc 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -2,11 +2,19 @@ #include #include #include -#include #include +#include + +const int MAIN_TEXT_COLOR = WHITE; +const int MAIN_TEXT_SIZE = 2; + int selectedItem = 0; String selectedNode = ""; +int selectedLXC = 0; +int selectedVM = 0; + + @@ -24,10 +32,12 @@ int buttonListener(int numItems) { GO.update(); if (GO.JOY_Y.isAxisPressed() == 1 && selectedItem < (numItems - 1)) { selectedItem++; + Serial.println(selectedItem); break; } if (GO.JOY_Y.isAxisPressed() == 2 && selectedItem > 0) { selectedItem--; + Serial.println(selectedItem); break; } if (GO.BtnA.isPressed() == 1) { @@ -46,6 +56,8 @@ int buttonListener(int numItems) { void drawMenu(MenuItem menuItems[], int numItems) { GO.lcd.clearDisplay(); GO.lcd.setCursor(0, 0); + GO.lcd.setTextColor(MAIN_TEXT_COLOR); + GO.lcd.setTextSize(MAIN_TEXT_SIZE); for (int i = 0; i < numItems; i++) { @@ -65,6 +77,7 @@ void drawMenu(MenuItem menuItems[], int numItems) { break; default: drawMenu(menuItems, numItems); + break; } } @@ -74,56 +87,84 @@ void mainMenu(){ drawMenu(mainMenuItems, numItems); } -void listNodes(JsonArray nodes) { +void listNodes(Node* nodes, int numItems) { GO.lcd.clearDisplay(); + GO.lcd.setTextColor(MAIN_TEXT_COLOR); + GO.lcd.setTextSize(MAIN_TEXT_SIZE); GO.lcd.setCursor(0, 0); - int numItems = nodes.size(); + for (int i = 0; i < numItems; i++) { if (selectedItem == i) { GO.lcd.print("> "); } - GO.lcd.println(nodes[i]["node"].as()); + GO.lcd.println(nodes[i].name); } switch (buttonListener(numItems)) { case 1: Serial.println("selected " + selectedItem); - selectedNode = nodes[selectedItem]["node"].as(); + selectedNode = nodes[selectedItem].name; break; case 2: Serial.println("back"); break; default: - listNodes(nodes); + listNodes(nodes, numItems); + break; } } -int listContainers(JsonArray containers) { +void listContainers(Container* containers, int numItems) { GO.lcd.clearDisplay(); GO.lcd.setCursor(0, 0); - int numItems = containers.size(); - int selectedLXC = 0; + GO.lcd.setTextColor(MAIN_TEXT_COLOR); + GO.lcd.setTextSize(MAIN_TEXT_SIZE); + for (int i = 0; i < numItems; i++) { if (selectedItem == i) { GO.lcd.print("> "); } - GO.lcd.println(containers[i]["vmid"].as() + ": " + containers[i]["name"].as()); + GO.lcd.println(String(containers[i].id) + ": " + containers[i].name); } switch (buttonListener(numItems)) { case 1: - selectedLXC = containers[selectedItem]["vmid"].as(); - Serial.println(selectedLXC); + selectedLXC = containers[selectedItem].id; break; case 2: Serial.println("back"); break; default: - listContainers(containers); + listContainers(containers, numItems); + break; + } + +} + + +void listVMs(VM* vms, int numItems) { + GO.lcd.clearDisplay(); + GO.lcd.setCursor(0, 0); + GO.lcd.setTextColor(MAIN_TEXT_COLOR); + GO.lcd.setTextSize(MAIN_TEXT_SIZE); + + for (int i = 0; i < numItems; i++) { + if (selectedItem == i) { + GO.lcd.print("> "); + } + GO.lcd.println(String(vms[i].id) + ": " + vms[i].name); + } + + switch (buttonListener(numItems)) { + case 1: + selectedVM = vms[selectedItem].id; + break; + case 2: + Serial.println("back"); + break; + default: + listVMs(vms, numItems); + break; } - Serial.println(selectedLXC); - Serial.println("end"); - return selectedLXC; - } diff --git a/src/statistics.cpp b/src/statistics.cpp index 60f46db..98dec1b 100644 --- a/src/statistics.cpp +++ b/src/statistics.cpp @@ -3,139 +3,131 @@ #include #include #include +#include +String convertBytes(long long value) { + if (value > 1099511627776) { + return String(value / 1099511627776) + " TB"; + } + if (value > 1073741824) { + return String(value / 1073741824) + " GB"; + } + if (value > 1048576) { + return String(value / 1048576) + " MB"; + } + if (value > 1024) { + return String(value / 1024) + " KB"; + } + return String(value) + " Bytes"; +} -void printNodeStats(JsonObject node) { - String name = node["node"].as(); - int cpu = round(node["cpu"].as() * 100); - int threads = node["maxcpu"].as(); - String onlineStatus = node["status"].as(); - String mem = node["mem"].as(); - String maxmem = node["maxmem"].as(); - String disk = node["disk"].as(); - String maxdisk = node["maxdisk"].as(); - String uptime = node["uptime"].as(); - // these are strings for now because they could be way too big +String convertTime(long long value) { + if (value > 3600) { + return String(value / 3600) + " hrs"; + } + if (value > 60) { + return String(value / 60) + " mins"; + } + return String(value) + " secs"; +} + +void printNodeStats(Node node) +{ GO.lcd.fillScreen(WHITE); GO.lcd.setCursor(0, 0); GO.lcd.setTextColor(BLACK); GO.lcd.setTextSize(3); - GO.lcd.println(name); + GO.lcd.println(node.name); GO.lcd.setTextSize(2); GO.lcd.println("--------------------------"); - GO.lcd.println("Status: " + onlineStatus); - GO.lcd.println("Uptime: " + uptime + "s"); - GO.lcd.println("CPU: " + String(cpu) + "%"); - GO.lcd.println("Threads: " + String(threads)); - GO.lcd.println("RAM: " + mem); - GO.lcd.println("Max RAM: " + maxmem); - GO.lcd.println("Disk: " + disk); - GO.lcd.println("Max Disk: " + maxdisk); - + GO.lcd.println("Status: " + node.onlineStatus); + GO.lcd.println("Uptime: " + convertTime(node.uptime)); + GO.lcd.println("CPU: " + String(node.cpu) + "%"); + GO.lcd.println("Threads: " + String(node.threads)); + GO.lcd.println("RAM: " + convertBytes(node.mem)); + GO.lcd.println("Max RAM: " + convertBytes(node.maxmem)); + GO.lcd.println("Disk: " + convertBytes(node.disk)); + GO.lcd.println("Max Disk: " + convertBytes(node.maxdisk)); } -void printContainerStats(JsonObject container) { - String name = container["name"].as(); - String id = container["vmid"].as(); - String onlineStatus = container["status"].as(); - String maxmem = container["maxmem"].as(); - String maxdisk = container["maxdisk"].as(); - String uptime = container["uptime"].as(); - // these are strings for now because they could be way too big +void printContainerStats(Container container) +{ GO.lcd.fillScreen(WHITE); GO.lcd.setCursor(0, 0); GO.lcd.setTextColor(BLACK); GO.lcd.setTextSize(3); - GO.lcd.println(id + ": " + name); + GO.lcd.println(String(container.id) + ": " + container.name); GO.lcd.setTextSize(2); GO.lcd.println("--------------------------"); - GO.lcd.println("Status: " + onlineStatus); - GO.lcd.println("Uptime: " + uptime + "s"); - GO.lcd.println("Max RAM: " + maxmem); - GO.lcd.println("Max Disk: " + maxdisk); - + GO.lcd.println("Status: " + container.onlineStatus); + GO.lcd.println("Uptime: " + convertTime(container.uptime)); + GO.lcd.println("Max RAM: " + convertBytes(container.maxmem)); + GO.lcd.println("Max Disk: " + convertBytes(container.maxdisk)); } -void printVMStats(JsonObject vm) { - String name = vm["name"].as(); - String id = vm["vmid"].as(); - int cpu = round(vm["cpu"].as() * 100); - int cores = vm["cpus"].as(); - String onlineStatus = vm["status"].as(); - String mem = vm["mem"].as(); - String maxmem = vm["maxmem"].as(); - String maxdisk = vm["maxdisk"].as(); - String uptime = vm["uptime"].as(); - String netin = vm["netin"].as(); - String netout = vm["netout"].as(); - // these are strings for now because they could be way too big +void printVMStats(VM vm) +{ GO.lcd.fillScreen(WHITE); GO.lcd.setCursor(0, 0); GO.lcd.setTextColor(BLACK); GO.lcd.setTextSize(3); - GO.lcd.println(id + ": " + name); + GO.lcd.println(String(vm.id) + ": " + vm.name); GO.lcd.setTextSize(2); GO.lcd.println("--------------------------"); - GO.lcd.println("Status: " + onlineStatus); - GO.lcd.println("Uptime: " + uptime + "s"); - GO.lcd.println("CPU: " + String(cpu) + "%"); - GO.lcd.println("Cores: " + String(cores)); - GO.lcd.println("RAM: " + mem); - GO.lcd.println("Max RAM: " + maxmem); - GO.lcd.println("Max Disk: " + maxdisk); - GO.lcd.println("Net In: " + netin); - GO.lcd.println("Net Out: " + netout); - - + GO.lcd.println("Status: " + vm.onlineStatus); + GO.lcd.println("Uptime: " + convertTime(vm.uptime)); + GO.lcd.println("Max RAM: " + convertBytes(vm.maxmem)); + GO.lcd.println("Max Disk: " + convertBytes(vm.maxdisk)); } - -void nodeInfo() { +void nodeInfo() +{ Serial.println("nodeinfo"); - while (true) { - printNodeStats(getNode(selectedNode, getNodeInfo())); + while (true) + { + + printNodeStats(getNode(selectedNode)); delay(2000); } } -void containerInfo() { +void containerInfo() +{ Serial.println("container info"); - JsonArray containerArray = getContainerInfo(selectedNode); selectedItem = 0; - int selectedLXC = listContainers(containerArray); - JsonObject container = getContainer(selectedLXC, containerArray); - printContainerStats(container); - delay(1000); - - // only redraw screen if changed - while (true) { - if (getContainer(selectedLXC, containerArray) != container) { - containerInfo(); - } + int numContainers; + + Container *containers = getContainerInfo(&numContainers, selectedNode); + + listContainers(containers, numContainers); + + while (true) + { + printContainerStats(getContainer(selectedLXC, selectedNode)); + delay(5000); } - } -void vmInfo() { +void vmInfo() +{ Serial.println("vm info"); - JsonArray vmArray = getVMInfo(selectedNode); selectedItem = 0; - int selectedVM = listContainers(vmArray); - Serial.println("vm"); - Serial.println(selectedVM); - Serial.println(selectedVM); - Serial.println(selectedVM); - Serial.println(selectedVM); - - while (true) { - printVMStats(getContainer(selectedVM, vmArray)); - delay(2000); + int numVMs; + VM *vms = getVMInfo(&numVMs, selectedNode); + if (vms != NULL) + { + listVMs(vms, numVMs); + + while (true) + { + printVMStats(getVM(selectedVM, selectedNode)); + delay(5000); + } } - -} \ No newline at end of file +}