/*
* 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.EnergyComponent;
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 StatelessMotorComponent implements EnergyComponent {
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 StatelessMotorComponent(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();
}
/**
* 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);
System.out.println("consumed energy : " + (cons/uJconversionFactor) + " in " + Time.getFormattedTime(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 <= characteristics.getFirst().getThrust()) {
setLoad(characteristics.getFirst());
}
else if(targetThrust >= characteristics.getLast().getThrust()) {
setLoad(characteristics.getLast());
}
else {
approximateThrustCurrentRelation(targetThrust);
}
System.out.println("Now providing Thrust: " + this.thrust + " N");
return this.thrust;
}
/**
* 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
*/
private void approximateThrustCurrentRelation(double targetThrust) {
MotorCharacteristic lower = null, upper = null;
// find the lower and upper bounding characteristics
for (MotorCharacteristic ch : characteristics) {
//
if(ch.getThrust() == targetThrust) {
setLoad(ch);
return;
}
else {
// list is sorted, lower bound is the biggest that is lower
if(ch.getThrust() < targetThrust) {
lower = ch;
}
// the first that is greater is used as upper bound
else if(ch.getThrust() > targetThrust) {
upper = ch;
break;
}
}
}
if(upper == null || lower == null) {
throw new UnsupportedOperationException("Lower or upper bounds cannot be null");
}
if(upper.getThrust() < targetThrust || lower.getThrust() > 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())/(upper.getThrust() - lower.getThrust());
double calculatedAmps = lower.getCurrent() + delta * (upper.getCurrent() - lower.getCurrent());
System.out.println(targetThrust + " N targeted => " + calculatedAmps + " A calculated | " + delta + " <> " + lower + " " + upper);
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;
}
}