javajavafxjfxtras

JavaFX pass data to jfxtras gauge from Arduino


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!!!!


Solution

  • @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();
            }
        }
    
    }