You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

465 lines
14 KiB

#include <menu.h>
#include <Arduino.h>
#include <odroid_go.h>
#include <statistics.h>
#include <manage.h>
// UI constants for the menu system.
const int MAIN_TEXT_COLOR = WHITE;
const int MAIN_TEXT_SIZE = 2;
const int ITEMS_PER_PAGE = 12;
const int OFFLINE_COLOR = RED;
const int ONLINE_COLOR = GREEN;
// Initializing variables which hold the selections.
int selectedItem = 0;
int selectedPage = 0;
String selectedNode = "";
int selectedLXC = 0;
int selectedVM = 0;
String selectedDisk = "";
String selectedPool = "";
// Array of the main menu items
MenuItem mainMenuItems[] = {
{"Node Information", &nodeInfo},
{"Container Information", &containerInfo},
{"VM Information", &vmInfo},
{"Disk Information", &diskInfo},
{"ZFS Pool Information", &poolInfo},
{"Manage VM", &manageVMMenu},
{"Manage Container", &manageContainerMenu}};
// Array of the VM managment menu items.
MenuItem manageVMItems[] = {
{"Restart", &vmRestart},
{"Start", &vmStart},
{"Stop", &vmStop},
};
// Array of the container management menu items.
MenuItem manageContainerItems[] = {
{"Restart", &containerRestart},
{"Start", &containerStart},
{"Stop", &containerStop},
};
/// @brief Function to listen for button presses. Once called it will loop until a button is pressed. For the directional pad the selected page or item is changed and 0 is returned. For the A/B buttons 1/2 is returned to indicate that the user wants to select something or go back.
/// @param numItems A reference an integer which holds the number of items for the user to pick from in the menu.
/// @return An integer indicating what action was taken. 1 for the A button, 2 for the B button, 0 for any other input.
int buttonListener(const int &numItems)
{
delay(300);
while (true)
{
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.JOY_X.isAxisPressed() == 1 && selectedPage + 1 < (float(numItems) / ITEMS_PER_PAGE))
{
selectedPage++;
selectedItem = selectedPage * ITEMS_PER_PAGE;
Serial.println(selectedPage);
break;
}
if (GO.JOY_X.isAxisPressed() == 2 && selectedPage > 0)
{
selectedPage--;
selectedItem = selectedPage * ITEMS_PER_PAGE;
Serial.println(selectedPage);
break;
}
if (GO.BtnA.isPressed() == 1)
{
delay(200);
return 1;
}
if (GO.BtnB.isPressed() == 1)
{
delay(200);
return 2;
}
delay(50);
}
return 0;
}
/** @brief A function to draw a menu on the screen. This is for static menus which have a function tied to each item.
* Loops through the menu items and draws a ">" before the currently selected item.
* After printing the items, listens for button inputs. If the A button is pressed then the function of the currently selected item is run. If the B button is pressed the function stops running (returning the previous menu).
* If the directional pad buttons are pressed it means that the selected item or page has changed, so the function is run again to redraw the menu with the new values.
* @param menuItems An array of menuItems used to draw the menu.
* @param numItems A reference to an int which holds the number of items in the menu.
* @param menuTitle A reference to a string which holds the title of the menu.
**/
void drawMenu(MenuItem menuItems[], const int &numItems, const String &menuTitle)
{
GO.lcd.clearDisplay();
GO.lcd.setCursor(0, 0);
GO.lcd.setTextColor(MAIN_TEXT_COLOR);
GO.lcd.setTextSize(MAIN_TEXT_SIZE);
GO.lcd.println(menuTitle);
GO.lcd.println("--------------------------");
for (int i = 0; i < numItems; i++)
{
if (selectedItem == i)
{
GO.lcd.print("> ");
}
else
{
GO.lcd.print(" ");
}
GO.lcd.println(menuItems[i].name);
}
switch (buttonListener(numItems))
{
case 1:
Serial.println("selected " + selectedItem);
menuItems[selectedItem].function();
break;
case 2:
Serial.println("back");
break;
default:
drawMenu(menuItems, numItems, menuTitle);
break;
}
}
/**
* @brief Function to draw the main menu. Calls the draw menu function using the array of main menu items.
*/
void mainMenu()
{
selectedItem = 0;
int numItems = sizeof(mainMenuItems) / sizeof(MenuItem);
drawMenu(mainMenuItems, numItems, "Main Menu");
}
/**
* @brief Function to draw the VM management menu. Calls the draw menu function using the array of VM mangement menu items.
*/
void manageVMMenu()
{
selectedItem = 0;
int numItems = sizeof(manageVMItems) / sizeof(MenuItem);
drawMenu(manageVMItems, numItems, "Manage VM");
mainMenu();
}
/**
* @brief Function to draw the container management menu. Calls the draw menu function using the array of container management menu items.
*/
void manageContainerMenu()
{
selectedItem = 0;
int numItems = sizeof(manageContainerItems) / sizeof(MenuItem);
drawMenu(manageContainerItems, numItems, "Manage Container");
mainMenu();
}
/**
* @brief Callback function to print a container in the menu. Prints an online indicator which is red if offline and green if online.
* Then prints the id of the container followed by it's name.
*
* @param container The container to print.
*/
void containerMenuPrint(void *container)
{
Container *cont = static_cast<Container *>(container);
if ((*cont).onlineStatus == "running")
{
GO.lcd.setTextColor(ONLINE_COLOR);
}
else
{
GO.lcd.setTextColor(OFFLINE_COLOR);
}
GO.lcd.print("O ");
GO.lcd.setTextColor(MAIN_TEXT_COLOR);
GO.lcd.println(String((*cont).id) + ": " + (*cont).name);
};
/**
* @brief Callback function to print a VM in the menu. Prints an online indicator which is red if offline and green if online.
* Then prints the id of the VM followed by it's name.
*
* @param vm The VM to print.
*/
void vmMenuPrint(void *vm)
{
VM *v = static_cast<VM *>(vm);
if ((*v).onlineStatus == "running")
{
GO.lcd.setTextColor(ONLINE_COLOR);
}
else
{
GO.lcd.setTextColor(OFFLINE_COLOR);
}
GO.lcd.print("O ");
GO.lcd.setTextColor(MAIN_TEXT_COLOR);
GO.lcd.println(String((*v).id) + ": " + (*v).name);
};
/**
* @brief Callback function to print a node in the menu. Prints an online indicator which is red if offline and green if online.
* Then prints the name of the node.
*
* @param node
*/
void nodeMenuPrint(void *node)
{
Node *n = static_cast<Node *>(node);
if ((*n).onlineStatus == "online")
{
GO.lcd.setTextColor(ONLINE_COLOR);
}
else
{
GO.lcd.setTextColor(OFFLINE_COLOR);
}
GO.lcd.print("O ");
GO.lcd.setTextColor(MAIN_TEXT_COLOR);
GO.lcd.println((*n).name);
}
/**
* @brief Callback function to print a disk in the menu. Prints the devpath of the disk.
*
* @param disk The disk to print
*/
void diskMenuPrint(void *disk)
{
Disk *d = static_cast<Disk *>(disk);
GO.lcd.println((*d).devpath);
}
/**
* @brief Callback function to print a ZFS pool in the menu. Prints the name of the pool.
*
* @param pool The pool to print.
*/
void poolMenuPrint(void *pool)
{
Pool *p = static_cast<Pool *>(pool);
GO.lcd.println((*p).name);
}
/**
* @brief Main function to print a menu of items (such as nodes or containers). First prints a title, followed by page number.
* Then prints the items starting at the page number multiplied by the number of items per page, and stops when the items per page is reached.
* Prints a ">" on the currently selected item to indicate that it is selected.
* Then uses the callback function to print the name of whatever type of item is being printed.
*
* After printing items listens for input from the user using the ButtonListener() function.
*
*
* @param items The array of items to print.
* @param item The item selected by the user.
* @param title The menu title.
* @param numItems The number of items in the array.
* @param itemSize The size of each item. Used for pointer calculation.
* @param backEnabled Whether the back button should be enabled for this menu.
* @param printMachine The callback function to print the name of the item.
*/
void listItems(
void *items, void **item,
const String title,
const int &numItems,
const int &itemSize,
const bool &backEnabled,
MenuPrintCallback printMachine)
{
GO.lcd.clearDisplay();
GO.lcd.setCursor(0, 0);
GO.lcd.setTextColor(MAIN_TEXT_COLOR);
GO.lcd.setTextSize(MAIN_TEXT_SIZE);
GO.lcd.println(title + " (Page " + String(selectedPage + 1) + ")");
GO.lcd.println("--------------------------");
// Cast items to char for pointer calculation
char *itemsAsBytes = static_cast<char *>(items);
for (int i = selectedPage * ITEMS_PER_PAGE; i < numItems && i < (selectedPage + 1) * ITEMS_PER_PAGE; i++)
{
if (selectedItem == i)
{
GO.lcd.print("> ");
}
else
{
GO.lcd.print(" ");
}
printMachine(itemsAsBytes + i * itemSize);
}
switch (buttonListener(numItems))
{
case 1:
*item = itemsAsBytes + selectedItem * itemSize;
break;
case 2:
if (backEnabled)
{
Serial.println("back");
break;
}
default:
listItems(items, item, title, numItems, itemSize, backEnabled, printMachine);
break;
}
}
/**
* @brief The calling function for listing nodes. Calls the listItems() function with the appropriate parameters for nodes.
* Passes an item pointer to the function which gets set to whatever node has been selected during the listItems() function.
* Then sets the item pointer to the selected node.
*
* Passes false to the backEnabled parameter as the back button should be disabled on the node selection screen.
*
* Passes through nodeMenuPrint() as the callback function so that the correct names are printed.
*
* @param nodes The array of nodes being listed.
* @param numItems The number of nodes being listed.
*/
void listNodes(Node *nodes, const int &numItems)
{
selectedItem = 0;
selectedPage = 0;
void *item;
listItems(nodes, &item, "Select Node", numItems, sizeof(Node), false, nodeMenuPrint);
if (item != nullptr)
{
Node *node = static_cast<Node *>(item);
selectedNode = (*node).name;
}
}
/**
* @brief The calling function for listing containers. Calls the listItems() function with the appropriate parameters for containers.
* Passes an item pointer to the function which gets set to whatever container has been selected during the listItems() function.
* Then sets the item pointer to the selected container.
*
* Passes true to the backEnabled parameter as the back button should be enabled on this screen.
*
* Passes through containerMenuPrint() as the callback function so that the correct names are printed.
*
* @param containers The array of containers to list.
* @param numItems The number of containers to list.
* @return The ID of the selected container, or 0 if none selected
*/
int listContainers(Container *containers, const int &numItems)
{
selectedItem = 0;
selectedPage = 0;
void *item;
listItems(containers, &item, "Select Container", numItems, sizeof(Container), true, containerMenuPrint);
if (item != nullptr)
{
Container *container = static_cast<Container *>(item);
return (*container).id;
}
return 0;
}
/**
* @brief The calling function for listing VMs. Calls the listItems() function with the appropriate parameters for VMs.
* Passes an item pointer to the function which gets set to whatever VM has been selected during the listItems() function.
* Then sets the item pointer to the selected VM.
*
* Passes true to the backEnabled parameter as the back button should be enabled on this screen.
*
* Passes through vmMenuPrint() as the callback function so that the correct names are printed.
*
* @param vms The array of VMs to list.
* @param numItems The number of VMs to list.
* @return The ID of the selected VM, or 0 if none selected
*/
int listVMs(VM *vms, const int &numItems)
{
selectedItem = 0;
selectedPage = 0;
void *item;
listItems(vms, &item, "Select VM", numItems, sizeof(VM), true, vmMenuPrint);
if (item != nullptr)
{
VM *vm = static_cast<VM *>(item);
return (*vm).id;
}
return 0;
}
/**
* @brief The calling function for listing disks. Calls the listItems() function with the appropriate parameters for disks.
* Passes an item pointer to the function which gets set to whatever disk has been selected during the listItems() function.
* Then sets the item pointer to the selected disk.
*
* Passes true to the backEnabled parameter as the back button should be enabled on this screen.
*
* Passes through diskMenuPrint() as the callback function so that the correct names are printed.
*
* @param disks The array of disks to list.
* @param numItems The number of disks to list.
* @return The devpath of the chosen disk, or an empty string if nothing has been selected
*/
String listDisks(Disk *disks, const int &numItems)
{
selectedItem = 0;
selectedPage = 0;
void *item;
listItems(disks, &item, "Select Disk", numItems, sizeof(Disk), true, diskMenuPrint);
if (item != nullptr)
{
Disk *disk = static_cast<Disk *>(item);
return (*disk).devpath;
}
return "";
}
/**
* @brief The calling function for listing pools. Calls the listItems() function with the appropriate parameters for pools.
* Passes an item pointer to the function which gets set to whatever pool has been selected during the listItems() function.
* Then sets the item pointer to the selected pool.
*
* Passes true to the backEnabled parameter as the back button should be enabled on this screen.
*
* Passes through poolMenuPrint() as the callback function so that the correct names are printed.
*
* @param pools The array of pools to list.
* @param numItems The number of pools to list.
* @return The name of the chosen pool, or a blank string if no pool selected
*/
String listPools(Pool *pools, const int &numItems)
{
selectedItem = 0;
selectedPage = 0;
void *item;
listItems(pools, &item, "Select Pool", numItems, sizeof(Pool), true, poolMenuPrint);
if (item != nullptr)
{
Pool *pool = static_cast<Pool *>(item);
return (*pool).name;
}
return "";
}