/*
* JaamSim Discrete Event Simulation
* Copyright (C) 2013 Ausenco Engineering Canada Inc.
* Copyright (C) 2016 JaamSim Software Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jaamsim.CalculationObjects;
import com.jaamsim.Samples.SampleConstant;
import com.jaamsim.Samples.SampleInput;
import com.jaamsim.Samples.SampleProvider;
import com.jaamsim.events.EventManager;
import com.jaamsim.input.Input;
import com.jaamsim.input.Keyword;
import com.jaamsim.input.Output;
import com.jaamsim.input.UnitTypeInput;
import com.jaamsim.ui.FrameBox;
import com.jaamsim.units.Unit;
import com.jaamsim.units.UserSpecifiedUnit;
/**
* DoubleCalculation is the super-class for all calculations that return a double.
* @author Harry King
*
*/
public abstract class DoubleCalculation extends CalculationEntity
implements SampleProvider {
@Keyword(description = "The unit type for the input value(s) to the calculation.",
exampleList = {"DistanceUnit"})
protected final UnitTypeInput unitType;
@Keyword(description = "The input value for the present calculation.\n "
+ "The input can be a number or an entity that returns a number, such as "
+ "a CalculationObject, an Expression, a ProbabilityDistribution, or a "
+ "TimeSeries.",
exampleList = {"1.5", "TimeSeries1", "'3 + 2*[Queue1].QueueLength'"})
protected final SampleInput inputValue;
private double lastUpdateTime; // The time at which the last update was performed
private double lastInputValue; // Input to this object evaluated at the last update time
private double lastValue; // Output from this object evaluated at the last update time
protected Class<? extends Unit> outUnitType; // Unit type for the output from this calculation
{
unitType = new UnitTypeInput("UnitType", "Key Inputs", UserSpecifiedUnit.class);
unitType.setRequired(true);
this.addInput(unitType);
SampleConstant def = new SampleConstant(UserSpecifiedUnit.class, 0.0d);
inputValue = new SampleInput("InputValue", "Key Inputs", def);
inputValue.setUnitType(UserSpecifiedUnit.class);
inputValue.setEntity(this);
this.addInput(inputValue);
}
public DoubleCalculation() {}
@Override
public void updateForInput(Input<?> in) {
super.updateForInput(in);
if (in == unitType) {
Class<? extends Unit> ut = unitType.getUnitType();
this.setUnitType(ut);
FrameBox.reSelectEntity(); // Update the units in the Output Viewer
return;
}
}
protected void setUnitType(Class<? extends Unit> ut) {
inputValue.setUnitType(ut);
outUnitType = ut;
}
@Override
public Class<? extends Unit> getUnitType() {
return outUnitType;
}
@Override
public Class<? extends Unit> getUserUnitType() {
return outUnitType;
}
@Override
public void earlyInit() {
super.earlyInit();
lastUpdateTime = 0.0;
lastValue = this.getInitialValue();
}
public double getInitialValue() {
return 0.0;
}
/**
* Returns the value for the input to this calculation object at the
* specified simulation time.
* @param simTime - specified simulation time.
* @return input value to this calculation object.
*/
public double getInputValue(double simTime) {
// An exception will be generated if the model has an infinite loop causing the
// call stack size to be exceeded
double ret = lastInputValue;
try {
ret = inputValue.getValue().getNextSample(simTime);
} catch(Exception e) {
if (EventManager.hasCurrent()) {
error("Closed loop detected in calculation. Insert a UnitDelay object.");
}
}
return ret;
}
/*
* Return the stored value for this calculation.
*/
public double getLastValue() {
return lastValue;
}
/**
* Returns the output value at the specified simulation time.
* <p>
* This method returns an output value that varies smoothly between the
* values stored at each update.
* @param simTime - specified simulation time.
* @param inputVal - input value at the specified simulation time.
* @param lastTime - simulation time when the most recent update was performed.
* @param lastInputVal - input value when the most recent update was performed.
* @param lastVal - output value when the moset recent update was performed.
* @return output value at the specified simulation time.
*/
protected abstract double calculateValue(double simTime, double inputVal, double lastTime, double lastInputVal, double lastVal);
@Override
public void update(double simTime) {
// Calculate the new input value to the calculation
double inputVal = getInputValue(simTime);
// Calculate the new output value
double newValue = this.calculateValue(simTime, inputVal, lastUpdateTime, lastInputValue, lastValue);
// Store the new input and output values
lastUpdateTime = simTime;
lastInputValue = inputVal;
lastValue = newValue;
}
@Override
@Output(name = "Value",
description = "The result of the calculation at the present time.",
unitType = UserSpecifiedUnit.class)
public double getNextSample(double simTime) {
// Calculate the new input value to the calculation
double inputVal = getInputValue(simTime);
// Return the new output value
return this.calculateValue(simTime, inputVal, lastUpdateTime, lastInputValue, lastValue);
}
@Override
public double getMeanValue(double simTime) {
return lastValue;
}
@Override
public double getMinValue() {
return lastValue;
}
@Override
public double getMaxValue() {
return lastValue;
}
}