451 lines
14 KiB
Java
451 lines
14 KiB
Java
/*
|
|
* %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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
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;
|
|
}
|
|
}
|