Как отображать потоковые данные в режиме реального времени с помощью AreaChart в JAVAFX 2 -Параллелизм, анимация, построение диаграмм

Требование -Создайте анимированную диаграмму областей с потоковыми данными в реальном времени. Может быть, построить 300 точек данных каждую 1 секунду.

Детали -Поэтому мне нужно считывать потоковые данные в реальном времени с медицинского устройства, о характере дыхания пациента и отображать их в виде волны, используя AreaChart в JavaFX. Я новичок в JavaFX, поэтому я создал небольшой POC, чтобы увидеть, как параллелизм и анимация работают в JavaFX.

Концепция работает, и я доволен базовым тестом, а также реализацией функциональности. Но меня не устраивает производительность, которую я получаю от приведенного ниже кода.

В приведенном ниже рабочем коде я создаю отдельный поток для имитации получения данных из медицинского устройства.Поток просто генерирует случайное число и добавляет его в ConcurrentLinkedQueue.

Поток приложения JavaFX извлекает эти данные из очереди через временную шкалу и добавляет их в серию AreaChart.

Это дает мне нужную мне анимацию, и данные добавляются во время выполнения. Вы можете скопировать -вставить этот код и протестировать его. Он должен работать.

НО производительность не впечатляет -Загрузка процессора составляет 56% -У меня на ноутбуке Intel Core 2 Duo @ 2,53 ГГц и 4 ГБ оперативной памяти. Моя графическая карта Mobile Intel 4 Series Express с последним драйвером.

Как я могу улучшить эту анимацию или отображение данных в реальном времени, чтобы повысить производительность?

ПРИМЕЧАНИЕ. :Я готов пойти на компромисс с анимацией, если это узкое место. Я открыт для реализации, как показано здесьhttp://smoothiecharts.org/где форма волны просто предварительно построена и просто течет справа налево.

  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 dataQ = new ConcurrentLinkedQueue();
      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 sc = new AreaChart(xAxis,yAxis);
          sc.setId("liveAreaChart");
          sc.setTitle("Animated Area Chart");

          //-- Chart Series
          series=new AreaChart.Series();
          series.setName("Area Chart Series");
          series.getData().add(new AreaChart.Data(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() {
                      @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;
              }
          }
      }


  }

15
задан FatherFigure 6 July 2012 в 16:15
поделиться