2012-07-03 17 views
15

Requisito: cree un área animada con datos de transmisión en tiempo real. Tal vez trazar 300 puntos de datos cada 1 segundo.Cómo representar los datos de transmisión en tiempo real utilizando AreaChart en JAVAFX 2 - Concurrencia, Animación, Trazado

Detalles- Necesito leer en tiempo real los datos de transmisión de un dispositivo médico, del patrón de respiración de un paciente y mostrarlo en forma de onda con AreaChart en JavaFX. Soy nuevo en JavaFX, así que construí un pequeño POC, para ver cómo funciona la simultaneidad y la animación en JavaFX.

El concepto funciona y estoy contento con la prueba básica, en cuanto a la implementación de la funcionalidad. Pero no estoy contento con el rendimiento que obtengo del código a continuación.

En el código de trabajo a continuación, creo un hilo separado para simular la obtención de datos desde el dispositivo médico. El hilo solo genera un número aleatorio y lo agrega a una ConcurrentLinkedQueue.

El subproceso de aplicación JavaFX extrae estos datos de la cola, a través de la línea de tiempo, y los agrega a una serie AreaChart.

Este tipo de me da la animación que necesito y los datos se agregan en tiempo de ejecución. Puedes copiar y pegar este código y probarlo ... Debería funcionar.

PERO el rendimiento no es impresionante: la CPU alcanza el 56% de uso. Tengo un Intel Core 2 Duo @ 2.53 GHZ y 4GB de RAM en mi computadora portátil. Mi tarjeta gráfica es Mobile Intel 4 Series express con el controlador más reciente.

¿Cómo puedo mejorar esta animación o trazado de datos en tiempo real, para obtener un mejor rendimiento?

NOTA: Estoy dispuesto a comprometer la animación, si es el cuello de la botella. Estoy abierto a una implementación como la que se muestra aquí http://smoothiecharts.org/ donde la forma de onda está recién precompilada y solo se transmite de derecha a izquierda.

import java.util.concurrent.ConcurrentLinkedQueue; 
    import java.util.concurrent.ExecutorService; 
    import java.util.concurrent.Executors; 
    import java.util.logging.Level; 
    import java.util.logging.Logger; 
    import javafx.animation.Animation; 
    import javafx.animation.KeyFrame; 
    import javafx.animation.SequentialTransition; 
    import javafx.animation.Timeline; 
    import javafx.application.Application; 
    import javafx.event.ActionEvent; 
    import javafx.event.EventHandler; 
    import javafx.scene.Group; 
    import javafx.scene.Scene; 
    import javafx.scene.chart.AreaChart; 
    import javafx.scene.chart.NumberAxis; 
    import javafx.scene.chart.XYChart.Series; 
    import javafx.stage.Stage; 
    import javafx.util.Duration; 

    /** 
    * A chart that fills in the area between a line of data points and the axes. 
    * Good for comparing accumulated totals over time. 
    * 
    * @see javafx.scene.chart.Chart 
    * @see javafx.scene.chart.Axis 
    * @see javafx.scene.chart.NumberAxis 
    * @related charts/line/LineChart 
    * @related charts/scatter/ScatterChart 
    */ 
    public class AreaChartSample extends Application { 
     private Series series; 
     private int xSeriesData=0; 
     private ConcurrentLinkedQueue<Number> dataQ = new ConcurrentLinkedQueue<Number>(); 
     private ExecutorService executor; 
     private AddToQueue addToQueue; 
     private Timeline timeline2; 
     private SequentialTransition animation; 

     private void init(Stage primaryStage) { 
      Group root = new Group(); 
      primaryStage.setScene(new Scene(root)); 

      NumberAxis xAxis = new NumberAxis(); 
      xAxis.setAutoRanging(true); 

      NumberAxis yAxis = new NumberAxis(); 
      yAxis.setAutoRanging(true); 

      //-- Chart 
      final AreaChart<Number,Number> sc = new AreaChart<Number,Number>(xAxis,yAxis); 
      sc.setId("liveAreaChart"); 
      sc.setTitle("Animated Area Chart"); 

      //-- Chart Series 
      series=new AreaChart.Series<Number,Number>(); 
      series.setName("Area Chart Series"); 
      series.getData().add(new AreaChart.Data<Number, Number>(5d, 5d)); 
      sc.getData().add(series); 


      root.getChildren().add(sc); 



     } 

     @Override public void start(Stage primaryStage) throws Exception { 
      init(primaryStage); 
      primaryStage.show(); 

      //-- Prepare Executor Services 
      executor = Executors.newCachedThreadPool(); 
      addToQueue=new AddToQueue(); 
      executor.execute(addToQueue); 


      //-- Prepare Timeline 
      prepareTimeline(); 


     } 

     public static void main(String[] args) { launch(args); } 

     private class AddToQueue extends Thread { 

      public void run(){ 

      try { 
       Thread.currentThread().setName(Thread.currentThread().getId()+"-DataAdder"); 
       //-- Add Random numbers to Q 
       dataQ.add(Math.random()); 
       Thread.sleep(50); 

       executor.execute(addToQueue); 

      } catch (InterruptedException ex) { 
       Logger.getLogger(AreaChartSample.class.getName()).log(Level.SEVERE, null, ex); 
      } 

      } 
     } 

     //-- Timeline gets called in the JavaFX Main thread 
     private void prepareTimeline(){ 
      //-- Second slower timeline 
      timeline2 = new Timeline(); 
      //-- This timeline is indefinite. 
      timeline2.setCycleCount(Animation.INDEFINITE); 

      timeline2.getKeyFrames().add(
        new KeyFrame(Duration.millis(100), new EventHandler<ActionEvent>() { 
         @Override public void handle(ActionEvent actionEvent) { 
          addDataToSeries(); 

         } 
        }) 
     ); 

      //-- Set Animation- Timeline is created now. 
      animation = new SequentialTransition(); 
      animation.getChildren().addAll(timeline2); 
      animation.play();   

     } 

     private void addDataToSeries(){ 

      for(int i=0;i<20;i++){ //-- add 20 numbers to the plot 
       if(dataQ.isEmpty()==false) { 
        series.getData().add(new AreaChart.Data(xSeriesData++,dataQ.remove())); 

        //-- Get rid of a bunch from the chart 
        if (series.getData().size() > 1000) { 
         series.getData().remove(0,999); 
        } 

       } 
       else{ 
        return; 
       } 
      } 
     } 


    } 
+0

Esta pregunta se publicó de forma cruzada (y se respondió bien) en un [subproceso del foro Oracle JavaFX] (https://forums.oracle.com/forums/thread.jspa?threadID=2411087). – jewelsea

Respuesta

1

Como jewelsea declarado en su/su comentario:

Esta pregunta fue publicada cruz (y respondió bien) en un Oracle JavaFX forum thread.

Para resumir, la solución consiste en:

  • Volviendo animación fuera ya que está diseñado para más lentas datos cambiantes de manera que está animado a la llegada;
  • Cambiando el Timeline a un AnimationTimer, ya que se desea actualizar el gráfico en cada cuadro para mantener la sincronización con los datos entrantes y moverlo lo más suavemente posible;
  • Fijación de subprocesos como OP no era necesario extender el subproceso cuando se utiliza un ejecutor. Cambiar la creación del servicio del ejecutor.
0

Una gran caída de rendimiento podría provenir de la recopilación de datos. No hay ninguna razón para usar un ExecutorService y agregar continuamente el nuevo Threads para su ejecución con el fin de obtener datos repetitivos. Puede conformarse con un solo hilo que lea/reciba los datos y los agregue a la cola e inicie llamando al addToQueue.start().Para que funcione correctamente, desea que un bucle se ejecute continuamente en el hilo con un retraso al final de cada iteración.

Cuestiones relacionadas