/* * %W% %E% Dymik739 * Email: dymik739@109.86.70.81 * * Copyright (C) 2023 FIOT Dev Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import java.util.Arrays; /** * Main class that controls all the devices and regulates vent power. * * @author Dymik739 * @since 0.3 */ public class Main { /** * Defines the ServerRack power consumption at which the vent * power is enabled. */ private static final float VENT_START_POWER = 240f; /** * Defines the ServerRack power consumption at which the vent * power is disabled. */ private static final float VENT_STOP_POWER = 185f; /** Defines magic number for real-time TTY output mode */ private static final int TTY_LIVE_MONITORING_MODE = 0; /** * Defines magic number for raw graph output mode for current * power consumption and vent RPM */ private static final int POWER_RPM_GRAPH_MODE = 1; /** Defines magic number for raw graph output mode for total consumption */ private static final int TOTAL_CONSUMPTION_GRAPH_MODE = 2; /** * Defines magic number for raw graph output mode for vent rpm and * power consumption relation */ private static final int VENT_POWER_RPM_GRAPH_MODE = 1; /** Defines the amount of simulation samples to get per every second */ private static final int TICKS_PER_SECOND = 960; /** Defines the time simulation will run for (in seconds) */ private static final int SIMULATION_TIME = 240; /** * Main method for containing the devices, performing all * checks and controlling power supply to all the appliances. * * @param args accepts CLI arguments. */ public static void main(String[] args) { float ventConsumed = 0f; float totalPowerConsumed = 0f; int outputMode = parseMode(args); int liveStatsOutputDelay = parseOutputDelay(args); float[] searchRange = parseSearchRange(args); Vent vent = new Vent(false); // defining vent as a special appliance Appliance[] devices = { new ServerRack(false), new ServerRack(false), new RPI(false), new Dishwasher(false) }; devices[2].plug(); // turning on RPI right away // (RPI stands for Raspberry Pi) // letting the simulation run for (int i = 1; i <= SIMULATION_TIME*TICKS_PER_SECOND; i++) { step(vent, devices, (float) 1/TICKS_PER_SECOND); // stepping time // performing accounting ventConsumed += vent.getPowerConsumption() / TICKS_PER_SECOND; totalPowerConsumed += getTotalPowerConsumption(vent, devices) / TICKS_PER_SECOND / 3600; // outputting the data in the desired format if (outputMode == TTY_LIVE_MONITORING_MODE) { System.out.print("Time: " + floatFormat((float)i/TICKS_PER_SECOND, 2, 2) + "; " + getStats(vent, devices) + "; vent avg = " + floatFormat(ventConsumed/TICKS_PER_SECOND/i, 4, 1) + "W; total = " + floatFormat(totalPowerConsumed, 4, 3) + "W\r"); } else if (outputMode == POWER_RPM_GRAPH_MODE) { System.out.println(getTotalPowerConsumption(vent, devices) + " " + vent.getRPM()/10); } else if (outputMode == TOTAL_CONSUMPTION_GRAPH_MODE) { System.out.println(totalPowerConsumed); } else if (outputMode == VENT_POWER_RPM_GRAPH_MODE) { System.out.println(vent.getRPM()/10 + " " + vent.getPowerConsumption()); } adjustVentPower(devices, vent); managePower(i, devices); if ((outputMode == TTY_LIVE_MONITORING_MODE) && (liveStatsOutputDelay != 0)) { try { Thread.sleep(liveStatsOutputDelay); } catch (Exception e) { System.exit(0); } } } if (outputMode == TTY_LIVE_MONITORING_MODE) { Appliance[] totalDevices = new Appliance[devices.length + 1]; for (int i = 0; i < devices.length; i++) { totalDevices[i] = devices[i]; } totalDevices[devices.length] = vent; System.out.println("\nCurrently devices draw " + floatFormat(getTotalPowerConsumption(totalDevices), 4, 2) + "W from the power lines."); Arrays.sort(totalDevices); System.out.println("\nArray of appliances, sorted by the power " + "consumption:"); printAppliances(totalDevices); Appliance[] foundItems = filterByRadiation(totalDevices, searchRange); if (foundItems.length == 0) { System.out.println("\nCould not find any devices that match " + "your request (" + searchRange[0] + "-" + searchRange[1] + ")."); } else { System.out.println("\nFound items:"); printAppliances(foundItems); } } } /** * Method which decides on how to manage the vent power supply. * It looks at power consumption of every ServerRack device * and: * - turns the vent on if ANY of them meet the VENT_START_POWER threshold * - turns the vent off if ALL of them meet the VENT_STOP_POWER threshold * * @param devices list of devices to look at. * @param vent vent to manage power for. */ public static void adjustVentPower(Appliance[] devices, Vent vent) { for (Appliance i : devices) { if ("ServerRack".equals(i.getType()) && (i.getPowerConsumption() > VENT_START_POWER)) { vent.plug(); return; } } for (Appliance i : devices) { if ("ServerRack".equals(i.getType()) && !(i.getPowerConsumption() < VENT_STOP_POWER)) { return; } } vent.unplug(); } /** * Method that plugs in and out the devices in a predefined manner. * * @param i current simulation time. * @param devices device list to control. */ public static void managePower(int i, Appliance[] devices) { if (i == 25 * TICKS_PER_SECOND) { devices[1].plug(); } else if (i == 35 * TICKS_PER_SECOND) { devices[0].plug(); } else if (i == 50 * TICKS_PER_SECOND) { devices[0].unplug(); } else if (i == 60 * TICKS_PER_SECOND) { devices[3].plug(); } else if (i == 130 * TICKS_PER_SECOND) { devices[0].plug(); } } /** * Method for printing out a gives array of appliances. * * @param devices appliance array to print out. */ public static void printAppliances(Appliance[] devices) { for (Appliance i : devices) { System.out.println(i); } } /** * Method for selecting devices based on their radiation levels. * * @param totalDevices devices to select from * @param searchRange search boundaries * * @return Appliance array containing found items */ public static Appliance[] filterByRadiation(Appliance[] totalDevices, float[] searchRange) { int l = -1; int r = totalDevices.length; boolean barrierFound; barrierFound = false; for (int i = totalDevices.length-1; i >= 0; i--) { if (totalDevices[i].getRadiationAmount() < searchRange[0]) { l = i; barrierFound = true; break; } } if (!barrierFound) { l = -1; } barrierFound = false; for (int i = 0; i < totalDevices.length; i++) { if (totalDevices[i].getRadiationAmount() > searchRange[1]) { r = i; barrierFound = true; break; } } if (!barrierFound) { r = totalDevices.length; } if (l < -1 || l >= totalDevices.length || l >= r || r < 1 || r >= totalDevices.length + 1 || (r-l) == 1) { Appliance[] foundItems = new Appliance[0]; return foundItems; } else { Appliance[] foundItems = new Appliance[r-l-1]; for (int i = l+1; i < r; i++) { foundItems[i-l-1] = totalDevices[i]; } return foundItems; } } /** * Method for extracting output mode setting set from CLI. * * @param args CLI args array to use. * * @return int representing requested mode. */ public static int parseMode(String[] args) { for (String i : args) { if ("--power-rpm-graph".equals(i)) { return POWER_RPM_GRAPH_MODE; } else if ("--total-consumption-graph".equals(i)) { return TOTAL_CONSUMPTION_GRAPH_MODE; } else if ("--vent-monitoring-graph".equals(i)) { return VENT_POWER_RPM_GRAPH_MODE; } } return TTY_LIVE_MONITORING_MODE; } /** * Method for extracting output delay setting set from CLI. * * @param args CLI args array to use. * * @return delay in miliseconds. */ public static int parseOutputDelay(String[] args) { for (int i = 0; i < args.length; i++) { if ("--output-delay".equals(args[i])) { return Integer.parseInt(args[i+1]); } } return (int) (1000 / TICKS_PER_SECOND); } /** * Method for extracting output delay setting set from CLI. * * @param args CLI args array to use. * * @return delay in miliseconds. */ public static float[] parseSearchRange(String[] args) { for (int i = 0; i < args.length; i++) { if ("--search".equals(args[i])) { String[] rawParams = args[i+1].split("-"); float[] bakedParams = new float[2]; for (int j = 0; j < 2; j++) { bakedParams[j] = Float.parseFloat(rawParams[j]); } return bakedParams; } } float[] bakedParams = {0f, 0f}; return bakedParams; } /** * Method for performing the simulation. Runs the respective .step() * methods on all of the given appliances. * * @param vent vent object to process * @param devices devices array to process * @param seconds delta time to move forward. */ public static void step(Vent vent, Appliance[] devices, float seconds) { vent.step(seconds); for (Appliance i : devices) { i.step(seconds, vent.getRPM()); } } /** * Method for collecting the overall usage statistics to make it * easy to output status line during TTY mode execution. * * @param vent vent object to track * @param devices devices array to track * * @return String containing current stats for all the devices */ public static String getStats(Vent vent, Appliance[] devices) { float[] powerConsumption = new float[devices.length]; float totalPowerConsumption = 0; for (int i = 0; i < devices.length; i++) { powerConsumption[i] = devices[i].getPowerConsumption(); totalPowerConsumption += devices[i].getPowerConsumption(); } String result = "PPD: "; for (float i : powerConsumption) { result += String.format(floatFormat(i, 3, 1) + "W "); } float powerLinesDraw = totalPowerConsumption + vent.getPowerConsumption(); result += "; Vent: " + floatFormat(vent.getPowerConsumption(), 3, 1) + "W, " + floatFormat(vent.getRPM(), 5, 0) + " RPM; Total power: " + floatFormat(powerLinesDraw, 4, 1) + "W"; return result; } /** * Overloaded method for calculating the total power consumption * (devices + the vent). * * @param vent vent to account * @param devices array of devices to account * * @return sum of all the .getPowerConsumption() returned from devices */ public static float getTotalPowerConsumption(Vent vent, Appliance[] devices) { float result = vent.getPowerConsumption(); for (Appliance a : devices) { result += a.getPowerConsumption(); } return result; } /** * Overloaded method for calculating the total power consumption * (devices only). * * @param devices array of devices to account * * @return sum of all the .getPowerConsumption() returned from devices */ public static float getTotalPowerConsumption(Appliance[] devices) { float result = 0; for (Appliance a : devices) { result += a.getPowerConsumption(); } return result; } /** * Custom method which adds the support for arbitrary formatting of float * numbers. * * @param num value to format * @param leading amount of digits before period to print * @param trailing amount of digits after perio to print * * @return String with formatted result */ public static String floatFormat(float num, int leading, int trailing) { String newNum = String.format("%0" + leading + "." + trailing + "f", num); int targetLength = leading + trailing + 1; for (int i = newNum.length(); i < targetLength; i++) { newNum = "0" + newNum; } return newNum; } }