2012-07-03 16 views
15

Requisito: crea un AreaChart animato con dati di streaming in tempo reale. Forse trama 300 punti dati ogni 1 s.Come tracciare i dati di streaming in tempo reale utilizzando AreaChart in JAVAFX 2 - Concurrency, Animation, Charting

Dettagli- Quindi ho bisogno di leggere i dati di streaming in tempo reale da un dispositivo medico, del modello di respirazione di un paziente e visualizzarlo in forma d'onda utilizzando AreaChart in JavaFX. Sono nuovo di JavaFX e così ho costruito un piccolo POC, per vedere come la concorrenza e le opere di animazione in JavaFX.

Il concetto funziona e sono felice con il test di base, per quanto riguarda l'attuazione della funzionalità. Ma non sono contento della prestazione che ottengo dal codice qui sotto.

Nel codice di lavoro seguente, creo un thread separato per simulare il recupero dei dati dal dispositivo medico. Il thread genera solo un numero casuale e lo aggiunge a ConcurrentLinkedQueue.

Il thread JavaFX Application estrae questi dati dalla coda, tramite la linea temporale, e li aggiunge a una serie AreaChart.

Questo tipo di animazione mi serve e i dati vengono aggiunti in fase di esecuzione. Puoi copiare e incollare questo codice e testarlo. Dovrebbe funzionare.

MA le prestazioni non sono impressionanti - la CPU raggiunge il 56% di utilizzo - Ho un processore Intel Core 2 Duo @ 2.53 GHZ e 4 GB ram sul mio laptop. La mia scheda grafica è Mobile Intel serie 4 Express con driver di ultima generazione.

Come è possibile migliorare questa animazione o il tracciamento dei dati in tempo reale, al fine di ottenere prestazioni migliori?

NOTA: io sono disposto a compromettere l'animazione, se il collo della bottiglia. Sono aperto a un'implementazione come mostrato qui http://smoothiecharts.org/ in cui la forma d'onda è solo precompilati e solo lo streaming da destra a sinistra.

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

Questa domanda è stata incrociata (e ha risposto correttamente) su un [thread del forum JavaFX Oracle] (https://forums.oracle.com/forums/thread.jspa?threadID=2411087). – jewelsea

risposta

1

Come jewelsea affermato nella sua/il suo commento:

La domanda è stata croce pubblicato (e ha risposto bene) su un Oracle JavaFX forum thread.

Riassumendo, la soluzione consisteva nel:

  • Accensione animazione il quale è progettato per la modifica dei dati più lenti in modo che è animato all'arrivo;
  • Modifica di Timeline a AnimationTimer come si desidera aggiornare il grafico ogni fotogramma per mantenere la sincronizzazione con i dati in arrivo e spostarsi nel modo più fluido possibile;
  • filettatura di fissaggio come OP non ha bisogno di estendere la discussione quando si utilizza un esecutore. Modifica della creazione del servizio executor.
0

Un calo notevole delle prestazioni potrebbe derivare dalla raccolta dei dati. Non v'è alcun motivo di usare un ExecutorService e continuamente aggiungere nuovi Threads per l'esecuzione da essa al fine di ottenere dati ripetitivi aggiungendo. È possibile accontentarsi di un singolo thread che legge/riceve i dati e lo aggiunge alla coda e avviarlo chiamando lo addToQueue.start().Affinché funzioni correttamente, si desidera che un ciclo funzioni continuamente nel thread con un ritardo alla fine di ogni iterazione.

Problemi correlati