diff --git a/labs/6/Appliance.java b/labs/6/Appliance.java index 2a65e61..4026645 100644 --- a/labs/6/Appliance.java +++ b/labs/6/Appliance.java @@ -1,33 +1,145 @@ -public class Appliance { +/* + * %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 . + */ + +/** + * Class representing a general appliance and containing basic methods + * that are used in multiple child classes. Supposed to be extended by + * other classes and not to be used as a standalone class. + * + * @author Dymik739 + * @since 0.3 + */ +public class Appliance implements Comparable { + /** Indicates if this device is drawing power from the power network. */ private boolean powerConnected; + /** Defines the type of this device */ + private String type; + + /** + * Constructor for this class. Should be called only from within + * constructors of other classes which inherit this one. + * + * @param connected defines if the device is connected to the network + * at the start + */ public Appliance(boolean connected) { this.powerConnected = connected; } + /** + * Getter for checking the power connection. + * + * @return true if connected and false otherwise + */ public boolean getPowerState() { return powerConnected; } + /** + * Getter for the device type variable. + * + * @return device type string + */ + public String getType() { + return type; + } + + /** + * Setter for setting the device type string. + * Should be used only from the constructors of the classes + * which inherit this one! + * + * @param type type of the device + */ + public void setType(String type) { + this.type = type; + } + + /** + * Method for connecting power to the device. + */ public void plug() { powerConnected = true; } + /** + * Method for disconnecting power from the device. + */ public void unplug() { powerConnected = false; } + /** + * Method for getting the smaller value out of two. + * + * @param v1 first value + * @param v2 second value + * + * @return smaller value of the two given + */ public float min(float v1, float v2) { return v1 <= v2 ? v1 : v2; } + /** + * Method for getting the bigger value out of two. + * + * @param v1 first value + * @param v2 second value + * + * @return bigger value of the two given + */ public float max(float v1, float v2) { return v1 >= v2 ? v1 : v2; } + /** + * Dummy method for getting the power consumption of the device. + * Should be overridden by the child class! + * + * @return current power consumption. + */ public float getPowerConsumption() { return 0f; } + /** + * Dummy method for performing the simulation. + * Should be overridden by the child class! + * + * @param seconds delta time for the correct simulation step + * @param ventRPM air flow created by the vent + */ public void step(float seconds, float ventRPM) {} + + /** + * Method for comparing this appliance's power consumption to another + * one. Part os the Comparable implementation. + * + * @param o another appliance to compare to + * + * @return difference between power consumption values + */ + @Override + public int compareTo(Appliance o) { + return (int) (getPowerConsumption() - o.getPowerConsumption()); + } } diff --git a/labs/6/Dishwasher.java b/labs/6/Dishwasher.java index 838005f..ed282c7 100644 --- a/labs/6/Dishwasher.java +++ b/labs/6/Dishwasher.java @@ -1,13 +1,58 @@ +/* + * %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 . + */ + +/** + * Class which represents the behaviour of a dishwasher. + * + * @author Dymik739 + * @since 0.3 + */ public class Dishwasher extends Appliance { - private float basePower = 160.0f; + /** Defines power usage at different stages of washing dishes. */ private float[] powerStates = {400f, 60f, 130f, 350f}; + + /** Shows how much time should pass before switching to the next stage */ private float nextPowerStateIn = 20f; + + /** Shows current stage the dishwasher is performing, -1 for none */ private int currentState = -1; + /** + * Constructor for this class. + * + * @param plugged sets the starting power state of this device + */ public Dishwasher(boolean plugged) { super(plugged); + super.setType("Dishwasher"); } + /** + * Method for simulating the devices' behaviour. + * Once started, it goes through every stage until it finishes washing + * the dishes (every stage has it's own power usage level. After that, + * it resets the device and turns it off automatically. + * + * @param seconds delta time to simulate for + * @param ventRPM air flow created by the vent + */ public void step(float seconds, float ventRPM) { if (!super.getPowerState()) { return; @@ -24,7 +69,11 @@ public class Dishwasher extends Appliance { unplug(); } } - + + /** + * Overridden method for turning on this device. + * It automatically sets it to the correct stage and delay. + */ @Override public void plug() { super.plug(); @@ -33,6 +82,10 @@ public class Dishwasher extends Appliance { nextPowerStateIn = 20f; } + /** + * Overridden method for turning this device off. + * It automatically resets the current washing stage to -1. + */ @Override public void unplug() { super.unplug(); @@ -40,6 +93,12 @@ public class Dishwasher extends Appliance { currentState = -1; } + /** + * Method for calculating the power consumption of this device. + * Power usage depends on the current washing stage. + * + * @return float showing current power consumption + */ public float getPowerConsumption() { if (super.getPowerState()) { return powerStates[currentState]; @@ -47,4 +106,18 @@ public class Dishwasher extends Appliance { return 0f; } } + + /** + * Method for printing this devices' object in a nice way. + * + * @return String containing text description of this devices' state + */ + @Override + public String toString() { + return String.format("Dishwasher(%s, %4.1fW, %2.1fs)", + super.getPowerState() ? "on" : "off", getPowerConsumption(), + super.getPowerState() + ? (3 - currentState) * 20 + nextPowerStateIn + : 0f); + } } diff --git a/labs/6/Main.java b/labs/6/Main.java index d347ef1..eae546a 100644 --- a/labs/6/Main.java +++ b/labs/6/Main.java @@ -1,8 +1,82 @@ -import java.util.concurrent.TimeUnit; +/* + * %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 = 3; + + /** Defines the amount of simulation samples to get per every second */ + private static final int TICKS_PER_SECOND = 720; + + /** 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 plugging the devices in and out. + * + * @param args accepts CLI arguments. + */ public static void main(String[] args) { - Vent vent = new Vent(true); + float ventConsumed = 0f; + float totalPowerConsumed = 0f; + + int outputMode = parseMode(args); + int liveStatsOutputDelay = parseOutputDelay(args); + + Vent vent = new Vent(false); // defining vent as a special appliance Appliance[] devices = { new ServerRack(false), @@ -11,53 +85,174 @@ public class Main { new Dishwasher(false) }; - devices[0].plug(); - devices[2].plug(); + devices[2].plug(); // turning on RPI right away + // (RPI stands for Raspberry Pi) - int i = 0; - float ventConsumed = 0f; - float totalPowerConsumed = 0f; - //for (int i = 0; i < 180*10; i++) { - while (true) { - step(vent, devices, 0.1f); - ventConsumed += vent.getPowerConsumption() * 0.1; - totalPowerConsumed += getTotalPowerConsumption(vent, devices) * 0.1 / 3600; - //System.out.print(String.format("Time: %02.1f; ", (float) i/10) - System.out.print("Time: " + floatFormat((float)i/10, 2, 1) + "; " - + getStats(vent, devices) + "; vent avg = " - + floatFormat(ventConsumed*10/i, 3, 1) - + "W; total = " - + floatFormat(totalPowerConsumed, 3, 3) + "W\r"); - if (devices[0].getPowerConsumption() > 205) { - vent.plug(); + // 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()); } - if (devices[0].getPowerConsumption() < 190) { - vent.unplug(); + 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]; } - if (i == 150) { - devices[1].plug(); - } else if (i == 300) { - devices[0].unplug(); - } else if (i == 400) { - devices[3].plug(); - } else if (i == 700) { - devices[0].plug(); - } + totalDevices[devices.length] = vent; - try { - //TimeUnit.SECONDS.sleep(1); - Thread.sleep(2); - } catch (Exception e) { - System.exit(0); - } + System.out.println("\nCurrently devices draw " + + floatFormat(getTotalPowerConsumption(totalDevices), 4, 2) + + "W from the power lines."); - i++; + Arrays.sort(totalDevices); + printAppliances(totalDevices); } } + /** + * 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 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 90; + } + + /** + * 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) { @@ -65,6 +260,15 @@ public class Main { } } + /** + * 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; @@ -77,25 +281,29 @@ public class Main { String result = "PPD: "; for (float i : powerConsumption) { - //result += String.format("%03.1fW ", i); result += String.format(floatFormat(i, 3, 1) + "W "); - } float powerLinesDraw = totalPowerConsumption + vent.getPowerConsumption(); - //result += String.format("; Vent: %03.1fW, %05.0f RPM; Total power: %04.1fW", result += "; Vent: " + floatFormat(vent.getPowerConsumption(), 3, 1) + "W, " + floatFormat(vent.getRPM(), 5, 0) + " RPM; Total power: " + floatFormat(powerLinesDraw, 4, 1) + "W"; - //vent.getPowerConsumption(), vent.getRPM(), - //totalPowerConsumption + vent.getPowerConsumption()); 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(); @@ -107,6 +315,34 @@ public class Main { 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); diff --git a/labs/6/RPI.java b/labs/6/RPI.java index 7187d4c..40a698f 100644 --- a/labs/6/RPI.java +++ b/labs/6/RPI.java @@ -1,11 +1,59 @@ +/* + * %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 . + */ + +/** + * Class which represents the RPI (Raspberry Pi) microcomputer. + * + * @author Dymik739 + * @since 0.3 + */ public class RPI extends Appliance { + /** Contains current power draw from the power supply. */ private float power = 15.0f; + + /** + * Defines the delay after the startup when the power usage starts to drop + * to it's lowest value. + */ private float postBootDecreaseIn = 10.0f; + /** + * Constructor for this class. + * + * @param plugged sets the power state on the beginning + */ public RPI(boolean plugged) { super(plugged); + super.setType("RPI"); } + /** + * Method which is used to simulate the device's behaviour. + * The device draws it's maximum power for postBootDecreaseIn + * seconds and gradually drops to it's lowest level, after that + * it always stays on the lowest power usage level until a + * reboot happens. + * + * @param seconds delta time to simulate for + * @param ventRPM air flow generated by the vent + */ public void step(float seconds, float ventRPM) { postBootDecreaseIn -= seconds; @@ -18,6 +66,24 @@ public class RPI extends Appliance { } } + /** + * Custom method for unplugging the device. + * Adds the automatic resetting to the default values right after + * turning the device off. + */ + @Override + public void unplug() { + super.unplug(); + + power = 15f; + postBootDecreaseIn = 10f; + } + + /** + * Method for getting the power draw of this device. + * + * @return current power consumption + */ public float getPowerConsumption() { if (super.getPowerState()) { return power; @@ -25,4 +91,15 @@ public class RPI extends Appliance { return 0f; } } + + /** + * Method for printing this devices' object in a nice way. + * + * @return String containing text description of this devices' state + */ + @Override + public String toString() { + return String.format("RPI(%s, %4.1fW)", + super.getPowerState() ? "on" : "off", getPowerConsumption()); + } } diff --git a/labs/6/ServerRack.java b/labs/6/ServerRack.java index d1cc71e..8a931ed 100644 --- a/labs/6/ServerRack.java +++ b/labs/6/ServerRack.java @@ -1,28 +1,124 @@ +/* + * %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 . + */ + +/** + * Class which represents a server rack. + * + * @author Dymik739 + * @since 0.3 + */ public class ServerRack extends Appliance { - private String type = "ServerRack"; + /** Defines power used by the rack at lowest temperature. */ private float basePower = 160.0f; + + /** Defines power limit for this rack */ private float maxPower = 350.0f; + + /** Contains the inner temperature of this rack */ private float temperature = 20f; + /** Defines current usage of this device */ + private float currentLoad = 0.2f; + + /** Predicts the future change of the load */ + private float loadVector = +1f; + + /** + * Constructor for this class. + * + * @param plugged defines if the device is plugged in at the start + */ public ServerRack(boolean plugged) { super(plugged); + super.setType("ServerRack"); } + /** + * Method for simulating this device behaviour. + * This rack processes video segments for the streaming platform and + * serves them to the public in different qualities. Every device requires + * the fragments to be encoded in it's respective format in order to play + * the stream. As such, the load rises when new fragment arrives and falls + * as it converts it into all the formats required. + * + * Temperature depends on many factors. Firstly, the device heats up while + * performing tasks and the rate is affected by: + * - current temperature (hotter = more power drawn); + * - load on the CPU (more load = more heat); + * + * Of course, the temperature can be reduced using the vent installed in + * the house. The rate of reduction is calculated using: + * - current temperature (the bigger the difference compared to the outside + * temperature, the larger impact the vent has on it); + * - air flow, created by the vent (the faster the air moves, the more + * heat it takes away from the system); + * + * This device can also cool itself down while standing still as the heat + * slowly transfers to the air even when the vent doesn't force it. + * The rate is calculated by only the temperature difference between inner + * and outer temperatures. + * + * @param seconds delta time to simulate for + * @param ventRPM air flow created by the vent + */ public void step(float seconds, float ventRPM) { - if (super.getPowerState()) { - temperature += min(basePower + (temperature - 20f) * 1.8f, maxPower) - * seconds * 0.024f; + currentLoad += loadVector/10 * seconds; + + if (currentLoad >= 1) { + loadVector = -1f; + } else if (currentLoad <= 0.2) { + loadVector = +1f; } - temperature -= (temperature - 20f) * ventRPM*0.0003f * seconds; - temperature -= 0.006f * (temperature - 20f) * seconds; + if (super.getPowerState()) { + temperature += min(basePower + (temperature - 20f) * 1.8f + * max(min(currentLoad, 1), 0), maxPower) * seconds + * 0.024f; + } + + temperature -= (temperature - 20f) * ventRPM * 0.00013f * seconds; + temperature -= 0.002f * (temperature - 20f) * seconds; } + /** + * Method which calculates power consumption if this device. + * + * @return power consumption of this device + */ public float getPowerConsumption() { if (super.getPowerState()) { - return min(basePower + (temperature - 20f) * 1.8f, maxPower); + return min(basePower + (temperature - 20f) * 1.8f * max(min(currentLoad, 1), 0), maxPower); } else { return 0f; } } + + /** + * Overridden toString() method for printing the state of this device. + * + * @return String representing current state of this device + */ + @Override + public String toString() { + return String.format("ServerRack(%s, %4.1fW, %3.1f℃C, %3.1f%%)", + super.getPowerState() ? "on" : "off", getPowerConsumption(), + temperature, currentLoad * 100); + } } diff --git a/labs/6/Vent.java b/labs/6/Vent.java index df62f3b..64336af 100644 --- a/labs/6/Vent.java +++ b/labs/6/Vent.java @@ -1,13 +1,111 @@ -public class Vent extends Appliance { - private float maxPower = 90.0f; - private float rpm = 0.0f; - private final float rotorInertia = 7.0f; - private final float maxRPM = 6500.0f; +/* + * %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 . + */ +/** + * Class that represents a vent installed in the house. + * This device acts as a reactive load. + * + * @author Dymik739 + * @since 0.3 + */ +public class Vent extends Appliance { + /** + * Power draw limit set by the digital controller, designed by a KPI + * student. Built with the JK-triggers, may malfunction sometimes. + * Exceeding this limit may damage the engine, so it's hard limited. + */ + private float maxPower = 80.0f; + + /** + * Current rotor revolutions per minute. + * Can also be defined as angular velocity or kinetic energy + * accumulated in the device. + */ + private float rpm = 0.0f; + + /** + * Defines inertia of the rotor. + * Allows the rotor to withstand forces changing it's angular speed. + */ + private final float rotorInertia = 2.0f; + + /** + * Target RPM the vent is tuned to maintain. + * Rotor draws full power until it reaches this speed, after that it + * draws only as much power as needed to maintain this speed. + * + * Might be tweaked up to match the exact RPM required, as the target + * RPM is more than actual RPM while running due to additional forces + * and failures in design of the microcontroller (it was also designed + * using JK-triggers as they were the ones that student used in their + * coursework last year). + */ + private final float maxRPM = 2013.0f; + + /** + * Constructor for this class. + * + * @param plugged defines if the device is plugged into the power + * network right away + */ public Vent(boolean plugged) { super(plugged); + super.setType("Vent"); } + /** + * Method for simulating the device behaviour. + * As was stated before in the class docs, this device has reactive + * properties when it comes to loading the network. This means, it + * doesn't only change it's power usage during operation, but also + * follows some real-world physics laws while running. + * + * First, it uses the power, limited by a device microcontroller, + * to gain angular velocity, measured in RPM. Right before it reaches + * it's target RPM, the power draw falls with the exponential decrement + * law (can be seen from the graph in --vent-monitoring-graph mode). + * + * Once it meets the target RPM, it draws power to only maintain it's + * speed (the power goes to withstand air forces trying to slow the + * fan - and the attached rotor - down). + * + * After the power cuts off, the fan keeps rotating due to it's inertia + * and, when plugged back in, starts getting back up to it's target speed + * according to it's current RPM. The power draw from the network always + * meets the power used to gain angular velocity of the rotor. + * + * Also, as this vent is forcing the air through, the blades experience + * the air drag - it always tries to slow the fan down. As such, the + * air rag force is always calculated and depends of the RPM, which + * is proportional to the force being put on the blades. + * + * And the engineering level is kind of weird: on one hand, they + * engineer a smark device that can manage the RPM and limit the power + * to the rotor, but on the other hand they're unable to deal with + * reverse polarity the rotor generates while running, so they've + * just soldered a single diode on the wire and thus limited possible + * power down to just 80W! At least, the vent is still functional, so + * I guess it's good enough... + * + * @param seconds delta time to simulate for + */ public void step(float seconds) { // electric current usage if (super.getPowerState()) { @@ -16,9 +114,15 @@ public class Vent extends Appliance { } // air drag (always present) - rpm -= (rpm / 200) / rotorInertia; + rpm -= (rpm / 20) / rotorInertia * seconds; } + /** + * Method for calculating current power consumption of this device. + * Calculations are similar to the step() method above. + * + * @return current power consumption of this device + */ public float getPowerConsumption() { if (super.getPowerState()) { return max(min(rotorInertia*(maxRPM - rpm), maxPower), 0); @@ -27,7 +131,24 @@ public class Vent extends Appliance { } } + /** + * Getter for RPM. + * + * @return current RPM + */ public float getRPM() { return rpm; } + + /** + * Method for ptinting out this device state in a nice way. + * + * @return string representation of this device state + */ + @Override + public String toString() { + return String.format("Vent(%s, %4.1fW, %4.0f RPM)", + super.getPowerState() ? "on" : "off", getPowerConsumption(), + rpm); + } }