Hallo Zusammen,
ich stehe mal wieder auf dem Schlauch und bräuchte Eure Hilfe bei folgendem Problem. Hier kurz zu dem was ich machen möchte.
Ich möchte, nachdem ich über mein Programm die Datenbank Verbindung erfolgreich getestet habe, einen Button anbieren der einen s.g. Database Monitor Window startet, in dem es nur ein AreaChart gibt der fortlaufend die TPS (Transaction per second) Values der DB anzeigt.
Ich habe mir viel angeschaut und rum probiert, bin dann schließlich bei den Oracle JvaFX Beispielen in "Ensemble" - eine coole Beispiel Sammlung, auf das Beispiel "StockLineChartApp" gestoßen. Ich habe dieses Beispiel dann so gut wie ich kann und ich es vesrtanden habe auf meine Anwendung umzuschreiben, und dabei ist dann folgende Applikation rausgekommen:
Das funktioniert auch sehr gut.
Ohne Licht kein Schatten - nun zu meinem Problem - eigentlich klingt es einfach - doch irgendwie bin ich zu blöd um es zu verstehen wie das geht ...
Wie oben geschrieben habe ich ja bereits eine Applikation die DB Verbindungen testet, und die mit der Hilfe hier aus dem Forum auch schon so etwas wie ControllerInjectable etc. hat.
Das View erstelle ich in dem Programm mit SceneBuilder. Ich habe also einen Button, und mit der Methode die dem Button zugeordnet ist würde ich jetzt gerne ein neues Fenster starten was mein DBMonitor.java "enthält".
Mir ist jetzt nicht klar wie ich das in Zusamenhang mit SceneBuilder bewerkstelligen soll.
Hier mal was ich gemacht habe um es zu verdeutlichen. Ich habe wie gesagt die Controller Klasse, diese enthält nur einen Button welcher dann den DB Monitor startet.
Hier dann das zugehörige "DBMonitorView.fxml":
Ich habe dann noch eine Klasse "ControllerDBMonitor" in der ich dann versucht habe den Code aus meinem funktiinierendem eigenständigen Programm "DBMonitor.java" zu implementieren. Hier das Ergebnis als Grundgerüst, ohne den Code aus DBMonitor.java:
Ich habe wie Ihr seht nur die FXML Node AreaChart definiert, wobei im SceneBuilder noch die Children "CategoryAxis" und "NumberAxis" konfiguriert werden. Im meinem Beispiel habe ich aber für x und y eine NumberAxis - kann das aber im SceneBuilder nicht ändern!?
Dann weiß ich auch nicht wo genau ich den Code zum konfigurieren des Chart und der Animation plazieren soll?
Irgendwie habe ich das Gefühl das nicht mehr viel fehlt, aber wie gesagt bin ich wie vernagelt bzw. verstehe vermutlich den Zusammenhang noch nicht gut genug.
Ich hoffe ich habe mich verständlich ausgedrückt - wenn nicht BITTE einfach fragen - bin für jede Diskussion sehr dankbar!
Gruß
Ralf
ich stehe mal wieder auf dem Schlauch und bräuchte Eure Hilfe bei folgendem Problem. Hier kurz zu dem was ich machen möchte.
Ich möchte, nachdem ich über mein Programm die Datenbank Verbindung erfolgreich getestet habe, einen Button anbieren der einen s.g. Database Monitor Window startet, in dem es nur ein AreaChart gibt der fortlaufend die TPS (Transaction per second) Values der DB anzeigt.
Ich habe mir viel angeschaut und rum probiert, bin dann schließlich bei den Oracle JvaFX Beispielen in "Ensemble" - eine coole Beispiel Sammlung, auf das Beispiel "StockLineChartApp" gestoßen. Ich habe dieses Beispiel dann so gut wie ich kann und ich es vesrtanden habe auf meine Anwendung umzuschreiben, und dabei ist dann folgende Applikation rausgekommen:
Java:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.chart.AreaChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
*
* A Database Monitor with TPS values.
*
*/
public class DBMonitor extends Application {
// Fields
private AreaChart<Number, Number> chart;
private Series<Number, Number> xAxisDataSeries;
private NumberAxis xAxis;
private Timeline animation;
private double xTock = 0;
private double xTick = 0;
private double xTockValue = 0;
private int valueY = 0;
private String dbSysUser = "sys as sysdba";
private String dbSysUserPassword = "****";
private String dbHost = "****";
private String tnsPort = "6506";
private String dbSID = "ORCL51";
private String connectString = "jdbc:oracle:thin:@" + dbHost + ":" + tnsPort + "/" + dbSID;
private Connection connSys;
private int valueSqlTPS;
private int vorWertTPS = 0;
private int savedValueSqlTPS;
private boolean firstInTPS = true;
// Constructor
public DBMonitor() {
// Create Database connection
try {
connSys = DriverManager.getConnection(connectString, dbSysUser, dbSysUserPassword);
} catch (Exception ex) {
System.out.println(ex);
}
// 6 "minutes" data per frame
final KeyFrame frame = new KeyFrame(Duration.millis(1000 / 10), (ActionEvent actionEvent) -> {
for (int count = 0; count < 6; count++) {
nextTime();
plotTime();
}
});
// create timeline to add new data every 10th (1000 / 10 in frame) of second --> 100ms
animation = new Timeline();
animation.getKeyFrames().add(frame);
animation.setCycleCount(Animation.INDEFINITE);
}
public Parent createContent() {
xAxis = new NumberAxis(0, 50, 5);
final NumberAxis yAxis = new NumberAxis(0, 100, 10);
chart = new AreaChart<>(xAxis, yAxis);
// setup AreaChart
final String dbMonitorCss = getClass().getResource("DBMonitor.css").toExternalForm();
chart.getStylesheets().add(dbMonitorCss);
chart.setCreateSymbols(true);
chart.setAnimated(false);
chart.setLegendVisible(true);
chart.setTitle("DB Monitor " + dbSID);
//xAxis.setLabel("Seconds since Start");
xAxis.setForceZeroInRange(false);
xAxis.setAutoRanging(false);
yAxis.setAutoRanging(true);
yAxis.setLabel("TPS");
//yAxis.setTickLabelFormatter(new DefaultFormatter(yAxis, "TPS ", null));
// add starting data
xAxisDataSeries = new Series<>();
xAxisDataSeries.setName("Transactions per Second");
// create some starting data
xAxisDataSeries.getData().add(new Data<Number, Number>(xTockValue, valueY));
chart.getData().add(xAxisDataSeries);
return chart;
}
private void nextTime() {
if (xTick == 59) {
xTock++;
xTick = 0;
} else {
xTick++;
}
xTockValue = xTock + ((1d / 60d) * xTick);
}
private void plotTime() {
if ((xTockValue % 1) == 0) {
// get TPS values from DB
try {
String queryTPS = "SELECT VALUE from v$sysstat where name in ('user commits')";
Statement stmt = connSys.createStatement();
ResultSet rset = stmt.executeQuery(queryTPS);
while (rset.next()) {
String tps = rset.getString("VALUE");
valueSqlTPS = Integer.parseInt(tps);
}
stmt.close();
} catch (Exception ex) {
System.out.println(ex);
}
savedValueSqlTPS = valueSqlTPS;
valueSqlTPS = valueSqlTPS - vorWertTPS;
vorWertTPS = savedValueSqlTPS;
// In the 1st execution we don't have a savedValue, so don't print anything and
// wait for the 2nd execution
if (!firstInTPS) {
// updateMessage(String.valueOf(valueSqlTPS));
valueY = valueSqlTPS;
} else {
valueY = 0;
firstInTPS = false;
}
xAxisDataSeries.getData().add(new Data<Number, Number>(xTockValue, valueY));
// every xTockValue (second xAxis) after 50 (initial xAxis = new NumberAxis(0, 50, 5)) move range 1 xTock
if (xTockValue > 50) {
xAxis.setLowerBound(xAxis.getLowerBound() + 1);
xAxis.setUpperBound(xAxis.getUpperBound() + 1);
}
}
}
public void play() {
animation.play();
}
@Override
public void stop() {
animation.pause();
}
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(createContent()));
primaryStage.show();
play();
}
/**
*
* Java main for when running without JavaFX launcher
*
*/
public static void main(String[] args) {
launch(args);
}
}
Ohne Licht kein Schatten - nun zu meinem Problem - eigentlich klingt es einfach - doch irgendwie bin ich zu blöd um es zu verstehen wie das geht ...
Wie oben geschrieben habe ich ja bereits eine Applikation die DB Verbindungen testet, und die mit der Hilfe hier aus dem Forum auch schon so etwas wie ControllerInjectable etc. hat.
Das View erstelle ich in dem Programm mit SceneBuilder. Ich habe also einen Button, und mit der Methode die dem Button zugeordnet ist würde ich jetzt gerne ein neues Fenster starten was mein DBMonitor.java "enthält".
Mir ist jetzt nicht klar wie ich das in Zusamenhang mit SceneBuilder bewerkstelligen soll.
Hier mal was ich gemacht habe um es zu verdeutlichen. Ich habe wie gesagt die Controller Klasse, diese enthält nur einen Button welcher dann den DB Monitor startet.
Java:
package application;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
public class Controller implements ExceptionListener, ModelInjectable, DBInjectable {
// Fields
private Model model;
private DB db;
// Constructor
public Controller() {
super();
}
/*
* @Override Methods
*/
@Override
public void setModel(Model model) {
this.model = model;
}
@Override
public void setDB(DB db) {
this.db = db;
}
@Override
public void exceptionOccurred(Throwable th) {
String msg = String.valueOf(th);
System.out.println(msg);
}
/*
* FXML Nodes (fx:id)
*/
@FXML
Button dbMonitor;
@FXML
Button exit;
/*
* Methods
*/
// Method initialize() will be executed after all FXML Nodes are ready. This is
// the place for Initialization of Nodes.
@FXML
public void initialize() {
this.db = new DB();
this.model = new Model();
// Register ExceptionListener with DB(Model) class to get Exceptions from DB
// class in Controller for showing in the View
db.registerExceptionListener(th -> {
String msg = String.valueOf(th);
System.out.println(msg);
// messageTextArea.appendText(msg + "\n");
// LOGGER.severe(msg);
});
model.registerExceptionListener(th -> {
String msg = String.valueOf(th);
System.out.println(msg);
// messageTextArea.appendText(msg + "\n");
// LOGGER.severe(msg);
});
}
public void exitButtonTapped(ActionEvent event) {
// Cast the Window of UI Control Button exit to Stage
((Stage) exit.getScene().getWindow())
.fireEvent(new WindowEvent(((Stage) exit.getScene().getWindow()), WindowEvent.WINDOW_CLOSE_REQUEST));
}
// Method for DB Monitor Button
public void dbMonitorButtonTapped() {
Parent rootDBMonitor = ViewLoader.load("DBMonitorView.fxml",
clazz -> ControllerFactory.controllerForClass(clazz, model, db, this));
Stage stageDBMonitor = new Stage();
stageDBMonitor.initModality(Modality.APPLICATION_MODAL);
stageDBMonitor.setOpacity(1);
stageDBMonitor.setTitle("DB Monitor View");
Scene sceneDBMonitor = new Scene(rootDBMonitor);
sceneDBMonitor.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
stageDBMonitor.setScene(sceneDBMonitor);
stageDBMonitor.show();
}
}
Java:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.chart.*?>
<?import javafx.scene.text.*?>
<?import javafx.geometry.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.ControllerDBMonitor">
<children>
<HBox alignment="CENTER" prefHeight="40.0" prefWidth="600.0">
<VBox.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</VBox.margin>
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="DB Monitor">
<font>
<Font size="18.0" />
</font>
</Text>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="303.0" prefWidth="600.0">
<VBox.margin>
<Insets left="10.0" right="10.0" />
</VBox.margin>
<children>
<AreaChart fx:id="chart">
<xAxis>
<CategoryAxis side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis side="LEFT" />
</yAxis>
</AreaChart>
</children>
</HBox>
<HBox alignment="CENTER_RIGHT" prefHeight="29.0" prefWidth="600.0">
<VBox.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</VBox.margin>
<children>
<Button fx:id="exit" mnemonicParsing="false" onAction="#exitButtonTapped" text="EXIT" />
</children>
</HBox>
</children>
</VBox>
Ich habe dann noch eine Klasse "ControllerDBMonitor" in der ich dann versucht habe den Code aus meinem funktiinierendem eigenständigen Programm "DBMonitor.java" zu implementieren. Hier das Ergebnis als Grundgerüst, ohne den Code aus DBMonitor.java:
Java:
package application;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javafx.animation.Timeline;
import javafx.fxml.FXML;
import javafx.scene.chart.AreaChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Button;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
public class ControllerDBMonitor implements ExceptionListener, ModelInjectable, ControllerInjectable, DBInjectable {
// Fields
private Controller controller;
private Model model;
private DB db;
private Connection connSys;
private String dbSysUser = "sys as sysdba";
private String dbSysUserPassword = "*****";
private String dbHost = "*****";
private String tnsPort = "6506";
private String dbSID = "ORCL51";
private String connectString = "jdbc:oracle:thin:@" + dbHost + ":" + tnsPort + "/" + dbSID;
private String dbBanner;
private int valueSqlTPS;
private int vorWertTPS = 0;
private int savedValueSqlTPS;
private boolean firstInTPS = true;
private Series<Number, Number> xAxisDataSeries;
private NumberAxis xAxis;
private NumberAxis yAxis;
private Timeline animation;
private double xTock = 0;
private double xTick = 0;
private double xTockValue = 0;
private int valueY = 0;
// Constructor
public ControllerDBMonitor() {
super();
}
// FXML Nodes
@FXML
Button exit;
@FXML
AreaChart<Number, Number> chart;
/*
* Implemented Methods
*/
@Override
public void setDB(DB db) {
this.db = db;
}
@Override
public void setController(Controller controller) {
this.controller = controller;
}
@Override
public void setModel(Model model) {
this.model = model;
}
@Override
public void exceptionOccurred(Throwable th) {
String msg = String.valueOf(th);
System.out.println(msg);
}
// Initialize Method
public void initialize() throws SQLException {
connSys = db.dbConnect(connectString, dbSysUser, dbSysUserPassword);
String queryGetDbVersion = "select banner from v$version where banner like '%Oracle%'";
Statement stmtQueryGetDbVersion = connSys.createStatement();
ResultSet rsetGetDbVersion = stmtQueryGetDbVersion.executeQuery(queryGetDbVersion);
while (rsetGetDbVersion.next()) {
dbBanner = rsetGetDbVersion.getString("BANNER");
}
stmtQueryGetDbVersion.close();
System.out.println(dbBanner);
}
/*
* Methods
*/
// Method executed when Exit Button tapped
public void exitButtonTapped() {
// Cast the Window of UI Control Button exit to Stage
animation.pause();
((Stage) exit.getScene().getWindow())
.fireEvent(new WindowEvent(((Stage) exit.getScene().getWindow()), WindowEvent.WINDOW_CLOSE_REQUEST));
}
private void nextTime() {
if (xTick == 59) {
xTock++;
xTick = 0;
} else {
xTick++;
}
xTockValue = xTock + ((1d / 60d) * xTick);
}
private void plotTime() {
if ((xTockValue % 1) == 0) {
// get TPS values from DB
try {
String queryTPS = "SELECT VALUE from v$sysstat where name in ('user commits')";
Statement stmt = connSys.createStatement();
ResultSet rset = stmt.executeQuery(queryTPS);
while (rset.next()) {
String tps = rset.getString("VALUE");
valueSqlTPS = Integer.parseInt(tps);
}
stmt.close();
} catch (Exception ex) {
System.out.println(ex);
}
savedValueSqlTPS = valueSqlTPS;
valueSqlTPS = valueSqlTPS - vorWertTPS;
vorWertTPS = savedValueSqlTPS;
// In the 1st execution we don't have a savedValue, so don't print anything and
// wait for the 2nd execution
if (!firstInTPS) {
// updateMessage(String.valueOf(valueSqlTPS));
valueY = valueSqlTPS;
} else {
valueY = 0;
firstInTPS = false;
}
xAxisDataSeries.getData().add(new Data<Number, Number>(xTockValue, valueY));
// every xTockValue (second xAxis) after 50 (initial xAxis = new NumberAxis(0,
// 50, 5)) move range 1 xTock
if (xTockValue > 50) {
xAxis.setLowerBound(xAxis.getLowerBound() + 1);
xAxis.setUpperBound(xAxis.getUpperBound() + 1);
}
}
}
public void play() {
animation.play();
}
}
Ich habe wie Ihr seht nur die FXML Node AreaChart definiert, wobei im SceneBuilder noch die Children "CategoryAxis" und "NumberAxis" konfiguriert werden. Im meinem Beispiel habe ich aber für x und y eine NumberAxis - kann das aber im SceneBuilder nicht ändern!?
Dann weiß ich auch nicht wo genau ich den Code zum konfigurieren des Chart und der Animation plazieren soll?
Irgendwie habe ich das Gefühl das nicht mehr viel fehlt, aber wie gesagt bin ich wie vernagelt bzw. verstehe vermutlich den Zusammenhang noch nicht gut genug.
Ich hoffe ich habe mich verständlich ausgedrückt - wenn nicht BITTE einfach fragen - bin für jede Diskussion sehr dankbar!
Gruß
Ralf