From 5f707e4869cc2c34afcce5e084f634b9848d8862 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Thu, 11 Sep 2025 15:04:25 +0200 Subject: [PATCH 1/3] CSSTUDIO-3420 Change type from TreeMap to ConcurrentSkipListMap, and remove synchronization on pvData. --- .../WaterfallPlotController.java | 27 +++-- .../WaterfallPlotRuntime.java | 113 ++++++++---------- .../WaterfallPlotWidgetRepresentation.java | 10 +- 3 files changed, 66 insertions(+), 84 deletions(-) diff --git a/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotController.java b/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotController.java index 57822ecd5b..e72baa4d7e 100644 --- a/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotController.java +++ b/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotController.java @@ -22,6 +22,7 @@ import java.util.Optional; import java.util.SortedMap; import java.util.TreeMap; +import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -320,21 +321,24 @@ public synchronized void redraw(WaterfallPlotRuntime.PVData pvData) { minFromPV = waveformPVData.minFromPV().get(); maxFromPV = waveformPVData.maxFromPV().get(); - TreeMap> instantToWaveform = waveformPVData.instantToValue().get(); + ConcurrentSkipListMap> instantToWaveform = waveformPVData.instantToValue(); + Instant startKey = instantToWaveform.ceilingKey(Instant.MIN); for (Instant t = t1.plus(stepsize); t.compareTo(t2) <= 0; t = t.plus(stepsize)) { timeValuesLinkedList.add(((double) t.toEpochMilli()) / 1000.0); - var instant = instantToWaveform.floorKey(t); - if (instant != null) { + if (startKey == null || t.isBefore(startKey)) { + zValuesLinkedList.add(null); // null means absence of data for this point in time + } + else { + var instant = instantToWaveform.floorKey(t); + LinkedList waveform = instantToWaveform.get(instant); waveformLength = Math.max(waveformLength, waveform.size()); zValuesLinkedList.add(waveform); - } else { - zValuesLinkedList.add(null); // null means absence of data for this point in time } } } else if (pvData instanceof WaterfallPlotRuntime.ScalarPVsData scalarPVsData) { - LinkedList>>> pvNameToInstantToValue = scalarPVsData.pvNameToInstantToValue(); + LinkedList>> pvNameToInstantToValue = scalarPVsData.pvNameToInstantToValue(); pvNameToInstantToValue.forEach(pvNameAndInstantToValueAtomicReference -> garbageCollectInstantToValue(pvNameAndInstantToValueAtomicReference.getValue(), t1)); minFromPV = scalarPVsData.minFromPV().get(); @@ -349,7 +353,7 @@ public synchronized void redraw(WaterfallPlotRuntime.PVData pvData) { for (var pvNameAndInstantToValue : pvNameToInstantToValue) { String pvName = pvNameAndInstantToValue.getKey(); - TreeMap instantToValue = pvNameAndInstantToValue.getValue().get(); + ConcurrentSkipListMap instantToValue = pvNameAndInstantToValue.getValue(); var instant = instantToValue.floorKey(t); if (instant == null) { @@ -479,14 +483,13 @@ else if (zAxisMinMax.equals(WaterfallPlotWidget.ZAxisMinMax.FromPVLimits)) { } } - private synchronized static void garbageCollectInstantToValue(AtomicReference> instantToValueAtomicReference, Instant t1) { + private synchronized static void garbageCollectInstantToValue(ConcurrentSkipListMap instantToWaveform, Instant t1) { // Garbage collect old values that are no longer needed: - TreeMap instantToWaveform = instantToValueAtomicReference.get(); Instant instantOfOldestRelevantKey = instantToWaveform.floorKey(t1); if (instantOfOldestRelevantKey != null) { - SortedMap instantToWaveformGarbageCollectedSortedMap = instantToWaveform.subMap(instantOfOldestRelevantKey, Instant.MAX); - TreeMap instantToWaveformGarbageCollected = new TreeMap<>(instantToWaveformGarbageCollectedSortedMap); - instantToValueAtomicReference.set(instantToWaveformGarbageCollected); + for (var key : instantToWaveform.subMap(Instant.MIN, instantOfOldestRelevantKey).keySet()) { + instantToWaveform.remove(key); + } } } diff --git a/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotRuntime.java b/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotRuntime.java index 2e082c827a..8c723a0721 100644 --- a/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotRuntime.java +++ b/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotRuntime.java @@ -24,8 +24,7 @@ import java.time.Instant; import java.util.LinkedList; import java.util.List; -import java.util.TreeMap; -import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.locks.Lock; import java.util.function.Consumer; import java.util.logging.Level; @@ -57,7 +56,7 @@ public void initialize(WaterfallPlotWidget waterfallPlotWidget) { if (isWaveform) { pvData = new WaveformPVData(new AtomicDouble(Double.NaN), new AtomicDouble(Double.NaN), - new AtomicReference<>(new TreeMap<>())); + new ConcurrentSkipListMap<>()); } else { pvData = new ScalarPVsData(new AtomicDouble(Double.NaN), @@ -69,16 +68,13 @@ public void initialize(WaterfallPlotWidget waterfallPlotWidget) { } public sealed interface PVData permits WaveformPVData, ScalarPVsData {} - // In order to be able to garbage collect data points that are no longer needed, - // the TreeMap<> instantToValue is contained in an AtomicReference<>, so that we - // can set the map to a new map without values that are no longer needed. This is - // the case for both 'WaveformPVData' and 'ScalarPVsData'. + // The type ConcurrentSkipListMap is used for the data points to allow for concurrent insertions and deletions: public record WaveformPVData (AtomicDouble minFromPV, AtomicDouble maxFromPV, - AtomicReference>> instantToValue) implements PVData {} + ConcurrentSkipListMap> instantToValue) implements PVData {} public record ScalarPVsData (AtomicDouble minFromPV, AtomicDouble maxFromPV, - LinkedList>>> pvNameToInstantToValue) implements PVData {} + LinkedList>> pvNameToInstantToValue) implements PVData {} @Override public void start() { @@ -90,32 +86,28 @@ public void start() { try { RuntimePV runtimePV = PVFactory.getPV(pvName); super.addPV(runtimePV, false); - AtomicReference> instantToValueAtomicReference = new AtomicReference<>(new TreeMap<>()); - scalarPVsData.pvNameToInstantToValue.add(new Pair(pvName, instantToValueAtomicReference)); + ConcurrentSkipListMap instantToValue = new ConcurrentSkipListMap<>(); + scalarPVsData.pvNameToInstantToValue.add(new Pair(pvName, instantToValue)); runtimePV.addListener((pv, vType) -> { if (vType instanceof VNumber vnumber) { - synchronized (pvData) { - instantToValueAtomicReference.get().put(vnumber.getTime().getTimestamp(), vnumber.getValue().doubleValue()); - { - Range displayRange = vnumber.getDisplay().getDisplayRange(); - double minFromPV = displayRange.getMinimum(); - scalarPVsData.minFromPV.set(minFromPV); - double maxFromPV = displayRange.getMaximum(); - scalarPVsData.maxFromPV.set(maxFromPV); - } + instantToValue.put(vnumber.getTime().getTimestamp(), vnumber.getValue().doubleValue()); + { + Range displayRange = vnumber.getDisplay().getDisplayRange(); + double minFromPV = displayRange.getMinimum(); + scalarPVsData.minFromPV.set(minFromPV); + double maxFromPV = displayRange.getMaximum(); + scalarPVsData.maxFromPV.set(maxFromPV); } } else if (vType instanceof VEnum vEnum) { - synchronized (pvData) { - instantToValueAtomicReference.get().put(vEnum.getTime().getTimestamp(), (double) vEnum.getIndex()); - - { - int enumSize = vEnum.getDisplay().getChoices().size(); - double minFromPV = 0; - scalarPVsData.minFromPV.set(minFromPV); - double maxFromPV = enumSize - 1; - scalarPVsData.maxFromPV.set(maxFromPV); - } + instantToValue.put(vEnum.getTime().getTimestamp(), (double) vEnum.getIndex()); + + { + int enumSize = vEnum.getDisplay().getChoices().size(); + double minFromPV = 0; + scalarPVsData.minFromPV.set(minFromPV); + double maxFromPV = enumSize - 1; + scalarPVsData.maxFromPV.set(maxFromPV); } } else { @@ -128,18 +120,13 @@ else if (vType instanceof VEnum vEnum) { Instant.now().minusSeconds(timeSpanInSeconds), Instant.now(), values -> { - synchronized (pvData) { - for (var vtype : values) { - if (vtype instanceof VNumber vnumber) { - instantToValueAtomicReference.get().put(vnumber.getTime().getTimestamp(), vnumber.getValue().doubleValue()); - } - else if (vtype instanceof VStatistics vstatistics) { - instantToValueAtomicReference.get().put(vstatistics.getTime().getTimestamp(), vstatistics.getAverage()); - } - else if (vtype instanceof VEnum vEnum) { - instantToValueAtomicReference.get().put(vEnum.getTime().getTimestamp(), (double) vEnum.getIndex()); - } - + for (var vtype : values) { + if (vtype instanceof VNumber vnumber) { + instantToValue.put(vnumber.getTime().getTimestamp(), vnumber.getValue().doubleValue()); + } else if (vtype instanceof VStatistics vstatistics) { + instantToValue.put(vstatistics.getTime().getTimestamp(), vstatistics.getAverage()); + } else if (vtype instanceof VEnum vEnum) { + instantToValue.put(vEnum.getTime().getTimestamp(), (double) vEnum.getIndex()); } } }); @@ -162,20 +149,16 @@ else if (pvData instanceof WaveformPVData waveformPVData && pvNames.size() == 1) var value = vNumberArray.getData().getDouble(m); waveform.add(value); } - - synchronized (pvData) { - waveformPVData.instantToValue.get().put(vNumberArray.getTime().getTimestamp(), waveform); - - { - Range displayRange = vNumberArray.getDisplay().getDisplayRange(); - double minFromPV = displayRange.getMinimum(); - waveformPVData.minFromPV.set(minFromPV); - double maxFromPV = displayRange.getMaximum(); - waveformPVData.maxFromPV.set(maxFromPV); - } + waveformPVData.instantToValue.put(vNumberArray.getTime().getTimestamp(), waveform); + + { + Range displayRange = vNumberArray.getDisplay().getDisplayRange(); + double minFromPV = displayRange.getMinimum(); + waveformPVData.minFromPV.set(minFromPV); + double maxFromPV = displayRange.getMaximum(); + waveformPVData.maxFromPV.set(maxFromPV); } - } - else if (vType instanceof VEnumArray vEnumArray) { + } else if (vType instanceof VEnumArray vEnumArray) { LinkedList waveform = new LinkedList<>(); ListNumber listNumber = vEnumArray.getIndexes(); @@ -184,16 +167,14 @@ else if (vType instanceof VEnumArray vEnumArray) { waveform.add(value); } - synchronized (pvData) { - waveformPVData.instantToValue.get().put(vEnumArray.getTime().getTimestamp(), waveform); + waveformPVData.instantToValue.put(vEnumArray.getTime().getTimestamp(), waveform); - { - int enumSize = vEnumArray.getDisplay().getChoices().size(); - double minFromPV = 0; - waveformPVData.minFromPV.set(minFromPV); - double maxFromPV = enumSize - 1; - waveformPVData.maxFromPV.set(maxFromPV); - } + { + int enumSize = vEnumArray.getDisplay().getChoices().size(); + double minFromPV = 0; + waveformPVData.minFromPV.set(minFromPV); + double maxFromPV = enumSize - 1; + waveformPVData.maxFromPV.set(maxFromPV); } } }); @@ -213,7 +194,7 @@ else if (vType instanceof VEnumArray vEnumArray) { waveform.add(value); } - waveformPVData.instantToValue.get().put(vNumberArray.getTime().getTimestamp(), waveform); + waveformPVData.instantToValue.put(vNumberArray.getTime().getTimestamp(), waveform); } else if (vtype instanceof VEnumArray vEnumArray) { @@ -224,7 +205,7 @@ else if (vtype instanceof VEnumArray vEnumArray) { waveform.add(value); } - waveformPVData.instantToValue.get().put(vEnumArray.getTime().getTimestamp(), waveform); + waveformPVData.instantToValue.put(vEnumArray.getTime().getTimestamp(), waveform); } } } diff --git a/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotWidgetRepresentation.java b/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotWidgetRepresentation.java index d081b1bd0c..443c0c5a7e 100644 --- a/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotWidgetRepresentation.java +++ b/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotWidgetRepresentation.java @@ -110,12 +110,10 @@ public WaterfallPlotWidgetRepresentation() { } WaterfallPlotRuntime.PVData pvData = waterfallPlotRuntime.getPVData(); - synchronized (pvData) { - try { - waterfallPlotController.redraw(pvData); - } catch (Exception e) { - // Catch exceptions in order to retry redrawing again if an exception occurs. - } + try { + waterfallPlotController.redraw(pvData); + } catch (Exception e) { + // Catch exceptions in order to retry redrawing again if an exception occurs. } }; From 0a5667402a71d465a25aa76da4f556c08e876c21 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Thu, 11 Sep 2025 15:14:58 +0200 Subject: [PATCH 2/3] CSSTUDIO-3420 Change type from LinkedList to ArrayList for representing waveforms. --- .../WaterfallPlotController.java | 18 ++++++++---------- .../WaterfallPlotRuntime.java | 15 ++++++++++----- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotController.java b/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotController.java index e72baa4d7e..cdc6c8f5a5 100644 --- a/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotController.java +++ b/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotController.java @@ -17,14 +17,12 @@ import java.time.Duration; import java.time.Instant; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Optional; -import java.util.SortedMap; -import java.util.TreeMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; public class WaterfallPlotController { @@ -310,7 +308,7 @@ public synchronized void redraw(WaterfallPlotRuntime.PVData pvData) { previousT2[0] = Optional.of(t2); LinkedList timeValuesLinkedList = new LinkedList<>(); - LinkedList> zValuesLinkedList = new LinkedList<>(); + LinkedList> zValuesLinkedList = new LinkedList<>(); int waveformLength = 1; double minFromPV = Double.NaN; @@ -321,7 +319,7 @@ public synchronized void redraw(WaterfallPlotRuntime.PVData pvData) { minFromPV = waveformPVData.minFromPV().get(); maxFromPV = waveformPVData.maxFromPV().get(); - ConcurrentSkipListMap> instantToWaveform = waveformPVData.instantToValue(); + ConcurrentSkipListMap> instantToWaveform = waveformPVData.instantToValue(); Instant startKey = instantToWaveform.ceilingKey(Instant.MIN); for (Instant t = t1.plus(stepsize); t.compareTo(t2) <= 0; t = t.plus(stepsize)) { timeValuesLinkedList.add(((double) t.toEpochMilli()) / 1000.0); @@ -332,7 +330,7 @@ public synchronized void redraw(WaterfallPlotRuntime.PVData pvData) { else { var instant = instantToWaveform.floorKey(t); - LinkedList waveform = instantToWaveform.get(instant); + ArrayList waveform = instantToWaveform.get(instant); waveformLength = Math.max(waveformLength, waveform.size()); zValuesLinkedList.add(waveform); } @@ -348,7 +346,7 @@ public synchronized void redraw(WaterfallPlotRuntime.PVData pvData) { for (Instant t = t1.plus(stepsize); t.compareTo(t2) <= 0; t = t.plus(stepsize)) { timeValuesLinkedList.add(((double) t.toEpochMilli()) / 1000.0); - LinkedList zValues = new LinkedList<>(); + ArrayList zValues = new ArrayList<>(); for (var pvNameAndInstantToValue : pvNameToInstantToValue) { String pvName = pvNameAndInstantToValue.getKey(); @@ -367,7 +365,7 @@ public synchronized void redraw(WaterfallPlotRuntime.PVData pvData) { // Append the last value one more time in order to // fix the plotting when there is only 1 scalar PV: - var lastValue = zValues.getLast(); + var lastValue = zValues.get(zValues.size()-1); zValues.add(lastValue); zValuesLinkedList.add(zValues); @@ -396,7 +394,7 @@ public synchronized void redraw(WaterfallPlotRuntime.PVData pvData) { } for (int n = 0; n < zValuesLinkedList.size(); n++) { - LinkedList waveformValues = zValuesLinkedList.get(n); + ArrayList waveformValues = zValuesLinkedList.get(n); for (int m = 0; m < waveformLength; m++) { double value; @@ -435,7 +433,7 @@ public synchronized void redraw(WaterfallPlotRuntime.PVData pvData) { } for (int n = 0; n < zValuesLinkedList.size(); n++) { - LinkedList waveformValues = zValuesLinkedList.get(n); + ArrayList waveformValues = zValuesLinkedList.get(n); for (int m = 0; m < waveformLength; m++) { double value; diff --git a/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotRuntime.java b/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotRuntime.java index 8c723a0721..838341a04e 100644 --- a/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotRuntime.java +++ b/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotRuntime.java @@ -22,6 +22,7 @@ import org.python.google.common.util.concurrent.AtomicDouble; import java.time.Instant; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.concurrent.ConcurrentSkipListMap; @@ -71,7 +72,7 @@ public sealed interface PVData permits WaveformPVData, ScalarPVsData {} // The type ConcurrentSkipListMap is used for the data points to allow for concurrent insertions and deletions: public record WaveformPVData (AtomicDouble minFromPV, AtomicDouble maxFromPV, - ConcurrentSkipListMap> instantToValue) implements PVData {} + ConcurrentSkipListMap> instantToValue) implements PVData {} public record ScalarPVsData (AtomicDouble minFromPV, AtomicDouble maxFromPV, LinkedList>> pvNameToInstantToValue) implements PVData {} @@ -144,7 +145,8 @@ else if (pvData instanceof WaveformPVData waveformPVData && pvNames.size() == 1) runtimePV.addListener((pv, vType) -> { if (vType instanceof VNumberArray vNumberArray) { - LinkedList waveform = new LinkedList<>(); + int size = vNumberArray.getData().size(); + ArrayList waveform = new ArrayList<>(size); for (int m = 0; m < vNumberArray.getData().size(); m++) { var value = vNumberArray.getData().getDouble(m); waveform.add(value); @@ -160,7 +162,8 @@ else if (pvData instanceof WaveformPVData waveformPVData && pvNames.size() == 1) } } else if (vType instanceof VEnumArray vEnumArray) { - LinkedList waveform = new LinkedList<>(); + int size = vEnumArray.getData().size(); + ArrayList waveform = new ArrayList<>(size); ListNumber listNumber = vEnumArray.getIndexes(); for (int m = 0; m < vEnumArray.getData().size(); m++) { var value = listNumber.getDouble(m); @@ -188,7 +191,8 @@ else if (pvData instanceof WaveformPVData waveformPVData && pvNames.size() == 1) for (var vtype : values) { if (vtype instanceof VNumberArray vNumberArray) { - LinkedList waveform = new LinkedList<>(); + int size = vNumberArray.getData().size(); + ArrayList waveform = new ArrayList<>(size); for (int m = 0; m < vNumberArray.getData().size(); m++) { var value = vNumberArray.getData().getDouble(m); waveform.add(value); @@ -198,7 +202,8 @@ else if (pvData instanceof WaveformPVData waveformPVData && pvNames.size() == 1) } else if (vtype instanceof VEnumArray vEnumArray) { - LinkedList waveform = new LinkedList<>(); + int size = vEnumArray.getData().size(); + ArrayList waveform = new ArrayList<>(size); ListNumber listNumber = vEnumArray.getIndexes(); for (int m = 0; m < vEnumArray.getData().size(); m++) { var value = listNumber.getDouble(m); From 4d604e6e8855bbd629cb3058118560862493e9c3 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Thu, 11 Sep 2025 15:27:35 +0200 Subject: [PATCH 3/3] CSSTUDIO-3420 Change type from LinkedList<> to ArrayList<> for 'pvNameToInstantToValue'. --- .../waterfallplotwidget/WaterfallPlotController.java | 2 +- .../waterfallplotwidget/WaterfallPlotRuntime.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotController.java b/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotController.java index cdc6c8f5a5..ef48e1ef6e 100644 --- a/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotController.java +++ b/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotController.java @@ -336,7 +336,7 @@ public synchronized void redraw(WaterfallPlotRuntime.PVData pvData) { } } } else if (pvData instanceof WaterfallPlotRuntime.ScalarPVsData scalarPVsData) { - LinkedList>> pvNameToInstantToValue = scalarPVsData.pvNameToInstantToValue(); + ArrayList>> pvNameToInstantToValue = scalarPVsData.pvNameToInstantToValue(); pvNameToInstantToValue.forEach(pvNameAndInstantToValueAtomicReference -> garbageCollectInstantToValue(pvNameAndInstantToValueAtomicReference.getValue(), t1)); minFromPV = scalarPVsData.minFromPV().get(); diff --git a/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotRuntime.java b/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotRuntime.java index 838341a04e..e21e3eee42 100644 --- a/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotRuntime.java +++ b/app/display/waterfallplot/src/main/java/org/phoebus/applications/waterfallplotwidget/WaterfallPlotRuntime.java @@ -62,7 +62,7 @@ public void initialize(WaterfallPlotWidget waterfallPlotWidget) { else { pvData = new ScalarPVsData(new AtomicDouble(Double.NaN), new AtomicDouble(Double.NaN), - new LinkedList<>()); + new ArrayList<>()); } retrieveHistoricValuesFromTheArchiver = waterfallPlotWidget.propRetrieveHistoricValuesFromTheArchiver().getValue(); pvNames = waterfallPlotWidget.propInputPVs().getValue().stream().map(widgetProperty -> widgetProperty.getValue()).collect(Collectors.toUnmodifiableList()); @@ -75,7 +75,7 @@ public record WaveformPVData (AtomicDouble minFromPV, ConcurrentSkipListMap> instantToValue) implements PVData {} public record ScalarPVsData (AtomicDouble minFromPV, AtomicDouble maxFromPV, - LinkedList>> pvNameToInstantToValue) implements PVData {} + ArrayList>> pvNameToInstantToValue) implements PVData {} @Override public void start() {