/*
* JaamSim Discrete Event Simulation
* Copyright (C) 2014 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.Thresholds;
import com.jaamsim.DisplayModels.ShapeModel;
import com.jaamsim.basicsim.EntityTarget;
import com.jaamsim.basicsim.ErrorException;
import com.jaamsim.events.Conditional;
import com.jaamsim.events.EventManager;
import com.jaamsim.events.ProcessTarget;
import com.jaamsim.input.BooleanInput;
import com.jaamsim.input.ColourInput;
import com.jaamsim.input.ExpError;
import com.jaamsim.input.ExpEvaluator;
import com.jaamsim.input.ExpressionInput;
import com.jaamsim.input.Input;
import com.jaamsim.input.Keyword;
import com.jaamsim.input.Output;
import com.jaamsim.math.Color4d;
import com.jaamsim.units.DimensionlessUnit;
public class ExpressionThreshold extends Threshold {
@Keyword(description = "The logical condition for the ExpressionThreshold to open.",
exampleList = { "'[Queue1].QueueLength > 3'" })
private final ExpressionInput openCondition;
@Keyword(description = "The logical condition for the ExpressionThreshold to close.\n"
+ "If not specified, the CloseCondition defaults to the opposite of the "
+ "OpenCondition. If the OpenCondition and CloseCondition are both TRUE, "
+ "then the ExpressionThreshold is set to open.",
exampleList = { "'[Queue1].QueueLength < 2'" })
private final ExpressionInput closeCondition;
@Keyword(description = "The initial state for the ExpressionThreshold: "
+ "TRUE = Open, FALSE = Closed.\n"
+ "This input is only relevant when the CloseCondition input is used "
+ "and both the OpenCondition and CloseCondition are FALSE at the "
+ "start of the simulation run. Otherwise, the initial state is "
+ "determined explicitly by the OpenCondition and CloseCondition.",
exampleList = { "TRUE" })
private final BooleanInput initialOpenValue;
@Keyword(description = "The colour of the ExpressionThreshold graphic when the threshold "
+ "condition is open, but the gate is still closed.",
exampleList = { "yellow" })
private final ColourInput pendingOpenColour;
@Keyword(description = "The colour of the ExpressionThreshold graphic when the threshold "
+ "condition is closed, but the gate is still open.",
exampleList = { "magenta" })
private final ColourInput pendingClosedColour;
@Keyword(description = "A Boolean value. If TRUE, the ExpressionThreshold displays the "
+ "pending open and pending closed states.",
exampleList = { "FALSE" })
private final BooleanInput showPendingStates;
private boolean lastOpenValue; // state of the threshold that was calculated on-demand
{
attributeDefinitionList.setHidden(false);
openCondition = new ExpressionInput("OpenCondition", "Key Inputs", null);
openCondition.setEntity(this);
openCondition.setRequired(true);
this.addInput(openCondition);
closeCondition = new ExpressionInput("CloseCondition", "Key Inputs", null);
closeCondition.setEntity(this);
this.addInput(closeCondition);
initialOpenValue = new BooleanInput("InitialOpenValue", "Key Inputs", false);
this.addInput(initialOpenValue);
pendingOpenColour = new ColourInput("PendingOpenColour", "Graphics", ColourInput.YELLOW);
this.addInput(pendingOpenColour);
this.addSynonym(pendingOpenColour, "PendingOpenColor");
pendingClosedColour = new ColourInput("PendingClosedColour", "Graphics", ColourInput.PURPLE);
this.addInput(pendingClosedColour);
this.addSynonym(pendingClosedColour, "PendingClosedColor");
showPendingStates = new BooleanInput("ShowPendingStates", "Graphics", true);
this.addInput(showPendingStates);
}
public ExpressionThreshold() {}
@Override
public void earlyInit() {
super.earlyInit();
lastOpenValue = initialOpenValue.getValue();
lastOpenValue = this.getOpenConditionValue(0.0);
}
@Override
public void updateForInput(Input<?> in) {
super.updateForInput(in);
if (in == openCondition || in == closeCondition || in == initialOpenValue) {
lastOpenValue = initialOpenValue.getValue();
this.setInitialOpenValue(this.getOpenConditionValue(0.0));
return;
}
}
@Override
public void startUp() {
super.startUp();
doOpenClose();
}
/**
* Loops from one state change to the next.
*/
void doOpenClose() {
// Set the present state
setOpen(this.getOpenConditionValue(this.getSimTime()));
// Wait until the state is ready to change
EventManager.scheduleUntil(doOpenClose, openChanged, null);
}
/**
* Returns true if the saved state differs from the state implied by the OpenCondition
* and CloseCondition
* @return true if the state has changed
*/
boolean openStateChanged() {
return getOpenConditionValue(getSimTime()) != super.isOpen();
}
/**
* Returns the state implied by the present values for the OpenCondition
* and CloseCondition expressions.
* @param simTime - present simulation time.
* @return state implied by the OpenCondition and CloseCondition expressions.
*/
private boolean getOpenConditionValue(double simTime) {
try {
if (openCondition.getValue() == null)
return super.isOpen();
// Evaluate the open condition (0 = false, non-zero = true)
boolean openCond = ExpEvaluator.evaluateExpression(openCondition.getValue(),
simTime).value != 0;
// If the open condition is satisfied or there is no close condition, then we are done
boolean ret;
if (openCond || closeCondition.getValue() == null) {
ret = openCond;
}
// The open condition is false
else {
// If the close condition is satisfied, then the threshold is closed
boolean closeCond = ExpEvaluator.evaluateExpression(closeCondition.getValue(),
simTime).value != 0;
if (closeCond) {
ret = false;
}
// If the open and close conditions are both false, then the state is unchanged
else {
ret = lastOpenValue;
}
}
// Save the threshold's last state (unless called by the UI thread)
if (EventManager.hasCurrent())
lastOpenValue = ret;
return ret;
}
catch (ExpError e) {
throw new ErrorException(this, e);
}
}
@Override
public boolean isOpen() {
// If called from the user interface, return the saved state
if (!EventManager.hasCurrent())
return super.isOpen();
// Determine the state implied by the OpenCondition and CloseCondition expressions
boolean ret = this.getOpenConditionValue(getSimTime());
// If necessary, schedule an event to change the saved state
if (ret != super.isOpen())
this.scheduleProcessTicks(0, 2, setOpenTarget);
// Return the value calculated on demand
return ret;
}
/**
* Conditional that tests whether the state has changed
*/
class OpenChangedConditional extends Conditional {
@Override
public boolean evaluate() {
return ExpressionThreshold.this.openStateChanged();
}
}
private final Conditional openChanged = new OpenChangedConditional();
/**
* ProcessTarget the executes the doOpenClose() method
*/
class DoOpenCloseTarget extends ProcessTarget {
@Override
public String getDescription() {
return ExpressionThreshold.this.getName() + ".doOpenClose";
}
@Override
public void process() {
doOpenClose();
}
}
private final ProcessTarget doOpenClose = new DoOpenCloseTarget();
private final SetOpenTarget setOpenTarget = new SetOpenTarget(this);
private static class SetOpenTarget extends EntityTarget<ExpressionThreshold> {
SetOpenTarget(ExpressionThreshold thresh) {
super(thresh, "setOpen");
}
@Override
public void process() {
ent.setOpen(ent.getOpenConditionValue(ent.getSimTime()));
}
}
@Override
public void updateGraphics(double simTime) {
super.updateGraphics(simTime);
// Trap the pending cases
if (!showPendingStates.getValue())
return;
boolean threshOpen = super.isOpen();
try {
if (getOpenConditionValue(simTime) == threshOpen)
return;
}
catch (Throwable t) {
return;
}
// Select the colour
Color4d col;
if (threshOpen)
col = pendingClosedColour.getValue();
else
col = pendingOpenColour.getValue();
// Display the threshold icon
setTagVisibility(ShapeModel.TAG_CONTENTS, true);
setTagVisibility(ShapeModel.TAG_OUTLINES, true);
setTagColour(ShapeModel.TAG_CONTENTS, col);
setTagColour(ShapeModel.TAG_OUTLINES, ColourInput.BLACK);
}
@Output(name = "Open",
description = "If open, then return TRUE. Otherwise, return FALSE.",
unitType = DimensionlessUnit.class)
@Override
public Boolean getOpen(double simTime) {
return this.getOpenConditionValue(simTime);
}
}