From 377f6971b069814acf64b7a6269473eec03bdd43 Mon Sep 17 00:00:00 2001 From: marlon Date: Wed, 18 Jun 2014 22:35:55 -0500 Subject: [PATCH] Pre-Test --- src/main/java/Main.java | 15 ++- .../ConicalTankTransferFunction.java | 101 +++++++++++++++ src/main/java/gui/Gui.java | 116 ++++++++++++------ src/main/java/process/FirstOrderSystem.java | 78 +++++++++--- src/main/java/process/SystemSimulator.java | 64 ++++++++++ 5 files changed, 320 insertions(+), 54 deletions(-) create mode 100644 src/main/java/conicaltank/ConicalTankTransferFunction.java create mode 100644 src/main/java/process/SystemSimulator.java diff --git a/src/main/java/Main.java b/src/main/java/Main.java index 5ffed27..9d95276 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -1,10 +1,21 @@ -import process.SecondOrderSystem; +import gui.Gui; +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.stage.Stage; /** * Created by marlon on 6/13/14. */ -public class Main { +public class Main extends Application { public static void main(String[] args){ + launch(args); + } + @Override + public void start(Stage primaryStage) throws Exception { + Gui gui = new Gui(); + Scene scene = new Scene(gui); + primaryStage.setScene(scene); + primaryStage.show(); } } diff --git a/src/main/java/conicaltank/ConicalTankTransferFunction.java b/src/main/java/conicaltank/ConicalTankTransferFunction.java new file mode 100644 index 0000000..48a459a --- /dev/null +++ b/src/main/java/conicaltank/ConicalTankTransferFunction.java @@ -0,0 +1,101 @@ +package conicaltank; + +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.DoublePropertyBase; + +/** + * Created by marlon on 6/18/14. + * + * G(s) = gain + * --------- + * tau*s + 1 + */ +public class ConicalTankTransferFunction { + + // PROPERTIES // + private static final double HEIGHT = 10.0; + private static final double RADIUS = 5.0; + private static final double GRAVITY = 9.8; + private static final double OBSTRUCTION = 1.5; + + private DoubleProperty heightOperationPoint; + private DoubleProperty inflowOperationPoint; + + private double gain; + private double tau; + + // CONSTRUCTOR // + public ConicalTankTransferFunction() { + this.heightOperationPoint = new DoublePropertyBase(0.0001) { + @Override protected void invalidated() { + set(get()); + } + @Override public Object getBean() { + return this; + } + @Override public String getName() { + return "heightOperationPoint"; + } + }; + this.inflowOperationPoint = new DoublePropertyBase(1.0) { + @Override + protected void invalidated() { + recalculate(); + set(get()); + } + @Override public Object getBean() { + return this; + } + @Override public String getName() { + return "inflowOperationPoint"; + } + }; + recalculate(); + } + + // METHODS // + private void recalculate(){ + double alpha = ((9/2) * + (OBSTRUCTION * Math.pow(HEIGHT,2) * Math.sqrt(2*GRAVITY) * Math.pow(heightOperationPoint.get(), -5/2)) / + (2 * Math.PI * Math.pow(RADIUS,2))) - + ((6 * Math.pow(HEIGHT,2) * inflowOperationPoint.get() * Math.pow(heightOperationPoint.get(),-3)) / + (Math.PI * Math.pow(RADIUS,2))); + + double beta = (3 * Math.pow(HEIGHT,2) * Math.pow(heightOperationPoint.get(),-2)) / + (Math.PI * Math.pow(RADIUS,2)); + + gain = beta / alpha; + tau = 1 / alpha; + } + + // SETTERS AND GETTERS // + public double getGain() { + recalculate(); + return gain; + } + public double getTau() { + recalculate(); + return tau; + } + + + public double getHeightOperationPoint() { + return heightOperationPoint.get(); + } + public DoubleProperty heightOperationPointProperty() { + return heightOperationPoint; + } + public void setHeightOperationPoint(double heightOperationPoint) { + this.heightOperationPoint.set(heightOperationPoint); + } + + public double getInflowOperationPoint() { + return inflowOperationPoint.get(); + } + public DoubleProperty inflowOperationPointProperty() { + return inflowOperationPoint; + } + public void setInflowOperationPoint(double inflowOperationPoint) { + this.inflowOperationPoint.set(inflowOperationPoint); + } +} diff --git a/src/main/java/gui/Gui.java b/src/main/java/gui/Gui.java index 8998d90..4d2d108 100644 --- a/src/main/java/gui/Gui.java +++ b/src/main/java/gui/Gui.java @@ -1,42 +1,54 @@ package gui; -import javafx.beans.property.*; +import conicaltank.ConicalTankTransferFunction; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; import javafx.scene.chart.CategoryAxis; import javafx.scene.chart.LineChart; import javafx.scene.chart.NumberAxis; import javafx.scene.chart.XYChart; import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.MenuItem; import javafx.scene.control.TextField; import javafx.scene.layout.AnchorPane; +import process.SystemSimulator; import java.io.IOException; +import java.net.URL; import java.sql.Timestamp; +import java.util.ResourceBundle; /** * Created by marlon on 6/16/14. */ -public class Gui extends AnchorPane{ +public class Gui extends AnchorPane implements Initializable{ private static final int TRENDING_DATA_LIMIT = 50; private static final long SAMPLING_TIME = 500; - @FXML private TextField tankAreaTextField; - @FXML private TextField gravityTextField; - @FXML private TextField obstructionTextField; - @FXML private TextField samplingTimeTextField; - @FXML private TextField kp; - @FXML private TextField Ti; - @FXML private Button applyButton; - @FXML private CategoryAxis categoryAxis; - @FXML private NumberAxis numberAxis; - @FXML private LineChart trendings; + @FXML private TextField heightSetPointTextField; + @FXML private Button newHeightEnteredButton; + @FXML private MenuItem startSimulationMenuItem; + @FXML private MenuItem stopSimulationMenuItem; + @FXML private Label heightOperationPointLabel; + @FXML private Label inflowOperationPointLabel; + @FXML private Label samplingTimeLabel; + @FXML private Label kpLabel; + @FXML private Label kiLabel; + @FXML private Label tauLabel; + @FXML private Label gainLabel; + @FXML private Label inputLabel; + @FXML private Label outputLabel; - private DoubleProperty output; - private long lastUpdate; - private ObjectProperty timestamp; + @FXML private CategoryAxis categoryAxis; + @FXML private NumberAxis numberAxis; + @FXML private LineChart trendings; + private final XYChart.Series outputTrendingSeries; - private final XYChart.Series systemOutput = new XYChart.Series<>(); + private ConicalTankTransferFunction conicalTank; + private SystemSimulator processSimulator; + private long lastUpdate; public Gui() { try { @@ -48,32 +60,64 @@ public Gui() { throw new RuntimeException(exception); } - output = new SimpleDoubleProperty(this, "systemOutput", 0.0); - lastUpdate = System.currentTimeMillis(); - timestamp = new ObjectPropertyBase(new Timestamp(System.currentTimeMillis())) { - @Override protected void invalidated() { - if ((System.currentTimeMillis() - lastUpdate) > SAMPLING_TIME) { - systemOutput.getData().add(new XYChart.Data<>(output.getValue(), new Timestamp(System.currentTimeMillis()))); - while (systemOutput.getData().size() > TRENDING_DATA_LIMIT) { - systemOutput.getData().remove(0); - } - } - } - @Override public Object getBean() { - return this; - } - @Override public String getName() { - return "timestamp"; - } - }; - + outputTrendingSeries = new XYChart.Series<>(); numberAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(numberAxis) { @Override public String toString(Number object) { return String.format("%6.2f", object); } }); - trendings.getData().add(systemOutput); + trendings.getData().add(outputTrendingSeries); + + conicalTank = new ConicalTankTransferFunction(); + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + registerListeners(); + } + + private void registerListeners() { + startSimulationMenuItem.setOnAction(value -> startSimulation()); + stopSimulationMenuItem.setOnAction(value -> stopSimulation()); + newHeightEnteredButton.setOnAction(value -> recalculate()); + + heightOperationPointLabel.textProperty().bind(conicalTank.heightOperationPointProperty().asString()); + inflowOperationPointLabel.textProperty().bind(conicalTank.inflowOperationPointProperty().asString()); + samplingTimeLabel.textProperty().bind(processSimulator.getProcess().samplingTimeProperty().asString()); + tauLabel.textProperty().bind(processSimulator.getProcess().tauProperty().asString()); + gainLabel.textProperty().bind(processSimulator.getProcess().gainProperty().asString()); + inputLabel.textProperty().bind(processSimulator.getProcess().inputProperty().asString()); + outputLabel.textProperty().bind(processSimulator.getProcess().outputProperty().asString()); + processSimulator.timeStampProperty().addListener((observable, oldValue, newValue) -> updateTrending(newValue)); +// kpLabel; +// kiLabel; + } + + private void startSimulation(){ + lastUpdate = System.currentTimeMillis(); + processSimulator = new SystemSimulator(); + processSimulator.run(); + } + + private void stopSimulation(){ + processSimulator.interrupt(); + } + + private void updateTrending(Timestamp currentTime){ + if ((System.currentTimeMillis() - lastUpdate) > SAMPLING_TIME) { + outputTrendingSeries.getData().add(new XYChart.Data<>(processSimulator.getProcess().getOutput(), currentTime)); + while (outputTrendingSeries.getData().size() > TRENDING_DATA_LIMIT) { + outputTrendingSeries.getData().remove(0); + } + lastUpdate = currentTime.getTime(); + } + } + + private void recalculate() { + conicalTank.setHeightOperationPoint(Double.parseDouble(heightOperationPointLabel.getText())); + processSimulator.getProcess().setGain(conicalTank.getGain()); + processSimulator.getProcess().setTau(conicalTank.getTau()); } } diff --git a/src/main/java/process/FirstOrderSystem.java b/src/main/java/process/FirstOrderSystem.java index 0d1e710..a49aa73 100644 --- a/src/main/java/process/FirstOrderSystem.java +++ b/src/main/java/process/FirstOrderSystem.java @@ -1,36 +1,82 @@ package process; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.LongProperty; +import javafx.beans.property.SimpleDoubleProperty; +import javafx.beans.property.SimpleLongProperty; + /** * Created by marlon on 6/16/14. * * The transfer function is in form: * - * G(s) = zero - * ------ - * s+pole + * G(s) = gain + * --------- + * tau*s + 1 * */ public class FirstOrderSystem { - private double zero; - private double pole; + private DoubleProperty input; + private DoubleProperty output; + private DoubleProperty gain; + private DoubleProperty tau; + private LongProperty samplingTime; - public FirstOrderSystem(double zero) { - this.zero = zero; + public FirstOrderSystem() { + input = new SimpleDoubleProperty(this, "input", 0.0); + output = new SimpleDoubleProperty(this, "output", 0.0); + gain = new SimpleDoubleProperty(this, "gaing", 0.0); + tau = new SimpleDoubleProperty(this, "tau", 0.0); + samplingTime = new SimpleLongProperty(this, "samplingTime", 100); } - public double getZero() { - return zero; + public double getInput() { + return input.get(); + } + public DoubleProperty inputProperty() { + return input; + } + public void setInput(double input) { + this.input.set(input); } - public void setZero(double zero) { - this.zero = zero; + public double getOutput() { + return output.get(); + } + public DoubleProperty outputProperty() { + return output; + } + public void setOutput(double output) { + this.output.set(output); } - public double getPole() { - return pole; + public double getGain() { + return gain.get(); + } + public DoubleProperty gainProperty() { + return gain; + } + public void setGain(double gain) { + this.gain.set(gain); } - public void setPole(double pole) { - this.pole = pole; + public double getTau() { + return tau.get(); + } + public DoubleProperty tauProperty() { + return tau; + } + public void setTau(double tau) { + this.tau.set(tau); + } + + public long getSamplingTime() { + return samplingTime.get(); + } + public LongProperty samplingTimeProperty() { + return samplingTime; + } + public void setSamplingTime(long samplingTime) { + this.samplingTime.set(samplingTime); } -} +} \ No newline at end of file diff --git a/src/main/java/process/SystemSimulator.java b/src/main/java/process/SystemSimulator.java new file mode 100644 index 0000000..51cfaa1 --- /dev/null +++ b/src/main/java/process/SystemSimulator.java @@ -0,0 +1,64 @@ +package process; + +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; + +import java.sql.Time; +import java.sql.Timestamp; + +/** + * Created by marlon on 6/17/14. + * + * | gain | + * Input ------>| --------- |-------> Output + * | tau*s + 1 | + * + */ +public class SystemSimulator extends Thread{ + + private FirstOrderSystem process; + private double[] vz; + private ObjectProperty timeStamp; + + public SystemSimulator() { + setName("OpenLoopSimulator"); + setDaemon(true); + process = new FirstOrderSystem(); + vz = new double[2]; + timeStamp = new SimpleObjectProperty<>(this, "timestamp", new Timestamp(System.currentTimeMillis())); + } + + @Override + public void run() { + while(true){ + vz[0] = process.getInput() - ((process.getSamplingTime() - 2 * process.getTau()) / (process.getSamplingTime() + 2 * process.getTau())) * vz[1]; + process.setOutput(((process.getGain() * process.getSamplingTime()) / (process.getSamplingTime() + 2 * process.getTau())) * (vz[0] + vz[1])); + + try { + Thread.sleep(process.getSamplingTime()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + vz[1] = vz[0]; + timeStamp.set(new Timestamp(System.currentTimeMillis())); + } + } + + public FirstOrderSystem getProcess() { + return process; + } + public void setProcess(FirstOrderSystem process) { + this.process = process; + } + + public Timestamp getTimeStamp() { + return timeStamp.get(); + } + public ObjectProperty timeStampProperty() { + return timeStamp; + } + public void setTimeStamp(Timestamp timeStamp) { + this.timeStamp.set(timeStamp); + } +}