Требование -Создайте анимированную диаграмму областей с потоковыми данными в реальном времени. Может быть, построить 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;
}
}
}
}