First let me thank you for any guidance you might be able to provide on this. I have been racking my brains to get this to work, and have been looking at many threads here, with no luck. I am new to Java and JavaFX, but have a C# background, primarily on MSFT visual Studio. I am currently using IntelliJ as my IDE platform.
What I am trying to do is (I thought) simple enough: I am reading and successfully parsing data coming in from an Arduino controller, which is reading a sensor, that provides readings for a power meter. I have my data in a comma delimited form, and with a buffered reader and serial port event, I am able to parse a line, string.split it, and then place in into an array (after transforming to Double). This part works well, and as you can see from the code, I am also printing this to the console with no issues:
package sample;
import eu.hansolo.enzo.gauge.RectangularGauge;
import gnu.io.*;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.fxml.FXML;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.*;
public class Main extends Application implements SerialPortEventListener {
SerialPort serialPort = null;
private BufferedReader serialIn;
private OutputStream serialOut;
public Double power;
@FXML
private RectangularGauge fwdpower;
@Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Power Meter");
primaryStage.setScene(new Scene(root, 380, 320));
primaryStage.show();
//
CommPortIdentifier port = CommPortIdentifier.getPortIdentifier("COM60");
CommPort commPort = port.open(this.getClass().getName(), 2000);
serialPort = (SerialPort) commPort;
serialPort.setSerialPortParams(115200, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
serialIn = new BufferedReader(new InputStreamReader((serialPort.getInputStream())));
serialPort.addEventListener(this);
serialPort.notifyOnDataAvailable(true);
//
}
public static void main(String[] args) {
launch(args);
}
@Override
public void serialEvent(SerialPortEvent e) {
try {
String line = serialIn.readLine();
String[] Values = line.split(",");
power = Double.parseDouble(Values[1]);
System.out.println(power / 5);
} //This works as expected and shows values on console
fwdpower.setValue(power/5); //Shows NOTHING! UGHHHH!
catch (IOException ex) {ex.printStackTrace();
}
}
Now the HARD part: Has me completely frustrated!!
I am trying to now drive a gauge from the jfxtras gauges. I have all the proper libraries imported, and I create a very simple GUI in scenebuilder. Here is the XML file created:
<?xml version="1.0" encoding="UTF-8"?>
<?import eu.hansolo.enzo.gauge.RectangularGauge?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.RowConstraints?>
<GridPane alignment="center" hgap="10" vgap="10" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.65" fx:controller="sample.Controller">
<columnConstraints>
<ColumnConstraints />
</columnConstraints>
<rowConstraints>
<RowConstraints />
</rowConstraints>
<children>
<Pane prefHeight="342.0" prefWidth="377.0">
<children>
<RectangularGauge fx:id="fwdpower" layoutX="25.0" layoutY="22.0" minValue="1.0" />
</children>
</Pane>
</children>
</GridPane>
and this is the Controller: (nothing in it at the moment, since I do not want to have any buttons or anything like that...just display of data:
package sample;
import eu.hansolo.enzo.gauge.RectangularGauge;
import gnu.io.SerialPortEvent;
import javafx.fxml.FXML;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Controller {
}
I have tried different combinations, but the guage refuses to show the data read from the serialPort. I think this might have to do with the GUI being on a different thread from main (have been searching and reading) but for the life of me I cant figure out how I might change my code to make this seemingly simple task work. All I want is for the gauge to display the data....I know I might be doing something completely stupid.....but cant figure this out.
Any ides as to how I might be able to achieve this?
PS: If I create an event handler in controller, with say a button, I am able to make the gauge move with an arbitrary value upon button press. Why cant I do the same when the serial event fires????
HELP PLEASE!!!!
@FXML
-annotated fields are only initialized in the controller created by the FXMLLoader
; you can't simply annotate a field in an arbitrary object and expect it to be initialized by the loader.
Create the annotated field in the controller and define a method to set it:
package sample;
import eu.hansolo.enzo.gauge.RectangularGauge;
import gnu.io.SerialPortEvent;
import javafx.fxml.FXML;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Controller {
@FXML
private RectangularGauge fwdpower ;
public void setFwdPower(double power) {
fwdpower.setValue(power);
}
}
Then just get a reference to the controller and invoke the method on it. Note that since your serialEvent
method is invoked from a background thread, you should wrap the call to the controller in Platform.runLater(...)
:
package sample;
import eu.hansolo.enzo.gauge.RectangularGauge;
import gnu.io.*;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.fxml.FXML;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.*;
public class Main extends Application implements SerialPortEventListener {
SerialPort serialPort = null;
private BufferedReader serialIn;
private OutputStream serialOut;
private Controller controller ;
@Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
Parent root = loader.load();
controller = loader.getController();
primaryStage.setTitle("Power Meter");
primaryStage.setScene(new Scene(root, 380, 320));
primaryStage.show();
//
CommPortIdentifier port = CommPortIdentifier.getPortIdentifier("COM60");
CommPort commPort = port.open(this.getClass().getName(), 2000);
serialPort = (SerialPort) commPort;
serialPort.setSerialPortParams(115200, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
serialIn = new BufferedReader(new InputStreamReader((serialPort.getInputStream())));
serialPort.addEventListener(this);
serialPort.notifyOnDataAvailable(true);
//
}
public static void main(String[] args) {
launch(args);
}
@Override
public void serialEvent(SerialPortEvent e) {
try {
String line = serialIn.readLine();
String[] values = line.split(",");
final double power = Double.parseDouble(values[1]);
System.out.println(power / 5);
Platform.runLater(() -> controller.setFwdpower(power/5));
} catch (IOException ex) {
ex.printStackTrace();
}
}
}