/* * Copyright (c) 2005-2010 KOM � Multimedia Communications Lab * * This file is part of PeerfactSim.KOM. * * PeerfactSim.KOM 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 * any later version. * * PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see . * */ package de.tud.kom.p2psim.impl.energy.components; import java.util.Comparator; import java.util.LinkedList; import de.tud.kom.p2psim.api.energy.ComponentType; import de.tud.kom.p2psim.api.energy.EnergyEventListener; import de.tud.kom.p2psim.api.energy.EnergyState; import de.tud.kom.p2psim.impl.energy.DefaultEnergyState; import de.tudarmstadt.maki.simonstrator.api.Time; /** * Component for devices that provide thrust, such as electrical motors for UAV propulsion. * Is configured by {@link MotorCharacteristic}s with a given thrust (N) and a given current (A). * Values in between given characteristics are calculated by linear interpolation. * * @author Julian Zobel * @version 1.0, 05.03.2019 */ public class StatelessActuatorComponent implements ActuatorComponent { public enum componentState {OFF, ON} private componentState state; private EnergyEventListener energyModel; private long lastEnergyConsumationTime; private double volts; private final double uJconversionFactor = 1000000; private final int numberOfActuators; private double thrust; private double amps; private EnergyState energyState; private LinkedList characteristics = new LinkedList<>(); public StatelessActuatorComponent(int numberOfActuators, double volt) { this.volts = volt; this.numberOfActuators = numberOfActuators; this.state = componentState.OFF; this.lastEnergyConsumationTime = Time.getCurrentTime(); thrust = 0; amps = 0; energyState = new DefaultEnergyState("OFF", 0); } /** * Get the maximum thrust provided by this component. * @return */ public double getMaxThrust() { return characteristics.getLast().getThrust() * numberOfActuators; } /** * Set the new energy state and calculate the energy consumption from the last state */ private void setEnergyState() { // set the new energy state EnergyState newState; if(state == componentState.ON) { newState = new DefaultEnergyState("Actuator", numberOfActuators * (amps * volts) * uJconversionFactor); } else { newState = new DefaultEnergyState("OFF", 0); } // calculate energy consumption for the previous state long timeSpentInState = Time.getCurrentTime() - lastEnergyConsumationTime; double cons = calculateEnergyConsumation(energyState, timeSpentInState); energyModel.componentConsumedEnergy(this, cons); // set new state energyState = newState; lastEnergyConsumationTime = Time.getCurrentTime(); } /** * Request a given amount of thrust to be provided from this component. If the amount is less than the minimum * or more than the maximum, the minimum or maximum thrust values, respectively, are enforced. * * @param targetThrust * @return The amount of thrust this component now generates. */ public double requestThrust(double targetThrust) { if(targetThrust == 0 || targetThrust <= numberOfActuators * characteristics.getFirst().getThrust()) { setLoad(characteristics.getFirst()); } else if(targetThrust >= numberOfActuators * characteristics.getLast().getThrust()) { setLoad(characteristics.getLast()); } else { calculateAndSetThrustRelatedAmpereDraw(targetThrust); } return this.thrust; } /** * * @param targetThrust * @return The power consumption for the target thrust in Watt */ public double estimatePowerConsumptionWatt(double targetThrust) { if(targetThrust == 0 || targetThrust <= numberOfActuators * characteristics.getFirst().getThrust()) { // not allowed return Double.NaN; } else if(targetThrust > numberOfActuators * characteristics.getLast().getThrust()) { // not allowed return Double.NaN; } else { double amps = approximateAmpereDraw(targetThrust); //System.out.println(amps); return numberOfActuators * amps * volts; } } /** * Given an amount of thrust between the minimum and maximum values, the required current * to provide this amount of thrust is calculated by linear interpolation by the nearest lower * and upper {@link MotorCharacteristic}s. * * @param targetThrust * @return the approximated ampere draw */ private double approximateAmpereDraw(double targetThrust) { MotorCharacteristic lower = null, upper = null; // find the lower and upper bounding characteristics for (MotorCharacteristic ch : characteristics) { // if(ch.getThrust() * numberOfActuators == targetThrust) { return ch.getCurrent(); } else { // list is sorted, lower bound is the biggest that is lower if(ch.getThrust() * numberOfActuators < targetThrust) { lower = ch; } // the first that is greater is used as upper bound else if(ch.getThrust() * numberOfActuators > targetThrust) { upper = ch; break; } } } if(upper == null || lower == null) { throw new UnsupportedOperationException("Lower or upper bounds cannot be null"); } if(upper.getThrust() * numberOfActuators < targetThrust || lower.getThrust() * numberOfActuators > targetThrust) { throw new UnsupportedOperationException("Lower or upper bound do not match"); } /* * Calculate the approximated current with the upper and lower bounds: * Amp_approx = Amp_lower + (T_target - T_lower)/(T_upper - T_lower) * (Amp_upper - Amp_lower) */ double delta = (targetThrust - (lower.getThrust() * numberOfActuators))/(numberOfActuators * (upper.getThrust() - lower.getThrust())); return lower.getCurrent() + delta * (upper.getCurrent() - lower.getCurrent()); } /** * Approximates the ampere draw required forthe requested thrust * * Target thrust should be strictly within the possible thrust limits * */ private void calculateAndSetThrustRelatedAmpereDraw(double targetThrust) { double calculatedAmps = approximateAmpereDraw(targetThrust); setLoad(targetThrust, calculatedAmps); } private void setLoad(double thrust, double amps) { this.thrust = thrust; this.amps = amps; setEnergyState(); } private void setLoad(MotorCharacteristic ch) { this.thrust = ch.getThrust(); this.amps = ch.getCurrent(); setEnergyState(); } /** * Add a {@link MotorCharacteristic} for this motor. * * @param c */ public void addChar(MotorCharacteristic c) { characteristics.add(c); // sort the characteristics starting from low to high thrust characteristics.sort(new Comparator() { @Override public int compare(MotorCharacteristic o1, MotorCharacteristic o2) { return (int) (o1.getThrust() - o2.getThrust()); } }); } @Override public void eventOccurred(Object content, int type) { // TODO Auto-generated method stub } @Override public ComponentType getType() { return ComponentType.ACTUATOR; } @Override public boolean turnOff() { this.thrust = 0; this.amps = 0; this.state = componentState.OFF; setEnergyState(); return true; } @Override public boolean turnOn() { if (isAvailable()) { if(this.state != componentState.ON) { this.state = componentState.ON; requestThrust(0); } return true; } return false; } public boolean isAvailable() { if (energyModel.componentCanBeActivated(this)) { return true; } return false; } @Override public boolean isOn() { if(this.state != componentState.OFF && isAvailable()) { return true; } return false; } @Override public void setEnergyEventListener(EnergyEventListener listener) { energyModel = listener; } public EnergyState getCurrentState() { return energyState; } }