From 67de29ae73d63738ce9adea719d093dd189e3fa8 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Fri, 8 Aug 2025 14:08:41 +0200 Subject: [PATCH 01/35] CSSTUDIO-3360 Set minimumSize to 10. --- .../extra/widgets/linearmeter/LinearMeterRepresentation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java index b57afd78f1..12946369ca 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java @@ -244,7 +244,7 @@ protected void unregisterListeners() super.unregisterListeners(); } - int minimumSize = 25; + int minimumSize = 10; private void widthChanged(WidgetProperty prop, Integer old_value, Integer new_value) { if (new_value < minimumSize) { From 2d35f9caca8530f183b4e5f60cfdbce663bd660a Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Mon, 11 Aug 2025 15:55:20 +0200 Subject: [PATCH 02/35] CSSTUDIO-3347 Make concurrency more fine grained with the goal of improving the performance on multi-core processors. --- .../widgets/linearmeter/RTLinearMeter.java | 560 +++++++++--------- 1 file changed, 268 insertions(+), 292 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index 812957e1ac..b81023d528 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -21,6 +21,9 @@ import java.awt.image.DataBufferInt; import java.io.IOException; import java.util.Arrays; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import javafx.application.Platform; @@ -86,10 +89,10 @@ public RTLinearMeter(double initialValue, } if (Double.isFinite(min) && Double.isFinite(max)) { - validRange = true; + validRange.set(true); } else { - validRange = false; + validRange.set(false); min = 0.0; max = 100.0; } @@ -101,25 +104,25 @@ public RTLinearMeter(double initialValue, min, max); - this.minMaxTolerance = minMaxTolerance; - this.loLo = loLo; - this.low = low; - this.high = high; - this.hiHi = hiHi; - this.showUnits = showUnits; - this.showLimits = showLimits; - this.showWarnings = showWarnings; + this.minMaxTolerance.set(minMaxTolerance); + this.loLo.set(loLo); + this.low.set(low); + this.high.set(high); + this.hiHi.set(hiHi); + this.showUnits.set(showUnits); + this.showLimits.set(showLimits); + this.showWarnings.set(showWarnings); layout(); - this.currentValue = initialValue; - this.isGradientEnabled = isGradientEnabled; - this.isHighlightActiveRegionEnabled = isHighlightActiveRegionEnabled; + this.currentValue.set(initialValue); + this.isGradientEnabled.set(isGradientEnabled); + this.isHighlightActiveRegionEnabled.set(isHighlightActiveRegionEnabled); - this.needleWidth = needleWidth; - this.needleColor = needleColor; - this.knobSize = knobSize; - this.knobColor = knobColor; + this.needleWidth.set(needleWidth); + this.needleColor.set(needleColor); + this.knobSize.set(knobSize); + this.knobColor.set(knobColor); setNormalStatusColor(normalStatusColor); setMinorAlarmColor(minorAlarmColor); @@ -128,14 +131,14 @@ public RTLinearMeter(double initialValue, requestLayout(); } - private boolean showUnits; - private String units = ""; - private boolean showLimits; + private AtomicBoolean showUnits = new AtomicBoolean(true); + private AtomicReference units = new AtomicReference<>(""); + private AtomicBoolean showLimits = new AtomicBoolean(true); - private double loLo; - private double low; - private double high; - private double hiHi; + private AtomicReference loLo = new AtomicReference<>(0.0); + private AtomicReference low = new AtomicReference<>(0.0); + private AtomicReference high = new AtomicReference<>(100.0); + private AtomicReference hiHi = new AtomicReference<>(100.0); private static Image warningTriangle = null; @@ -162,11 +165,11 @@ private enum WARNING { NO_UNIT } - private WARNING currentWarning = WARNING.NONE; + private AtomicReference currentWarning = new AtomicReference(WARNING.NONE); /** Colors */ - private Color foreground = Color.BLACK; - private Color background = Color.WHITE; + private AtomicReference foreground = new AtomicReference<>(Color.BLACK); + private AtomicReference background = new AtomicReference<>(Color.WHITE); /** Fonts */ private Font font; @@ -181,51 +184,55 @@ public void layoutPlotPart(PlotPart plotPart) { } public void refreshPlotPart(PlotPart plotPart) { } }; - private boolean validRange; - private double minMaxTolerance; + private AtomicBoolean validRange = new AtomicBoolean(false); + private AtomicReference minMaxTolerance = new AtomicReference<>(0.0); public boolean getValidRange() { - return validRange; + return validRange.get(); } /** Optional scale of this linear meter */ public LinearMeterScale linearMeterScale; /** Value to display */ - private double currentValue = Double.NaN; + private AtomicReference currentValue = new AtomicReference(Double.NaN); - private Color normalStatusColor_lowlighted = Color.LIGHT_GRAY; - private Color normalStatusColorGradientStartPoint_lowlighted = Color.LIGHT_GRAY; - private Color normalStatusColorGradientEndPoint_lowlighted = Color.LIGHT_GRAY; - private Color normalStatusColor_highlighted = Color.LIGHT_GRAY; - private Color normalStatusColorGradientStartPoint_highlighted = Color.LIGHT_GRAY; - private Color normalStatusColorGradientEndPoint_highlighted = Color.LIGHT_GRAY; + private AtomicReference normalStatusColor_lowlighted = new AtomicReference(Color.LIGHT_GRAY); + private AtomicReference normalStatusColorGradientStartPoint_lowlighted = new AtomicReference(Color.LIGHT_GRAY); + private AtomicReference normalStatusColorGradientEndPoint_lowlighted = new AtomicReference(Color.LIGHT_GRAY); + private AtomicReference normalStatusColor_highlighted = new AtomicReference(Color.LIGHT_GRAY); + private AtomicReference normalStatusColorGradientStartPoint_highlighted = new AtomicReference(Color.LIGHT_GRAY); + private AtomicReference normalStatusColorGradientEndPoint_highlighted = new AtomicReference(Color.LIGHT_GRAY); - private Color minorAlarmColor_lowlighted = Color.ORANGE; - private Color minorAlarmColor_highlighted = Color.ORANGE; - private Color minorAlarmColorGradientStartPoint_lowlighted = Color.ORANGE; - private Color minorAlarmColorGradientEndPoint_lowlighted = Color.ORANGE; - private Color minorAlarmColorGradientStartPoint_highlighted = Color.ORANGE; - private Color minorAlarmColorGradientEndPoint_highlighted = Color.ORANGE; + private AtomicReference minorAlarmColor_lowlighted = new AtomicReference(Color.ORANGE); + private AtomicReference minorAlarmColor_highlighted = new AtomicReference(Color.ORANGE); + private AtomicReference minorAlarmColorGradientStartPoint_lowlighted = new AtomicReference(Color.ORANGE); + private AtomicReference minorAlarmColorGradientEndPoint_lowlighted = new AtomicReference(Color.ORANGE); + private AtomicReference minorAlarmColorGradientStartPoint_highlighted = new AtomicReference(Color.ORANGE); + private AtomicReference minorAlarmColorGradientEndPoint_highlighted = new AtomicReference(Color.ORANGE); - private Color majorAlarmColor_lowlighted = Color.RED; - private Color majorAlarmColor_highlighted = Color.RED; - private Color majorAlarmColorGradientStartPoint_lowlighted = Color.RED; - private Color majorAlarmColorGradientEndPoint_lowlighted = Color.RED; - private Color majorAlarmColorGradientStartPoint_highlighted = Color.RED; - private Color majorAlarmColorGradientEndPoint_highlighted = Color.RED; + private AtomicReference majorAlarmColor_lowlighted = new AtomicReference(Color.RED); + private AtomicReference majorAlarmColor_highlighted = new AtomicReference(Color.RED); + private AtomicReference majorAlarmColorGradientStartPoint_lowlighted = new AtomicReference(Color.RED); + private AtomicReference majorAlarmColorGradientEndPoint_lowlighted = new AtomicReference(Color.RED); + private AtomicReference majorAlarmColorGradientStartPoint_highlighted = new AtomicReference(Color.RED); + private AtomicReference majorAlarmColorGradientEndPoint_highlighted = new AtomicReference(Color.RED); - Paint normalStatusActiveColor_lowlighted, minorAlarmActiveColor_lowlighted, majorAlarmActiveColor_lowlighted; + AtomicReference normalStatusActiveColor_lowlighted = new AtomicReference<>(Color.WHITE); + AtomicReference minorAlarmActiveColor_lowlighted = new AtomicReference<>(Color.YELLOW); + AtomicReference majorAlarmActiveColor_lowlighted = new AtomicReference<>(Color.RED); - Paint normalStatusActiveColor_highlighted, majorAlarmActiveColor_highlighted, minorAlarmActiveColor_highlighted; + AtomicReference normalStatusActiveColor_highlighted = new AtomicReference<>(Color.WHITE); + AtomicReference majorAlarmActiveColor_highlighted = new AtomicReference<>(Color.YELLOW); + AtomicReference minorAlarmActiveColor_highlighted = new AtomicReference<>(Color.RED); - private int needleWidth; + private AtomicInteger needleWidth = new AtomicInteger(2); - private Color needleColor; + private AtomicReference needleColor = new AtomicReference<>(Color.BLACK); - private Boolean isGradientEnabled; + private AtomicBoolean isGradientEnabled = new AtomicBoolean(false); - private Boolean isHighlightActiveRegionEnabled; + private AtomicBoolean isHighlightActiveRegionEnabled = new AtomicBoolean(true); private void runOnJavaFXThread(Runnable runnable) { if (Platform.isFxApplicationThread()) { @@ -238,43 +245,43 @@ private void runOnJavaFXThread(Runnable runnable) { public void setIsGradientEnabled(boolean isGradientEnabled) { runOnJavaFXThread(() -> { - this.isGradientEnabled = isGradientEnabled; + this.isGradientEnabled.set(isGradientEnabled); updateActiveColors(); }); } public void setIsHighlightActiveRegionEnabled(boolean isHighlightActiveRegionEnabled) { - runOnJavaFXThread(() -> this.isHighlightActiveRegionEnabled = isHighlightActiveRegionEnabled); + this.isHighlightActiveRegionEnabled.set(isHighlightActiveRegionEnabled); } private void updateActiveColors() { - if (isGradientEnabled) { + if (isGradientEnabled.get()) { if (linearMeterScale.isHorizontal()) { - majorAlarmActiveColor_lowlighted = createVerticalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_lowlighted, majorAlarmColorGradientEndPoint_lowlighted); - minorAlarmActiveColor_lowlighted = createVerticalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_lowlighted, minorAlarmColorGradientEndPoint_lowlighted); - normalStatusActiveColor_lowlighted = createVerticalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted, normalStatusColorGradientEndPoint_highlighted); // The normal status region is never lowlighted. + majorAlarmActiveColor_lowlighted.set(createVerticalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_lowlighted.get(), majorAlarmColorGradientEndPoint_lowlighted.get())); + minorAlarmActiveColor_lowlighted.set(createVerticalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_lowlighted.get(), minorAlarmColorGradientEndPoint_lowlighted.get())); + normalStatusActiveColor_lowlighted.set(createVerticalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted.get(), normalStatusColorGradientEndPoint_highlighted.get())); // The normal status region is never lowlighted. - minorAlarmActiveColor_highlighted = createVerticalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_highlighted, minorAlarmColorGradientEndPoint_highlighted); - majorAlarmActiveColor_highlighted = createVerticalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_highlighted, majorAlarmColorGradientEndPoint_highlighted); - normalStatusActiveColor_highlighted = createVerticalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted, normalStatusColorGradientEndPoint_highlighted); + minorAlarmActiveColor_highlighted.set(createVerticalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_highlighted.get(), minorAlarmColorGradientEndPoint_highlighted.get())); + majorAlarmActiveColor_highlighted.set(createVerticalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_highlighted.get(), majorAlarmColorGradientEndPoint_highlighted.get())); + normalStatusActiveColor_highlighted.set(createVerticalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted.get(), normalStatusColorGradientEndPoint_highlighted.get())); } else { - majorAlarmActiveColor_lowlighted = createHorizontalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_lowlighted, majorAlarmColorGradientEndPoint_lowlighted); - minorAlarmActiveColor_lowlighted = createHorizontalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_lowlighted, minorAlarmColorGradientEndPoint_lowlighted); - normalStatusActiveColor_lowlighted = createHorizontalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted, normalStatusColorGradientEndPoint_highlighted); // The normal status region is never lowlighted. + majorAlarmActiveColor_lowlighted.set(createHorizontalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_lowlighted.get(), majorAlarmColorGradientEndPoint_lowlighted.get())); + minorAlarmActiveColor_lowlighted.set(createHorizontalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_lowlighted.get(), minorAlarmColorGradientEndPoint_lowlighted.get())); + normalStatusActiveColor_lowlighted.set(createHorizontalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted.get(), normalStatusColorGradientEndPoint_highlighted.get())); // The normal status region is never lowlighted. - minorAlarmActiveColor_highlighted = createHorizontalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_highlighted, minorAlarmColorGradientEndPoint_highlighted); - majorAlarmActiveColor_highlighted = createHorizontalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_highlighted, majorAlarmColorGradientEndPoint_highlighted); - normalStatusActiveColor_highlighted = createHorizontalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted, normalStatusColorGradientEndPoint_highlighted); + minorAlarmActiveColor_highlighted.set(createHorizontalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_highlighted.get(), minorAlarmColorGradientEndPoint_highlighted.get())); + majorAlarmActiveColor_highlighted.set(createHorizontalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_highlighted.get(), majorAlarmColorGradientEndPoint_highlighted.get())); + normalStatusActiveColor_highlighted.set(createHorizontalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted.get(), normalStatusColorGradientEndPoint_highlighted.get())); } } else { - normalStatusActiveColor_lowlighted = normalStatusColor_highlighted; // The normal status region is never lowlighted. - majorAlarmActiveColor_lowlighted = majorAlarmColor_lowlighted; - minorAlarmActiveColor_lowlighted = minorAlarmColor_lowlighted; + normalStatusActiveColor_lowlighted.set(normalStatusColor_highlighted.get()); // The normal status region is never lowlighted. + majorAlarmActiveColor_lowlighted.set(majorAlarmColor_lowlighted.get()); + minorAlarmActiveColor_lowlighted.set(minorAlarmColor_lowlighted.get()); - normalStatusActiveColor_highlighted = normalStatusColor_highlighted; - minorAlarmActiveColor_highlighted = minorAlarmColor_highlighted; - majorAlarmActiveColor_highlighted = majorAlarmColor_highlighted; + normalStatusActiveColor_highlighted.set(normalStatusColor_highlighted.get()); + minorAlarmActiveColor_highlighted.set(minorAlarmColor_highlighted.get()); + majorAlarmActiveColor_highlighted.set(majorAlarmColor_highlighted.get()); } } @@ -332,239 +339,205 @@ private Color computeLowlightedColor(Color color) { } public void setNormalStatusColor(Color normalStatusColor) { - runOnJavaFXThread(() -> { - this.normalStatusColor_lowlighted = computeLowlightedColor(normalStatusColor); - this.normalStatusColor_highlighted = normalStatusColor; + this.normalStatusColor_lowlighted.set(computeLowlightedColor(normalStatusColor)); + this.normalStatusColor_highlighted.set(normalStatusColor); - this.normalStatusColorGradientStartPoint_lowlighted = computeGradientStartPoint(normalStatusColor_lowlighted); - this.normalStatusColorGradientEndPoint_lowlighted = computeGradientEndPoint(normalStatusColor_lowlighted); + this.normalStatusColorGradientStartPoint_lowlighted.set(computeGradientStartPoint(normalStatusColor_lowlighted.get())); + this.normalStatusColorGradientEndPoint_lowlighted.set(computeGradientEndPoint(normalStatusColor_lowlighted.get())); - this.normalStatusColorGradientStartPoint_highlighted = computeGradientStartPoint(normalStatusColor_highlighted); - this.normalStatusColorGradientEndPoint_highlighted = computeGradientEndPoint(normalStatusColor_highlighted); + this.normalStatusColorGradientStartPoint_highlighted.set(computeGradientStartPoint(normalStatusColor_highlighted.get())); + this.normalStatusColorGradientEndPoint_highlighted.set(computeGradientEndPoint(normalStatusColor_highlighted.get())); updateActiveColors(); - }); } public void setMinorAlarmColor(Color minorAlarmColor) { - runOnJavaFXThread(() -> { - this.minorAlarmColor_lowlighted = computeLowlightedColor(minorAlarmColor); - this.minorAlarmColor_highlighted = minorAlarmColor; + this.minorAlarmColor_lowlighted.set(computeLowlightedColor(minorAlarmColor)); + this.minorAlarmColor_highlighted.set(minorAlarmColor); - this.minorAlarmColorGradientStartPoint_lowlighted = computeGradientStartPoint(minorAlarmColor_lowlighted); - this.minorAlarmColorGradientEndPoint_lowlighted = computeGradientEndPoint(minorAlarmColor_lowlighted); + this.minorAlarmColorGradientStartPoint_lowlighted.set(computeGradientStartPoint(minorAlarmColor_lowlighted.get())); + this.minorAlarmColorGradientEndPoint_lowlighted.set(computeGradientEndPoint(minorAlarmColor_lowlighted.get())); - this.minorAlarmColorGradientStartPoint_highlighted = computeGradientStartPoint(minorAlarmColor_highlighted); - this.minorAlarmColorGradientEndPoint_highlighted = computeGradientEndPoint(minorAlarmColor_highlighted); + this.minorAlarmColorGradientStartPoint_highlighted.set(computeGradientStartPoint(minorAlarmColor_highlighted.get())); + this.minorAlarmColorGradientEndPoint_highlighted.set(computeGradientEndPoint(minorAlarmColor_highlighted.get())); updateActiveColors(); - }); } public void setMajorAlarmColor(Color majorAlarmColor) { runOnJavaFXThread(() -> { - this.majorAlarmColor_lowlighted = computeLowlightedColor(majorAlarmColor); - this.majorAlarmColor_highlighted = majorAlarmColor; + this.majorAlarmColor_lowlighted.set(computeLowlightedColor(majorAlarmColor)); + this.majorAlarmColor_highlighted.set(majorAlarmColor); - this.majorAlarmColorGradientStartPoint_lowlighted = computeGradientStartPoint(majorAlarmColor_lowlighted); - this.majorAlarmColorGradientEndPoint_lowlighted = computeGradientEndPoint(majorAlarmColor_lowlighted); + this.majorAlarmColorGradientStartPoint_lowlighted.set(computeGradientStartPoint(majorAlarmColor_lowlighted.get())); + this.majorAlarmColorGradientEndPoint_lowlighted.set(computeGradientEndPoint(majorAlarmColor_lowlighted.get())); - this.majorAlarmColorGradientStartPoint_highlighted = computeGradientStartPoint(majorAlarmColor_highlighted); - this.majorAlarmColorGradientEndPoint_highlighted = computeGradientEndPoint(majorAlarmColor_highlighted); + this.majorAlarmColorGradientStartPoint_highlighted.set(computeGradientStartPoint(majorAlarmColor_highlighted.get())); + this.majorAlarmColorGradientEndPoint_highlighted.set(computeGradientEndPoint(majorAlarmColor_highlighted.get())); updateActiveColors(); }); } public void setNeedleWidth(int needleWidth) { - runOnJavaFXThread(() -> this.needleWidth = needleWidth); + this.needleWidth.set(needleWidth); } public void setNeedleColor(Color needleColor) { - runOnJavaFXThread(() -> this.needleColor = needleColor); + this.needleColor.set(needleColor); } public void setShowUnits(boolean newValue) { - runOnJavaFXThread(() -> { - showUnits = newValue; - updateMeterBackground(); - redrawIndicator(currentValue, currentWarning); - }); + showUnits.set(newValue); + updateMeterBackground(); + redrawIndicator(currentValue.get(), currentWarning.get()); } public void setUnits(String newValue) { - runOnJavaFXThread(() -> { - if (!units.equals(newValue)) { - units = newValue; - updateMeterBackground(); - redrawIndicator(currentValue, currentWarning); - } - }); + + if (!units.equals(newValue)) { + units.set(newValue); + updateMeterBackground(); + redrawIndicator(currentValue.get(), currentWarning.get()); + } } public void setShowLimits(boolean newValue) { - runOnJavaFXThread(() -> { - showLimits = newValue; - updateMeterBackground(); - determineWarning(); - redrawIndicator(currentValue, currentWarning); - }); + showLimits.set(newValue); + updateMeterBackground(); + determineWarning(); + redrawIndicator(currentValue.get(), currentWarning.get()); } public void setRange(double minimum, double maximum, boolean validRange) { - runOnJavaFXThread(() -> { - this.validRange = validRange; - linearMeterScale.setValueRange(minimum, maximum); + this.validRange.set(validRange); + linearMeterScale.setValueRange(minimum, maximum); - updateMeterBackground(); - redrawIndicator(currentValue, currentWarning); - }); + updateMeterBackground(); + redrawIndicator(currentValue.get(), currentWarning.get()); } public void setMinMaxTolerance(double minMaxTolerance) { - runOnJavaFXThread(() -> { - this.minMaxTolerance = minMaxTolerance; + this.minMaxTolerance.set(minMaxTolerance); determineWarning(); - redrawIndicator(currentValue, currentWarning); - }); + redrawIndicator(currentValue.get(), currentWarning.get()); } public double getLoLo() { - return loLo; + return loLo.get(); } public void setLoLo(double loLo) { - runOnJavaFXThread(() -> { - this.loLo = loLo; - layout(); - updateMeterBackground(); - }); + this.loLo.set(loLo); + layout(); + updateMeterBackground(); } public double getLow() { - return low; + return low.get(); } public void setLow(double low) { runOnJavaFXThread(() -> { - this.low = low; + this.low.set(low); layout(); updateMeterBackground(); }); } public double getHigh() { - return high; + return high.get(); } public void setHigh(double high) { - runOnJavaFXThread(() -> { - this.high = high; - layout(); - updateMeterBackground(); - }); + this.high.set(high); + layout(); + updateMeterBackground(); } public double getHiHi() { - return hiHi; + return hiHi.get(); } public void setHiHi(double hiHi) { - runOnJavaFXThread(() -> { - this.hiHi = hiHi; - layout(); - updateMeterBackground(); - }); + this.hiHi.set(hiHi); + layout(); + updateMeterBackground(); } - private Color knobColor = new Color(0, 0, 0, 255); + private AtomicReference knobColor = new AtomicReference(new Color(0, 0, 0, 255)); public void setKnobColor(Color knobColor) { - runOnJavaFXThread(() -> { - this.knobColor = knobColor; - requestLayout(); - }); + this.knobColor.set(knobColor); + requestLayout(); } - private int knobSize; + private AtomicInteger knobSize = new AtomicInteger(1); public void setKnobSize(int knobSize) { - runOnJavaFXThread(() -> { - this.knobSize = knobSize; + this.knobSize.set(knobSize); requestLayout(); - }); } private BufferedImage meter_background = null; - private WritableImage awt_jfx_convert_buffer = null; - /** Redraw on UI thread by adding needle to 'meter_background' */ - private void redrawIndicator (double value, WARNING warning) - { - runOnJavaFXThread(() -> { - if (meter_background != null) - { - if (meter_background.getType() != BufferedImage.TYPE_INT_ARGB){ - throw new IllegalPathStateException("Need TYPE_INT_ARGB for direct buffer access, not " + meter_background.getType()); - } + private void redrawIndicator(double value, WARNING warning) { + if (meter_background != null) { + if (meter_background.getType() != BufferedImage.TYPE_INT_ARGB) { + throw new IllegalPathStateException("Need TYPE_INT_ARGB for direct buffer access, not " + meter_background.getType()); + } - BufferedImage combined = new BufferedImage(linearMeterScale.getBounds().width, linearMeterScale.getBounds().height, BufferedImage.TYPE_INT_ARGB); - int[] src = ((DataBufferInt) meter_background.getRaster().getDataBuffer()).getData(); - int[] dest = ((DataBufferInt) combined.getRaster().getDataBuffer()).getData(); - System.arraycopy(src, 0, dest, 0, linearMeterScale.getBounds().width * linearMeterScale.getBounds().height); + BufferedImage combined = new BufferedImage(linearMeterScale.getBounds().width, linearMeterScale.getBounds().height, BufferedImage.TYPE_INT_ARGB); + int[] src = ((DataBufferInt) meter_background.getRaster().getDataBuffer()).getData(); + int[] dest = ((DataBufferInt) combined.getRaster().getDataBuffer()).getData(); + System.arraycopy(src, 0, dest, 0, linearMeterScale.getBounds().width * linearMeterScale.getBounds().height); - // Add needle & label - Graphics2D gc = combined.createGraphics(); + // Add needle & label + Graphics2D gc = combined.createGraphics(); - drawValue(gc, value); - drawWarning(gc, warning); - if (showUnits) { - drawUnit(gc); - } + drawValue(gc, value); + drawWarning(gc, warning); + if (showUnits.get()) { + drawUnit(gc); + } - // Convert to JFX image and show - if (awt_jfx_convert_buffer == null || - awt_jfx_convert_buffer.getWidth() != linearMeterScale.getBounds().width || - awt_jfx_convert_buffer.getHeight() != linearMeterScale.getBounds().height) - awt_jfx_convert_buffer = new WritableImage(linearMeterScale.getBounds().width, linearMeterScale.getBounds().height); + // Convert to JFX image and show - awt_jfx_convert_buffer.getPixelWriter().setPixels(0, 0, linearMeterScale.getBounds().width, linearMeterScale.getBounds().height, PixelFormat.getIntArgbInstance(), dest, 0, linearMeterScale.getBounds().width); + WritableImage awt_jfx_convert_buffer = new WritableImage(linearMeterScale.getBounds().width, linearMeterScale.getBounds().height); - setImage(awt_jfx_convert_buffer); - logger.log(Level.FINE, "Redraw meter"); - } - }); - }; + awt_jfx_convert_buffer.getPixelWriter().setPixels(0, 0, linearMeterScale.getBounds().width, linearMeterScale.getBounds().height, PixelFormat.getIntArgbInstance(), dest, 0, linearMeterScale.getBounds().width); + runOnJavaFXThread(() -> { draw(awt_jfx_convert_buffer); }); + logger.log(Level.FINE, "Redraw meter"); + } + } + + private void draw(WritableImage awt_jfx_convert_buffer) { + setImage(awt_jfx_convert_buffer); + } /** Call to update size of meter * * @param width * @param height */ - public void setSize(int width, int height) - { - runOnJavaFXThread(() -> { - linearMeterScale.setBounds(0, 0, width, height); - layout(); - updateActiveColors(); - requestLayout(); - }); + public void setSize(int width, int height) { + linearMeterScale.setBounds(0, 0, width, height); + layout(); + updateActiveColors(); + requestLayout(); } /** @param color Foreground (labels, tick marks) color */ - public void setForeground(javafx.scene.paint.Color color) - { - runOnJavaFXThread(() -> { - foreground = GraphicsUtils.convert(color); - linearMeterScale.setColor(color); - }); + public void setForeground(javafx.scene.paint.Color color) { + foreground.set(GraphicsUtils.convert(color)); + linearMeterScale.setColor(color); } /** @param color Background color */ public void setBackground(javafx.scene.paint.Color color) { - runOnJavaFXThread(() -> background = GraphicsUtils.convert(color)); + background.set(GraphicsUtils.convert(color)); } /** @param font Label font */ @@ -576,44 +549,40 @@ public void setFont(javafx.scene.text.Font font) }); } - private boolean showWarnings = true; + private AtomicBoolean showWarnings = new AtomicBoolean(true); public void setShowWarnings(boolean showWarnings) { - runOnJavaFXThread(() -> { this.showWarnings = showWarnings; }); + this.showWarnings.set(showWarnings); } - private boolean lag = false; - private Boolean isValueWaitingToBeDrawn = false; - private double valueWaitingToBeDrawn; + private AtomicBoolean lag = new AtomicBoolean(false); + private AtomicBoolean isValueWaitingToBeDrawn = new AtomicBoolean(false); + private AtomicReference valueWaitingToBeDrawn = new AtomicReference<>(Double.NaN); /** @param newValue Current value */ public void setCurrentValue(double newValue) { - runOnJavaFXThread(() -> { - valueWaitingToBeDrawn = newValue; + valueWaitingToBeDrawn.set(newValue); - if (isValueWaitingToBeDrawn) { - lag = true; - } - else { - isValueWaitingToBeDrawn = true; + if (isValueWaitingToBeDrawn.get()) { + lag.set(true); + } + else { + isValueWaitingToBeDrawn.set(true); - Platform.runLater(() -> { - drawNewValue(valueWaitingToBeDrawn); - isValueWaitingToBeDrawn = false; - lag = false; - }); - } - }); + drawNewValue(valueWaitingToBeDrawn.get()); + isValueWaitingToBeDrawn.set(false); + lag.set(false); + } } private void drawNewValue(double newValue) { - double oldValue = currentValue; - currentValue = newValue; + double oldValue = currentValue.get(); + currentValue.set(newValue); - if (newValue > linearMeterScale.getValueRange().getHigh() && newValue <= linearMeterScale.getValueRange().getHigh() + minMaxTolerance) { + if (newValue > linearMeterScale.getValueRange().getHigh() && newValue <= linearMeterScale.getValueRange().getHigh() + minMaxTolerance.get()) { newValue = linearMeterScale.getValueRange().getHigh(); } - if (newValue < linearMeterScale.getValueRange().getLow() && newValue >= linearMeterScale.getValueRange().getLow() - minMaxTolerance) { + if (newValue < linearMeterScale.getValueRange().getLow() && newValue >= linearMeterScale.getValueRange().getLow() - minMaxTolerance.get()) { newValue = linearMeterScale.getValueRange().getLow(); } @@ -627,7 +596,7 @@ private void drawNewValue(double newValue) { newIndicatorPosition = (int) (linearMeterScale.getBounds().height - marginBelow - pixelsPerScaleUnit * (newValue - linearMeterScale.getValueRange().getLow())); } WARNING newWarning = determineWarning(); - if (currentIndicatorPosition == null || currentIndicatorPosition != newIndicatorPosition || currentWarning != newWarning) { + if (currentIndicatorPosition == null || currentIndicatorPosition != newIndicatorPosition || currentWarning.get() != newWarning) { redrawIndicator(newValue, newWarning); } } @@ -638,22 +607,22 @@ else if (!Double.isNaN(oldValue)) { } private WARNING determineWarning() { - if (!showWarnings) { + if (!showWarnings.get()) { return WARNING.NONE; } - else if (lag) { + else if (lag.get()) { return WARNING.LAG; } - else if (showUnits && units.equals("")) { + else if (showUnits.get() && units.get().equals("")) { return WARNING.NO_UNIT; } - else if (!validRange) { + else if (!validRange.get()) { return WARNING.MIN_AND_MAX_NOT_DEFINED; } - else if (currentValue < linearMeterScale.getValueRange().getLow() - minMaxTolerance) { + else if (currentValue.get() < linearMeterScale.getValueRange().getLow() - minMaxTolerance.get()) { return WARNING.VALUE_LESS_THAN_MIN; } - else if (currentValue > linearMeterScale.getValueRange().getHigh() + minMaxTolerance) { + else if (currentValue.get() > linearMeterScale.getValueRange().getHigh() + minMaxTolerance.get()) { return WARNING.VALUE_GREATER_THAN_MAX; } else { @@ -684,11 +653,11 @@ else if (warning == WARNING.LAG) { } drawWarningText(gc, warningText); - if (currentWarning != warning) { + if (currentWarning.get() != warning) { logger.log(Level.WARNING, warningText + " on Linear Meter!"); } } - currentWarning = warning; + currentWarning.set(warning); } /** @param visible Whether the scale must be displayed or not. */ @@ -704,7 +673,7 @@ public void setScaleVisible (boolean visible) private void requestLayout() { updateMeterBackground(); - redrawIndicator(currentValue, currentWarning); + redrawIndicator(currentValue.get(), currentWarning.get()); } private void computeLayout() @@ -748,7 +717,7 @@ private void updateMeterBackground() linearMeterScale.computeTicks(gc); computeLayout(); - gc.setBackground(background); + gc.setBackground(background.get()); gc.clearRect(0, 0, width, height); linearMeterScale.paint(gc, new Rectangle(0,0,0,0)); @@ -759,20 +728,20 @@ private void updateMeterBackground() private void paintMeter(Graphics2D graphics) { Color color = graphics.getColor(); - if (showLimits) { - if (isHighlightActiveRegionEnabled) { - paintRectangle(graphics, normalRectangle, normalStatusActiveColor_lowlighted); - paintRectangle(graphics, lowRectangle, minorAlarmActiveColor_lowlighted); - paintRectangle(graphics, highRectangle, minorAlarmActiveColor_lowlighted); - paintRectangle(graphics, loLoRectangle, majorAlarmActiveColor_lowlighted); - paintRectangle(graphics, hiHiRectangle, majorAlarmActiveColor_lowlighted); + if (showLimits.get()) { + if (isHighlightActiveRegionEnabled.get()) { + paintRectangle(graphics, normalRectangle, normalStatusActiveColor_lowlighted.get()); + paintRectangle(graphics, lowRectangle, minorAlarmActiveColor_lowlighted.get()); + paintRectangle(graphics, highRectangle, minorAlarmActiveColor_lowlighted.get()); + paintRectangle(graphics, loLoRectangle, majorAlarmActiveColor_lowlighted.get()); + paintRectangle(graphics, hiHiRectangle, majorAlarmActiveColor_lowlighted.get()); } else { - paintRectangle(graphics, normalRectangle, normalStatusActiveColor_highlighted); - paintRectangle(graphics, lowRectangle, minorAlarmActiveColor_highlighted); - paintRectangle(graphics, highRectangle, minorAlarmActiveColor_highlighted); - paintRectangle(graphics, loLoRectangle, majorAlarmActiveColor_highlighted); - paintRectangle(graphics, hiHiRectangle, majorAlarmActiveColor_highlighted); + paintRectangle(graphics, normalRectangle, normalStatusActiveColor_highlighted.get()); + paintRectangle(graphics, lowRectangle, minorAlarmActiveColor_highlighted.get()); + paintRectangle(graphics, highRectangle, minorAlarmActiveColor_highlighted.get()); + paintRectangle(graphics, loLoRectangle, majorAlarmActiveColor_highlighted.get()); + paintRectangle(graphics, hiHiRectangle, majorAlarmActiveColor_highlighted.get()); } } else { @@ -781,7 +750,7 @@ private void paintMeter(Graphics2D graphics) { marginAbove, linearMeterScale.getBounds().width - marginLeft - marginRight, linearMeterScale.getBounds().height - marginAbove - marginBelow), - normalStatusActiveColor_lowlighted); + normalStatusActiveColor_lowlighted.get()); } graphics.setColor(color); } @@ -821,22 +790,22 @@ private void drawValue(Graphics2D gc, double value) { gc.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); gc.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - if (showLimits) { - if (isHighlightActiveRegionEnabled) { - if (value <= loLo) { - paintRectangle(gc, loLoRectangle, majorAlarmColor_highlighted); + if (showLimits.get()) { + if (isHighlightActiveRegionEnabled.get()) { + if (value <= loLo.get()) { + paintRectangle(gc, loLoRectangle, majorAlarmColor_highlighted.get()); } - else if (value >= hiHi) { - paintRectangle(gc, hiHiRectangle, majorAlarmColor_highlighted); + else if (value >= hiHi.get()) { + paintRectangle(gc, hiHiRectangle, majorAlarmColor_highlighted.get()); } - else if (value <= low && value > loLo) { - paintRectangle(gc, lowRectangle, minorAlarmActiveColor_highlighted); + else if (value <= low.get() && value > loLo.get()) { + paintRectangle(gc, lowRectangle, minorAlarmActiveColor_highlighted.get()); } - else if (value >= high && value < hiHi) { - paintRectangle(gc, highRectangle, minorAlarmActiveColor_highlighted); + else if (value >= high.get() && value < hiHi.get()) { + paintRectangle(gc, highRectangle, minorAlarmActiveColor_highlighted.get()); } else { - paintRectangle(gc, normalRectangle, normalStatusActiveColor_highlighted); + paintRectangle(gc, normalRectangle, normalStatusActiveColor_highlighted.get()); } } } @@ -846,27 +815,27 @@ else if (value >= high && value < hiHi) { currentIndicatorPosition = (int) (marginLeft + pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); - if (knobSize > 0) { - int[] XVal = { currentIndicatorPosition - (int) Math.round((1.0 * knobSize) / 4.0), - currentIndicatorPosition + (int) Math.round((1.0 * knobSize) / 4.0), + if (knobSize.get() > 0) { + int[] XVal = { currentIndicatorPosition - (int) Math.round((1.0 * knobSize.get()) / 4.0), + currentIndicatorPosition + (int) Math.round((1.0 * knobSize.get()) / 4.0), currentIndicatorPosition }; int[] YVal = { 0, 0, marginAbove - 2 }; gc.setStroke(AxisPart.TICK_STROKE); - gc.setColor(knobColor); + gc.setColor(knobColor.get()); gc.fillPolygon(XVal, YVal, 3); - gc.setColor(knobColor); + gc.setColor(knobColor.get()); gc.drawPolygon(XVal, YVal, 3); } - if (needleWidth > 0) { - gc.setStroke(new BasicStroke((float) needleWidth)); + if (needleWidth.get() > 0) { + gc.setStroke(new BasicStroke((float) needleWidth.get())); gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); - gc.setPaint(needleColor); + gc.setPaint(needleColor.get()); - int y1 = marginAbove + needleWidth / 2 + 1; - int y2 = linearMeterScale.getBounds().height - marginBelow - (needleWidth - 1) / 2 - 1; + int y1 = marginAbove + needleWidth.get() / 2 + 1; + int y2 = linearMeterScale.getBounds().height - marginBelow - (needleWidth.get() - 1) / 2 - 1; gc.drawLine(currentIndicatorPosition, y1, currentIndicatorPosition, y2); } @@ -876,27 +845,27 @@ else if (value >= high && value < hiHi) { currentIndicatorPosition = (int) (linearMeterScale.getBounds().height - marginBelow - pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); - if (knobSize > 0) { - int[] YVal = { currentIndicatorPosition + (int) Math.round((1.0 * knobSize / 4.0)), - currentIndicatorPosition - (int) Math.round((1.0 * knobSize / 4.0)), + if (knobSize.get() > 0) { + int[] YVal = { currentIndicatorPosition + (int) Math.round((1.0 * knobSize.get() / 4.0)), + currentIndicatorPosition - (int) Math.round((1.0 * knobSize.get() / 4.0)), currentIndicatorPosition }; int[] XVal = { 0, 0, marginLeft - 2 }; gc.setStroke(AxisPart.TICK_STROKE); - gc.setColor(knobColor); + gc.setColor(knobColor.get()); gc.fillPolygon(XVal, YVal, 3); - gc.setColor(knobColor); + gc.setColor(knobColor.get()); gc.drawPolygon(XVal, YVal, 3); } - if (needleWidth > 0) { - gc.setStroke(new BasicStroke((float) needleWidth)); + if (needleWidth.get() > 0) { + gc.setStroke(new BasicStroke((float) needleWidth.get())); gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); - gc.setPaint(needleColor); + gc.setPaint(needleColor.get()); - int x1 = marginLeft + (needleWidth)/2 + 1; - int x2 = linearMeterScale.getBounds().width - marginRight - (needleWidth+1)/2; + int x1 = marginLeft + (needleWidth.get())/2 + 1; + int x2 = linearMeterScale.getBounds().width - marginRight - (needleWidth.get()+1)/2; gc.drawLine(x1, currentIndicatorPosition, x2, currentIndicatorPosition); } @@ -920,7 +889,7 @@ public void setHorizontal(boolean horizontal) { linearMeterScale.setHorizontal(horizontal); redrawLinearMeterScale(); updateMeterBackground(); - redrawIndicator(currentValue, currentWarning); + redrawIndicator(currentValue.get(), currentWarning.get()); }); } @@ -1022,7 +991,7 @@ private void paintRectangle(Graphics2D gc, Rectangle rectangle, Paint paint){ //Draw border of rectangle. //TODO: can this be included in the paint? - gc.setColor(foreground); + gc.setColor(foreground.get()); gc.draw(rectangle); gc.setColor(oldColor); @@ -1047,11 +1016,16 @@ private void layout() { double displayedHiHi; double displayedHigh; - displayedLoLo = Double.isFinite(loLo) ? Math.max(loLo, linearMeterScale.getValueRange().getLow()) : linearMeterScale.getValueRange().getLow(); - displayedLow = Double.isFinite(low) ? Math.max(Math.max(low, linearMeterScale.getValueRange().getLow()), displayedLoLo) : linearMeterScale.getValueRange().getLow(); + double loLoValue = loLo.get(); + double lowValue = low.get(); + double highValue = high.get(); + double hiHiValue = hiHi.get(); + + displayedLoLo = Double.isFinite(loLoValue) ? Math.max(loLoValue, linearMeterScale.getValueRange().getLow()) : linearMeterScale.getValueRange().getLow(); + displayedLow = Double.isFinite(lowValue) ? Math.max(Math.max(lowValue, linearMeterScale.getValueRange().getLow()), displayedLoLo) : linearMeterScale.getValueRange().getLow(); - displayedHiHi = Double.isFinite(high) ? Math.min(hiHi, linearMeterScale.getValueRange().getHigh()) : linearMeterScale.getValueRange().getHigh(); - displayedHigh = Double.isFinite(hiHi) ? Math.min(Math.min(high, linearMeterScale.getValueRange().getHigh()), displayedHiHi) : linearMeterScale.getValueRange().getHigh(); + displayedHiHi = Double.isFinite(highValue) ? Math.min(hiHiValue, linearMeterScale.getValueRange().getHigh()) : linearMeterScale.getValueRange().getHigh(); + displayedHigh = Double.isFinite(hiHiValue) ? Math.min(Math.min(highValue, linearMeterScale.getValueRange().getHigh()), displayedHiHi) : linearMeterScale.getValueRange().getHigh(); FontMetrics fontMetrics = null; if (font != null) { @@ -1060,7 +1034,8 @@ private void layout() { } if (linearMeterScale.isHorizontal()) { - marginAbove = knobSize >= 1 ? knobSize + 2 : 0; + int knobSizeValue = knobSize.get(); + marginAbove = knobSizeValue >= 1 ? knobSizeValue + 2 : 0; if (linearMeterScale.isVisible() && fontMetrics != null) { var majorTicks = linearMeterScale.getTicks().getMajorTicks(); if (majorTicks.size() >= 2) { @@ -1079,7 +1054,7 @@ private void layout() { marginBelow = 1; } - if (showUnits && fontMetrics != null) { + if (showUnits.get() && fontMetrics != null) { marginBelow += 1 + fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent(); } @@ -1118,7 +1093,8 @@ private void layout() { meterBreadth); } else { - marginLeft = knobSize >= 1 ? knobSize + 2 : 0; + int knobSizeValue = knobSize.get(); + marginLeft = knobSizeValue >= 1 ? knobSizeValue + 2 : 0; if (linearMeterScale.isVisible() && fontMetrics != null) { int maxTickLabelWidth = 0; maxTickLabelWidth = 0; @@ -1136,7 +1112,7 @@ private void layout() { marginBelow = 1; } - if (showUnits && fontMetrics != null) { + if (showUnits.get() && fontMetrics != null) { marginBelow += 1 + fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent(); } From faaab250b853f24dc4d0da0c2290608e181fb92c Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Tue, 12 Aug 2025 10:46:38 +0200 Subject: [PATCH 03/35] CSSTUDIO-3347 Improve memory usage: re-use buffers when drawing the indicator. --- .../widgets/linearmeter/RTLinearMeter.java | 75 +++++++++++-------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index b81023d528..806dc6694e 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -21,12 +21,15 @@ import java.awt.image.DataBufferInt; import java.io.IOException; import java.util.Arrays; +import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import javafx.application.Platform; +import javafx.util.Pair; +import org.apache.commons.lang3.tuple.ImmutableTriple; import org.csstudio.javafx.rtplot.internal.AxisPart; import org.csstudio.javafx.rtplot.internal.PlotPart; import org.csstudio.javafx.rtplot.internal.PlotPartListener; @@ -479,41 +482,44 @@ public void setKnobSize(int knobSize) { requestLayout(); } - private BufferedImage meter_background = null; - - /** Redraw on UI thread by adding needle to 'meter_background' */ + private AtomicReference, Pair>>> meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference = new AtomicReference<>(Optional.empty()); + /** + * Redraw on UI thread by adding needle to 'meter_background' + */ private void redrawIndicator(double value, WARNING warning) { - if (meter_background != null) { - if (meter_background.getType() != BufferedImage.TYPE_INT_ARGB) { - throw new IllegalPathStateException("Need TYPE_INT_ARGB for direct buffer access, not " + meter_background.getType()); - } - - BufferedImage combined = new BufferedImage(linearMeterScale.getBounds().width, linearMeterScale.getBounds().height, BufferedImage.TYPE_INT_ARGB); - int[] src = ((DataBufferInt) meter_background.getRaster().getDataBuffer()).getData(); - int[] dest = ((DataBufferInt) combined.getRaster().getDataBuffer()).getData(); - System.arraycopy(src, 0, dest, 0, linearMeterScale.getBounds().width * linearMeterScale.getBounds().height); - - // Add needle & label - Graphics2D gc = combined.createGraphics(); - - drawValue(gc, value); - drawWarning(gc, warning); - if (showUnits.get()) { - drawUnit(gc); - } + var meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference.get(); + if (meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.isEmpty()) { + return; + } + BufferedImage meterBackground = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getLeft(); + BufferedImage combined = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getMiddle().getKey(); + var awtJFXConvertBuffer = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getMiddle().getValue(); + int width = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getRight().getKey(); + int height = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getRight().getValue(); + if (meterBackground.getType() != BufferedImage.TYPE_INT_ARGB) { + throw new IllegalPathStateException("Need TYPE_INT_ARGB for direct buffer access, not " + meterBackground.getType()); + } - // Convert to JFX image and show + int[] src = ((DataBufferInt) meterBackground.getRaster().getDataBuffer()).getData(); + int[] dest = ((DataBufferInt) combined.getRaster().getDataBuffer()).getData(); + System.arraycopy(src, 0, dest, 0, width * height); - WritableImage awt_jfx_convert_buffer = new WritableImage(linearMeterScale.getBounds().width, linearMeterScale.getBounds().height); + // Add needle & label + Graphics2D gc = combined.createGraphics(); - awt_jfx_convert_buffer.getPixelWriter().setPixels(0, 0, linearMeterScale.getBounds().width, linearMeterScale.getBounds().height, PixelFormat.getIntArgbInstance(), dest, 0, linearMeterScale.getBounds().width); - runOnJavaFXThread(() -> { draw(awt_jfx_convert_buffer); }); - logger.log(Level.FINE, "Redraw meter"); + drawValue(gc, value); + drawWarning(gc, warning); + if (showUnits.get()) { + drawUnit(gc); } - } - private void draw(WritableImage awt_jfx_convert_buffer) { - setImage(awt_jfx_convert_buffer); + // Convert to JFX image and show + runOnJavaFXThread(() -> { + awtJFXConvertBuffer.getPixelWriter().setPixels(0, 0, width, height, PixelFormat.getIntArgbInstance(), dest, 0, width); + setImage(awtJFXConvertBuffer); + }); + + logger.log(Level.FINE, "Redraw meter"); } /** Call to update size of meter @@ -523,6 +529,7 @@ private void draw(WritableImage awt_jfx_convert_buffer) { */ public void setSize(int width, int height) { linearMeterScale.setBounds(0, 0, width, height); + updateMeterBackground(); layout(); updateActiveColors(); requestLayout(); @@ -723,7 +730,13 @@ private void updateMeterBackground() linearMeterScale.paint(gc, new Rectangle(0,0,0,0)); paintMeter(gc); - meter_background = image; + { + BufferedImage combined = new BufferedImage(linearMeterScale.getBounds().width, linearMeterScale.getBounds().height, BufferedImage.TYPE_INT_ARGB); + WritableImage awtJFXConvertBuffer = new WritableImage(linearMeterScale.getBounds().width, linearMeterScale.getBounds().height); + Pair imageBuffersForWriting = new Pair<>(combined, awtJFXConvertBuffer); + Pair widthAndHeight = new Pair<>(width, height); + meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference.set(Optional.of(new ImmutableTriple<>(image, imageBuffersForWriting, widthAndHeight))); + } } private void paintMeter(Graphics2D graphics) { @@ -881,7 +894,7 @@ else if (value >= high.get() && value < hiHi.get()) { public void dispose() { // Release memory ASAP - meter_background = null; + meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference.set(Optional.empty()); } public void setHorizontal(boolean horizontal) { From 70f00d9c862391518f40b7000dbf9d5560ee12f5 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Tue, 12 Aug 2025 14:30:15 +0200 Subject: [PATCH 04/35] CSSTUDIO-3380 Make it possible to display the current value using a bar instead of a needle. --- .../LinearMeterRepresentation.java | 6 + .../linearmeter/LinearMeterWidget.java | 18 +++ .../widgets/linearmeter/RTLinearMeter.java | 114 +++++++++++++++++- 3 files changed, 137 insertions(+), 1 deletion(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java index 12946369ca..50fe2c87d3 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java @@ -86,6 +86,7 @@ public Pane createJFXNode() model_widget.propKnobSize().getValue(), widgetColorToAWTColor(model_widget.propKnobColor().getValue()), model_widget.propShowWarnings().getValue()); + meter.setDisplayMode(model_widget.propDisplayMode().getValue()); meter.setSize(model_widget.propWidth().getValue(),model_widget.propHeight().getValue()); meter.setHorizontal(model_widget.propDisplayHorizontal().getValue()); meter.setManaged(false); @@ -229,6 +230,11 @@ protected void registerListeners() meter.setKnobSize(new_value); layoutChanged(null, null, null); }); + + addWidgetPropertyListener(model_widget.propDisplayMode(), (property, old_value, new_value) -> { + meter.setDisplayMode(new_value); + layoutChanged(null, null, null); + }); } @Override diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterWidget.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterWidget.java index 68f643b3fe..fb111b0f83 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterWidget.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterWidget.java @@ -10,6 +10,7 @@ import org.csstudio.display.builder.model.persist.NamedWidgetFonts; import org.csstudio.display.builder.model.persist.WidgetColorService; import org.csstudio.display.builder.model.persist.WidgetFontService; +import org.csstudio.display.builder.model.properties.EnumWidgetProperty; import org.csstudio.display.builder.model.properties.WidgetColor; import org.csstudio.display.builder.model.properties.WidgetFont; import org.csstudio.display.builder.model.widgets.PVWidget; @@ -112,6 +113,18 @@ else if (xml_version.getMajor() < 3) public static WidgetPropertyDescriptor propScaleVisible = newBooleanPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "scale_visible", Messages.WidgetProperties_ScaleVisible); + private WidgetProperty display_mode; + public static WidgetPropertyDescriptor propDisplayMode = + new WidgetPropertyDescriptor<>( + WidgetPropertyCategory.DISPLAY, "display_mode", "Display Mode") + { + @Override + public EnumWidgetProperty createProperty(Widget widget, RTLinearMeter.DisplayMode default_value) + { + return new EnumWidgetProperty<>(this, widget, default_value); + } + }; + public static WidgetPropertyDescriptor propNeedleColor = newColorPropertyDescriptor(WidgetPropertyCategory.MISC, "needle_color", Messages.WidgetProperties_NeedleColor); @@ -205,6 +218,7 @@ protected void defineProperties(List> properties) { super.defineProperties(properties); + properties.add(display_mode = propDisplayMode.createProperty(this, RTLinearMeter.DisplayMode.NEEDLE)); properties.add(font = propFont.createProperty(this, WidgetFontService.get(NamedWidgetFonts.DEFAULT))); properties.add(format = propFormat.createProperty(this, FormatOption.DEFAULT)); properties.add(show_units = propShowUnits.createProperty(this, true)); @@ -368,4 +382,8 @@ public WidgetProperty propMajorWarningColor() { public WidgetProperty propNeedleWidth() { return needleWidth; } + public WidgetProperty propDisplayMode() { + return display_mode; +} + } diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index 806dc6694e..17122dc4d0 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -237,6 +237,17 @@ public boolean getValidRange() { private AtomicBoolean isHighlightActiveRegionEnabled = new AtomicBoolean(true); + enum DisplayMode { + NEEDLE, + BAR + } + + public void setDisplayMode(DisplayMode newDisplayMode) { + this.displayMode.set(newDisplayMode); + } + + private AtomicReference displayMode = new AtomicReference<>(DisplayMode.NEEDLE); + private void runOnJavaFXThread(Runnable runnable) { if (Platform.isFxApplicationThread()) { runnable.run(); @@ -507,7 +518,17 @@ private void redrawIndicator(double value, WARNING warning) { // Add needle & label Graphics2D gc = combined.createGraphics(); - drawValue(gc, value); + DisplayMode displayMode = this.displayMode.get(); + if (displayMode.equals(DisplayMode.NEEDLE)) { + drawValue(gc, value); + } + else if (displayMode.equals(DisplayMode.BAR)) { + drawBar(gc, value); + } + else { + throw new RuntimeException("Unhandled case"); + } + drawWarning(gc, warning); if (showUnits.get()) { drawUnit(gc); @@ -890,6 +911,97 @@ else if (value >= high.get() && value < hiHi.get()) { } } + private void drawBar(Graphics2D gc, double value) { + if (Double.isNaN(value)) { + currentIndicatorPosition = null; + } + else { + Stroke oldStroke = gc.getStroke(); + Paint oldPaint = gc.getPaint(); + RenderingHints oldrenderingHints = gc.getRenderingHints(); + + gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + gc.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); + gc.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); + gc.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + + if (showLimits.get()) { + if (isHighlightActiveRegionEnabled.get()) { + if (value <= loLo.get()) { + paintRectangle(gc, loLoRectangle, majorAlarmColor_highlighted.get()); + } + else if (value >= hiHi.get()) { + paintRectangle(gc, hiHiRectangle, majorAlarmColor_highlighted.get()); + } + else if (value <= low.get() && value > loLo.get()) { + paintRectangle(gc, lowRectangle, minorAlarmActiveColor_highlighted.get()); + } + else if (value >= high.get() && value < hiHi.get()) { + paintRectangle(gc, highRectangle, minorAlarmActiveColor_highlighted.get()); + } + else { + paintRectangle(gc, normalRectangle, normalStatusActiveColor_highlighted.get()); + } + } + } + + if (linearMeterScale.isHorizontal()) { + if (value >= linearMeterScale.getValueRange().getLow() && value <= linearMeterScale.getValueRange().getHigh()) { + if (isHighlightActiveRegionEnabled.get()) { + if (value <= loLo.get()) { + gc.setPaint(majorAlarmColor_highlighted.get()); + } + else if (value >= hiHi.get()) { + gc.setPaint(majorAlarmColor_highlighted.get()); + } + else if (value <= low.get() && value > loLo.get()) { + gc.setPaint(minorAlarmActiveColor_highlighted.get()); + } + else if (value >= high.get() && value < hiHi.get()) { + gc.setPaint(minorAlarmActiveColor_highlighted.get()); + } + else { + gc.setPaint(needleColor.get()); + } + } + else { + gc.setPaint(needleColor.get()); + } + currentIndicatorPosition = (int) (marginLeft + pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); + gc.fillRect(marginLeft+1, marginAbove+1, (int) currentIndicatorPosition, meterBreadth-1); + } + } else { + if (value >= linearMeterScale.getValueRange().getLow() && value <= linearMeterScale.getValueRange().getHigh()) { + if (isHighlightActiveRegionEnabled.get()) { + if (value <= loLo.get()) { + gc.setPaint(majorAlarmColor_highlighted.get()); + } + else if (value >= hiHi.get()) { + gc.setPaint(majorAlarmColor_highlighted.get()); + } + else if (value <= low.get() && value > loLo.get()) { + gc.setPaint(minorAlarmActiveColor_highlighted.get()); + } + else if (value >= high.get() && value < hiHi.get()) { + gc.setPaint(minorAlarmActiveColor_highlighted.get()); + } + else { + gc.setPaint(needleColor.get()); + } + } + else { + gc.setPaint(needleColor.get()); + } + currentIndicatorPosition = (int) (linearMeterScale.getBounds().height - marginBelow - pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); + gc.fillRect(marginLeft+1, currentIndicatorPosition+1, (int) meterBreadth-1, linearMeterScale.getBounds().height-currentIndicatorPosition-marginBelow-1); + } + } + gc.setRenderingHints(oldrenderingHints); + gc.setStroke(oldStroke); + gc.setPaint(oldPaint); + } + } + /** Should be invoked when meter no longer used to release resources */ public void dispose() { From d8927907ae8d6b6052598ba4f9ec63ee7c5fa80e Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Tue, 12 Aug 2025 15:52:41 +0200 Subject: [PATCH 05/35] CSSTUDIO-3380 Bugfix: Draw the bar also when the value is larger than the max value of the linear meter. --- .../display/extra/widgets/linearmeter/RTLinearMeter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index 17122dc4d0..12dc6bec95 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -946,7 +946,7 @@ else if (value >= high.get() && value < hiHi.get()) { } if (linearMeterScale.isHorizontal()) { - if (value >= linearMeterScale.getValueRange().getLow() && value <= linearMeterScale.getValueRange().getHigh()) { + if (value >= linearMeterScale.getValueRange().getLow()) { if (isHighlightActiveRegionEnabled.get()) { if (value <= loLo.get()) { gc.setPaint(majorAlarmColor_highlighted.get()); From a1a8ce55e1ce0e7dd5fe803e0f8e0ba8dd8082b9 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Tue, 12 Aug 2025 16:10:59 +0200 Subject: [PATCH 06/35] index on CSSTUDIO-3360: d8927907a CSSTUDIO-3380 Bugfix: Draw the bar also when the value is larger than the max value of the linear meter. From 58db7a8213c9b4deff2b590076db8417cb19f896 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Wed, 13 Aug 2025 09:59:53 +0200 Subject: [PATCH 07/35] CSSTUDIO-3362 Add the two options "Min & max from PV" and "Limits from PV" to the widget property "Limits from PV". --- .../LinearMeterRepresentation.java | 117 +++++++++--------- .../linearmeter/LinearMeterWidget.java | 37 ++++-- 2 files changed, 83 insertions(+), 71 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java index 0fbc62dd88..9b3673db0a 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java @@ -42,24 +42,16 @@ public Pane createJFXNode() double initialValue = toolkit.isEditMode() ? (model_widget.propMinimum().getValue() + model_widget.propMaximum().getValue()) / 2.0 : Double.NaN; - double minimum, maximum; - double loLo, low, high, hiHi; - if (model_widget.propLimitsFromPV().getValue().equals(LinearMeterWidget.LimitsFromPV.LimitsFromPV) && !toolkit.isEditMode()) { - minimum = Double.NaN; - maximum = Double.NaN; - loLo = Double.NaN; - low = Double.NaN; - high = Double.NaN; - hiHi = Double.NaN; - } - else { - minimum = model_widget.propMinimum().getValue(); - maximum = model_widget.propMaximum().getValue(); - loLo = model_widget.propLevelLoLo().getValue(); - low = model_widget.propLevelLow().getValue(); - high = model_widget.propLevelHigh().getValue(); - hiHi = model_widget.propLevelHiHi().getValue(); - } + // Set min, max, and alarm limits according to settings. + // Some or all of these may be overridden by the PV when + // values are received, if the option "Limits from PV" + // is not set to "No limits from PV". + double minimum = model_widget.propMinimum().getValue(); + double maximum = model_widget.propMaximum().getValue(); + double loLo = model_widget.propLevelLoLo().getValue(); + double low = model_widget.propLevelLow().getValue(); + double high = model_widget.propLevelHigh().getValue(); + double hiHi = model_widget.propLevelHiHi().getValue(); double minMaxTolerance = model_widget.propMinMaxTolerance().getValue(); @@ -318,55 +310,60 @@ private void valueChanged(WidgetProperty property, Object old_value, Object n if (model_widget != null && model_widget.propShowUnits().getValue()) { meter.setUnits(display.getUnit()); } - - if (model_widget != null && model_widget.propLimitsFromPV().getValue().equals(LinearMeterWidget.LimitsFromPV.LimitsFromPV)) { - Range displayRange = display.getDisplayRange(); - if (displayRange != null - && Double.isFinite(displayRange.getMinimum()) - && Double.isFinite(displayRange.getMaximum()) - && displayRange.getMaximum() - displayRange.getMinimum() > 0.0) { - if (meter.linearMeterScale.getValueRange().getLow() != displayRange.getMinimum() || meter.linearMeterScale.getValueRange().getHigh() != displayRange.getMaximum() || !meter.getValidRange()) { - meter.setRange(displayRange.getMinimum(), displayRange.getMaximum(), true); - } - } else { - Range controlRange = display.getControlRange(); - if (controlRange != null - && Double.isFinite(controlRange.getMinimum()) - && Double.isFinite(controlRange.getMaximum()) - && controlRange.getMaximum() - controlRange.getMinimum() > 0.0) { - if (meter.linearMeterScale.getValueRange().getLow() != controlRange.getMinimum() || meter.linearMeterScale.getValueRange().getHigh() != controlRange.getMaximum() || !meter.getValidRange()) { - meter.setRange(controlRange.getMinimum(), controlRange.getMaximum(), true); + if (model_widget != null) { + LinearMeterWidget.LimitsFromPV limitsFromPVSetting = model_widget.propLimitsFromPV().getValue(); + if (limitsFromPVSetting.equals(LinearMeterWidget.LimitsFromPV.LimitsFromPV) || + limitsFromPVSetting.equals(LinearMeterWidget.LimitsFromPV.MinAndMaxFromPV)) { + Range displayRange = display.getDisplayRange(); + if (displayRange != null + && Double.isFinite(displayRange.getMinimum()) + && Double.isFinite(displayRange.getMaximum()) + && displayRange.getMaximum() - displayRange.getMinimum() > 0.0) { + if (meter.linearMeterScale.getValueRange().getLow() != displayRange.getMinimum() || meter.linearMeterScale.getValueRange().getHigh() != displayRange.getMaximum() || !meter.getValidRange()) { + meter.setRange(displayRange.getMinimum(), displayRange.getMaximum(), true); + } + } else { + Range controlRange = display.getControlRange(); + if (controlRange != null + && Double.isFinite(controlRange.getMinimum()) + && Double.isFinite(controlRange.getMaximum()) + && controlRange.getMaximum() - controlRange.getMinimum() > 0.0) { + if (meter.linearMeterScale.getValueRange().getLow() != controlRange.getMinimum() || meter.linearMeterScale.getValueRange().getHigh() != controlRange.getMaximum() || !meter.getValidRange()) { + meter.setRange(controlRange.getMinimum(), controlRange.getMaximum(), true); + } + } else if (newObservedMinAndMaxValues && !Double.isNaN(observedMin) && !Double.isNaN(observedMax)) { + meter.setRange(observedMin - 1, observedMax + 1, false); + newObservedMinAndMaxValues = false; + } else if (meter.linearMeterScale.getValueRange().getLow() != 0.0 || meter.linearMeterScale.getValueRange().getHigh() != 100) { + meter.setRange(0.0, 100.0, false); } - } else if (newObservedMinAndMaxValues && !Double.isNaN(observedMin) && !Double.isNaN(observedMax)) { - meter.setRange(observedMin - 1, observedMax + 1, false); - newObservedMinAndMaxValues = false; - } else if (meter.linearMeterScale.getValueRange().getLow() != 0.0 || meter.linearMeterScale.getValueRange().getHigh() != 100) { - meter.setRange(0.0, 100.0, false); } } - - { - Range warningRange = display.getWarningRange(); - if (warningRange != null) { - if (!Double.isNaN(warningRange.getMinimum()) && meter.getLow() != warningRange.getMinimum()) { - meter.setLow(warningRange.getMinimum()); - } - - if (!Double.isNaN(warningRange.getMaximum()) && meter.getHigh() != warningRange.getMaximum()) { - meter.setHigh(warningRange.getMaximum()); + if (limitsFromPVSetting.equals(LinearMeterWidget.LimitsFromPV.LimitsFromPV) || + limitsFromPVSetting.equals(LinearMeterWidget.LimitsFromPV.AlarmLimitsFromPV)) { + { + Range warningRange = display.getWarningRange(); + if (warningRange != null) { + if (!Double.isNaN(warningRange.getMinimum()) && meter.getLow() != warningRange.getMinimum()) { + meter.setLow(warningRange.getMinimum()); + } + + if (!Double.isNaN(warningRange.getMaximum()) && meter.getHigh() != warningRange.getMaximum()) { + meter.setHigh(warningRange.getMaximum()); + } } } - } - { - Range alarmRange = display.getAlarmRange(); - if (alarmRange != null) { - if (!Double.isNaN(alarmRange.getMinimum()) && meter.getLoLo() != alarmRange.getMinimum()) { - meter.setLoLo(alarmRange.getMinimum()); - } + { + Range alarmRange = display.getAlarmRange(); + if (alarmRange != null) { + if (!Double.isNaN(alarmRange.getMinimum()) && meter.getLoLo() != alarmRange.getMinimum()) { + meter.setLoLo(alarmRange.getMinimum()); + } - if (!Double.isNaN(alarmRange.getMaximum()) && meter.getHiHi() != alarmRange.getMaximum()) { - meter.setHiHi(alarmRange.getMaximum()); + if (!Double.isNaN(alarmRange.getMaximum()) && meter.getHiHi() != alarmRange.getMaximum()) { + meter.setHiHi(alarmRange.getMaximum()); + } } } } diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterWidget.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterWidget.java index f5866ab6a0..a75a4937bb 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterWidget.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterWidget.java @@ -115,22 +115,20 @@ else if (xml_version.getMajor() < 3) enum LimitsFromPV { - LimitsFromPV("true"), - Limits("false"); + LimitsFromPV("All limits from PV"), + MinAndMaxFromPV("Min & max from PV"), + AlarmLimitsFromPV("Alarm limits from PV"), + NoLimitsFromPV("No limits from PV"); - private final String name; + private final String displayName; - private LimitsFromPV(String name) { - this.name = name; + private LimitsFromPV(String displayName) { + this.displayName = displayName; }; - public String getName() { - return this.name; - } - @Override public String toString() { - return this.name; + return this.displayName; } } @@ -138,7 +136,24 @@ public String toString() { new WidgetPropertyDescriptor<>(WidgetPropertyCategory.BEHAVIOR, "limits_from_pv", Messages.WidgetProperties_LimitsFromPV) { @Override public EnumWidgetProperty createProperty(final Widget widget, LimitsFromPV default_value) { - return new EnumWidgetProperty<>(this, widget, LimitsFromPV.LimitsFromPV); + EnumWidgetProperty widgetProperty = new EnumWidgetProperty<>(this, widget, LimitsFromPV.LimitsFromPV) { + @Override + public void setSpecification(final String specification) { + // Backwards compatibility to previous version of the Linear Meter, where + // propLimitsFromPV was a boolean: + if (specification.equals("true")) { + super.setSpecification(LimitsFromPV.LimitsFromPV.ordinal() + ""); + } + else if (specification.equals("false")) { + super.setSpecification(LimitsFromPV.NoLimitsFromPV.ordinal() + ""); + } + else { + // If not a boolean, set according to enum LimitsFromPV: + super.setSpecification(specification); + } + } + }; + return widgetProperty; } }; From 0d2def87e8936ed8226ae48fa3abc24298b6a98b Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Wed, 13 Aug 2025 11:09:43 +0200 Subject: [PATCH 08/35] CSSTUDIO-3347 Add 'readWriteLock'. --- .../widgets/linearmeter/RTLinearMeter.java | 1312 +++++++++-------- 1 file changed, 713 insertions(+), 599 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index 12dc6bec95..68e869e2e1 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -25,6 +25,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.logging.Level; import javafx.application.Platform; @@ -134,6 +136,24 @@ public RTLinearMeter(double initialValue, requestLayout(); } + private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + private void withReadLock(Runnable runnable) { + readWriteLock.readLock().lock(); + try { + runnable.run(); + } finally { + readWriteLock.readLock().unlock(); + } + } + private void withWriteLock(Runnable runnable) { + readWriteLock.writeLock().lock(); + try { + runnable.run(); + } finally { + readWriteLock.writeLock().unlock(); + } + } + private AtomicBoolean showUnits = new AtomicBoolean(true); private AtomicReference units = new AtomicReference<>(""); private AtomicBoolean showLimits = new AtomicBoolean(true); @@ -146,17 +166,16 @@ public RTLinearMeter(double initialValue, private static Image warningTriangle = null; public void redrawLinearMeterScale() { - boolean isHorizontal = linearMeterScale.isHorizontal(); - linearMeterScale = new LinearMeterScale(plot_part_listener, - linearMeterScale.getBounds().width, - linearMeterScale.getBounds().height, - linearMeterScale.isHorizontal(), - linearMeterScale.getValueRange().getLow(), - linearMeterScale.getValueRange().getHigh()); - linearMeterScale.setHorizontal(isHorizontal); - if (font != null) { - linearMeterScale.setScaleFont(GraphicsUtils.convert(font)); - } + withReadLock(() -> { + boolean isHorizontal = linearMeterScale.isHorizontal(); + linearMeterScale = new LinearMeterScale(plot_part_listener, + linearMeterScale.getBounds().width, + linearMeterScale.getBounds().height, + linearMeterScale.isHorizontal(), + linearMeterScale.getValueRange().getLow(), + linearMeterScale.getValueRange().getHigh()); + linearMeterScale.setHorizontal(isHorizontal); + }); } private enum WARNING { @@ -191,7 +210,11 @@ public void refreshPlotPart(PlotPart plotPart) { } private AtomicReference minMaxTolerance = new AtomicReference<>(0.0); public boolean getValidRange() { - return validRange.get(); + AtomicBoolean returnValue = new AtomicBoolean(false); + withReadLock(() -> { + returnValue.set(validRange.get()); + }); + return returnValue.get(); } /** Optional scale of this linear meter */ @@ -243,7 +266,9 @@ enum DisplayMode { } public void setDisplayMode(DisplayMode newDisplayMode) { - this.displayMode.set(newDisplayMode); + withWriteLock(() -> { + this.displayMode.set(newDisplayMode); + }); } private AtomicReference displayMode = new AtomicReference<>(DisplayMode.NEEDLE); @@ -258,45 +283,49 @@ private void runOnJavaFXThread(Runnable runnable) { } public void setIsGradientEnabled(boolean isGradientEnabled) { - runOnJavaFXThread(() -> { + withWriteLock(() -> { this.isGradientEnabled.set(isGradientEnabled); updateActiveColors(); }); } public void setIsHighlightActiveRegionEnabled(boolean isHighlightActiveRegionEnabled) { - this.isHighlightActiveRegionEnabled.set(isHighlightActiveRegionEnabled); + withWriteLock(() -> { + this.isHighlightActiveRegionEnabled.set(isHighlightActiveRegionEnabled); + }); } private void updateActiveColors() { - if (isGradientEnabled.get()) { - if (linearMeterScale.isHorizontal()) { - majorAlarmActiveColor_lowlighted.set(createVerticalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_lowlighted.get(), majorAlarmColorGradientEndPoint_lowlighted.get())); - minorAlarmActiveColor_lowlighted.set(createVerticalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_lowlighted.get(), minorAlarmColorGradientEndPoint_lowlighted.get())); - normalStatusActiveColor_lowlighted.set(createVerticalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted.get(), normalStatusColorGradientEndPoint_highlighted.get())); // The normal status region is never lowlighted. + withWriteLock(() -> { + if (isGradientEnabled.get()) { + if (linearMeterScale.isHorizontal()) { + majorAlarmActiveColor_lowlighted.set(createVerticalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_lowlighted.get(), majorAlarmColorGradientEndPoint_lowlighted.get())); + minorAlarmActiveColor_lowlighted.set(createVerticalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_lowlighted.get(), minorAlarmColorGradientEndPoint_lowlighted.get())); + normalStatusActiveColor_lowlighted.set(createVerticalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted.get(), normalStatusColorGradientEndPoint_highlighted.get())); // The normal status region is never lowlighted. - minorAlarmActiveColor_highlighted.set(createVerticalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_highlighted.get(), minorAlarmColorGradientEndPoint_highlighted.get())); - majorAlarmActiveColor_highlighted.set(createVerticalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_highlighted.get(), majorAlarmColorGradientEndPoint_highlighted.get())); - normalStatusActiveColor_highlighted.set(createVerticalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted.get(), normalStatusColorGradientEndPoint_highlighted.get())); - } else { - majorAlarmActiveColor_lowlighted.set(createHorizontalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_lowlighted.get(), majorAlarmColorGradientEndPoint_lowlighted.get())); - minorAlarmActiveColor_lowlighted.set(createHorizontalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_lowlighted.get(), minorAlarmColorGradientEndPoint_lowlighted.get())); - normalStatusActiveColor_lowlighted.set(createHorizontalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted.get(), normalStatusColorGradientEndPoint_highlighted.get())); // The normal status region is never lowlighted. + minorAlarmActiveColor_highlighted.set(createVerticalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_highlighted.get(), minorAlarmColorGradientEndPoint_highlighted.get())); + majorAlarmActiveColor_highlighted.set(createVerticalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_highlighted.get(), majorAlarmColorGradientEndPoint_highlighted.get())); + normalStatusActiveColor_highlighted.set(createVerticalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted.get(), normalStatusColorGradientEndPoint_highlighted.get())); + } else { + majorAlarmActiveColor_lowlighted.set(createHorizontalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_lowlighted.get(), majorAlarmColorGradientEndPoint_lowlighted.get())); + minorAlarmActiveColor_lowlighted.set(createHorizontalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_lowlighted.get(), minorAlarmColorGradientEndPoint_lowlighted.get())); + normalStatusActiveColor_lowlighted.set(createHorizontalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted.get(), normalStatusColorGradientEndPoint_highlighted.get())); // The normal status region is never lowlighted. - minorAlarmActiveColor_highlighted.set(createHorizontalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_highlighted.get(), minorAlarmColorGradientEndPoint_highlighted.get())); - majorAlarmActiveColor_highlighted.set(createHorizontalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_highlighted.get(), majorAlarmColorGradientEndPoint_highlighted.get())); - normalStatusActiveColor_highlighted.set(createHorizontalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted.get(), normalStatusColorGradientEndPoint_highlighted.get())); + minorAlarmActiveColor_highlighted.set(createHorizontalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_highlighted.get(), minorAlarmColorGradientEndPoint_highlighted.get())); + majorAlarmActiveColor_highlighted.set(createHorizontalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_highlighted.get(), majorAlarmColorGradientEndPoint_highlighted.get())); + normalStatusActiveColor_highlighted.set(createHorizontalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted.get(), normalStatusColorGradientEndPoint_highlighted.get())); + } } - } - else { - normalStatusActiveColor_lowlighted.set(normalStatusColor_highlighted.get()); // The normal status region is never lowlighted. - majorAlarmActiveColor_lowlighted.set(majorAlarmColor_lowlighted.get()); - minorAlarmActiveColor_lowlighted.set(minorAlarmColor_lowlighted.get()); + else { + normalStatusActiveColor_lowlighted.set(normalStatusColor_highlighted.get()); // The normal status region is never lowlighted. + majorAlarmActiveColor_lowlighted.set(majorAlarmColor_lowlighted.get()); + minorAlarmActiveColor_lowlighted.set(minorAlarmColor_lowlighted.get()); - normalStatusActiveColor_highlighted.set(normalStatusColor_highlighted.get()); - minorAlarmActiveColor_highlighted.set(minorAlarmColor_highlighted.get()); - majorAlarmActiveColor_highlighted.set(majorAlarmColor_highlighted.get()); - } + normalStatusActiveColor_highlighted.set(normalStatusColor_highlighted.get()); + minorAlarmActiveColor_highlighted.set(minorAlarmColor_highlighted.get()); + majorAlarmActiveColor_highlighted.set(majorAlarmColor_highlighted.get()); + } + }); } private Color computeGradientStartPoint(Color color) { @@ -353,6 +382,7 @@ private Color computeLowlightedColor(Color color) { } public void setNormalStatusColor(Color normalStatusColor) { + withWriteLock(() -> { this.normalStatusColor_lowlighted.set(computeLowlightedColor(normalStatusColor)); this.normalStatusColor_highlighted.set(normalStatusColor); @@ -363,9 +393,11 @@ public void setNormalStatusColor(Color normalStatusColor) { this.normalStatusColorGradientEndPoint_highlighted.set(computeGradientEndPoint(normalStatusColor_highlighted.get())); updateActiveColors(); + }); } public void setMinorAlarmColor(Color minorAlarmColor) { + withWriteLock(() -> { this.minorAlarmColor_lowlighted.set(computeLowlightedColor(minorAlarmColor)); this.minorAlarmColor_highlighted.set(minorAlarmColor); @@ -376,10 +408,11 @@ public void setMinorAlarmColor(Color minorAlarmColor) { this.minorAlarmColorGradientEndPoint_highlighted.set(computeGradientEndPoint(minorAlarmColor_highlighted.get())); updateActiveColors(); + }); } public void setMajorAlarmColor(Color majorAlarmColor) { - runOnJavaFXThread(() -> { + withWriteLock(() -> { this.majorAlarmColor_lowlighted.set(computeLowlightedColor(majorAlarmColor)); this.majorAlarmColor_highlighted.set(majorAlarmColor); @@ -394,65 +427,83 @@ public void setMajorAlarmColor(Color majorAlarmColor) { } public void setNeedleWidth(int needleWidth) { - this.needleWidth.set(needleWidth); + withWriteLock(() -> { + this.needleWidth.set(needleWidth); + }); } public void setNeedleColor(Color needleColor) { - this.needleColor.set(needleColor); + withWriteLock(() -> { + this.needleColor.set(needleColor); + }); } public void setShowUnits(boolean newValue) { - showUnits.set(newValue); - updateMeterBackground(); - redrawIndicator(currentValue.get(), currentWarning.get()); + withWriteLock(() -> { + showUnits.set(newValue); + redraw(); + }); } public void setUnits(String newValue) { - - if (!units.equals(newValue)) { - units.set(newValue); - updateMeterBackground(); - redrawIndicator(currentValue.get(), currentWarning.get()); - } + withWriteLock(() -> { + if (!units.equals(newValue)) { + units.set(newValue); + redraw(); + } + }); } public void setShowLimits(boolean newValue) { - showLimits.set(newValue); - updateMeterBackground(); - determineWarning(); - redrawIndicator(currentValue.get(), currentWarning.get()); + withWriteLock(() -> { + showLimits.set(newValue); + determineWarning(); + redraw(); + }); } public void setRange(double minimum, double maximum, boolean validRange) { - this.validRange.set(validRange); - linearMeterScale.setValueRange(minimum, maximum); - - updateMeterBackground(); - redrawIndicator(currentValue.get(), currentWarning.get()); + withWriteLock(() -> { + this.validRange.set(validRange); + linearMeterScale.setValueRange(minimum, maximum); + }); + redraw(); } public void setMinMaxTolerance(double minMaxTolerance) { + withWriteLock(() -> { this.minMaxTolerance.set(minMaxTolerance); determineWarning(); redrawIndicator(currentValue.get(), currentWarning.get()); + }); } public double getLoLo() { - return loLo.get(); + AtomicReference returnValue = new AtomicReference<>(); + withReadLock(() -> { + returnValue.set(loLo.get()); + }); + return returnValue.get(); } public void setLoLo(double loLo) { - this.loLo.set(loLo); - layout(); - updateMeterBackground(); + withWriteLock(() -> { + this.loLo.set(loLo); + layout(); + updateMeterBackground(); + }); } public double getLow() { - return low.get(); + AtomicReference returnValue = new AtomicReference<>(); + withReadLock(() -> { + returnValue.set(low.get()); + }); + return returnValue.get(); } public void setLow(double low) { - runOnJavaFXThread(() -> { + withWriteLock(() -> { this.low.set(low); layout(); updateMeterBackground(); @@ -460,87 +511,111 @@ public void setLow(double low) { } public double getHigh() { - return high.get(); + AtomicReference returnValue = new AtomicReference<>(); + withReadLock(() -> { + returnValue.set(high.get()); + }); + return returnValue.get(); } public void setHigh(double high) { - this.high.set(high); - layout(); - updateMeterBackground(); + withWriteLock(() -> { + this.high.set(high); + layout(); + updateMeterBackground(); + }); } public double getHiHi() { - return hiHi.get(); + AtomicReference returnValue = new AtomicReference<>(); + withReadLock(() -> { + returnValue.set(hiHi.get()); + }); + return returnValue.get(); } public void setHiHi(double hiHi) { - this.hiHi.set(hiHi); - layout(); - updateMeterBackground(); + withWriteLock(() -> { + this.hiHi.set(hiHi); + layout(); + updateMeterBackground(); + }); } private AtomicReference knobColor = new AtomicReference(new Color(0, 0, 0, 255)); public void setKnobColor(Color knobColor) { - this.knobColor.set(knobColor); - requestLayout(); + withWriteLock(() -> { + this.knobColor.set(knobColor); + requestLayout(); + }); } private AtomicInteger knobSize = new AtomicInteger(1); public void setKnobSize(int knobSize) { + withWriteLock(() -> { this.knobSize.set(knobSize); requestLayout(); + }); } + private void redraw() { + withReadLock(() -> { + updateMeterBackground(); + redrawIndicator(currentValue.get(), currentWarning.get()); + }); + } private AtomicReference, Pair>>> meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference = new AtomicReference<>(Optional.empty()); /** * Redraw on UI thread by adding needle to 'meter_background' */ private void redrawIndicator(double value, WARNING warning) { - var meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference.get(); - if (meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.isEmpty()) { - return; - } - BufferedImage meterBackground = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getLeft(); - BufferedImage combined = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getMiddle().getKey(); - var awtJFXConvertBuffer = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getMiddle().getValue(); - int width = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getRight().getKey(); - int height = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getRight().getValue(); - if (meterBackground.getType() != BufferedImage.TYPE_INT_ARGB) { - throw new IllegalPathStateException("Need TYPE_INT_ARGB for direct buffer access, not " + meterBackground.getType()); - } + withReadLock(() -> { + var meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference.get(); + if (meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.isEmpty()) { + return; + } + BufferedImage meterBackground = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getLeft(); + BufferedImage combined = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getMiddle().getKey(); + var awtJFXConvertBuffer = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getMiddle().getValue(); + int width = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getRight().getKey(); + int height = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getRight().getValue(); + if (meterBackground.getType() != BufferedImage.TYPE_INT_ARGB) { + throw new IllegalPathStateException("Need TYPE_INT_ARGB for direct buffer access, not " + meterBackground.getType()); + } - int[] src = ((DataBufferInt) meterBackground.getRaster().getDataBuffer()).getData(); - int[] dest = ((DataBufferInt) combined.getRaster().getDataBuffer()).getData(); - System.arraycopy(src, 0, dest, 0, width * height); + int[] src = ((DataBufferInt) meterBackground.getRaster().getDataBuffer()).getData(); + int[] dest = ((DataBufferInt) combined.getRaster().getDataBuffer()).getData(); + System.arraycopy(src, 0, dest, 0, width * height); - // Add needle & label - Graphics2D gc = combined.createGraphics(); + // Add needle & label + Graphics2D gc = combined.createGraphics(); - DisplayMode displayMode = this.displayMode.get(); - if (displayMode.equals(DisplayMode.NEEDLE)) { - drawValue(gc, value); - } - else if (displayMode.equals(DisplayMode.BAR)) { - drawBar(gc, value); - } - else { - throw new RuntimeException("Unhandled case"); - } + DisplayMode displayMode = this.displayMode.get(); + if (displayMode.equals(DisplayMode.NEEDLE)) { + drawValue(gc, value); + } + else if (displayMode.equals(DisplayMode.BAR)) { + drawBar(gc, value); + } + else { + throw new RuntimeException("Unhandled case"); + } - drawWarning(gc, warning); - if (showUnits.get()) { - drawUnit(gc); - } + drawWarning(gc, warning); + if (showUnits.get()) { + drawUnit(gc); + } - // Convert to JFX image and show - runOnJavaFXThread(() -> { - awtJFXConvertBuffer.getPixelWriter().setPixels(0, 0, width, height, PixelFormat.getIntArgbInstance(), dest, 0, width); - setImage(awtJFXConvertBuffer); - }); + // Convert to JFX image and show + runOnJavaFXThread(() -> { + awtJFXConvertBuffer.getPixelWriter().setPixels(0, 0, width, height, PixelFormat.getIntArgbInstance(), dest, 0, width); + setImage(awtJFXConvertBuffer); + }); - logger.log(Level.FINE, "Redraw meter"); + logger.log(Level.FINE, "Redraw meter"); + }); } /** Call to update size of meter @@ -549,29 +624,35 @@ else if (displayMode.equals(DisplayMode.BAR)) { * @param height */ public void setSize(int width, int height) { - linearMeterScale.setBounds(0, 0, width, height); - updateMeterBackground(); - layout(); - updateActiveColors(); - requestLayout(); + withWriteLock(() -> { + linearMeterScale.setBounds(0, 0, width, height); + updateMeterBackground(); + layout(); + updateActiveColors(); + requestLayout(); + }); } /** @param color Foreground (labels, tick marks) color */ public void setForeground(javafx.scene.paint.Color color) { - foreground.set(GraphicsUtils.convert(color)); - linearMeterScale.setColor(color); + withWriteLock(() -> { + foreground.set(GraphicsUtils.convert(color)); + linearMeterScale.setColor(color); + }); } /** @param color Background color */ public void setBackground(javafx.scene.paint.Color color) { - background.set(GraphicsUtils.convert(color)); + withWriteLock(() -> { + background.set(GraphicsUtils.convert(color)); + }); } /** @param font Label font */ public void setFont(javafx.scene.text.Font font) { - runOnJavaFXThread(() -> { + withWriteLock(() -> { linearMeterScale.setScaleFont(font); this.font = GraphicsUtils.convert(font); }); @@ -580,7 +661,9 @@ public void setFont(javafx.scene.text.Font font) private AtomicBoolean showWarnings = new AtomicBoolean(true); public void setShowWarnings(boolean showWarnings) { - this.showWarnings.set(showWarnings); + withWriteLock(() -> { + this.showWarnings.set(showWarnings); + }); } private AtomicBoolean lag = new AtomicBoolean(false); @@ -589,73 +672,82 @@ public void setShowWarnings(boolean showWarnings) { /** @param newValue Current value */ public void setCurrentValue(double newValue) { - valueWaitingToBeDrawn.set(newValue); + withWriteLock(() -> { + valueWaitingToBeDrawn.set(newValue); - if (isValueWaitingToBeDrawn.get()) { - lag.set(true); - } - else { - isValueWaitingToBeDrawn.set(true); + if (isValueWaitingToBeDrawn.get()) { + lag.set(true); + } + else { + isValueWaitingToBeDrawn.set(true); - drawNewValue(valueWaitingToBeDrawn.get()); - isValueWaitingToBeDrawn.set(false); - lag.set(false); - } + drawNewValue(valueWaitingToBeDrawn.get()); + isValueWaitingToBeDrawn.set(false); + lag.set(false); + } + }); } private void drawNewValue(double newValue) { - double oldValue = currentValue.get(); - currentValue.set(newValue); + withWriteLock(() -> { + AtomicReference newValueAtomicReference = new AtomicReference<>(newValue); // Workaround, since captured variables need to be effectively final in Java. + double oldValue = currentValue.get(); + currentValue.set(newValueAtomicReference.get()); - if (newValue > linearMeterScale.getValueRange().getHigh() && newValue <= linearMeterScale.getValueRange().getHigh() + minMaxTolerance.get()) { - newValue = linearMeterScale.getValueRange().getHigh(); - } - if (newValue < linearMeterScale.getValueRange().getLow() && newValue >= linearMeterScale.getValueRange().getLow() - minMaxTolerance.get()) { - newValue = linearMeterScale.getValueRange().getLow(); - } + if (newValueAtomicReference.get() > linearMeterScale.getValueRange().getHigh() && newValueAtomicReference.get() <= linearMeterScale.getValueRange().getHigh() + minMaxTolerance.get()) { + newValueAtomicReference.set(linearMeterScale.getValueRange().getHigh()); + } + if (newValueAtomicReference.get() < linearMeterScale.getValueRange().getLow() && newValueAtomicReference.get() >= linearMeterScale.getValueRange().getLow() - minMaxTolerance.get()) { + newValueAtomicReference.set(linearMeterScale.getValueRange().getLow()); + } - if (oldValue != newValue) { - if (!Double.isNaN(newValue)){ - int newIndicatorPosition; - if (linearMeterScale.isHorizontal()) { - newIndicatorPosition = (int) (marginLeft + pixelsPerScaleUnit * (newValue - linearMeterScale.getValueRange().getLow())); - } - else { - newIndicatorPosition = (int) (linearMeterScale.getBounds().height - marginBelow - pixelsPerScaleUnit * (newValue - linearMeterScale.getValueRange().getLow())); + if (oldValue != newValueAtomicReference.get()) { + if (!Double.isNaN(newValueAtomicReference.get())){ + int newIndicatorPosition; + if (linearMeterScale.isHorizontal()) { + newIndicatorPosition = (int) (marginLeft + pixelsPerScaleUnit * (newValueAtomicReference.get() - linearMeterScale.getValueRange().getLow())); + } + else { + newIndicatorPosition = (int) (linearMeterScale.getBounds().height - marginBelow - pixelsPerScaleUnit * (newValueAtomicReference.get() - linearMeterScale.getValueRange().getLow())); + } + WARNING newWarning = determineWarning(); + if (currentIndicatorPosition == null || currentIndicatorPosition != newIndicatorPosition || currentWarning.get() != newWarning) { + redrawIndicator(newValueAtomicReference.get(), newWarning); + } } - WARNING newWarning = determineWarning(); - if (currentIndicatorPosition == null || currentIndicatorPosition != newIndicatorPosition || currentWarning.get() != newWarning) { - redrawIndicator(newValue, newWarning); + else if (!Double.isNaN(oldValue)) { + redrawIndicator(newValueAtomicReference.get(), determineWarning()); } } - else if (!Double.isNaN(oldValue)) { - redrawIndicator(newValue, determineWarning()); - } - } + }); } private WARNING determineWarning() { - if (!showWarnings.get()) { - return WARNING.NONE; - } - else if (lag.get()) { - return WARNING.LAG; - } - else if (showUnits.get() && units.get().equals("")) { - return WARNING.NO_UNIT; - } - else if (!validRange.get()) { - return WARNING.MIN_AND_MAX_NOT_DEFINED; - } - else if (currentValue.get() < linearMeterScale.getValueRange().getLow() - minMaxTolerance.get()) { - return WARNING.VALUE_LESS_THAN_MIN; - } - else if (currentValue.get() > linearMeterScale.getValueRange().getHigh() + minMaxTolerance.get()) { - return WARNING.VALUE_GREATER_THAN_MAX; - } - else { - return WARNING.NONE; - } + AtomicReference returnValue = new AtomicReference<>(); + withReadLock(() -> { + if (!showWarnings.get()) { + returnValue.set(WARNING.NONE); + } + else if (lag.get()) { + returnValue.set(WARNING.LAG); + } + else if (showUnits.get() && units.get().equals("")) { + returnValue.set(WARNING.NO_UNIT); + } + else if (!validRange.get()) { + returnValue.set(WARNING.MIN_AND_MAX_NOT_DEFINED); + } + else if (currentValue.get() < linearMeterScale.getValueRange().getLow() - minMaxTolerance.get()) { + returnValue.set(WARNING.VALUE_LESS_THAN_MIN); + } + else if (currentValue.get() > linearMeterScale.getValueRange().getHigh() + minMaxTolerance.get()) { + returnValue.set(WARNING.VALUE_GREATER_THAN_MAX); + } + else { + returnValue.set(WARNING.NONE); + } + }); + return returnValue.get(); } private void drawWarning(Graphics2D gc, WARNING warning) { @@ -691,7 +783,7 @@ else if (warning == WARNING.LAG) { /** @param visible Whether the scale must be displayed or not. */ public void setScaleVisible (boolean visible) { - runOnJavaFXThread(() -> { + withWriteLock(() -> { linearMeterScale.setVisible(visible); updateMeterBackground(); }); @@ -700,26 +792,30 @@ public void setScaleVisible (boolean visible) /** Request a complete redraw with new layout */ private void requestLayout() { - updateMeterBackground(); - redrawIndicator(currentValue.get(), currentWarning.get()); + withReadLock(() -> { + updateMeterBackground(); + redrawIndicator(currentValue.get(), currentWarning.get()); + }); } private void computeLayout() { - logger.log(Level.FINE, "computeLayout"); - layout(); + withReadLock(() -> { + logger.log(Level.FINE, "computeLayout"); + layout(); - if (linearMeterScale.isHorizontal()) { - linearMeterScale.configure( - loLoRectangle.x, - lowRectangle.y + loLoRectangle.height, - pixelsPerScaleUnit); - } else { - linearMeterScale.configure( - linearMeterScale.getBounds().width - marginRight, - linearMeterScale.getBounds().height - marginBelow, - pixelsPerScaleUnit); - } + if (linearMeterScale.isHorizontal()) { + linearMeterScale.configure( + loLoRectangle.x, + lowRectangle.y + loLoRectangle.height, + pixelsPerScaleUnit); + } else { + linearMeterScale.configure( + linearMeterScale.getBounds().width - marginRight, + linearMeterScale.getBounds().height - marginBelow, + pixelsPerScaleUnit); + } + }); } /** Draw meter background (scale) into image buffer @@ -727,66 +823,70 @@ private void computeLayout() */ private void updateMeterBackground() { - int width = linearMeterScale.getBounds().width; - int height = linearMeterScale.getBounds().height; + withReadLock(() -> { + int width = linearMeterScale.getBounds().width; + int height = linearMeterScale.getBounds().height; - if (width <= 0 || height <= 0){ - return; - } + if (width <= 0 || height <= 0){ + return; + } - BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - Graphics2D gc = image.createGraphics(); + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D gc = image.createGraphics(); - gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - gc.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); - gc.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); - gc.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + gc.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); + gc.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); + gc.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - linearMeterScale.computeTicks(gc); - computeLayout(); + linearMeterScale.computeTicks(gc); + computeLayout(); - gc.setBackground(background.get()); - gc.clearRect(0, 0, width, height); + gc.setBackground(background.get()); + gc.clearRect(0, 0, width, height); - linearMeterScale.paint(gc, new Rectangle(0,0,0,0)); - paintMeter(gc); + linearMeterScale.paint(gc, new Rectangle(0,0,0,0)); + paintMeter(gc); - { - BufferedImage combined = new BufferedImage(linearMeterScale.getBounds().width, linearMeterScale.getBounds().height, BufferedImage.TYPE_INT_ARGB); - WritableImage awtJFXConvertBuffer = new WritableImage(linearMeterScale.getBounds().width, linearMeterScale.getBounds().height); - Pair imageBuffersForWriting = new Pair<>(combined, awtJFXConvertBuffer); - Pair widthAndHeight = new Pair<>(width, height); - meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference.set(Optional.of(new ImmutableTriple<>(image, imageBuffersForWriting, widthAndHeight))); - } + { + BufferedImage combined = new BufferedImage(linearMeterScale.getBounds().width, linearMeterScale.getBounds().height, BufferedImage.TYPE_INT_ARGB); + WritableImage awtJFXConvertBuffer = new WritableImage(linearMeterScale.getBounds().width, linearMeterScale.getBounds().height); + Pair imageBuffersForWriting = new Pair<>(combined, awtJFXConvertBuffer); + Pair widthAndHeight = new Pair<>(width, height); + meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference.set(Optional.of(new ImmutableTriple<>(image, imageBuffersForWriting, widthAndHeight))); + } + }); } private void paintMeter(Graphics2D graphics) { - Color color = graphics.getColor(); - if (showLimits.get()) { - if (isHighlightActiveRegionEnabled.get()) { - paintRectangle(graphics, normalRectangle, normalStatusActiveColor_lowlighted.get()); - paintRectangle(graphics, lowRectangle, minorAlarmActiveColor_lowlighted.get()); - paintRectangle(graphics, highRectangle, minorAlarmActiveColor_lowlighted.get()); - paintRectangle(graphics, loLoRectangle, majorAlarmActiveColor_lowlighted.get()); - paintRectangle(graphics, hiHiRectangle, majorAlarmActiveColor_lowlighted.get()); + withReadLock(() -> { + Color color = graphics.getColor(); + if (showLimits.get()) { + if (isHighlightActiveRegionEnabled.get()) { + paintRectangle(graphics, normalRectangle, normalStatusActiveColor_lowlighted.get()); + paintRectangle(graphics, lowRectangle, minorAlarmActiveColor_lowlighted.get()); + paintRectangle(graphics, highRectangle, minorAlarmActiveColor_lowlighted.get()); + paintRectangle(graphics, loLoRectangle, majorAlarmActiveColor_lowlighted.get()); + paintRectangle(graphics, hiHiRectangle, majorAlarmActiveColor_lowlighted.get()); + } + else { + paintRectangle(graphics, normalRectangle, normalStatusActiveColor_highlighted.get()); + paintRectangle(graphics, lowRectangle, minorAlarmActiveColor_highlighted.get()); + paintRectangle(graphics, highRectangle, minorAlarmActiveColor_highlighted.get()); + paintRectangle(graphics, loLoRectangle, majorAlarmActiveColor_highlighted.get()); + paintRectangle(graphics, hiHiRectangle, majorAlarmActiveColor_highlighted.get()); + } } else { - paintRectangle(graphics, normalRectangle, normalStatusActiveColor_highlighted.get()); - paintRectangle(graphics, lowRectangle, minorAlarmActiveColor_highlighted.get()); - paintRectangle(graphics, highRectangle, minorAlarmActiveColor_highlighted.get()); - paintRectangle(graphics, loLoRectangle, majorAlarmActiveColor_highlighted.get()); - paintRectangle(graphics, hiHiRectangle, majorAlarmActiveColor_highlighted.get()); + paintRectangle(graphics, + new Rectangle(marginLeft, + marginAbove, + linearMeterScale.getBounds().width - marginLeft - marginRight, + linearMeterScale.getBounds().height - marginAbove - marginBelow), + normalStatusActiveColor_lowlighted.get()); } - } - else { - paintRectangle(graphics, - new Rectangle(marginLeft, - marginAbove, - linearMeterScale.getBounds().width - marginLeft - marginRight, - linearMeterScale.getBounds().height - marginAbove - marginBelow), - normalStatusActiveColor_lowlighted.get()); - } - graphics.setColor(color); + graphics.setColor(color); + }); } private GradientPaint createHorizontalGradientPaint(Rectangle rectangle, @@ -810,207 +910,212 @@ private GradientPaint createVerticalGradientPaint(Rectangle rectangle, Integer currentIndicatorPosition; /** Draw needle and label for current value */ private void drawValue(Graphics2D gc, double value) { + withReadLock(() -> { + if (Double.isNaN(value)) { + currentIndicatorPosition = null; + } + else { + Stroke oldStroke = gc.getStroke(); + Paint oldPaint = gc.getPaint(); + RenderingHints oldrenderingHints = gc.getRenderingHints(); - if (Double.isNaN(value)) { - currentIndicatorPosition = null; - } - else { - Stroke oldStroke = gc.getStroke(); - Paint oldPaint = gc.getPaint(); - RenderingHints oldrenderingHints = gc.getRenderingHints(); - - gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - gc.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); - gc.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); - gc.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + gc.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); + gc.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); + gc.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - if (showLimits.get()) { - if (isHighlightActiveRegionEnabled.get()) { - if (value <= loLo.get()) { - paintRectangle(gc, loLoRectangle, majorAlarmColor_highlighted.get()); - } - else if (value >= hiHi.get()) { - paintRectangle(gc, hiHiRectangle, majorAlarmColor_highlighted.get()); - } - else if (value <= low.get() && value > loLo.get()) { - paintRectangle(gc, lowRectangle, minorAlarmActiveColor_highlighted.get()); - } - else if (value >= high.get() && value < hiHi.get()) { - paintRectangle(gc, highRectangle, minorAlarmActiveColor_highlighted.get()); - } - else { - paintRectangle(gc, normalRectangle, normalStatusActiveColor_highlighted.get()); + if (showLimits.get()) { + if (isHighlightActiveRegionEnabled.get()) { + if (value <= loLo.get()) { + paintRectangle(gc, loLoRectangle, majorAlarmColor_highlighted.get()); + } + else if (value >= hiHi.get()) { + paintRectangle(gc, hiHiRectangle, majorAlarmColor_highlighted.get()); + } + else if (value <= low.get() && value > loLo.get()) { + paintRectangle(gc, lowRectangle, minorAlarmActiveColor_highlighted.get()); + } + else if (value >= high.get() && value < hiHi.get()) { + paintRectangle(gc, highRectangle, minorAlarmActiveColor_highlighted.get()); + } + else { + paintRectangle(gc, normalRectangle, normalStatusActiveColor_highlighted.get()); + } } } - } - if (linearMeterScale.isHorizontal()) { - if (value >= linearMeterScale.getValueRange().getLow() && value <= linearMeterScale.getValueRange().getHigh()) { + if (linearMeterScale.isHorizontal()) { + if (value >= linearMeterScale.getValueRange().getLow() && value <= linearMeterScale.getValueRange().getHigh()) { - currentIndicatorPosition = (int) (marginLeft + pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); + currentIndicatorPosition = (int) (marginLeft + pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); - if (knobSize.get() > 0) { - int[] XVal = { currentIndicatorPosition - (int) Math.round((1.0 * knobSize.get()) / 4.0), - currentIndicatorPosition + (int) Math.round((1.0 * knobSize.get()) / 4.0), - currentIndicatorPosition }; + if (knobSize.get() > 0) { + int[] XVal = { currentIndicatorPosition - (int) Math.round((1.0 * knobSize.get()) / 4.0), + currentIndicatorPosition + (int) Math.round((1.0 * knobSize.get()) / 4.0), + currentIndicatorPosition }; - int[] YVal = { 0, 0, marginAbove - 2 }; + int[] YVal = { 0, 0, marginAbove - 2 }; - gc.setStroke(AxisPart.TICK_STROKE); - gc.setColor(knobColor.get()); - gc.fillPolygon(XVal, YVal, 3); - gc.setColor(knobColor.get()); - gc.drawPolygon(XVal, YVal, 3); - } + gc.setStroke(AxisPart.TICK_STROKE); + gc.setColor(knobColor.get()); + gc.fillPolygon(XVal, YVal, 3); + gc.setColor(knobColor.get()); + gc.drawPolygon(XVal, YVal, 3); + } - if (needleWidth.get() > 0) { - gc.setStroke(new BasicStroke((float) needleWidth.get())); - gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); - gc.setPaint(needleColor.get()); + if (needleWidth.get() > 0) { + gc.setStroke(new BasicStroke((float) needleWidth.get())); + gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + gc.setPaint(needleColor.get()); - int y1 = marginAbove + needleWidth.get() / 2 + 1; - int y2 = linearMeterScale.getBounds().height - marginBelow - (needleWidth.get() - 1) / 2 - 1; + int y1 = marginAbove + needleWidth.get() / 2 + 1; + int y2 = linearMeterScale.getBounds().height - marginBelow - (needleWidth.get() - 1) / 2 - 1; - gc.drawLine(currentIndicatorPosition, y1, currentIndicatorPosition, y2); + gc.drawLine(currentIndicatorPosition, y1, currentIndicatorPosition, y2); + } } - } - } else { - if (value >= linearMeterScale.getValueRange().getLow() && value <= linearMeterScale.getValueRange().getHigh()) { + } else { + if (value >= linearMeterScale.getValueRange().getLow() && value <= linearMeterScale.getValueRange().getHigh()) { - currentIndicatorPosition = (int) (linearMeterScale.getBounds().height - marginBelow - pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); + currentIndicatorPosition = (int) (linearMeterScale.getBounds().height - marginBelow - pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); - if (knobSize.get() > 0) { - int[] YVal = { currentIndicatorPosition + (int) Math.round((1.0 * knobSize.get() / 4.0)), - currentIndicatorPosition - (int) Math.round((1.0 * knobSize.get() / 4.0)), - currentIndicatorPosition }; + if (knobSize.get() > 0) { + int[] YVal = { currentIndicatorPosition + (int) Math.round((1.0 * knobSize.get() / 4.0)), + currentIndicatorPosition - (int) Math.round((1.0 * knobSize.get() / 4.0)), + currentIndicatorPosition }; - int[] XVal = { 0, 0, marginLeft - 2 }; + int[] XVal = { 0, 0, marginLeft - 2 }; - gc.setStroke(AxisPart.TICK_STROKE); - gc.setColor(knobColor.get()); - gc.fillPolygon(XVal, YVal, 3); - gc.setColor(knobColor.get()); - gc.drawPolygon(XVal, YVal, 3); - } + gc.setStroke(AxisPart.TICK_STROKE); + gc.setColor(knobColor.get()); + gc.fillPolygon(XVal, YVal, 3); + gc.setColor(knobColor.get()); + gc.drawPolygon(XVal, YVal, 3); + } - if (needleWidth.get() > 0) { - gc.setStroke(new BasicStroke((float) needleWidth.get())); - gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); - gc.setPaint(needleColor.get()); + if (needleWidth.get() > 0) { + gc.setStroke(new BasicStroke((float) needleWidth.get())); + gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + gc.setPaint(needleColor.get()); - int x1 = marginLeft + (needleWidth.get())/2 + 1; - int x2 = linearMeterScale.getBounds().width - marginRight - (needleWidth.get()+1)/2; + int x1 = marginLeft + (needleWidth.get())/2 + 1; + int x2 = linearMeterScale.getBounds().width - marginRight - (needleWidth.get()+1)/2; - gc.drawLine(x1, currentIndicatorPosition, x2, currentIndicatorPosition); + gc.drawLine(x1, currentIndicatorPosition, x2, currentIndicatorPosition); + } } } + gc.setRenderingHints(oldrenderingHints); + gc.setStroke(oldStroke); + gc.setPaint(oldPaint); } - gc.setRenderingHints(oldrenderingHints); - gc.setStroke(oldStroke); - gc.setPaint(oldPaint); - } + }); } private void drawBar(Graphics2D gc, double value) { - if (Double.isNaN(value)) { - currentIndicatorPosition = null; - } - else { - Stroke oldStroke = gc.getStroke(); - Paint oldPaint = gc.getPaint(); - RenderingHints oldrenderingHints = gc.getRenderingHints(); - - gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - gc.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); - gc.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); - gc.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - - if (showLimits.get()) { - if (isHighlightActiveRegionEnabled.get()) { - if (value <= loLo.get()) { - paintRectangle(gc, loLoRectangle, majorAlarmColor_highlighted.get()); - } - else if (value >= hiHi.get()) { - paintRectangle(gc, hiHiRectangle, majorAlarmColor_highlighted.get()); - } - else if (value <= low.get() && value > loLo.get()) { - paintRectangle(gc, lowRectangle, minorAlarmActiveColor_highlighted.get()); - } - else if (value >= high.get() && value < hiHi.get()) { - paintRectangle(gc, highRectangle, minorAlarmActiveColor_highlighted.get()); - } - else { - paintRectangle(gc, normalRectangle, normalStatusActiveColor_highlighted.get()); - } - } + withReadLock(() -> { + if (Double.isNaN(value)) { + currentIndicatorPosition = null; } + else { + Stroke oldStroke = gc.getStroke(); + Paint oldPaint = gc.getPaint(); + RenderingHints oldrenderingHints = gc.getRenderingHints(); - if (linearMeterScale.isHorizontal()) { - if (value >= linearMeterScale.getValueRange().getLow()) { + gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + gc.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); + gc.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); + gc.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + + if (showLimits.get()) { if (isHighlightActiveRegionEnabled.get()) { if (value <= loLo.get()) { - gc.setPaint(majorAlarmColor_highlighted.get()); + paintRectangle(gc, loLoRectangle, majorAlarmColor_highlighted.get()); } else if (value >= hiHi.get()) { - gc.setPaint(majorAlarmColor_highlighted.get()); + paintRectangle(gc, hiHiRectangle, majorAlarmColor_highlighted.get()); } else if (value <= low.get() && value > loLo.get()) { - gc.setPaint(minorAlarmActiveColor_highlighted.get()); + paintRectangle(gc, lowRectangle, minorAlarmActiveColor_highlighted.get()); } else if (value >= high.get() && value < hiHi.get()) { - gc.setPaint(minorAlarmActiveColor_highlighted.get()); + paintRectangle(gc, highRectangle, minorAlarmActiveColor_highlighted.get()); } else { - gc.setPaint(needleColor.get()); + paintRectangle(gc, normalRectangle, normalStatusActiveColor_highlighted.get()); } } - else { - gc.setPaint(needleColor.get()); - } - currentIndicatorPosition = (int) (marginLeft + pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); - gc.fillRect(marginLeft+1, marginAbove+1, (int) currentIndicatorPosition, meterBreadth-1); } - } else { - if (value >= linearMeterScale.getValueRange().getLow() && value <= linearMeterScale.getValueRange().getHigh()) { - if (isHighlightActiveRegionEnabled.get()) { - if (value <= loLo.get()) { - gc.setPaint(majorAlarmColor_highlighted.get()); - } - else if (value >= hiHi.get()) { - gc.setPaint(majorAlarmColor_highlighted.get()); + + if (linearMeterScale.isHorizontal()) { + if (value >= linearMeterScale.getValueRange().getLow()) { + if (isHighlightActiveRegionEnabled.get()) { + if (value <= loLo.get()) { + gc.setPaint(majorAlarmColor_highlighted.get()); + } + else if (value >= hiHi.get()) { + gc.setPaint(majorAlarmColor_highlighted.get()); + } + else if (value <= low.get() && value > loLo.get()) { + gc.setPaint(minorAlarmActiveColor_highlighted.get()); + } + else if (value >= high.get() && value < hiHi.get()) { + gc.setPaint(minorAlarmActiveColor_highlighted.get()); + } + else { + gc.setPaint(needleColor.get()); + } } - else if (value <= low.get() && value > loLo.get()) { - gc.setPaint(minorAlarmActiveColor_highlighted.get()); + else { + gc.setPaint(needleColor.get()); } - else if (value >= high.get() && value < hiHi.get()) { - gc.setPaint(minorAlarmActiveColor_highlighted.get()); + currentIndicatorPosition = (int) (marginLeft + pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); + gc.fillRect(marginLeft+1, marginAbove+1, (int) currentIndicatorPosition, meterBreadth-1); + } + } else { + if (value >= linearMeterScale.getValueRange().getLow() && value <= linearMeterScale.getValueRange().getHigh()) { + if (isHighlightActiveRegionEnabled.get()) { + if (value <= loLo.get()) { + gc.setPaint(majorAlarmColor_highlighted.get()); + } + else if (value >= hiHi.get()) { + gc.setPaint(majorAlarmColor_highlighted.get()); + } + else if (value <= low.get() && value > loLo.get()) { + gc.setPaint(minorAlarmActiveColor_highlighted.get()); + } + else if (value >= high.get() && value < hiHi.get()) { + gc.setPaint(minorAlarmActiveColor_highlighted.get()); + } + else { + gc.setPaint(needleColor.get()); + } } else { gc.setPaint(needleColor.get()); } + currentIndicatorPosition = (int) (linearMeterScale.getBounds().height - marginBelow - pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); + gc.fillRect(marginLeft+1, currentIndicatorPosition+1, (int) meterBreadth-1, linearMeterScale.getBounds().height-currentIndicatorPosition-marginBelow-1); } - else { - gc.setPaint(needleColor.get()); - } - currentIndicatorPosition = (int) (linearMeterScale.getBounds().height - marginBelow - pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); - gc.fillRect(marginLeft+1, currentIndicatorPosition+1, (int) meterBreadth-1, linearMeterScale.getBounds().height-currentIndicatorPosition-marginBelow-1); } + gc.setRenderingHints(oldrenderingHints); + gc.setStroke(oldStroke); + gc.setPaint(oldPaint); } - gc.setRenderingHints(oldrenderingHints); - gc.setStroke(oldStroke); - gc.setPaint(oldPaint); - } + }); } /** Should be invoked when meter no longer used to release resources */ public void dispose() { - // Release memory ASAP - meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference.set(Optional.empty()); + withWriteLock(() -> { + // Release memory ASAP + meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference.set(Optional.empty()); + }); } public void setHorizontal(boolean horizontal) { - runOnJavaFXThread(() -> { + withWriteLock(() -> { linearMeterScale.setHorizontal(horizontal); redrawLinearMeterScale(); updateMeterBackground(); @@ -1019,107 +1124,115 @@ public void setHorizontal(boolean horizontal) { } private void drawUnit(Graphics2D gc) { - int center_x = marginLeft + (linearMeterScale.getBounds().width - marginLeft - marginRight) / 2; - int center_y = linearMeterScale.getBounds().height; - RenderingHints renderingHints = gc.getRenderingHints(); - gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - gc.setFont(font); - gc.setColor(Color.BLACK); - FontMetrics fontMetrics = gc.getFontMetrics(gc.getFont()); - String stringToPrint = "[" + units + "]"; - int delta_x = fontMetrics.stringWidth(stringToPrint) / 2; - int delta_y = fontMetrics.getMaxDescent(); - - gc.drawString(stringToPrint, - center_x - delta_x, - center_y - delta_y); - gc.setRenderingHints(renderingHints); + withReadLock(() -> { + int center_x = marginLeft + (linearMeterScale.getBounds().width - marginLeft - marginRight) / 2; + int center_y = linearMeterScale.getBounds().height; + RenderingHints renderingHints = gc.getRenderingHints(); + gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + gc.setFont(font); + gc.setColor(Color.BLACK); + FontMetrics fontMetrics = gc.getFontMetrics(gc.getFont()); + String stringToPrint = "[" + units + "]"; + int delta_x = fontMetrics.stringWidth(stringToPrint) / 2; + int delta_y = fontMetrics.getMaxDescent(); + + gc.drawString(stringToPrint, + center_x - delta_x, + center_y - delta_y); + gc.setRenderingHints(renderingHints); + }); } private void drawWarning_horizontal(Graphics2D gc, String warningText) { - int center_x = marginLeft + (linearMeterScale.getBounds().width - marginLeft - marginRight) / 2; - int center_y = marginAbove + (linearMeterScale.getBounds().height - marginAbove - marginBelow) / 2; - gc.setFont(font); - gc.setColor(Color.BLACK); - FontMetrics fontMetrics = gc.getFontMetrics(gc.getFont()); - int delta_x = (warningTriangle.getWidth(null) + fontMetrics.stringWidth(warningText)) / 2; - int delta_y = fontMetrics.getAscent() / 2; - - gc.drawImage(warningTriangle, - center_x - delta_x, - center_y + delta_y - warningTriangle.getHeight(null) / 2 - 3 * fontMetrics.getAscent() / 8, - null); - gc.drawString(warningText, - center_x - delta_x + warningTriangle.getWidth(null) + 2, - center_y + delta_y); + withReadLock(() -> { + int center_x = marginLeft + (linearMeterScale.getBounds().width - marginLeft - marginRight) / 2; + int center_y = marginAbove + (linearMeterScale.getBounds().height - marginAbove - marginBelow) / 2; + gc.setFont(font); + gc.setColor(Color.BLACK); + FontMetrics fontMetrics = gc.getFontMetrics(gc.getFont()); + int delta_x = (warningTriangle.getWidth(null) + fontMetrics.stringWidth(warningText)) / 2; + int delta_y = fontMetrics.getAscent() / 2; + + gc.drawImage(warningTriangle, + center_x - delta_x, + center_y + delta_y - warningTriangle.getHeight(null) / 2 - 3 * fontMetrics.getAscent() / 8, + null); + gc.drawString(warningText, + center_x - delta_x + warningTriangle.getWidth(null) + 2, + center_y + delta_y); + }); } private void drawWarningText(Graphics2D gc, String warningText) { - RenderingHints oldRenderingHints = gc.getRenderingHints(); - gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - if (linearMeterScale.isHorizontal()) { - drawWarning_horizontal(gc, warningText); - } - else { - drawWarning_vertical(gc, warningText); - } - gc.setRenderingHints(oldRenderingHints); + withReadLock(() -> { + RenderingHints oldRenderingHints = gc.getRenderingHints(); + gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + if (linearMeterScale.isHorizontal()) { + drawWarning_horizontal(gc, warningText); + } + else { + drawWarning_vertical(gc, warningText); + } + gc.setRenderingHints(oldRenderingHints); + }); } private void drawWarning_vertical(Graphics2D gc, String warningText) { - - gc.setFont(font); - gc.setColor(Color.BLACK); - FontMetrics fontMetrics = gc.getFontMetrics(gc.getFont()); - String[] warningText_split; - if (fontMetrics.stringWidth(warningText) <= meterBreadth) { - warningText_split = new String[] { warningText }; - } else { - String[] warningText_splitByWhitespace = warningText.split("\\s+"); - if (Arrays.stream(warningText_splitByWhitespace).allMatch(subString -> fontMetrics.stringWidth(subString) <= meterBreadth)) { - warningText_split = warningText_splitByWhitespace; - } - else { - warningText_split = warningText.split(""); // Split on every character + withReadLock(() -> { + gc.setFont(font); + gc.setColor(Color.BLACK); + FontMetrics fontMetrics = gc.getFontMetrics(gc.getFont()); + String[] warningText_split; + if (fontMetrics.stringWidth(warningText) <= meterBreadth) { + warningText_split = new String[] { warningText }; + } else { + String[] warningText_splitByWhitespace = warningText.split("\\s+"); + if (Arrays.stream(warningText_splitByWhitespace).allMatch(subString -> fontMetrics.stringWidth(subString) <= meterBreadth)) { + warningText_split = warningText_splitByWhitespace; + } + else { + warningText_split = warningText.split(""); // Split on every character + } } - } - int center_x = marginLeft + (linearMeterScale.getBounds().width - marginLeft - marginRight) / 2; - int center_y = marginAbove + (linearMeterScale.getBounds().height - marginAbove - marginBelow) / 2; - int warningTriangleHeight = warningTriangle.getHeight(null); - int fontSizeInPixels = fontMetrics.getHeight(); - int delta_y = (warningTriangleHeight + warningText_split.length * fontSizeInPixels) / 2; - - gc.drawImage(warningTriangle, - center_x - warningTriangle.getWidth(null) / 2, - center_y - delta_y, - null); - - for (int i = 0; i < warningText_split.length; i++) { - gc.drawString(warningText_split[i], - center_x - fontMetrics.stringWidth(warningText_split[i]) / 2, - center_y - delta_y + warningTriangleHeight + (fontSizeInPixels + 4) * (i + 1)); - } + int center_x = marginLeft + (linearMeterScale.getBounds().width - marginLeft - marginRight) / 2; + int center_y = marginAbove + (linearMeterScale.getBounds().height - marginAbove - marginBelow) / 2; + int warningTriangleHeight = warningTriangle.getHeight(null); + int fontSizeInPixels = fontMetrics.getHeight(); + int delta_y = (warningTriangleHeight + warningText_split.length * fontSizeInPixels) / 2; + + gc.drawImage(warningTriangle, + center_x - warningTriangle.getWidth(null) / 2, + center_y - delta_y, + null); + + for (int i = 0; i < warningText_split.length; i++) { + gc.drawString(warningText_split[i], + center_x - fontMetrics.stringWidth(warningText_split[i]) / 2, + center_y - delta_y + warningTriangleHeight + (fontSizeInPixels + 4) * (i + 1)); + } + }); } private void paintRectangle(Graphics2D gc, Rectangle rectangle, Paint paint){ - - //Store old values of clip and color - Shape oldClip = gc.getClip(); - Color oldColor = gc.getColor(); - - //Paint rectangle with specified gradient - gc.setClip(rectangle); - gc.setPaint(paint); - gc.fill(rectangle); - gc.setClip(oldClip); - - //Draw border of rectangle. - //TODO: can this be included in the paint? - gc.setColor(foreground.get()); - gc.draw(rectangle); - - gc.setColor(oldColor); + withReadLock(() -> { + //Store old values of clip and color + Shape oldClip = gc.getClip(); + Color oldColor = gc.getColor(); + + //Paint rectangle with specified gradient + gc.setClip(rectangle); + gc.setPaint(paint); + gc.fill(rectangle); + gc.setClip(oldClip); + + //Draw border of rectangle. + //TODO: can this be included in the paint? + gc.setColor(foreground.get()); + gc.draw(rectangle); + + gc.setColor(oldColor); + }); } private Rectangle loLoRectangle; @@ -1135,144 +1248,145 @@ private void paintRectangle(Graphics2D gc, Rectangle rectangle, Paint paint){ private int meterBreadth = 0; private void layout() { + withReadLock(() -> { + double displayedLoLo; + double displayedLow; + double displayedHiHi; + double displayedHigh; + + double loLoValue = loLo.get(); + double lowValue = low.get(); + double highValue = high.get(); + double hiHiValue = hiHi.get(); + + displayedLoLo = Double.isFinite(loLoValue) ? Math.max(loLoValue, linearMeterScale.getValueRange().getLow()) : linearMeterScale.getValueRange().getLow(); + displayedLow = Double.isFinite(lowValue) ? Math.max(Math.max(lowValue, linearMeterScale.getValueRange().getLow()), displayedLoLo) : linearMeterScale.getValueRange().getLow(); + + displayedHiHi = Double.isFinite(highValue) ? Math.min(hiHiValue, linearMeterScale.getValueRange().getHigh()) : linearMeterScale.getValueRange().getHigh(); + displayedHigh = Double.isFinite(hiHiValue) ? Math.min(Math.min(highValue, linearMeterScale.getValueRange().getHigh()), displayedHiHi) : linearMeterScale.getValueRange().getHigh(); + + FontMetrics fontMetrics = null; + if (font != null) { + Canvas canvas = new Canvas(); + fontMetrics = canvas.getFontMetrics(font); + } - double displayedLoLo; - double displayedLow; - double displayedHiHi; - double displayedHigh; - - double loLoValue = loLo.get(); - double lowValue = low.get(); - double highValue = high.get(); - double hiHiValue = hiHi.get(); - - displayedLoLo = Double.isFinite(loLoValue) ? Math.max(loLoValue, linearMeterScale.getValueRange().getLow()) : linearMeterScale.getValueRange().getLow(); - displayedLow = Double.isFinite(lowValue) ? Math.max(Math.max(lowValue, linearMeterScale.getValueRange().getLow()), displayedLoLo) : linearMeterScale.getValueRange().getLow(); - - displayedHiHi = Double.isFinite(highValue) ? Math.min(hiHiValue, linearMeterScale.getValueRange().getHigh()) : linearMeterScale.getValueRange().getHigh(); - displayedHigh = Double.isFinite(hiHiValue) ? Math.min(Math.min(highValue, linearMeterScale.getValueRange().getHigh()), displayedHiHi) : linearMeterScale.getValueRange().getHigh(); - - FontMetrics fontMetrics = null; - if (font != null) { - Canvas canvas = new Canvas(); - fontMetrics = canvas.getFontMetrics(font); - } - - if (linearMeterScale.isHorizontal()) { - int knobSizeValue = knobSize.get(); - marginAbove = knobSizeValue >= 1 ? knobSizeValue + 2 : 0; - if (linearMeterScale.isVisible() && fontMetrics != null) { - var majorTicks = linearMeterScale.getTicks().getMajorTicks(); - if (majorTicks.size() >= 2) { - marginLeft = fontMetrics.stringWidth(majorTicks.get(0).getLabel()) / 2; - marginRight = fontMetrics.stringWidth(majorTicks.get(majorTicks.size() - 1).getLabel()) / 2; - } else if (majorTicks.size() == 1) { - marginRight = marginLeft = fontMetrics.stringWidth(majorTicks.get(0).getLabel()) / 2; + if (linearMeterScale.isHorizontal()) { + int knobSizeValue = knobSize.get(); + marginAbove = knobSizeValue >= 1 ? knobSizeValue + 2 : 0; + if (linearMeterScale.isVisible() && fontMetrics != null) { + var majorTicks = linearMeterScale.getTicks().getMajorTicks(); + if (majorTicks.size() >= 2) { + marginLeft = fontMetrics.stringWidth(majorTicks.get(0).getLabel()) / 2; + marginRight = fontMetrics.stringWidth(majorTicks.get(majorTicks.size() - 1).getLabel()) / 2; + } else if (majorTicks.size() == 1) { + marginRight = marginLeft = fontMetrics.stringWidth(majorTicks.get(0).getLabel()) / 2; + } else { + marginRight = 0; + marginLeft = 0; + } + marginBelow = (int) (0.5 * linearMeterScale.getTickLength() + 4 + fontMetrics.getAscent() + fontMetrics.getDescent()); } else { - marginRight = 0; marginLeft = 0; + marginRight = 1; + marginBelow = 1; } - marginBelow = (int) (0.5 * linearMeterScale.getTickLength() + 4 + fontMetrics.getAscent() + fontMetrics.getDescent()); - } else { - marginLeft = 0; - marginRight = 1; - marginBelow = 1; - } - if (showUnits.get() && fontMetrics != null) { - marginBelow += 1 + fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent(); + if (showUnits.get() && fontMetrics != null) { + marginBelow += 1 + fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent(); + } + + pixelsPerScaleUnit = (linearMeterScale.getBounds().width - marginLeft - marginRight) / (linearMeterScale.getValueRange().getHigh() - linearMeterScale.getValueRange().getLow()); + meterBreadth = Math.round(linearMeterScale.getBounds().height - marginAbove - marginBelow); + + double x_loLoRectangle = marginLeft; + double x_lowRectangle = marginLeft + pixelsPerScaleUnit * (displayedLoLo - linearMeterScale.getValueRange().getLow()); + double x_normalRectangle = marginLeft + pixelsPerScaleUnit * (displayedLow - linearMeterScale.getValueRange().getLow()); + double x_highRectangle = marginLeft + pixelsPerScaleUnit * (displayedHigh - linearMeterScale.getValueRange().getLow()); + double x_hiHiRectangle = marginLeft + pixelsPerScaleUnit * (displayedHiHi - linearMeterScale.getValueRange().getLow()); + + loLoRectangle = new Rectangle((int) Math.round(x_loLoRectangle), + marginAbove, + (int) (Math.round(x_lowRectangle) - Math.round(x_loLoRectangle)), + meterBreadth); + + lowRectangle = new Rectangle((int) Math.round(x_lowRectangle), + marginAbove, + (int) (Math.round(x_normalRectangle) - Math.round(x_lowRectangle)), + meterBreadth); + + normalRectangle = new Rectangle((int) Math.round(x_normalRectangle), + marginAbove, + (int) (Math.round(x_highRectangle) - Math.round(x_normalRectangle)), + meterBreadth); + + highRectangle = new Rectangle((int) Math.round(x_highRectangle), + marginAbove, + (int) (Math.round(x_hiHiRectangle) - Math.round(x_highRectangle)), + meterBreadth); + + hiHiRectangle = new Rectangle((int) Math.round(x_hiHiRectangle), + marginAbove, + (int) (Math.round(pixelsPerScaleUnit * (linearMeterScale.getValueRange().getHigh() - displayedHiHi))), + meterBreadth); } + else { + int knobSizeValue = knobSize.get(); + marginLeft = knobSizeValue >= 1 ? knobSizeValue + 2 : 0; + if (linearMeterScale.isVisible() && fontMetrics != null) { + int maxTickLabelWidth = 0; + maxTickLabelWidth = 0; + var majorTicks = linearMeterScale.getTicks().getMajorTicks(); + for (var majorTick : majorTicks) { + int labelStringWidth = fontMetrics.stringWidth(majorTick.getLabel()); + maxTickLabelWidth = Math.max(maxTickLabelWidth, labelStringWidth); + } + marginRight = RTLinearMeter.this.linearMeterScale.getTickLength() + maxTickLabelWidth + 1; + marginAbove = fontMetrics.getAscent() / 2 + 1; + marginBelow = fontMetrics.getAscent() / 2 + 1; + } else { + marginRight = 1; + marginAbove = 0; + marginBelow = 1; + } - pixelsPerScaleUnit = (linearMeterScale.getBounds().width - marginLeft - marginRight) / (linearMeterScale.getValueRange().getHigh() - linearMeterScale.getValueRange().getLow()); - meterBreadth = Math.round(linearMeterScale.getBounds().height - marginAbove - marginBelow); - - double x_loLoRectangle = marginLeft; - double x_lowRectangle = marginLeft + pixelsPerScaleUnit * (displayedLoLo - linearMeterScale.getValueRange().getLow()); - double x_normalRectangle = marginLeft + pixelsPerScaleUnit * (displayedLow - linearMeterScale.getValueRange().getLow()); - double x_highRectangle = marginLeft + pixelsPerScaleUnit * (displayedHigh - linearMeterScale.getValueRange().getLow()); - double x_hiHiRectangle = marginLeft + pixelsPerScaleUnit * (displayedHiHi - linearMeterScale.getValueRange().getLow()); - - loLoRectangle = new Rectangle((int) Math.round(x_loLoRectangle), - marginAbove, - (int) (Math.round(x_lowRectangle) - Math.round(x_loLoRectangle)), - meterBreadth); - - lowRectangle = new Rectangle((int) Math.round(x_lowRectangle), - marginAbove, - (int) (Math.round(x_normalRectangle) - Math.round(x_lowRectangle)), - meterBreadth); - - normalRectangle = new Rectangle((int) Math.round(x_normalRectangle), - marginAbove, - (int) (Math.round(x_highRectangle) - Math.round(x_normalRectangle)), - meterBreadth); - - highRectangle = new Rectangle((int) Math.round(x_highRectangle), - marginAbove, - (int) (Math.round(x_hiHiRectangle) - Math.round(x_highRectangle)), - meterBreadth); - - hiHiRectangle = new Rectangle((int) Math.round(x_hiHiRectangle), - marginAbove, - (int) (Math.round(pixelsPerScaleUnit * (linearMeterScale.getValueRange().getHigh() - displayedHiHi))), - meterBreadth); - } - else { - int knobSizeValue = knobSize.get(); - marginLeft = knobSizeValue >= 1 ? knobSizeValue + 2 : 0; - if (linearMeterScale.isVisible() && fontMetrics != null) { - int maxTickLabelWidth = 0; - maxTickLabelWidth = 0; - var majorTicks = linearMeterScale.getTicks().getMajorTicks(); - for (var majorTick : majorTicks) { - int labelStringWidth = fontMetrics.stringWidth(majorTick.getLabel()); - maxTickLabelWidth = Math.max(maxTickLabelWidth, labelStringWidth); + if (showUnits.get() && fontMetrics != null) { + marginBelow += 1 + fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent(); } - marginRight = RTLinearMeter.this.linearMeterScale.getTickLength() + maxTickLabelWidth + 1; - marginAbove = fontMetrics.getAscent() / 2 + 1; - marginBelow = fontMetrics.getAscent() / 2 + 1; - } else { - marginRight = 1; - marginAbove = 0; - marginBelow = 1; - } - if (showUnits.get() && fontMetrics != null) { - marginBelow += 1 + fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent(); + pixelsPerScaleUnit = (linearMeterScale.getBounds().height - marginAbove - marginBelow) / (linearMeterScale.getValueRange().getHigh() - linearMeterScale.getValueRange().getLow()); + meterBreadth = Math.round(linearMeterScale.getBounds().width - marginLeft - marginRight); + + double y_loLoRectangle = marginAbove + pixelsPerScaleUnit * (linearMeterScale.getValueRange().getHigh() - displayedLoLo); + double y_lowRectangle = marginAbove + pixelsPerScaleUnit * (linearMeterScale.getValueRange().getHigh() - displayedLow); + double y_normalRectangle = marginAbove + pixelsPerScaleUnit * (linearMeterScale.getValueRange().getHigh() - displayedHigh); + double y_highRectangle = marginAbove + pixelsPerScaleUnit * (linearMeterScale.getValueRange().getHigh() - displayedHiHi); + + loLoRectangle = new Rectangle(marginLeft, + (int) Math.round(y_loLoRectangle), + meterBreadth, + (int) (Math.round(pixelsPerScaleUnit * (displayedLoLo - linearMeterScale.getValueRange().getLow()) ))); + + lowRectangle = new Rectangle(marginLeft, + (int) Math.round(y_lowRectangle), + meterBreadth, + (int) (Math.round(y_loLoRectangle) - Math.round(y_lowRectangle))); + + normalRectangle = new Rectangle(marginLeft, + (int) Math.round(y_normalRectangle), + meterBreadth, + (int) (Math.round(y_lowRectangle) - Math.round(y_normalRectangle))); + + highRectangle = new Rectangle(marginLeft, + (int) Math.round(y_highRectangle), + meterBreadth, + (int) (Math.round(y_normalRectangle) - Math.round(y_highRectangle))); + + hiHiRectangle = new Rectangle(marginLeft, + marginAbove, + meterBreadth, + (int) Math.round(y_highRectangle) - marginAbove); } - - pixelsPerScaleUnit = (linearMeterScale.getBounds().height - marginAbove - marginBelow) / (linearMeterScale.getValueRange().getHigh() - linearMeterScale.getValueRange().getLow()); - meterBreadth = Math.round(linearMeterScale.getBounds().width - marginLeft - marginRight); - - double y_loLoRectangle = marginAbove + pixelsPerScaleUnit * (linearMeterScale.getValueRange().getHigh() - displayedLoLo); - double y_lowRectangle = marginAbove + pixelsPerScaleUnit * (linearMeterScale.getValueRange().getHigh() - displayedLow); - double y_normalRectangle = marginAbove + pixelsPerScaleUnit * (linearMeterScale.getValueRange().getHigh() - displayedHigh); - double y_highRectangle = marginAbove + pixelsPerScaleUnit * (linearMeterScale.getValueRange().getHigh() - displayedHiHi); - - loLoRectangle = new Rectangle(marginLeft, - (int) Math.round(y_loLoRectangle), - meterBreadth, - (int) (Math.round(pixelsPerScaleUnit * (displayedLoLo - linearMeterScale.getValueRange().getLow()) ))); - - lowRectangle = new Rectangle(marginLeft, - (int) Math.round(y_lowRectangle), - meterBreadth, - (int) (Math.round(y_loLoRectangle) - Math.round(y_lowRectangle))); - - normalRectangle = new Rectangle(marginLeft, - (int) Math.round(y_normalRectangle), - meterBreadth, - (int) (Math.round(y_lowRectangle) - Math.round(y_normalRectangle))); - - highRectangle = new Rectangle(marginLeft, - (int) Math.round(y_highRectangle), - meterBreadth, - (int) (Math.round(y_normalRectangle) - Math.round(y_highRectangle))); - - hiHiRectangle = new Rectangle(marginLeft, - marginAbove, - meterBreadth, - (int) Math.round(y_highRectangle) - marginAbove); - } + }); } } From 51b05f4f9581eecf48f0055ad1a384027ec46598 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Wed, 13 Aug 2025 15:33:27 +0200 Subject: [PATCH 09/35] CSSTUDIO-3347 Remove the field 'currentWarning' and remove the method drawWarning(). --- .../widgets/linearmeter/RTLinearMeter.java | 89 +++++++++---------- 1 file changed, 41 insertions(+), 48 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index 68e869e2e1..d0470f9af4 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -179,15 +179,24 @@ public void redrawLinearMeterScale() { } private enum WARNING { - NONE, - VALUE_LESS_THAN_MIN, - VALUE_GREATER_THAN_MAX, - MIN_AND_MAX_NOT_DEFINED, - LAG, - NO_UNIT - } + NONE(""), + VALUE_LESS_THAN_MIN("VALUE < MIN"), + VALUE_GREATER_THAN_MAX("VALUE > MAX"), + MIN_AND_MAX_NOT_DEFINED("NO UNIT DEFINED"), + LAG("MIN AND MAX ARE NOT SET"), + NO_UNIT("LAG"); + + private final String displayName; + + private WARNING(String displayName) { + this.displayName = displayName; + }; - private AtomicReference currentWarning = new AtomicReference(WARNING.NONE); + @Override + public String toString() { + return this.displayName; + } + } /** Colors */ private AtomicReference foreground = new AtomicReference<>(Color.BLACK); @@ -454,10 +463,17 @@ public void setUnits(String newValue) { }); } + private void logNewWarningIfDifferent(WARNING oldWarning, WARNING newWarning) { + if (oldWarning != newWarning) { + logger.log(Level.WARNING, newWarning.toString() + " on Linear Meter!"); + } + } public void setShowLimits(boolean newValue) { withWriteLock(() -> { + WARNING oldWarning = determineWarning(); showLimits.set(newValue); - determineWarning(); + WARNING newWarning = determineWarning(); + logNewWarningIfDifferent(oldWarning, newWarning); redraw(); }); } @@ -472,9 +488,11 @@ public void setRange(double minimum, double maximum, boolean validRange) { public void setMinMaxTolerance(double minMaxTolerance) { withWriteLock(() -> { + WARNING oldWarning = determineWarning(); this.minMaxTolerance.set(minMaxTolerance); - determineWarning(); - redrawIndicator(currentValue.get(), currentWarning.get()); + WARNING newWarning = determineWarning(); + logNewWarningIfDifferent(oldWarning, newWarning); + redrawIndicator(currentValue.get(), newWarning); }); } @@ -563,7 +581,7 @@ public void setKnobSize(int knobSize) { private void redraw() { withReadLock(() -> { updateMeterBackground(); - redrawIndicator(currentValue.get(), currentWarning.get()); + redrawIndicator(currentValue.get(), determineWarning()); }); } private AtomicReference, Pair>>> meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference = new AtomicReference<>(Optional.empty()); @@ -603,7 +621,9 @@ else if (displayMode.equals(DisplayMode.BAR)) { throw new RuntimeException("Unhandled case"); } - drawWarning(gc, warning); + if (warning != WARNING.NONE) { + drawWarningText(gc, warning.toString()); + } if (showUnits.get()) { drawUnit(gc); } @@ -690,6 +710,7 @@ public void setCurrentValue(double newValue) private void drawNewValue(double newValue) { withWriteLock(() -> { + WARNING oldWarning = determineWarning(); AtomicReference newValueAtomicReference = new AtomicReference<>(newValue); // Workaround, since captured variables need to be effectively final in Java. double oldValue = currentValue.get(); currentValue.set(newValueAtomicReference.get()); @@ -701,6 +722,9 @@ private void drawNewValue(double newValue) { newValueAtomicReference.set(linearMeterScale.getValueRange().getLow()); } + WARNING newWarning = determineWarning(); + logNewWarningIfDifferent(oldWarning, newWarning); + if (oldValue != newValueAtomicReference.get()) { if (!Double.isNaN(newValueAtomicReference.get())){ int newIndicatorPosition; @@ -710,13 +734,12 @@ private void drawNewValue(double newValue) { else { newIndicatorPosition = (int) (linearMeterScale.getBounds().height - marginBelow - pixelsPerScaleUnit * (newValueAtomicReference.get() - linearMeterScale.getValueRange().getLow())); } - WARNING newWarning = determineWarning(); - if (currentIndicatorPosition == null || currentIndicatorPosition != newIndicatorPosition || currentWarning.get() != newWarning) { + if (currentIndicatorPosition == null || currentIndicatorPosition != newIndicatorPosition || determineWarning() != newWarning) { redrawIndicator(newValueAtomicReference.get(), newWarning); } } else if (!Double.isNaN(oldValue)) { - redrawIndicator(newValueAtomicReference.get(), determineWarning()); + redrawIndicator(newValueAtomicReference.get(), newWarning); } } }); @@ -750,36 +773,6 @@ else if (currentValue.get() > linearMeterScale.getValueRange().getHigh() + minMa return returnValue.get(); } - private void drawWarning(Graphics2D gc, WARNING warning) { - if (warning != WARNING.NONE) { - String warningText = ""; - if (warning == WARNING.VALUE_LESS_THAN_MIN) { - warningText = "VALUE < MIN"; - - } - else if (warning == WARNING.VALUE_GREATER_THAN_MAX) { - warningText = "VALUE > MAX"; - - } - else if (warning == WARNING.NO_UNIT) { - warningText = "NO UNIT DEFINED"; - } - else if (warning == WARNING.MIN_AND_MAX_NOT_DEFINED) { - warningText = "MIN AND MAX ARE NOT SET"; - - } - else if (warning == WARNING.LAG) { - warningText = "LAG"; - } - - drawWarningText(gc, warningText); - if (currentWarning.get() != warning) { - logger.log(Level.WARNING, warningText + " on Linear Meter!"); - } - } - currentWarning.set(warning); - } - /** @param visible Whether the scale must be displayed or not. */ public void setScaleVisible (boolean visible) { @@ -794,7 +787,7 @@ private void requestLayout() { withReadLock(() -> { updateMeterBackground(); - redrawIndicator(currentValue.get(), currentWarning.get()); + redrawIndicator(currentValue.get(), determineWarning()); }); } @@ -1119,7 +1112,7 @@ public void setHorizontal(boolean horizontal) { linearMeterScale.setHorizontal(horizontal); redrawLinearMeterScale(); updateMeterBackground(); - redrawIndicator(currentValue.get(), currentWarning.get()); + redrawIndicator(currentValue.get(), determineWarning()); }); } From a83312fca07e75b6f6139d5f0033ce5768dc8090 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Wed, 13 Aug 2025 15:51:56 +0200 Subject: [PATCH 10/35] CSSTUDIO-3347 Remove the field 'currentIndicatorPosition'. --- .../widgets/linearmeter/RTLinearMeter.java | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index d0470f9af4..a704c0952e 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -27,6 +27,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Supplier; import java.util.logging.Level; import javafx.application.Platform; @@ -145,6 +146,14 @@ private void withReadLock(Runnable runnable) { readWriteLock.readLock().unlock(); } } + private T withReadLock(Supplier supplier) { + readWriteLock.readLock().lock(); + try { + return supplier.get(); + } finally { + readWriteLock.readLock().unlock(); + } + } private void withWriteLock(Runnable runnable) { readWriteLock.writeLock().lock(); try { @@ -708,11 +717,23 @@ public void setCurrentValue(double newValue) }); } + private int computeIndicatorPosition(double value) { + return withReadLock(() -> { + if (linearMeterScale.isHorizontal()) { + return (int) (marginLeft + pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); + } + else { + return (int) (linearMeterScale.getBounds().height - marginBelow - pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); + } + }); + } + private void drawNewValue(double newValue) { withWriteLock(() -> { WARNING oldWarning = determineWarning(); AtomicReference newValueAtomicReference = new AtomicReference<>(newValue); // Workaround, since captured variables need to be effectively final in Java. double oldValue = currentValue.get(); + double oldIndicatorPosition = computeIndicatorPosition(oldValue); currentValue.set(newValueAtomicReference.get()); if (newValueAtomicReference.get() > linearMeterScale.getValueRange().getHigh() && newValueAtomicReference.get() <= linearMeterScale.getValueRange().getHigh() + minMaxTolerance.get()) { @@ -727,14 +748,8 @@ private void drawNewValue(double newValue) { if (oldValue != newValueAtomicReference.get()) { if (!Double.isNaN(newValueAtomicReference.get())){ - int newIndicatorPosition; - if (linearMeterScale.isHorizontal()) { - newIndicatorPosition = (int) (marginLeft + pixelsPerScaleUnit * (newValueAtomicReference.get() - linearMeterScale.getValueRange().getLow())); - } - else { - newIndicatorPosition = (int) (linearMeterScale.getBounds().height - marginBelow - pixelsPerScaleUnit * (newValueAtomicReference.get() - linearMeterScale.getValueRange().getLow())); - } - if (currentIndicatorPosition == null || currentIndicatorPosition != newIndicatorPosition || determineWarning() != newWarning) { + int newIndicatorPosition = computeIndicatorPosition(newValue); + if (newIndicatorPosition != oldIndicatorPosition || determineWarning() != newWarning) { redrawIndicator(newValueAtomicReference.get(), newWarning); } } @@ -900,12 +915,11 @@ private GradientPaint createVerticalGradientPaint(Rectangle rectangle, return gradientPaint; } - Integer currentIndicatorPosition; /** Draw needle and label for current value */ private void drawValue(Graphics2D gc, double value) { withReadLock(() -> { if (Double.isNaN(value)) { - currentIndicatorPosition = null; + return; } else { Stroke oldStroke = gc.getStroke(); @@ -940,7 +954,7 @@ else if (value >= high.get() && value < hiHi.get()) { if (linearMeterScale.isHorizontal()) { if (value >= linearMeterScale.getValueRange().getLow() && value <= linearMeterScale.getValueRange().getHigh()) { - currentIndicatorPosition = (int) (marginLeft + pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); + int currentIndicatorPosition = computeIndicatorPosition(value); if (knobSize.get() > 0) { int[] XVal = { currentIndicatorPosition - (int) Math.round((1.0 * knobSize.get()) / 4.0), @@ -970,12 +984,12 @@ else if (value >= high.get() && value < hiHi.get()) { } else { if (value >= linearMeterScale.getValueRange().getLow() && value <= linearMeterScale.getValueRange().getHigh()) { - currentIndicatorPosition = (int) (linearMeterScale.getBounds().height - marginBelow - pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); + int currentIndicatorPosition = computeIndicatorPosition(value); if (knobSize.get() > 0) { int[] YVal = { currentIndicatorPosition + (int) Math.round((1.0 * knobSize.get() / 4.0)), - currentIndicatorPosition - (int) Math.round((1.0 * knobSize.get() / 4.0)), - currentIndicatorPosition }; + currentIndicatorPosition - (int) Math.round((1.0 * knobSize.get() / 4.0)), + currentIndicatorPosition }; int[] XVal = { 0, 0, marginLeft - 2 }; @@ -1008,7 +1022,7 @@ else if (value >= high.get() && value < hiHi.get()) { private void drawBar(Graphics2D gc, double value) { withReadLock(() -> { if (Double.isNaN(value)) { - currentIndicatorPosition = null; + return; } else { Stroke oldStroke = gc.getStroke(); @@ -1062,7 +1076,7 @@ else if (value >= high.get() && value < hiHi.get()) { else { gc.setPaint(needleColor.get()); } - currentIndicatorPosition = (int) (marginLeft + pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); + int currentIndicatorPosition = computeIndicatorPosition(value); gc.fillRect(marginLeft+1, marginAbove+1, (int) currentIndicatorPosition, meterBreadth-1); } } else { @@ -1087,7 +1101,7 @@ else if (value >= high.get() && value < hiHi.get()) { else { gc.setPaint(needleColor.get()); } - currentIndicatorPosition = (int) (linearMeterScale.getBounds().height - marginBelow - pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); + int currentIndicatorPosition = computeIndicatorPosition(value); gc.fillRect(marginLeft+1, currentIndicatorPosition+1, (int) meterBreadth-1, linearMeterScale.getBounds().height-currentIndicatorPosition-marginBelow-1); } } From 5ab8f92a418e8e08ec9c175f83c033514e698a33 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Wed, 13 Aug 2025 16:35:49 +0200 Subject: [PATCH 11/35] CSSTUDIO-3347 Replace types "AtomicBoolean", "AtomicInteger" and "AtomicReference" with base types. --- .../widgets/linearmeter/RTLinearMeter.java | 479 +++++++++--------- 1 file changed, 239 insertions(+), 240 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index a704c0952e..99d6915be6 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -23,7 +23,6 @@ import java.util.Arrays; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -95,10 +94,10 @@ public RTLinearMeter(double initialValue, } if (Double.isFinite(min) && Double.isFinite(max)) { - validRange.set(true); + validRange = true; } else { - validRange.set(false); + validRange = false; min = 0.0; max = 100.0; } @@ -110,25 +109,25 @@ public RTLinearMeter(double initialValue, min, max); - this.minMaxTolerance.set(minMaxTolerance); - this.loLo.set(loLo); - this.low.set(low); - this.high.set(high); - this.hiHi.set(hiHi); - this.showUnits.set(showUnits); - this.showLimits.set(showLimits); - this.showWarnings.set(showWarnings); + this.minMaxTolerance = minMaxTolerance; + this.loLo = loLo; + this.low = low; + this.high = high; + this.hiHi = hiHi; + this.showUnits = showUnits; + this.showLimits = showLimits; + this.showWarnings = showWarnings; layout(); - this.currentValue.set(initialValue); - this.isGradientEnabled.set(isGradientEnabled); - this.isHighlightActiveRegionEnabled.set(isHighlightActiveRegionEnabled); + this.currentValue = initialValue; + this.isGradientEnabled = isGradientEnabled; + this.isHighlightActiveRegionEnabled = isHighlightActiveRegionEnabled; - this.needleWidth.set(needleWidth); - this.needleColor.set(needleColor); - this.knobSize.set(knobSize); - this.knobColor.set(knobColor); + this.needleWidth = needleWidth; + this.needleColor = needleColor; + this.knobSize = knobSize; + this.knobColor = knobColor; setNormalStatusColor(normalStatusColor); setMinorAlarmColor(minorAlarmColor); @@ -163,14 +162,14 @@ private void withWriteLock(Runnable runnable) { } } - private AtomicBoolean showUnits = new AtomicBoolean(true); - private AtomicReference units = new AtomicReference<>(""); - private AtomicBoolean showLimits = new AtomicBoolean(true); + private boolean showUnits = true; + private String units = ""; + private boolean showLimits = true; - private AtomicReference loLo = new AtomicReference<>(0.0); - private AtomicReference low = new AtomicReference<>(0.0); - private AtomicReference high = new AtomicReference<>(100.0); - private AtomicReference hiHi = new AtomicReference<>(100.0); + private double loLo = 0.; + private double low = .0; + private double high = 100.; + private double hiHi = 100.; private static Image warningTriangle = null; @@ -208,8 +207,8 @@ public String toString() { } /** Colors */ - private AtomicReference foreground = new AtomicReference<>(Color.BLACK); - private AtomicReference background = new AtomicReference<>(Color.WHITE); + private Color foreground = Color.BLACK; + private Color background = Color.WHITE; /** Fonts */ private Font font; @@ -224,13 +223,13 @@ public void layoutPlotPart(PlotPart plotPart) { } public void refreshPlotPart(PlotPart plotPart) { } }; - private AtomicBoolean validRange = new AtomicBoolean(false); - private AtomicReference minMaxTolerance = new AtomicReference<>(0.0); + private boolean validRange = false; + private double minMaxTolerance = 0.0; public boolean getValidRange() { AtomicBoolean returnValue = new AtomicBoolean(false); withReadLock(() -> { - returnValue.set(validRange.get()); + returnValue.set(validRange); }); return returnValue.get(); } @@ -239,44 +238,44 @@ public boolean getValidRange() { public LinearMeterScale linearMeterScale; /** Value to display */ - private AtomicReference currentValue = new AtomicReference(Double.NaN); + private double currentValue = Double.NaN; - private AtomicReference normalStatusColor_lowlighted = new AtomicReference(Color.LIGHT_GRAY); - private AtomicReference normalStatusColorGradientStartPoint_lowlighted = new AtomicReference(Color.LIGHT_GRAY); - private AtomicReference normalStatusColorGradientEndPoint_lowlighted = new AtomicReference(Color.LIGHT_GRAY); - private AtomicReference normalStatusColor_highlighted = new AtomicReference(Color.LIGHT_GRAY); - private AtomicReference normalStatusColorGradientStartPoint_highlighted = new AtomicReference(Color.LIGHT_GRAY); - private AtomicReference normalStatusColorGradientEndPoint_highlighted = new AtomicReference(Color.LIGHT_GRAY); + private Color normalStatusColor_lowlighted = Color.LIGHT_GRAY; + private Color normalStatusColorGradientStartPoint_lowlighted = Color.LIGHT_GRAY; + private Color normalStatusColorGradientEndPoint_lowlighted = Color.LIGHT_GRAY; + private Color normalStatusColor_highlighted = Color.LIGHT_GRAY; + private Color normalStatusColorGradientStartPoint_highlighted = Color.LIGHT_GRAY; + private Color normalStatusColorGradientEndPoint_highlighted = Color.LIGHT_GRAY; - private AtomicReference minorAlarmColor_lowlighted = new AtomicReference(Color.ORANGE); - private AtomicReference minorAlarmColor_highlighted = new AtomicReference(Color.ORANGE); - private AtomicReference minorAlarmColorGradientStartPoint_lowlighted = new AtomicReference(Color.ORANGE); - private AtomicReference minorAlarmColorGradientEndPoint_lowlighted = new AtomicReference(Color.ORANGE); - private AtomicReference minorAlarmColorGradientStartPoint_highlighted = new AtomicReference(Color.ORANGE); - private AtomicReference minorAlarmColorGradientEndPoint_highlighted = new AtomicReference(Color.ORANGE); + private Color minorAlarmColor_lowlighted = Color.ORANGE; + private Color minorAlarmColor_highlighted = Color.ORANGE; + private Color minorAlarmColorGradientStartPoint_lowlighted = Color.ORANGE; + private Color minorAlarmColorGradientEndPoint_lowlighted = Color.ORANGE; + private Color minorAlarmColorGradientStartPoint_highlighted = Color.ORANGE; + private Color minorAlarmColorGradientEndPoint_highlighted = Color.ORANGE; - private AtomicReference majorAlarmColor_lowlighted = new AtomicReference(Color.RED); - private AtomicReference majorAlarmColor_highlighted = new AtomicReference(Color.RED); - private AtomicReference majorAlarmColorGradientStartPoint_lowlighted = new AtomicReference(Color.RED); - private AtomicReference majorAlarmColorGradientEndPoint_lowlighted = new AtomicReference(Color.RED); - private AtomicReference majorAlarmColorGradientStartPoint_highlighted = new AtomicReference(Color.RED); - private AtomicReference majorAlarmColorGradientEndPoint_highlighted = new AtomicReference(Color.RED); + private Color majorAlarmColor_lowlighted = Color.RED; + private Color majorAlarmColor_highlighted = Color.RED; + private Color majorAlarmColorGradientStartPoint_lowlighted = Color.RED; + private Color majorAlarmColorGradientEndPoint_lowlighted = Color.RED; + private Color majorAlarmColorGradientStartPoint_highlighted = Color.RED; + private Color majorAlarmColorGradientEndPoint_highlighted = Color.RED; - AtomicReference normalStatusActiveColor_lowlighted = new AtomicReference<>(Color.WHITE); - AtomicReference minorAlarmActiveColor_lowlighted = new AtomicReference<>(Color.YELLOW); - AtomicReference majorAlarmActiveColor_lowlighted = new AtomicReference<>(Color.RED); + Paint normalStatusActiveColor_lowlighted = Color.WHITE; + Paint minorAlarmActiveColor_lowlighted = Color.YELLOW; + Paint majorAlarmActiveColor_lowlighted = Color.RED; - AtomicReference normalStatusActiveColor_highlighted = new AtomicReference<>(Color.WHITE); - AtomicReference majorAlarmActiveColor_highlighted = new AtomicReference<>(Color.YELLOW); - AtomicReference minorAlarmActiveColor_highlighted = new AtomicReference<>(Color.RED); + Paint normalStatusActiveColor_highlighted = Color.WHITE; + Paint majorAlarmActiveColor_highlighted = Color.YELLOW; + Paint minorAlarmActiveColor_highlighted = Color.RED; - private AtomicInteger needleWidth = new AtomicInteger(2); + private int needleWidth = 2; - private AtomicReference needleColor = new AtomicReference<>(Color.BLACK); + private Color needleColor = Color.BLACK; - private AtomicBoolean isGradientEnabled = new AtomicBoolean(false); + private boolean isGradientEnabled = false; - private AtomicBoolean isHighlightActiveRegionEnabled = new AtomicBoolean(true); + private boolean isHighlightActiveRegionEnabled = true; enum DisplayMode { NEEDLE, @@ -285,11 +284,11 @@ enum DisplayMode { public void setDisplayMode(DisplayMode newDisplayMode) { withWriteLock(() -> { - this.displayMode.set(newDisplayMode); + this.displayMode = newDisplayMode; }); } - private AtomicReference displayMode = new AtomicReference<>(DisplayMode.NEEDLE); + private DisplayMode displayMode = DisplayMode.NEEDLE; private void runOnJavaFXThread(Runnable runnable) { if (Platform.isFxApplicationThread()) { @@ -302,46 +301,46 @@ private void runOnJavaFXThread(Runnable runnable) { public void setIsGradientEnabled(boolean isGradientEnabled) { withWriteLock(() -> { - this.isGradientEnabled.set(isGradientEnabled); + this.isGradientEnabled = isGradientEnabled; updateActiveColors(); }); } public void setIsHighlightActiveRegionEnabled(boolean isHighlightActiveRegionEnabled) { withWriteLock(() -> { - this.isHighlightActiveRegionEnabled.set(isHighlightActiveRegionEnabled); + this.isHighlightActiveRegionEnabled = isHighlightActiveRegionEnabled; }); } private void updateActiveColors() { withWriteLock(() -> { - if (isGradientEnabled.get()) { + if (isGradientEnabled) { if (linearMeterScale.isHorizontal()) { - majorAlarmActiveColor_lowlighted.set(createVerticalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_lowlighted.get(), majorAlarmColorGradientEndPoint_lowlighted.get())); - minorAlarmActiveColor_lowlighted.set(createVerticalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_lowlighted.get(), minorAlarmColorGradientEndPoint_lowlighted.get())); - normalStatusActiveColor_lowlighted.set(createVerticalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted.get(), normalStatusColorGradientEndPoint_highlighted.get())); // The normal status region is never lowlighted. + majorAlarmActiveColor_lowlighted = createVerticalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_lowlighted, majorAlarmColorGradientEndPoint_lowlighted); + minorAlarmActiveColor_lowlighted = createVerticalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_lowlighted, minorAlarmColorGradientEndPoint_lowlighted); + normalStatusActiveColor_lowlighted = createVerticalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted, normalStatusColorGradientEndPoint_highlighted); // The normal status region is never lowlighted. - minorAlarmActiveColor_highlighted.set(createVerticalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_highlighted.get(), minorAlarmColorGradientEndPoint_highlighted.get())); - majorAlarmActiveColor_highlighted.set(createVerticalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_highlighted.get(), majorAlarmColorGradientEndPoint_highlighted.get())); - normalStatusActiveColor_highlighted.set(createVerticalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted.get(), normalStatusColorGradientEndPoint_highlighted.get())); + minorAlarmActiveColor_highlighted = createVerticalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_highlighted, minorAlarmColorGradientEndPoint_highlighted); + majorAlarmActiveColor_highlighted = createVerticalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_highlighted, majorAlarmColorGradientEndPoint_highlighted); + normalStatusActiveColor_highlighted = createVerticalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted, normalStatusColorGradientEndPoint_highlighted); } else { - majorAlarmActiveColor_lowlighted.set(createHorizontalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_lowlighted.get(), majorAlarmColorGradientEndPoint_lowlighted.get())); - minorAlarmActiveColor_lowlighted.set(createHorizontalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_lowlighted.get(), minorAlarmColorGradientEndPoint_lowlighted.get())); - normalStatusActiveColor_lowlighted.set(createHorizontalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted.get(), normalStatusColorGradientEndPoint_highlighted.get())); // The normal status region is never lowlighted. + majorAlarmActiveColor_lowlighted = createHorizontalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_lowlighted, majorAlarmColorGradientEndPoint_lowlighted); + minorAlarmActiveColor_lowlighted = createHorizontalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_lowlighted, minorAlarmColorGradientEndPoint_lowlighted); + normalStatusActiveColor_lowlighted = createHorizontalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted, normalStatusColorGradientEndPoint_highlighted); // The normal status region is never lowlighted. - minorAlarmActiveColor_highlighted.set(createHorizontalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_highlighted.get(), minorAlarmColorGradientEndPoint_highlighted.get())); - majorAlarmActiveColor_highlighted.set(createHorizontalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_highlighted.get(), majorAlarmColorGradientEndPoint_highlighted.get())); - normalStatusActiveColor_highlighted.set(createHorizontalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted.get(), normalStatusColorGradientEndPoint_highlighted.get())); + minorAlarmActiveColor_highlighted = createHorizontalGradientPaint(lowRectangle, minorAlarmColorGradientStartPoint_highlighted, minorAlarmColorGradientEndPoint_highlighted); + majorAlarmActiveColor_highlighted = createHorizontalGradientPaint(loLoRectangle, majorAlarmColorGradientStartPoint_highlighted, majorAlarmColorGradientEndPoint_highlighted); + normalStatusActiveColor_highlighted = createHorizontalGradientPaint(normalRectangle, normalStatusColorGradientStartPoint_highlighted, normalStatusColorGradientEndPoint_highlighted); } } else { - normalStatusActiveColor_lowlighted.set(normalStatusColor_highlighted.get()); // The normal status region is never lowlighted. - majorAlarmActiveColor_lowlighted.set(majorAlarmColor_lowlighted.get()); - minorAlarmActiveColor_lowlighted.set(minorAlarmColor_lowlighted.get()); + normalStatusActiveColor_lowlighted = normalStatusColor_highlighted; // The normal status region is never lowlighted. + majorAlarmActiveColor_lowlighted = majorAlarmColor_lowlighted; + minorAlarmActiveColor_lowlighted = minorAlarmColor_lowlighted; - normalStatusActiveColor_highlighted.set(normalStatusColor_highlighted.get()); - minorAlarmActiveColor_highlighted.set(minorAlarmColor_highlighted.get()); - majorAlarmActiveColor_highlighted.set(majorAlarmColor_highlighted.get()); + normalStatusActiveColor_highlighted = normalStatusColor_highlighted; + minorAlarmActiveColor_highlighted = minorAlarmColor_highlighted; + majorAlarmActiveColor_highlighted = majorAlarmColor_highlighted; } }); } @@ -401,14 +400,14 @@ private Color computeLowlightedColor(Color color) { public void setNormalStatusColor(Color normalStatusColor) { withWriteLock(() -> { - this.normalStatusColor_lowlighted.set(computeLowlightedColor(normalStatusColor)); - this.normalStatusColor_highlighted.set(normalStatusColor); + this.normalStatusColor_lowlighted = computeLowlightedColor(normalStatusColor); + this.normalStatusColor_highlighted = normalStatusColor; - this.normalStatusColorGradientStartPoint_lowlighted.set(computeGradientStartPoint(normalStatusColor_lowlighted.get())); - this.normalStatusColorGradientEndPoint_lowlighted.set(computeGradientEndPoint(normalStatusColor_lowlighted.get())); + this.normalStatusColorGradientStartPoint_lowlighted = computeGradientStartPoint(normalStatusColor_lowlighted); + this.normalStatusColorGradientEndPoint_lowlighted = computeGradientEndPoint(normalStatusColor_lowlighted); - this.normalStatusColorGradientStartPoint_highlighted.set(computeGradientStartPoint(normalStatusColor_highlighted.get())); - this.normalStatusColorGradientEndPoint_highlighted.set(computeGradientEndPoint(normalStatusColor_highlighted.get())); + this.normalStatusColorGradientStartPoint_highlighted = computeGradientStartPoint(normalStatusColor_highlighted); + this.normalStatusColorGradientEndPoint_highlighted = computeGradientEndPoint(normalStatusColor_highlighted); updateActiveColors(); }); @@ -416,14 +415,14 @@ public void setNormalStatusColor(Color normalStatusColor) { public void setMinorAlarmColor(Color minorAlarmColor) { withWriteLock(() -> { - this.minorAlarmColor_lowlighted.set(computeLowlightedColor(minorAlarmColor)); - this.minorAlarmColor_highlighted.set(minorAlarmColor); + this.minorAlarmColor_lowlighted = computeLowlightedColor(minorAlarmColor); + this.minorAlarmColor_highlighted = minorAlarmColor; - this.minorAlarmColorGradientStartPoint_lowlighted.set(computeGradientStartPoint(minorAlarmColor_lowlighted.get())); - this.minorAlarmColorGradientEndPoint_lowlighted.set(computeGradientEndPoint(minorAlarmColor_lowlighted.get())); + this.minorAlarmColorGradientStartPoint_lowlighted = computeGradientStartPoint(minorAlarmColor_lowlighted); + this.minorAlarmColorGradientEndPoint_lowlighted = computeGradientEndPoint(minorAlarmColor_lowlighted); - this.minorAlarmColorGradientStartPoint_highlighted.set(computeGradientStartPoint(minorAlarmColor_highlighted.get())); - this.minorAlarmColorGradientEndPoint_highlighted.set(computeGradientEndPoint(minorAlarmColor_highlighted.get())); + this.minorAlarmColorGradientStartPoint_highlighted = computeGradientStartPoint(minorAlarmColor_highlighted); + this.minorAlarmColorGradientEndPoint_highlighted = computeGradientEndPoint(minorAlarmColor_highlighted); updateActiveColors(); }); @@ -431,14 +430,14 @@ public void setMinorAlarmColor(Color minorAlarmColor) { public void setMajorAlarmColor(Color majorAlarmColor) { withWriteLock(() -> { - this.majorAlarmColor_lowlighted.set(computeLowlightedColor(majorAlarmColor)); - this.majorAlarmColor_highlighted.set(majorAlarmColor); + this.majorAlarmColor_lowlighted = computeLowlightedColor(majorAlarmColor); + this.majorAlarmColor_highlighted = majorAlarmColor; - this.majorAlarmColorGradientStartPoint_lowlighted.set(computeGradientStartPoint(majorAlarmColor_lowlighted.get())); - this.majorAlarmColorGradientEndPoint_lowlighted.set(computeGradientEndPoint(majorAlarmColor_lowlighted.get())); + this.majorAlarmColorGradientStartPoint_lowlighted = computeGradientStartPoint(majorAlarmColor_lowlighted); + this.majorAlarmColorGradientEndPoint_lowlighted = computeGradientEndPoint(majorAlarmColor_lowlighted); - this.majorAlarmColorGradientStartPoint_highlighted.set(computeGradientStartPoint(majorAlarmColor_highlighted.get())); - this.majorAlarmColorGradientEndPoint_highlighted.set(computeGradientEndPoint(majorAlarmColor_highlighted.get())); + this.majorAlarmColorGradientStartPoint_highlighted = computeGradientStartPoint(majorAlarmColor_highlighted); + this.majorAlarmColorGradientEndPoint_highlighted = computeGradientEndPoint(majorAlarmColor_highlighted); updateActiveColors(); }); @@ -446,19 +445,19 @@ public void setMajorAlarmColor(Color majorAlarmColor) { public void setNeedleWidth(int needleWidth) { withWriteLock(() -> { - this.needleWidth.set(needleWidth); + this.needleWidth = needleWidth; }); } public void setNeedleColor(Color needleColor) { withWriteLock(() -> { - this.needleColor.set(needleColor); + this.needleColor = needleColor; }); } public void setShowUnits(boolean newValue) { withWriteLock(() -> { - showUnits.set(newValue); + showUnits = newValue; redraw(); }); } @@ -466,7 +465,7 @@ public void setShowUnits(boolean newValue) { public void setUnits(String newValue) { withWriteLock(() -> { if (!units.equals(newValue)) { - units.set(newValue); + units = newValue; redraw(); } }); @@ -480,7 +479,7 @@ private void logNewWarningIfDifferent(WARNING oldWarning, WARNING newWarning) { public void setShowLimits(boolean newValue) { withWriteLock(() -> { WARNING oldWarning = determineWarning(); - showLimits.set(newValue); + showLimits = newValue; WARNING newWarning = determineWarning(); logNewWarningIfDifferent(oldWarning, newWarning); redraw(); @@ -489,7 +488,7 @@ public void setShowLimits(boolean newValue) { public void setRange(double minimum, double maximum, boolean validRange) { withWriteLock(() -> { - this.validRange.set(validRange); + this.validRange = validRange; linearMeterScale.setValueRange(minimum, maximum); }); redraw(); @@ -498,24 +497,24 @@ public void setRange(double minimum, double maximum, boolean validRange) { public void setMinMaxTolerance(double minMaxTolerance) { withWriteLock(() -> { WARNING oldWarning = determineWarning(); - this.minMaxTolerance.set(minMaxTolerance); + this.minMaxTolerance = minMaxTolerance; WARNING newWarning = determineWarning(); logNewWarningIfDifferent(oldWarning, newWarning); - redrawIndicator(currentValue.get(), newWarning); + redrawIndicator(currentValue, newWarning); }); } public double getLoLo() { AtomicReference returnValue = new AtomicReference<>(); withReadLock(() -> { - returnValue.set(loLo.get()); + returnValue.set(loLo); }); return returnValue.get(); } public void setLoLo(double loLo) { withWriteLock(() -> { - this.loLo.set(loLo); + this.loLo = loLo; layout(); updateMeterBackground(); }); @@ -524,14 +523,14 @@ public void setLoLo(double loLo) { public double getLow() { AtomicReference returnValue = new AtomicReference<>(); withReadLock(() -> { - returnValue.set(low.get()); + returnValue.set(low); }); return returnValue.get(); } public void setLow(double low) { withWriteLock(() -> { - this.low.set(low); + this.low = low; layout(); updateMeterBackground(); }); @@ -540,14 +539,14 @@ public void setLow(double low) { public double getHigh() { AtomicReference returnValue = new AtomicReference<>(); withReadLock(() -> { - returnValue.set(high.get()); + returnValue.set(high); }); return returnValue.get(); } public void setHigh(double high) { withWriteLock(() -> { - this.high.set(high); + this.high = high; layout(); updateMeterBackground(); }); @@ -556,33 +555,33 @@ public void setHigh(double high) { public double getHiHi() { AtomicReference returnValue = new AtomicReference<>(); withReadLock(() -> { - returnValue.set(hiHi.get()); + returnValue.set(hiHi); }); return returnValue.get(); } public void setHiHi(double hiHi) { withWriteLock(() -> { - this.hiHi.set(hiHi); + this.hiHi = hiHi; layout(); updateMeterBackground(); }); } - private AtomicReference knobColor = new AtomicReference(new Color(0, 0, 0, 255)); + private Color knobColor = new Color(0, 0, 0, 255); public void setKnobColor(Color knobColor) { withWriteLock(() -> { - this.knobColor.set(knobColor); + this.knobColor = knobColor; requestLayout(); }); } - private AtomicInteger knobSize = new AtomicInteger(1); + private int knobSize = 1; public void setKnobSize(int knobSize) { withWriteLock(() -> { - this.knobSize.set(knobSize); + this.knobSize = knobSize; requestLayout(); }); } @@ -590,16 +589,16 @@ public void setKnobSize(int knobSize) { private void redraw() { withReadLock(() -> { updateMeterBackground(); - redrawIndicator(currentValue.get(), determineWarning()); + redrawIndicator(currentValue, determineWarning()); }); } - private AtomicReference, Pair>>> meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference = new AtomicReference<>(Optional.empty()); + private Optional, Pair>> meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference = Optional.empty(); /** * Redraw on UI thread by adding needle to 'meter_background' */ private void redrawIndicator(double value, WARNING warning) { withReadLock(() -> { - var meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference.get(); + var meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference; if (meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.isEmpty()) { return; } @@ -619,7 +618,7 @@ private void redrawIndicator(double value, WARNING warning) { // Add needle & label Graphics2D gc = combined.createGraphics(); - DisplayMode displayMode = this.displayMode.get(); + DisplayMode displayMode = this.displayMode; if (displayMode.equals(DisplayMode.NEEDLE)) { drawValue(gc, value); } @@ -633,7 +632,7 @@ else if (displayMode.equals(DisplayMode.BAR)) { if (warning != WARNING.NONE) { drawWarningText(gc, warning.toString()); } - if (showUnits.get()) { + if (showUnits) { drawUnit(gc); } @@ -665,7 +664,7 @@ public void setSize(int width, int height) { /** @param color Foreground (labels, tick marks) color */ public void setForeground(javafx.scene.paint.Color color) { withWriteLock(() -> { - foreground.set(GraphicsUtils.convert(color)); + foreground = GraphicsUtils.convert(color); linearMeterScale.setColor(color); }); } @@ -674,7 +673,7 @@ public void setForeground(javafx.scene.paint.Color color) { public void setBackground(javafx.scene.paint.Color color) { withWriteLock(() -> { - background.set(GraphicsUtils.convert(color)); + background = GraphicsUtils.convert(color); }); } @@ -687,32 +686,32 @@ public void setFont(javafx.scene.text.Font font) }); } - private AtomicBoolean showWarnings = new AtomicBoolean(true); + private boolean showWarnings = true; public void setShowWarnings(boolean showWarnings) { withWriteLock(() -> { - this.showWarnings.set(showWarnings); + this.showWarnings = showWarnings; }); } - private AtomicBoolean lag = new AtomicBoolean(false); - private AtomicBoolean isValueWaitingToBeDrawn = new AtomicBoolean(false); - private AtomicReference valueWaitingToBeDrawn = new AtomicReference<>(Double.NaN); + private boolean lag = false; + private boolean isValueWaitingToBeDrawn = false; + private double valueWaitingToBeDrawn = Double.NaN; /** @param newValue Current value */ public void setCurrentValue(double newValue) { withWriteLock(() -> { - valueWaitingToBeDrawn.set(newValue); + valueWaitingToBeDrawn = newValue; - if (isValueWaitingToBeDrawn.get()) { - lag.set(true); + if (isValueWaitingToBeDrawn) { + lag = true; } else { - isValueWaitingToBeDrawn.set(true); + isValueWaitingToBeDrawn = true; - drawNewValue(valueWaitingToBeDrawn.get()); - isValueWaitingToBeDrawn.set(false); - lag.set(false); + drawNewValue(valueWaitingToBeDrawn); + isValueWaitingToBeDrawn = false; + lag = false; } }); } @@ -732,14 +731,14 @@ private void drawNewValue(double newValue) { withWriteLock(() -> { WARNING oldWarning = determineWarning(); AtomicReference newValueAtomicReference = new AtomicReference<>(newValue); // Workaround, since captured variables need to be effectively final in Java. - double oldValue = currentValue.get(); + double oldValue = currentValue; double oldIndicatorPosition = computeIndicatorPosition(oldValue); - currentValue.set(newValueAtomicReference.get()); + currentValue = newValueAtomicReference.get(); - if (newValueAtomicReference.get() > linearMeterScale.getValueRange().getHigh() && newValueAtomicReference.get() <= linearMeterScale.getValueRange().getHigh() + minMaxTolerance.get()) { + if (newValueAtomicReference.get() > linearMeterScale.getValueRange().getHigh() && newValueAtomicReference.get() <= linearMeterScale.getValueRange().getHigh() + minMaxTolerance) { newValueAtomicReference.set(linearMeterScale.getValueRange().getHigh()); } - if (newValueAtomicReference.get() < linearMeterScale.getValueRange().getLow() && newValueAtomicReference.get() >= linearMeterScale.getValueRange().getLow() - minMaxTolerance.get()) { + if (newValueAtomicReference.get() < linearMeterScale.getValueRange().getLow() && newValueAtomicReference.get() >= linearMeterScale.getValueRange().getLow() - minMaxTolerance) { newValueAtomicReference.set(linearMeterScale.getValueRange().getLow()); } @@ -763,22 +762,22 @@ else if (!Double.isNaN(oldValue)) { private WARNING determineWarning() { AtomicReference returnValue = new AtomicReference<>(); withReadLock(() -> { - if (!showWarnings.get()) { + if (!showWarnings) { returnValue.set(WARNING.NONE); } - else if (lag.get()) { + else if (lag) { returnValue.set(WARNING.LAG); } - else if (showUnits.get() && units.get().equals("")) { + else if (showUnits && units.equals("")) { returnValue.set(WARNING.NO_UNIT); } - else if (!validRange.get()) { + else if (!validRange) { returnValue.set(WARNING.MIN_AND_MAX_NOT_DEFINED); } - else if (currentValue.get() < linearMeterScale.getValueRange().getLow() - minMaxTolerance.get()) { + else if (currentValue < linearMeterScale.getValueRange().getLow() - minMaxTolerance) { returnValue.set(WARNING.VALUE_LESS_THAN_MIN); } - else if (currentValue.get() > linearMeterScale.getValueRange().getHigh() + minMaxTolerance.get()) { + else if (currentValue > linearMeterScale.getValueRange().getHigh() + minMaxTolerance) { returnValue.set(WARNING.VALUE_GREATER_THAN_MAX); } else { @@ -802,7 +801,7 @@ private void requestLayout() { withReadLock(() -> { updateMeterBackground(); - redrawIndicator(currentValue.get(), determineWarning()); + redrawIndicator(currentValue, determineWarning()); }); } @@ -850,7 +849,7 @@ private void updateMeterBackground() linearMeterScale.computeTicks(gc); computeLayout(); - gc.setBackground(background.get()); + gc.setBackground(background); gc.clearRect(0, 0, width, height); linearMeterScale.paint(gc, new Rectangle(0,0,0,0)); @@ -861,7 +860,7 @@ private void updateMeterBackground() WritableImage awtJFXConvertBuffer = new WritableImage(linearMeterScale.getBounds().width, linearMeterScale.getBounds().height); Pair imageBuffersForWriting = new Pair<>(combined, awtJFXConvertBuffer); Pair widthAndHeight = new Pair<>(width, height); - meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference.set(Optional.of(new ImmutableTriple<>(image, imageBuffersForWriting, widthAndHeight))); + meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference = Optional.of(new ImmutableTriple<>(image, imageBuffersForWriting, widthAndHeight)); } }); } @@ -869,20 +868,20 @@ private void updateMeterBackground() private void paintMeter(Graphics2D graphics) { withReadLock(() -> { Color color = graphics.getColor(); - if (showLimits.get()) { - if (isHighlightActiveRegionEnabled.get()) { - paintRectangle(graphics, normalRectangle, normalStatusActiveColor_lowlighted.get()); - paintRectangle(graphics, lowRectangle, minorAlarmActiveColor_lowlighted.get()); - paintRectangle(graphics, highRectangle, minorAlarmActiveColor_lowlighted.get()); - paintRectangle(graphics, loLoRectangle, majorAlarmActiveColor_lowlighted.get()); - paintRectangle(graphics, hiHiRectangle, majorAlarmActiveColor_lowlighted.get()); + if (showLimits) { + if (isHighlightActiveRegionEnabled) { + paintRectangle(graphics, normalRectangle, normalStatusActiveColor_lowlighted); + paintRectangle(graphics, lowRectangle, minorAlarmActiveColor_lowlighted); + paintRectangle(graphics, highRectangle, minorAlarmActiveColor_lowlighted); + paintRectangle(graphics, loLoRectangle, majorAlarmActiveColor_lowlighted); + paintRectangle(graphics, hiHiRectangle, majorAlarmActiveColor_lowlighted); } else { - paintRectangle(graphics, normalRectangle, normalStatusActiveColor_highlighted.get()); - paintRectangle(graphics, lowRectangle, minorAlarmActiveColor_highlighted.get()); - paintRectangle(graphics, highRectangle, minorAlarmActiveColor_highlighted.get()); - paintRectangle(graphics, loLoRectangle, majorAlarmActiveColor_highlighted.get()); - paintRectangle(graphics, hiHiRectangle, majorAlarmActiveColor_highlighted.get()); + paintRectangle(graphics, normalRectangle, normalStatusActiveColor_highlighted); + paintRectangle(graphics, lowRectangle, minorAlarmActiveColor_highlighted); + paintRectangle(graphics, highRectangle, minorAlarmActiveColor_highlighted); + paintRectangle(graphics, loLoRectangle, majorAlarmActiveColor_highlighted); + paintRectangle(graphics, hiHiRectangle, majorAlarmActiveColor_highlighted); } } else { @@ -891,7 +890,7 @@ private void paintMeter(Graphics2D graphics) { marginAbove, linearMeterScale.getBounds().width - marginLeft - marginRight, linearMeterScale.getBounds().height - marginAbove - marginBelow), - normalStatusActiveColor_lowlighted.get()); + normalStatusActiveColor_lowlighted); } graphics.setColor(color); }); @@ -931,22 +930,22 @@ private void drawValue(Graphics2D gc, double value) { gc.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); gc.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - if (showLimits.get()) { - if (isHighlightActiveRegionEnabled.get()) { - if (value <= loLo.get()) { - paintRectangle(gc, loLoRectangle, majorAlarmColor_highlighted.get()); + if (showLimits) { + if (isHighlightActiveRegionEnabled) { + if (value <= loLo) { + paintRectangle(gc, loLoRectangle, majorAlarmColor_highlighted); } - else if (value >= hiHi.get()) { - paintRectangle(gc, hiHiRectangle, majorAlarmColor_highlighted.get()); + else if (value >= hiHi) { + paintRectangle(gc, hiHiRectangle, majorAlarmColor_highlighted); } - else if (value <= low.get() && value > loLo.get()) { - paintRectangle(gc, lowRectangle, minorAlarmActiveColor_highlighted.get()); + else if (value <= low && value > loLo) { + paintRectangle(gc, lowRectangle, minorAlarmActiveColor_highlighted); } - else if (value >= high.get() && value < hiHi.get()) { - paintRectangle(gc, highRectangle, minorAlarmActiveColor_highlighted.get()); + else if (value >= high && value < hiHi) { + paintRectangle(gc, highRectangle, minorAlarmActiveColor_highlighted); } else { - paintRectangle(gc, normalRectangle, normalStatusActiveColor_highlighted.get()); + paintRectangle(gc, normalRectangle, normalStatusActiveColor_highlighted); } } } @@ -956,27 +955,27 @@ else if (value >= high.get() && value < hiHi.get()) { int currentIndicatorPosition = computeIndicatorPosition(value); - if (knobSize.get() > 0) { - int[] XVal = { currentIndicatorPosition - (int) Math.round((1.0 * knobSize.get()) / 4.0), - currentIndicatorPosition + (int) Math.round((1.0 * knobSize.get()) / 4.0), + if (knobSize > 0) { + int[] XVal = { currentIndicatorPosition - (int) Math.round((1.0 * knobSize) / 4.0), + currentIndicatorPosition + (int) Math.round((1.0 * knobSize) / 4.0), currentIndicatorPosition }; int[] YVal = { 0, 0, marginAbove - 2 }; gc.setStroke(AxisPart.TICK_STROKE); - gc.setColor(knobColor.get()); + gc.setColor(knobColor); gc.fillPolygon(XVal, YVal, 3); - gc.setColor(knobColor.get()); + gc.setColor(knobColor); gc.drawPolygon(XVal, YVal, 3); } - if (needleWidth.get() > 0) { - gc.setStroke(new BasicStroke((float) needleWidth.get())); + if (needleWidth > 0) { + gc.setStroke(new BasicStroke((float) needleWidth)); gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); - gc.setPaint(needleColor.get()); + gc.setPaint(needleColor); - int y1 = marginAbove + needleWidth.get() / 2 + 1; - int y2 = linearMeterScale.getBounds().height - marginBelow - (needleWidth.get() - 1) / 2 - 1; + int y1 = marginAbove + needleWidth / 2 + 1; + int y2 = linearMeterScale.getBounds().height - marginBelow - (needleWidth - 1) / 2 - 1; gc.drawLine(currentIndicatorPosition, y1, currentIndicatorPosition, y2); } @@ -986,27 +985,27 @@ else if (value >= high.get() && value < hiHi.get()) { int currentIndicatorPosition = computeIndicatorPosition(value); - if (knobSize.get() > 0) { - int[] YVal = { currentIndicatorPosition + (int) Math.round((1.0 * knobSize.get() / 4.0)), - currentIndicatorPosition - (int) Math.round((1.0 * knobSize.get() / 4.0)), + if (knobSize > 0) { + int[] YVal = { currentIndicatorPosition + (int) Math.round((1.0 * knobSize / 4.0)), + currentIndicatorPosition - (int) Math.round((1.0 * knobSize / 4.0)), currentIndicatorPosition }; int[] XVal = { 0, 0, marginLeft - 2 }; gc.setStroke(AxisPart.TICK_STROKE); - gc.setColor(knobColor.get()); + gc.setColor(knobColor); gc.fillPolygon(XVal, YVal, 3); - gc.setColor(knobColor.get()); + gc.setColor(knobColor); gc.drawPolygon(XVal, YVal, 3); } - if (needleWidth.get() > 0) { - gc.setStroke(new BasicStroke((float) needleWidth.get())); + if (needleWidth > 0) { + gc.setStroke(new BasicStroke((float) needleWidth)); gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); - gc.setPaint(needleColor.get()); + gc.setPaint(needleColor); - int x1 = marginLeft + (needleWidth.get())/2 + 1; - int x2 = linearMeterScale.getBounds().width - marginRight - (needleWidth.get()+1)/2; + int x1 = marginLeft + (needleWidth)/2 + 1; + int x2 = linearMeterScale.getBounds().width - marginRight - (needleWidth+1)/2; gc.drawLine(x1, currentIndicatorPosition, x2, currentIndicatorPosition); } @@ -1034,72 +1033,72 @@ private void drawBar(Graphics2D gc, double value) { gc.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); gc.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - if (showLimits.get()) { - if (isHighlightActiveRegionEnabled.get()) { - if (value <= loLo.get()) { - paintRectangle(gc, loLoRectangle, majorAlarmColor_highlighted.get()); + if (showLimits) { + if (isHighlightActiveRegionEnabled) { + if (value <= loLo) { + paintRectangle(gc, loLoRectangle, majorAlarmColor_highlighted); } - else if (value >= hiHi.get()) { - paintRectangle(gc, hiHiRectangle, majorAlarmColor_highlighted.get()); + else if (value >= hiHi) { + paintRectangle(gc, hiHiRectangle, majorAlarmColor_highlighted); } - else if (value <= low.get() && value > loLo.get()) { - paintRectangle(gc, lowRectangle, minorAlarmActiveColor_highlighted.get()); + else if (value <= low && value > loLo) { + paintRectangle(gc, lowRectangle, minorAlarmActiveColor_highlighted); } - else if (value >= high.get() && value < hiHi.get()) { - paintRectangle(gc, highRectangle, minorAlarmActiveColor_highlighted.get()); + else if (value >= high && value < hiHi) { + paintRectangle(gc, highRectangle, minorAlarmActiveColor_highlighted); } else { - paintRectangle(gc, normalRectangle, normalStatusActiveColor_highlighted.get()); + paintRectangle(gc, normalRectangle, normalStatusActiveColor_highlighted); } } } if (linearMeterScale.isHorizontal()) { if (value >= linearMeterScale.getValueRange().getLow()) { - if (isHighlightActiveRegionEnabled.get()) { - if (value <= loLo.get()) { - gc.setPaint(majorAlarmColor_highlighted.get()); + if (isHighlightActiveRegionEnabled) { + if (value <= loLo) { + gc.setPaint(majorAlarmColor_highlighted); } - else if (value >= hiHi.get()) { - gc.setPaint(majorAlarmColor_highlighted.get()); + else if (value >= hiHi) { + gc.setPaint(majorAlarmColor_highlighted); } - else if (value <= low.get() && value > loLo.get()) { - gc.setPaint(minorAlarmActiveColor_highlighted.get()); + else if (value <= low && value > loLo) { + gc.setPaint(minorAlarmActiveColor_highlighted); } - else if (value >= high.get() && value < hiHi.get()) { - gc.setPaint(minorAlarmActiveColor_highlighted.get()); + else if (value >= high && value < hiHi) { + gc.setPaint(minorAlarmActiveColor_highlighted); } else { - gc.setPaint(needleColor.get()); + gc.setPaint(needleColor); } } else { - gc.setPaint(needleColor.get()); + gc.setPaint(needleColor); } int currentIndicatorPosition = computeIndicatorPosition(value); gc.fillRect(marginLeft+1, marginAbove+1, (int) currentIndicatorPosition, meterBreadth-1); } } else { if (value >= linearMeterScale.getValueRange().getLow() && value <= linearMeterScale.getValueRange().getHigh()) { - if (isHighlightActiveRegionEnabled.get()) { - if (value <= loLo.get()) { - gc.setPaint(majorAlarmColor_highlighted.get()); + if (isHighlightActiveRegionEnabled) { + if (value <= loLo) { + gc.setPaint(majorAlarmColor_highlighted); } - else if (value >= hiHi.get()) { - gc.setPaint(majorAlarmColor_highlighted.get()); + else if (value >= hiHi) { + gc.setPaint(majorAlarmColor_highlighted); } - else if (value <= low.get() && value > loLo.get()) { - gc.setPaint(minorAlarmActiveColor_highlighted.get()); + else if (value <= low && value > loLo) { + gc.setPaint(minorAlarmActiveColor_highlighted); } - else if (value >= high.get() && value < hiHi.get()) { - gc.setPaint(minorAlarmActiveColor_highlighted.get()); + else if (value >= high && value < hiHi) { + gc.setPaint(minorAlarmActiveColor_highlighted); } else { - gc.setPaint(needleColor.get()); + gc.setPaint(needleColor); } } else { - gc.setPaint(needleColor.get()); + gc.setPaint(needleColor); } int currentIndicatorPosition = computeIndicatorPosition(value); gc.fillRect(marginLeft+1, currentIndicatorPosition+1, (int) meterBreadth-1, linearMeterScale.getBounds().height-currentIndicatorPosition-marginBelow-1); @@ -1117,7 +1116,7 @@ public void dispose() { withWriteLock(() -> { // Release memory ASAP - meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference.set(Optional.empty()); + meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference = Optional.empty(); }); } @@ -1126,7 +1125,7 @@ public void setHorizontal(boolean horizontal) { linearMeterScale.setHorizontal(horizontal); redrawLinearMeterScale(); updateMeterBackground(); - redrawIndicator(currentValue.get(), determineWarning()); + redrawIndicator(currentValue, determineWarning()); }); } @@ -1235,7 +1234,7 @@ private void paintRectangle(Graphics2D gc, Rectangle rectangle, Paint paint){ //Draw border of rectangle. //TODO: can this be included in the paint? - gc.setColor(foreground.get()); + gc.setColor(foreground); gc.draw(rectangle); gc.setColor(oldColor); @@ -1261,10 +1260,10 @@ private void layout() { double displayedHiHi; double displayedHigh; - double loLoValue = loLo.get(); - double lowValue = low.get(); - double highValue = high.get(); - double hiHiValue = hiHi.get(); + double loLoValue = loLo; + double lowValue = low; + double highValue = high; + double hiHiValue = hiHi; displayedLoLo = Double.isFinite(loLoValue) ? Math.max(loLoValue, linearMeterScale.getValueRange().getLow()) : linearMeterScale.getValueRange().getLow(); displayedLow = Double.isFinite(lowValue) ? Math.max(Math.max(lowValue, linearMeterScale.getValueRange().getLow()), displayedLoLo) : linearMeterScale.getValueRange().getLow(); @@ -1279,7 +1278,7 @@ private void layout() { } if (linearMeterScale.isHorizontal()) { - int knobSizeValue = knobSize.get(); + int knobSizeValue = knobSize; marginAbove = knobSizeValue >= 1 ? knobSizeValue + 2 : 0; if (linearMeterScale.isVisible() && fontMetrics != null) { var majorTicks = linearMeterScale.getTicks().getMajorTicks(); @@ -1299,7 +1298,7 @@ private void layout() { marginBelow = 1; } - if (showUnits.get() && fontMetrics != null) { + if (showUnits && fontMetrics != null) { marginBelow += 1 + fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent(); } @@ -1338,7 +1337,7 @@ private void layout() { meterBreadth); } else { - int knobSizeValue = knobSize.get(); + int knobSizeValue = knobSize; marginLeft = knobSizeValue >= 1 ? knobSizeValue + 2 : 0; if (linearMeterScale.isVisible() && fontMetrics != null) { int maxTickLabelWidth = 0; @@ -1357,7 +1356,7 @@ private void layout() { marginBelow = 1; } - if (showUnits.get() && fontMetrics != null) { + if (showUnits && fontMetrics != null) { marginBelow += 1 + fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent(); } From c208211f085211129ab55d7c4d03413111b00677 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Wed, 13 Aug 2025 16:40:14 +0200 Subject: [PATCH 12/35] CSSTUDIO-3347 Change getters and determineWarning() to use a writeLock instead to use ' T withReadLock()' instead of 'void withReadLock()' --- .../widgets/linearmeter/RTLinearMeter.java | 49 +++++++------------ 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index 99d6915be6..99293b808b 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -22,7 +22,6 @@ import java.io.IOException; import java.util.Arrays; import java.util.Optional; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -227,11 +226,9 @@ public void refreshPlotPart(PlotPart plotPart) { } private double minMaxTolerance = 0.0; public boolean getValidRange() { - AtomicBoolean returnValue = new AtomicBoolean(false); - withReadLock(() -> { - returnValue.set(validRange); + return withReadLock(() -> { + return validRange; }); - return returnValue.get(); } /** Optional scale of this linear meter */ @@ -505,11 +502,9 @@ public void setMinMaxTolerance(double minMaxTolerance) { } public double getLoLo() { - AtomicReference returnValue = new AtomicReference<>(); - withReadLock(() -> { - returnValue.set(loLo); + return withReadLock(() -> { + return loLo; }); - return returnValue.get(); } public void setLoLo(double loLo) { @@ -521,11 +516,9 @@ public void setLoLo(double loLo) { } public double getLow() { - AtomicReference returnValue = new AtomicReference<>(); - withReadLock(() -> { - returnValue.set(low); + return withReadLock(() -> { + return low; }); - return returnValue.get(); } public void setLow(double low) { @@ -537,11 +530,9 @@ public void setLow(double low) { } public double getHigh() { - AtomicReference returnValue = new AtomicReference<>(); - withReadLock(() -> { - returnValue.set(high); + return withReadLock(() -> { + return high; }); - return returnValue.get(); } public void setHigh(double high) { @@ -553,11 +544,9 @@ public void setHigh(double high) { } public double getHiHi() { - AtomicReference returnValue = new AtomicReference<>(); - withReadLock(() -> { - returnValue.set(hiHi); + return withReadLock(() -> { + return hiHi; }); - return returnValue.get(); } public void setHiHi(double hiHi) { @@ -760,31 +749,29 @@ else if (!Double.isNaN(oldValue)) { } private WARNING determineWarning() { - AtomicReference returnValue = new AtomicReference<>(); - withReadLock(() -> { + return withReadLock(() -> { if (!showWarnings) { - returnValue.set(WARNING.NONE); + return WARNING.NONE; } else if (lag) { - returnValue.set(WARNING.LAG); + return WARNING.LAG; } else if (showUnits && units.equals("")) { - returnValue.set(WARNING.NO_UNIT); + return WARNING.NO_UNIT; } else if (!validRange) { - returnValue.set(WARNING.MIN_AND_MAX_NOT_DEFINED); + return WARNING.MIN_AND_MAX_NOT_DEFINED; } else if (currentValue < linearMeterScale.getValueRange().getLow() - minMaxTolerance) { - returnValue.set(WARNING.VALUE_LESS_THAN_MIN); + return WARNING.VALUE_LESS_THAN_MIN; } else if (currentValue > linearMeterScale.getValueRange().getHigh() + minMaxTolerance) { - returnValue.set(WARNING.VALUE_GREATER_THAN_MAX); + return WARNING.VALUE_GREATER_THAN_MAX; } else { - returnValue.set(WARNING.NONE); + return WARNING.NONE; } }); - return returnValue.get(); } /** @param visible Whether the scale must be displayed or not. */ From d33fb731ae51cdeb135abc1db7309e986d323318 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Wed, 13 Aug 2025 16:44:26 +0200 Subject: [PATCH 13/35] CSSTUDIO-3347 Change methods to use the write lock instead of read locks. --- .../display/extra/widgets/linearmeter/RTLinearMeter.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index 99293b808b..933085c320 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -576,7 +576,7 @@ public void setKnobSize(int knobSize) { } private void redraw() { - withReadLock(() -> { + withWriteLock(() -> { updateMeterBackground(); redrawIndicator(currentValue, determineWarning()); }); @@ -786,7 +786,7 @@ public void setScaleVisible (boolean visible) /** Request a complete redraw with new layout */ private void requestLayout() { - withReadLock(() -> { + withWriteLock(() -> { updateMeterBackground(); redrawIndicator(currentValue, determineWarning()); }); @@ -817,7 +817,7 @@ private void computeLayout() */ private void updateMeterBackground() { - withReadLock(() -> { + withWriteLock(() -> { int width = linearMeterScale.getBounds().width; int height = linearMeterScale.getBounds().height; From 7feba92167215a34c21145c861cb29fad00ad12f Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Thu, 14 Aug 2025 09:13:43 +0200 Subject: [PATCH 14/35] Use a record instead of an ImmutableTriple to represent the image buffers. --- .../widgets/linearmeter/RTLinearMeter.java | 91 ++++++++++--------- 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index 933085c320..f7af60f58d 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -581,57 +581,66 @@ private void redraw() { redrawIndicator(currentValue, determineWarning()); }); } - private Optional, Pair>> meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference = Optional.empty(); + + private record ImageBuffers(BufferedImage meterBackground, + BufferedImage combinedBufferedImage, + WritableImage awtJFXConvertBuffer, + int width, + int height) { } + + private Optional maybeImageBuffers = Optional.empty(); /** * Redraw on UI thread by adding needle to 'meter_background' */ private void redrawIndicator(double value, WARNING warning) { withReadLock(() -> { - var meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference; - if (meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.isEmpty()) { + if (maybeImageBuffers.isEmpty()) { return; } - BufferedImage meterBackground = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getLeft(); - BufferedImage combined = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getMiddle().getKey(); - var awtJFXConvertBuffer = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getMiddle().getValue(); - int width = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getRight().getKey(); - int height = meterBackgroundCombinedBufferedImageAndAWTJFXConvertBuffer.get().getRight().getValue(); - if (meterBackground.getType() != BufferedImage.TYPE_INT_ARGB) { - throw new IllegalPathStateException("Need TYPE_INT_ARGB for direct buffer access, not " + meterBackground.getType()); - } + else { + ImageBuffers imageBuffers = maybeImageBuffers.get(); + BufferedImage meterBackground = imageBuffers.meterBackground(); + BufferedImage combined = imageBuffers.combinedBufferedImage(); + WritableImage awtJFXConvertBuffer = imageBuffers.awtJFXConvertBuffer; + int width = imageBuffers.width(); + int height = imageBuffers.height(); + if (meterBackground.getType() != BufferedImage.TYPE_INT_ARGB) { + throw new IllegalPathStateException("Need TYPE_INT_ARGB for direct buffer access, not " + meterBackground.getType()); + } - int[] src = ((DataBufferInt) meterBackground.getRaster().getDataBuffer()).getData(); - int[] dest = ((DataBufferInt) combined.getRaster().getDataBuffer()).getData(); - System.arraycopy(src, 0, dest, 0, width * height); + int[] src = ((DataBufferInt) meterBackground.getRaster().getDataBuffer()).getData(); + int[] dest = ((DataBufferInt) combined.getRaster().getDataBuffer()).getData(); + System.arraycopy(src, 0, dest, 0, width * height); - // Add needle & label - Graphics2D gc = combined.createGraphics(); + // Add needle & label + Graphics2D gc = combined.createGraphics(); - DisplayMode displayMode = this.displayMode; - if (displayMode.equals(DisplayMode.NEEDLE)) { - drawValue(gc, value); - } - else if (displayMode.equals(DisplayMode.BAR)) { - drawBar(gc, value); - } - else { - throw new RuntimeException("Unhandled case"); - } + DisplayMode displayMode = this.displayMode; + if (displayMode.equals(DisplayMode.NEEDLE)) { + drawValue(gc, value); + } + else if (displayMode.equals(DisplayMode.BAR)) { + drawBar(gc, value); + } + else { + throw new RuntimeException("Unhandled case"); + } - if (warning != WARNING.NONE) { - drawWarningText(gc, warning.toString()); - } - if (showUnits) { - drawUnit(gc); - } + if (warning != WARNING.NONE) { + drawWarningText(gc, warning.toString()); + } + if (showUnits) { + drawUnit(gc); + } - // Convert to JFX image and show - runOnJavaFXThread(() -> { - awtJFXConvertBuffer.getPixelWriter().setPixels(0, 0, width, height, PixelFormat.getIntArgbInstance(), dest, 0, width); - setImage(awtJFXConvertBuffer); - }); + // Convert to JFX image and show + runOnJavaFXThread(() -> { + awtJFXConvertBuffer.getPixelWriter().setPixels(0, 0, width, height, PixelFormat.getIntArgbInstance(), dest, 0, width); + setImage(awtJFXConvertBuffer); + }); - logger.log(Level.FINE, "Redraw meter"); + logger.log(Level.FINE, "Redraw meter"); + } }); } @@ -845,9 +854,7 @@ private void updateMeterBackground() { BufferedImage combined = new BufferedImage(linearMeterScale.getBounds().width, linearMeterScale.getBounds().height, BufferedImage.TYPE_INT_ARGB); WritableImage awtJFXConvertBuffer = new WritableImage(linearMeterScale.getBounds().width, linearMeterScale.getBounds().height); - Pair imageBuffersForWriting = new Pair<>(combined, awtJFXConvertBuffer); - Pair widthAndHeight = new Pair<>(width, height); - meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference = Optional.of(new ImmutableTriple<>(image, imageBuffersForWriting, widthAndHeight)); + maybeImageBuffers = Optional.of(new ImageBuffers(image, combined, awtJFXConvertBuffer, width, height)); } }); } @@ -1103,7 +1110,7 @@ public void dispose() { withWriteLock(() -> { // Release memory ASAP - meterBackgroundCombinedBufferedImageAndAWTJFXConvertBufferAtomicReference = Optional.empty(); + maybeImageBuffers = Optional.empty(); }); } From 71efd52762f418af23a52d36d58a5103cdd91aca Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Thu, 14 Aug 2025 09:17:24 +0200 Subject: [PATCH 15/35] CSSTUDIO-3347 Acquire write lock when writing to awtJFXConvertBuffer and calling ImageView.setImage(). --- .../display/extra/widgets/linearmeter/RTLinearMeter.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index f7af60f58d..89c1bcaced 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -635,8 +635,10 @@ else if (displayMode.equals(DisplayMode.BAR)) { // Convert to JFX image and show runOnJavaFXThread(() -> { - awtJFXConvertBuffer.getPixelWriter().setPixels(0, 0, width, height, PixelFormat.getIntArgbInstance(), dest, 0, width); - setImage(awtJFXConvertBuffer); + withWriteLock(() -> { + awtJFXConvertBuffer.getPixelWriter().setPixels(0, 0, width, height, PixelFormat.getIntArgbInstance(), dest, 0, width); + setImage(awtJFXConvertBuffer); + }); }); logger.log(Level.FINE, "Redraw meter"); From 9aacd17f70dfba800b48d3de7a32a5ef44e6a6d4 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Thu, 14 Aug 2025 09:24:42 +0200 Subject: [PATCH 16/35] CSSTUDIO-3347 Use meter.withWriteLock() instead of synchronized(meter) { ... } in 'LinearMeterRepresentation'. --- .../linearmeter/LinearMeterRepresentation.java | 16 ++++++++-------- .../extra/widgets/linearmeter/RTLinearMeter.java | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java index 9b3673db0a..73fe8441ff 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java @@ -160,26 +160,26 @@ protected void registerListeners() addWidgetPropertyListener(model_widget.propMinimum(), (property, old_value, new_value) -> { - synchronized (meter) { + meter.withWriteLock(() -> { boolean validRange = Double.isFinite(new_value) && Double.isFinite(model_widget.propMaximum().getValue()); meter.setRange(new_value, model_widget.propMaximum().getValue(), validRange); if (toolkit.isEditMode() && validRange) { meter.setCurrentValue((new_value + model_widget.propMaximum().getValue()) / 2.0); } - } + }); layoutChanged(null, null, null); }); addWidgetPropertyListener(model_widget.propMaximum(), (property, old_value, new_value) -> { - synchronized (meter) { + meter.withWriteLock(() -> { boolean validRange = Double.isFinite(new_value) && Double.isFinite(model_widget.propMinimum().getValue()); meter.setRange(model_widget.propMinimum().getValue(), new_value, validRange); if (toolkit.isEditMode() && validRange) { meter.setCurrentValue((new_value + model_widget.propMinimum().getValue()) / 2.0); } - } + }); layoutChanged(null, null, null); }); @@ -265,13 +265,13 @@ private void orientationChanged(WidgetProperty prop, Boolean old, Boole { if (toolkit.isEditMode()) { - synchronized(meter) { + meter.withWriteLock(() -> { int w = model_widget.propWidth().getValue(); int h = model_widget.propHeight().getValue(); model_widget.propWidth().setValue(h); model_widget.propHeight().setValue(w); meter.setHorizontal(horizontal); - } + }); layoutChanged(null, null, null); } } @@ -378,7 +378,7 @@ public void updateChanges() if (dirty_look.checkAndClear()) { - synchronized (meter) { + meter.withWriteLock(() -> { boolean horizontal = model_widget.propDisplayHorizontal().getValue(); int width = model_widget.propWidth().getValue(); int height = model_widget.propHeight().getValue(); @@ -394,7 +394,7 @@ public void updateChanges() jfx_node.setPrefSize(width, height); meter.setSize(width, height); - } + }); } } diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index 89c1bcaced..b7ca8504b6 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -136,7 +136,7 @@ public RTLinearMeter(double initialValue, } private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); - private void withReadLock(Runnable runnable) { + protected void withReadLock(Runnable runnable) { readWriteLock.readLock().lock(); try { runnable.run(); @@ -144,7 +144,7 @@ private void withReadLock(Runnable runnable) { readWriteLock.readLock().unlock(); } } - private T withReadLock(Supplier supplier) { + protected T withReadLock(Supplier supplier) { readWriteLock.readLock().lock(); try { return supplier.get(); @@ -152,7 +152,7 @@ private T withReadLock(Supplier supplier) { readWriteLock.readLock().unlock(); } } - private void withWriteLock(Runnable runnable) { + protected void withWriteLock(Runnable runnable) { readWriteLock.writeLock().lock(); try { runnable.run(); From ef2c60f3fa176ed5e56780be4f8d9e2ed6b65574 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Thu, 14 Aug 2025 09:48:29 +0200 Subject: [PATCH 17/35] Bugfix: Fix display names of enum 'WARNING'. --- .../display/extra/widgets/linearmeter/RTLinearMeter.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index b7ca8504b6..e25f44815d 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -189,9 +189,9 @@ private enum WARNING { NONE(""), VALUE_LESS_THAN_MIN("VALUE < MIN"), VALUE_GREATER_THAN_MAX("VALUE > MAX"), - MIN_AND_MAX_NOT_DEFINED("NO UNIT DEFINED"), - LAG("MIN AND MAX ARE NOT SET"), - NO_UNIT("LAG"); + MIN_AND_MAX_NOT_DEFINED("MIN AND MAX ARE NOT SET"), + LAG("LAG"), + NO_UNIT("NO UNIT DEFINED"); private final String displayName; @@ -799,7 +799,8 @@ private void requestLayout() { withWriteLock(() -> { updateMeterBackground(); - redrawIndicator(currentValue, determineWarning()); + WARNING warning = determineWarning(); + redrawIndicator(currentValue, warning); }); } From c63fcea2d08deec15372e1bbd13a9fb40fa5a6ad Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Thu, 14 Aug 2025 09:51:01 +0200 Subject: [PATCH 18/35] Improve warning messages. --- .../extra/widgets/linearmeter/RTLinearMeter.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index e25f44815d..4676a17143 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -187,11 +187,11 @@ public void redrawLinearMeterScale() { private enum WARNING { NONE(""), - VALUE_LESS_THAN_MIN("VALUE < MIN"), - VALUE_GREATER_THAN_MAX("VALUE > MAX"), - MIN_AND_MAX_NOT_DEFINED("MIN AND MAX ARE NOT SET"), - LAG("LAG"), - NO_UNIT("NO UNIT DEFINED"); + VALUE_LESS_THAN_MIN("Value < Min"), + VALUE_GREATER_THAN_MAX("Value > Max"), + MIN_AND_MAX_NOT_DEFINED("Min and max are not set"), + LAG("Lag"), + NO_UNIT("No unit defined"); private final String displayName; From 8b65da29bd74e839f4920751abb79e12835e2b9a Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Thu, 14 Aug 2025 10:25:54 +0200 Subject: [PATCH 19/35] CSSTUDIO-3380 Improve drawBar(). --- .../extra/widgets/linearmeter/RTLinearMeter.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index 4676a17143..4c3cc89599 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -1051,7 +1051,7 @@ else if (value >= high && value < hiHi) { } if (linearMeterScale.isHorizontal()) { - if (value >= linearMeterScale.getValueRange().getLow()) { + if (value > linearMeterScale.getValueRange().getLow()) { if (isHighlightActiveRegionEnabled) { if (value <= loLo) { gc.setPaint(majorAlarmColor_highlighted); @@ -1072,11 +1072,11 @@ else if (value >= high && value < hiHi) { else { gc.setPaint(needleColor); } - int currentIndicatorPosition = computeIndicatorPosition(value); - gc.fillRect(marginLeft+1, marginAbove+1, (int) currentIndicatorPosition, meterBreadth-1); + int currentIndicatorPosition = computeIndicatorPosition(value <= linearMeterScale.getValueRange().getHigh() ? value : linearMeterScale.getValueRange().getHigh()); + gc.fillRect(marginLeft+1, marginAbove+1, (int) currentIndicatorPosition-4, meterBreadth-1); } } else { - if (value >= linearMeterScale.getValueRange().getLow() && value <= linearMeterScale.getValueRange().getHigh()) { + if (value > linearMeterScale.getValueRange().getLow()) { if (isHighlightActiveRegionEnabled) { if (value <= loLo) { gc.setPaint(majorAlarmColor_highlighted); @@ -1097,8 +1097,8 @@ else if (value >= high && value < hiHi) { else { gc.setPaint(needleColor); } - int currentIndicatorPosition = computeIndicatorPosition(value); - gc.fillRect(marginLeft+1, currentIndicatorPosition+1, (int) meterBreadth-1, linearMeterScale.getBounds().height-currentIndicatorPosition-marginBelow-1); + int currentIndicatorPosition = computeIndicatorPosition(value <= linearMeterScale.getValueRange().getHigh() ? value : linearMeterScale.getValueRange().getHigh()); + gc.fillRect(marginLeft+1, currentIndicatorPosition, (int) meterBreadth-1, linearMeterScale.getBounds().height-currentIndicatorPosition-marginBelow); } } gc.setRenderingHints(oldrenderingHints); From 3adefebc13842031f42ddbd26f103a14f4dcd7de Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Thu, 14 Aug 2025 10:54:15 +0200 Subject: [PATCH 20/35] Remove "No unit defined warning". --- .../display/extra/widgets/linearmeter/RTLinearMeter.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index 4c3cc89599..b77cf3b4fb 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -190,8 +190,7 @@ private enum WARNING { VALUE_LESS_THAN_MIN("Value < Min"), VALUE_GREATER_THAN_MAX("Value > Max"), MIN_AND_MAX_NOT_DEFINED("Min and max are not set"), - LAG("Lag"), - NO_UNIT("No unit defined"); + LAG("Lag"); private final String displayName; @@ -767,9 +766,6 @@ private WARNING determineWarning() { else if (lag) { return WARNING.LAG; } - else if (showUnits && units.equals("")) { - return WARNING.NO_UNIT; - } else if (!validRange) { return WARNING.MIN_AND_MAX_NOT_DEFINED; } From 6feccf2597d1e0203fe1223baaa7a525f8bf49a3 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Thu, 14 Aug 2025 15:48:46 +0200 Subject: [PATCH 21/35] CSSTUDIO-3380 Fixes to the drawing of the bar. --- .../widgets/linearmeter/RTLinearMeter.java | 39 +++++++------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index b77cf3b4fb..6aad894121 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -1010,6 +1010,7 @@ else if (value >= high && value < hiHi) { } }); } + private final Color TRANSPARENT = new Color(0, 0, 0, 0); private void drawBar(Graphics2D gc, double value) { withReadLock(() -> { @@ -1026,26 +1027,7 @@ private void drawBar(Graphics2D gc, double value) { gc.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); gc.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - if (showLimits) { - if (isHighlightActiveRegionEnabled) { - if (value <= loLo) { - paintRectangle(gc, loLoRectangle, majorAlarmColor_highlighted); - } - else if (value >= hiHi) { - paintRectangle(gc, hiHiRectangle, majorAlarmColor_highlighted); - } - else if (value <= low && value > loLo) { - paintRectangle(gc, lowRectangle, minorAlarmActiveColor_highlighted); - } - else if (value >= high && value < hiHi) { - paintRectangle(gc, highRectangle, minorAlarmActiveColor_highlighted); - } - else { - paintRectangle(gc, normalRectangle, normalStatusActiveColor_highlighted); - } - } - } - + int currentIndicatorPosition = computeIndicatorPosition(value <= linearMeterScale.getValueRange().getHigh() ? value : linearMeterScale.getValueRange().getHigh()); if (linearMeterScale.isHorizontal()) { if (value > linearMeterScale.getValueRange().getLow()) { if (isHighlightActiveRegionEnabled) { @@ -1068,8 +1050,8 @@ else if (value >= high && value < hiHi) { else { gc.setPaint(needleColor); } - int currentIndicatorPosition = computeIndicatorPosition(value <= linearMeterScale.getValueRange().getHigh() ? value : linearMeterScale.getValueRange().getHigh()); - gc.fillRect(marginLeft+1, marginAbove+1, (int) currentIndicatorPosition-4, meterBreadth-1); + // Draw the bar: + gc.fillRect(marginLeft, marginAbove, (int) currentIndicatorPosition - 3, meterBreadth); } } else { if (value > linearMeterScale.getValueRange().getLow()) { @@ -1093,10 +1075,19 @@ else if (value >= high && value < hiHi) { else { gc.setPaint(needleColor); } - int currentIndicatorPosition = computeIndicatorPosition(value <= linearMeterScale.getValueRange().getHigh() ? value : linearMeterScale.getValueRange().getHigh()); - gc.fillRect(marginLeft+1, currentIndicatorPosition, (int) meterBreadth-1, linearMeterScale.getBounds().height-currentIndicatorPosition-marginBelow); + // Draw the bar: + gc.fillRect(marginLeft, currentIndicatorPosition, (int) meterBreadth, linearMeterScale.getBounds().height-currentIndicatorPosition-marginBelow); } } + + // Re-draw the border of the linear meter, since it may have been covered by the bar (if the border is transparent): + paintRectangle(gc, + new Rectangle(marginLeft, + marginAbove, + linearMeterScale.getBounds().width - marginLeft - marginRight, + linearMeterScale.getBounds().height - marginAbove - marginBelow), + TRANSPARENT); + gc.setRenderingHints(oldrenderingHints); gc.setStroke(oldStroke); gc.setPaint(oldPaint); From d6e323221090aecf35b10cefc5a742c2da13d485 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Thu, 14 Aug 2025 17:00:43 +0200 Subject: [PATCH 22/35] CSSTUDIO-3380 Fix margins. --- .../extra/widgets/linearmeter/RTLinearMeter.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index 6aad894121..e72adf4c3e 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -1076,7 +1076,7 @@ else if (value >= high && value < hiHi) { gc.setPaint(needleColor); } // Draw the bar: - gc.fillRect(marginLeft, currentIndicatorPosition, (int) meterBreadth, linearMeterScale.getBounds().height-currentIndicatorPosition-marginBelow); + gc.fillRect(marginLeft, currentIndicatorPosition, (int) meterBreadth+1, linearMeterScale.getBounds().height-currentIndicatorPosition-marginBelow); } } @@ -1263,7 +1263,7 @@ private void layout() { if (linearMeterScale.isHorizontal()) { int knobSizeValue = knobSize; - marginAbove = knobSizeValue >= 1 ? knobSizeValue + 2 : 0; + marginAbove = displayMode == DisplayMode.NEEDLE && knobSizeValue >= 1 ? knobSizeValue + 2 : 0; if (linearMeterScale.isVisible() && fontMetrics != null) { var majorTicks = linearMeterScale.getTicks().getMajorTicks(); if (majorTicks.size() >= 2) { @@ -1278,8 +1278,8 @@ private void layout() { marginBelow = (int) (0.5 * linearMeterScale.getTickLength() + 4 + fontMetrics.getAscent() + fontMetrics.getDescent()); } else { marginLeft = 0; - marginRight = 1; - marginBelow = 1; + marginRight = 0; + marginBelow = 0; } if (showUnits && fontMetrics != null) { @@ -1322,7 +1322,7 @@ private void layout() { } else { int knobSizeValue = knobSize; - marginLeft = knobSizeValue >= 1 ? knobSizeValue + 2 : 0; + marginLeft = displayMode == DisplayMode.NEEDLE && knobSizeValue >= 1 ? knobSizeValue + 2 : 0; if (linearMeterScale.isVisible() && fontMetrics != null) { int maxTickLabelWidth = 0; maxTickLabelWidth = 0; @@ -1335,9 +1335,9 @@ private void layout() { marginAbove = fontMetrics.getAscent() / 2 + 1; marginBelow = fontMetrics.getAscent() / 2 + 1; } else { - marginRight = 1; + marginRight = 0; marginAbove = 0; - marginBelow = 1; + marginBelow = 0; } if (showUnits && fontMetrics != null) { From 23753bbdc73c672bc81bcda2ef0b3ffdec33bbbf Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Mon, 18 Aug 2025 15:48:56 +0200 Subject: [PATCH 23/35] Improve drawing. --- .../widgets/linearmeter/RTLinearMeter.java | 220 ++++++++++-------- 1 file changed, 121 insertions(+), 99 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index e72adf4c3e..cadfb70adc 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -29,8 +29,6 @@ import java.util.logging.Level; import javafx.application.Platform; -import javafx.util.Pair; -import org.apache.commons.lang3.tuple.ImmutableTriple; import org.csstudio.javafx.rtplot.internal.AxisPart; import org.csstudio.javafx.rtplot.internal.PlotPart; import org.csstudio.javafx.rtplot.internal.PlotPartListener; @@ -616,7 +614,7 @@ private void redrawIndicator(double value, WARNING warning) { DisplayMode displayMode = this.displayMode; if (displayMode.equals(DisplayMode.NEEDLE)) { - drawValue(gc, value); + drawNeedle(gc, value); } else if (displayMode.equals(DisplayMode.BAR)) { drawBar(gc, value); @@ -715,13 +713,16 @@ public void setCurrentValue(double newValue) }); } - private int computeIndicatorPosition(double value) { + private Optional computeIndicatorPosition(double value) { + if (Double.isNaN(value)) { + return Optional.empty(); + } return withReadLock(() -> { if (linearMeterScale.isHorizontal()) { - return (int) (marginLeft + pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); + return Optional.of((int) Math.round(marginLeft + pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow()))); } else { - return (int) (linearMeterScale.getBounds().height - marginBelow - pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow())); + return Optional.of((int) Math.round(linearMeterScale.getBounds().height - marginBelow - pixelsPerScaleUnit * (value - linearMeterScale.getValueRange().getLow()))); } }); } @@ -731,7 +732,7 @@ private void drawNewValue(double newValue) { WARNING oldWarning = determineWarning(); AtomicReference newValueAtomicReference = new AtomicReference<>(newValue); // Workaround, since captured variables need to be effectively final in Java. double oldValue = currentValue; - double oldIndicatorPosition = computeIndicatorPosition(oldValue); + Optional maybeOldIndicatorPosition = computeIndicatorPosition(oldValue); currentValue = newValueAtomicReference.get(); if (newValueAtomicReference.get() > linearMeterScale.getValueRange().getHigh() && newValueAtomicReference.get() <= linearMeterScale.getValueRange().getHigh() + minMaxTolerance) { @@ -746,8 +747,9 @@ private void drawNewValue(double newValue) { if (oldValue != newValueAtomicReference.get()) { if (!Double.isNaN(newValueAtomicReference.get())){ - int newIndicatorPosition = computeIndicatorPosition(newValue); - if (newIndicatorPosition != oldIndicatorPosition || determineWarning() != newWarning) { + Optional maybeNewIndicatorPosition = computeIndicatorPosition(newValue); + boolean indicatorPositionHasChanged = maybeNewIndicatorPosition.isPresent() != maybeOldIndicatorPosition.isPresent() || maybeOldIndicatorPosition.isPresent() && maybeNewIndicatorPosition.isPresent() && maybeOldIndicatorPosition.get() != maybeNewIndicatorPosition.get(); + if (indicatorPositionHasChanged || determineWarning() != newWarning) { redrawIndicator(newValueAtomicReference.get(), newWarning); } } @@ -908,7 +910,7 @@ private GradientPaint createVerticalGradientPaint(Rectangle rectangle, } /** Draw needle and label for current value */ - private void drawValue(Graphics2D gc, double value) { + private void drawNeedle(Graphics2D gc, double value) { withReadLock(() -> { if (Double.isNaN(value)) { return; @@ -942,68 +944,83 @@ else if (value >= high && value < hiHi) { } } } + // Re-draw border around widget: + paintRectangle(gc, + new Rectangle(marginLeft, + marginAbove, + linearMeterScale.getBounds().width - marginLeft - marginRight - 1, + linearMeterScale.getBounds().height - marginAbove - marginBelow - 1), + TRANSPARENT); if (linearMeterScale.isHorizontal()) { if (value >= linearMeterScale.getValueRange().getLow() && value <= linearMeterScale.getValueRange().getHigh()) { - int currentIndicatorPosition = computeIndicatorPosition(value); + Optional maybeCurrentIndicatorPosition = computeIndicatorPosition(value); - if (knobSize > 0) { - int[] XVal = { currentIndicatorPosition - (int) Math.round((1.0 * knobSize) / 4.0), - currentIndicatorPosition + (int) Math.round((1.0 * knobSize) / 4.0), - currentIndicatorPosition }; + if (maybeCurrentIndicatorPosition.isPresent()) { + int currentIndicatorPosition = maybeCurrentIndicatorPosition.get(); - int[] YVal = { 0, 0, marginAbove - 2 }; + if (knobSize > 0) { + int[] XVal = { currentIndicatorPosition - (int) Math.round((1.0 * knobSize) / 4.0), + currentIndicatorPosition + (int) Math.round((1.0 * knobSize) / 4.0), + currentIndicatorPosition }; - gc.setStroke(AxisPart.TICK_STROKE); - gc.setColor(knobColor); - gc.fillPolygon(XVal, YVal, 3); - gc.setColor(knobColor); - gc.drawPolygon(XVal, YVal, 3); - } + int[] YVal = { 0, 0, marginAbove - 2 }; - if (needleWidth > 0) { - gc.setStroke(new BasicStroke((float) needleWidth)); - gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); - gc.setPaint(needleColor); + gc.setStroke(AxisPart.TICK_STROKE); + gc.setColor(knobColor); + gc.fillPolygon(XVal, YVal, 3); + gc.setColor(knobColor); + gc.drawPolygon(XVal, YVal, 3); + } - int y1 = marginAbove + needleWidth / 2 + 1; - int y2 = linearMeterScale.getBounds().height - marginBelow - (needleWidth - 1) / 2 - 1; + if (needleWidth > 0) { + gc.setStroke(new BasicStroke((float) needleWidth)); + gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + gc.setPaint(needleColor); - gc.drawLine(currentIndicatorPosition, y1, currentIndicatorPosition, y2); + int y1 = marginAbove + needleWidth / 2 + 1; + int y2 = linearMeterScale.getBounds().height - marginBelow - (needleWidth - 1) / 2 - 1; + + gc.drawLine(currentIndicatorPosition, y1, currentIndicatorPosition, y2); + } } } } else { if (value >= linearMeterScale.getValueRange().getLow() && value <= linearMeterScale.getValueRange().getHigh()) { - int currentIndicatorPosition = computeIndicatorPosition(value); + Optional maybeCurrentIndicatorPosition = computeIndicatorPosition(value); + if (maybeCurrentIndicatorPosition.isPresent()) { + int currentIndicatorPosition = maybeCurrentIndicatorPosition.get() - 1; - if (knobSize > 0) { - int[] YVal = { currentIndicatorPosition + (int) Math.round((1.0 * knobSize / 4.0)), - currentIndicatorPosition - (int) Math.round((1.0 * knobSize / 4.0)), - currentIndicatorPosition }; + if (knobSize > 0) { + int[] YVal = { currentIndicatorPosition + (int) Math.round((1.0 * knobSize / 4.0)), + currentIndicatorPosition - (int) Math.round((1.0 * knobSize / 4.0)), + currentIndicatorPosition }; - int[] XVal = { 0, 0, marginLeft - 2 }; + int[] XVal = { 0, 0, marginLeft - 2 }; - gc.setStroke(AxisPart.TICK_STROKE); - gc.setColor(knobColor); - gc.fillPolygon(XVal, YVal, 3); - gc.setColor(knobColor); - gc.drawPolygon(XVal, YVal, 3); - } + gc.setStroke(AxisPart.TICK_STROKE); + gc.setColor(knobColor); + gc.fillPolygon(XVal, YVal, 3); + gc.setColor(knobColor); + gc.drawPolygon(XVal, YVal, 3); + } - if (needleWidth > 0) { - gc.setStroke(new BasicStroke((float) needleWidth)); - gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); - gc.setPaint(needleColor); + if (needleWidth > 0) { + gc.setStroke(new BasicStroke((float) needleWidth)); + gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + gc.setPaint(needleColor); - int x1 = marginLeft + (needleWidth)/2 + 1; - int x2 = linearMeterScale.getBounds().width - marginRight - (needleWidth+1)/2; + int x1 = marginLeft + (needleWidth)/2 + 1; + int x2 = linearMeterScale.getBounds().width - marginRight - (needleWidth+1)/2; - gc.drawLine(x1, currentIndicatorPosition, x2, currentIndicatorPosition); + gc.drawLine(x1, currentIndicatorPosition, x2, currentIndicatorPosition); + } } } } + gc.setRenderingHints(oldrenderingHints); gc.setStroke(oldStroke); gc.setPaint(oldPaint); @@ -1027,66 +1044,71 @@ private void drawBar(Graphics2D gc, double value) { gc.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); gc.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - int currentIndicatorPosition = computeIndicatorPosition(value <= linearMeterScale.getValueRange().getHigh() ? value : linearMeterScale.getValueRange().getHigh()); - if (linearMeterScale.isHorizontal()) { - if (value > linearMeterScale.getValueRange().getLow()) { - if (isHighlightActiveRegionEnabled) { - if (value <= loLo) { - gc.setPaint(majorAlarmColor_highlighted); - } - else if (value >= hiHi) { - gc.setPaint(majorAlarmColor_highlighted); - } - else if (value <= low && value > loLo) { - gc.setPaint(minorAlarmActiveColor_highlighted); - } - else if (value >= high && value < hiHi) { - gc.setPaint(minorAlarmActiveColor_highlighted); + Optional maybeCurrentIndicatorPosition = computeIndicatorPosition(value <= linearMeterScale.getValueRange().getHigh() ? value : linearMeterScale.getValueRange().getHigh()); + + if (maybeCurrentIndicatorPosition.isPresent()) { + if (linearMeterScale.isHorizontal()) { + int currentIndicatorPosition = maybeCurrentIndicatorPosition.get() + 1; + if (value > linearMeterScale.getValueRange().getLow()) { + if (isHighlightActiveRegionEnabled) { + if (value <= loLo) { + gc.setPaint(majorAlarmColor_highlighted); + } + else if (value >= hiHi) { + gc.setPaint(majorAlarmColor_highlighted); + } + else if (value <= low && value > loLo) { + gc.setPaint(minorAlarmActiveColor_highlighted); + } + else if (value >= high && value < hiHi) { + gc.setPaint(minorAlarmActiveColor_highlighted); + } + else { + gc.setPaint(needleColor); + } } else { gc.setPaint(needleColor); } + // Draw the bar: + gc.fillRect(marginLeft, marginAbove, currentIndicatorPosition-marginLeft, meterBreadth); } - else { - gc.setPaint(needleColor); - } - // Draw the bar: - gc.fillRect(marginLeft, marginAbove, (int) currentIndicatorPosition - 3, meterBreadth); - } - } else { - if (value > linearMeterScale.getValueRange().getLow()) { - if (isHighlightActiveRegionEnabled) { - if (value <= loLo) { - gc.setPaint(majorAlarmColor_highlighted); - } - else if (value >= hiHi) { - gc.setPaint(majorAlarmColor_highlighted); - } - else if (value <= low && value > loLo) { - gc.setPaint(minorAlarmActiveColor_highlighted); - } - else if (value >= high && value < hiHi) { - gc.setPaint(minorAlarmActiveColor_highlighted); + } else { + int currentIndicatorPosition = maybeCurrentIndicatorPosition.get() - 1; + if (value > linearMeterScale.getValueRange().getLow()) { + if (isHighlightActiveRegionEnabled) { + if (value <= loLo) { + gc.setPaint(majorAlarmColor_highlighted); + } + else if (value >= hiHi) { + gc.setPaint(majorAlarmColor_highlighted); + } + else if (value <= low && value > loLo) { + gc.setPaint(minorAlarmActiveColor_highlighted); + } + else if (value >= high && value < hiHi) { + gc.setPaint(minorAlarmActiveColor_highlighted); + } + else { + gc.setPaint(needleColor); + } } else { gc.setPaint(needleColor); } + // Draw the bar: + gc.fillRect(marginLeft, currentIndicatorPosition, meterBreadth, linearMeterScale.getBounds().height-currentIndicatorPosition-marginBelow); } - else { - gc.setPaint(needleColor); - } - // Draw the bar: - gc.fillRect(marginLeft, currentIndicatorPosition, (int) meterBreadth+1, linearMeterScale.getBounds().height-currentIndicatorPosition-marginBelow); } - } - // Re-draw the border of the linear meter, since it may have been covered by the bar (if the border is transparent): - paintRectangle(gc, - new Rectangle(marginLeft, - marginAbove, - linearMeterScale.getBounds().width - marginLeft - marginRight, - linearMeterScale.getBounds().height - marginAbove - marginBelow), - TRANSPARENT); + // Re-draw the border of the linear meter, since it may have been covered by the bar (if the border is transparent): + paintRectangle(gc, + new Rectangle(marginLeft, + marginAbove, + linearMeterScale.getBounds().width - marginLeft - marginRight - 1, + linearMeterScale.getBounds().height - marginAbove - marginBelow - 1), + TRANSPARENT); + } gc.setRenderingHints(oldrenderingHints); gc.setStroke(oldStroke); @@ -1268,7 +1290,7 @@ private void layout() { var majorTicks = linearMeterScale.getTicks().getMajorTicks(); if (majorTicks.size() >= 2) { marginLeft = fontMetrics.stringWidth(majorTicks.get(0).getLabel()) / 2; - marginRight = fontMetrics.stringWidth(majorTicks.get(majorTicks.size() - 1).getLabel()) / 2; + marginRight = fontMetrics.stringWidth(majorTicks.get(majorTicks.size() - 1).getLabel()) / 2 - 1; } else if (majorTicks.size() == 1) { marginRight = marginLeft = fontMetrics.stringWidth(majorTicks.get(0).getLabel()) / 2; } else { @@ -1286,7 +1308,7 @@ private void layout() { marginBelow += 1 + fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent(); } - pixelsPerScaleUnit = (linearMeterScale.getBounds().width - marginLeft - marginRight) / (linearMeterScale.getValueRange().getHigh() - linearMeterScale.getValueRange().getLow()); + pixelsPerScaleUnit = (linearMeterScale.getBounds().width - marginLeft - marginRight - 1) / (linearMeterScale.getValueRange().getHigh() - linearMeterScale.getValueRange().getLow()); meterBreadth = Math.round(linearMeterScale.getBounds().height - marginAbove - marginBelow); double x_loLoRectangle = marginLeft; @@ -1344,7 +1366,7 @@ private void layout() { marginBelow += 1 + fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent(); } - pixelsPerScaleUnit = (linearMeterScale.getBounds().height - marginAbove - marginBelow) / (linearMeterScale.getValueRange().getHigh() - linearMeterScale.getValueRange().getLow()); + pixelsPerScaleUnit = (linearMeterScale.getBounds().height - marginAbove - marginBelow - 1) / (linearMeterScale.getValueRange().getHigh() - linearMeterScale.getValueRange().getLow()); meterBreadth = Math.round(linearMeterScale.getBounds().width - marginLeft - marginRight); double y_loLoRectangle = marginAbove + pixelsPerScaleUnit * (linearMeterScale.getValueRange().getHigh() - displayedLoLo); From 95ceca4e111fb7c51a4d7ee686107cb064c92b29 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Tue, 19 Aug 2025 09:05:08 +0200 Subject: [PATCH 24/35] Move labels to messages.properties and fix indentation. --- .../linearmeter/LinearMeterWidget.java | 280 +++++++++--------- .../display/builder/model/Messages.java | 14 + .../display/builder/model/messages.properties | 14 + 3 files changed, 172 insertions(+), 136 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterWidget.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterWidget.java index a75a4937bb..2da4b62b60 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterWidget.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterWidget.java @@ -21,26 +21,22 @@ import static org.csstudio.display.builder.model.properties.CommonWidgetProperties.*; @SuppressWarnings("nls") -public class LinearMeterWidget extends PVWidget -{ - public LinearMeterWidget() - { +public class LinearMeterWidget extends PVWidget { + public LinearMeterWidget() { super(WIDGET_DESCRIPTOR.getType(), 240, 120); } public static WidgetDescriptor WIDGET_DESCRIPTOR = - new WidgetDescriptor("linearmeter", WidgetCategory.MONITOR, - "LinearMeter", - "/icons/linear-meter.png", - "Compact monitor widget for the value of a PV.", - Arrays.asList("")) - { - @Override - public Widget createWidget() - { - return new LinearMeterWidget(); - } - }; + new WidgetDescriptor("linearmeter", WidgetCategory.MONITOR, + "LinearMeter", + "/icons/linear-meter.png", + Messages.LinearMeterDescription, + Arrays.asList("")) { + @Override + public Widget createWidget() { + return new LinearMeterWidget(); + } + }; /** * 1.0.0: Linear meter by Claudio Rosatti based on 3rd party library @@ -48,28 +44,26 @@ public Widget createWidget() */ public static Version METER_VERSION = new Version(2, 0, 0); - /** Custom configurator to read legacy files */ - protected static class LinearMeterConfigurator extends WidgetConfigurator - { + /** + * Custom configurator to read legacy files + */ + protected static class LinearMeterConfigurator extends WidgetConfigurator { //TODO: This has to be fixed for the Linear Meter. Current implementation is // for the Meter widget and is not valid. No version 3. - public LinearMeterConfigurator(Version xmlVersion) - { + public LinearMeterConfigurator(Version xmlVersion) { super(xmlVersion); } @Override public boolean configureFromXML(ModelReader reader, Widget widget, - Element xml) throws Exception - { + Element xml) throws Exception { if (!super.configureFromXML(reader, widget, xml)) return false; LinearMeterWidget meter = (LinearMeterWidget) widget; - if (xml_version.getMajor() < 2) - { // BOY + if (xml_version.getMajor() < 2) { // BOY Element e = XMLUtil.getChildElement(xml, "scale_font"); if (e != null) @@ -77,23 +71,21 @@ public boolean configureFromXML(ModelReader reader, Widget widget, // Are any of the limits disabled, or 'Show Ramp' disabled? if ((!XMLUtil.getChildBoolean(xml, "show_hihi").orElse(true) && - !XMLUtil.getChildBoolean(xml, "show_hi").orElse(true) && - !XMLUtil.getChildBoolean(xml, "show_lo").orElse(true) && - !XMLUtil.getChildBoolean(xml, "show_lolo").orElse(true) + !XMLUtil.getChildBoolean(xml, "show_hi").orElse(true) && + !XMLUtil.getChildBoolean(xml, "show_lo").orElse(true) && + !XMLUtil.getChildBoolean(xml, "show_lolo").orElse(true) ) - || - !XMLUtil.getChildBoolean(xml, "show_markers").orElse(true)) + || + !XMLUtil.getChildBoolean(xml, "show_markers").orElse(true)) meter.propShowLimits().setValue(false); - } - else if (xml_version.getMajor() < 3) - { // Display Builder meter based on 3rd party JFX lib + } else if (xml_version.getMajor() < 3) { // Display Builder meter based on 3rd party JFX lib XMLUtil.getChildBoolean(xml, "unit_from_pv") - .ifPresent(meter.propShowUnits()::setValue); + .ifPresent(meter.propShowUnits()::setValue); if (!XMLUtil.getChildBoolean(xml, "show_hihi").orElse(true) && - !XMLUtil.getChildBoolean(xml, "show_high").orElse(true) && - !XMLUtil.getChildBoolean(xml, "show_low").orElse(true) && - !XMLUtil.getChildBoolean(xml, "show_lolo").orElse(true)) + !XMLUtil.getChildBoolean(xml, "show_high").orElse(true) && + !XMLUtil.getChildBoolean(xml, "show_low").orElse(true) && + !XMLUtil.getChildBoolean(xml, "show_lolo").orElse(true)) meter.propShowLimits().setValue(false); } @@ -102,29 +94,31 @@ else if (xml_version.getMajor() < 3) } public static WidgetPropertyDescriptor propShowLimits = - newBooleanPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "show_limits", Messages.WidgetProperties_ShowLimits); + newBooleanPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "show_limits", Messages.WidgetProperties_ShowLimits); public static WidgetPropertyDescriptor propShowWarnings = newBooleanPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "show_warnings", Messages.WidgetProperties_ShowWarnings); public static WidgetPropertyDescriptor propDisplayHorizontal = - newBooleanPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "displayHorizontal", Messages.WidgetProperties_Horizontal); + newBooleanPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "displayHorizontal", Messages.WidgetProperties_Horizontal); - public static WidgetPropertyDescriptor propScaleVisible = - newBooleanPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "scale_visible", Messages.WidgetProperties_ScaleVisible); + public static WidgetPropertyDescriptor propScaleVisible = + newBooleanPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "scale_visible", Messages.WidgetProperties_ScaleVisible); enum LimitsFromPV { - LimitsFromPV("All limits from PV"), - MinAndMaxFromPV("Min & max from PV"), - AlarmLimitsFromPV("Alarm limits from PV"), - NoLimitsFromPV("No limits from PV"); + LimitsFromPV(Messages.AllLimitsFromPV), + MinAndMaxFromPV(Messages.MinAndMaxFromPV), + AlarmLimitsFromPV(Messages.AlarmLimitsFromPV), + NoLimitsFromPV(Messages.NoLimitsFromPV); private final String displayName; private LimitsFromPV(String displayName) { this.displayName = displayName; - }; + } + + ; @Override public String toString() { @@ -143,11 +137,9 @@ public void setSpecification(final String specification) { // propLimitsFromPV was a boolean: if (specification.equals("true")) { super.setSpecification(LimitsFromPV.LimitsFromPV.ordinal() + ""); - } - else if (specification.equals("false")) { + } else if (specification.equals("false")) { super.setSpecification(LimitsFromPV.NoLimitsFromPV.ordinal() + ""); - } - else { + } else { // If not a boolean, set according to enum LimitsFromPV: super.setSpecification(specification); } @@ -160,61 +152,61 @@ else if (specification.equals("false")) { private WidgetProperty display_mode; public static WidgetPropertyDescriptor propDisplayMode = new WidgetPropertyDescriptor<>( - WidgetPropertyCategory.DISPLAY, "display_mode", "Display Mode") - { + WidgetPropertyCategory.DISPLAY, "display_mode", Messages.DisplayMode) { @Override - public EnumWidgetProperty createProperty(Widget widget, RTLinearMeter.DisplayMode default_value) - { + public EnumWidgetProperty createProperty(Widget widget, RTLinearMeter.DisplayMode default_value) { return new EnumWidgetProperty<>(this, widget, default_value); } }; public static WidgetPropertyDescriptor propNeedleColor = - newColorPropertyDescriptor(WidgetPropertyCategory.MISC, "needle_color", Messages.WidgetProperties_NeedleColor); + newColorPropertyDescriptor(WidgetPropertyCategory.MISC, "needle_color", Messages.WidgetProperties_NeedleColor); public static WidgetPropertyDescriptor knobColor_descriptor = - newColorPropertyDescriptor(WidgetPropertyCategory.MISC, "knob_color", Messages.WidgetProperties_KnobColor); + newColorPropertyDescriptor(WidgetPropertyCategory.MISC, "knob_color", Messages.WidgetProperties_KnobColor); public static WidgetPropertyDescriptor knobSize_descriptor = - newIntegerPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "knob_size", "Knob Size"); + newIntegerPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "knob_size", Messages.KnobSize); public static WidgetPropertyDescriptor propLevelHiHi = - newDoublePropertyDescriptor (WidgetPropertyCategory.BEHAVIOR, "level_hihi", Messages.WidgetProperties_LevelHiHi); + newDoublePropertyDescriptor(WidgetPropertyCategory.BEHAVIOR, "level_hihi", Messages.WidgetProperties_LevelHiHi); public static WidgetPropertyDescriptor propLevelHigh = - newDoublePropertyDescriptor (WidgetPropertyCategory.BEHAVIOR, "level_high", Messages.WidgetProperties_LevelHigh); + newDoublePropertyDescriptor(WidgetPropertyCategory.BEHAVIOR, "level_high", Messages.WidgetProperties_LevelHigh); public static WidgetPropertyDescriptor propLevelLoLo = - newDoublePropertyDescriptor (WidgetPropertyCategory.BEHAVIOR, "level_lolo", Messages.WidgetProperties_LevelLoLo); + newDoublePropertyDescriptor(WidgetPropertyCategory.BEHAVIOR, "level_lolo", Messages.WidgetProperties_LevelLoLo); public static WidgetPropertyDescriptor propLevelLow = - newDoublePropertyDescriptor (WidgetPropertyCategory.BEHAVIOR, "level_low", Messages.WidgetProperties_LevelLow); + newDoublePropertyDescriptor(WidgetPropertyCategory.BEHAVIOR, "level_low", Messages.WidgetProperties_LevelLow); - /** 'min_max_tolerance' property: Treat the value range [min - min_max_tolerance, max + min_max_tolerance] as the valid value range for the widget (can be used to avoid warnings due to precision errors in cases such as when a PV sends -0.0000001 when the value is actually 0.0. */ + /** + * 'min_max_tolerance' property: Treat the value range [min - min_max_tolerance, max + min_max_tolerance] as the valid value range for the widget (can be used to avoid warnings due to precision errors in cases such as when a PV sends -0.0000001 when the value is actually 0.0. + */ public static final WidgetPropertyDescriptor propMinMaxTolerance = newDoublePropertyDescriptor(WidgetPropertyCategory.BEHAVIOR, "min_max_tolerance", Messages.WidgetProperties_MinMaxTolerance); public static StructuredWidgetProperty.Descriptor colorsStructuredWidget_descriptor = - new StructuredWidgetProperty.Descriptor(WidgetPropertyCategory.DISPLAY, "colors", "Colors"); + new StructuredWidgetProperty.Descriptor(WidgetPropertyCategory.DISPLAY, "colors", Messages.Colors); public static WidgetPropertyDescriptor propNormalStatusColor_descriptor = - newColorPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "normal_status_color", "Normal Status Color"); + newColorPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "normal_status_color", Messages.NormalStatusColor); public static WidgetPropertyDescriptor minorWarningColor_descriptor = - newColorPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "minor_warning_color", "Low & High Warning Color"); + newColorPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "minor_warning_color", Messages.LowAndHighWarningColor); public static WidgetPropertyDescriptor majorWarningColor_descriptor = - newColorPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "major_warning_color", "LoLo & HiHi Warning Color"); + newColorPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "major_warning_color", Messages.LoLoAndHiHiWarningColor); public static WidgetPropertyDescriptor isGradientEnabled_descriptor = - newBooleanPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "is_gradient_enabled", "Enable Gradient"); + newBooleanPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "is_gradient_enabled", Messages.EnableGradient); public static WidgetPropertyDescriptor isHighlightingOfInactiveRegionsEnabled_descriptor = - newBooleanPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "is_highlighting_of_active_regions_enabled", "Highlight Active Region"); + newBooleanPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "is_highlighting_of_active_regions_enabled", Messages.HighlightActiveRegion); public static WidgetPropertyDescriptor needleWidth_descriptor = - newIntegerPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "needle_width", "Needle Width"); + newIntegerPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "needle_width", Messages.NeedleWidth); private WidgetProperty foreground; private WidgetProperty background; @@ -246,20 +238,17 @@ public EnumWidgetProperty createProperty(Widget widge private WidgetProperty majorWarningColor; @Override - public Version getVersion() - { + public Version getVersion() { return METER_VERSION; } @Override - public WidgetConfigurator getConfigurator(Version persistedVersion) throws Exception - { + public WidgetConfigurator getConfigurator(Version persistedVersion) throws Exception { return new LinearMeterConfigurator(persistedVersion); } @Override - protected void defineProperties(List> properties) - { + protected void defineProperties(List> properties) { super.defineProperties(properties); properties.add(display_mode = propDisplayMode.createProperty(this, RTLinearMeter.DisplayMode.NEEDLE)); @@ -278,23 +267,23 @@ protected void defineProperties(List> properties) background = propBackgroundColor.createProperty(this, new WidgetColor(0, 0, 0, 0)); needle_color = propNeedleColor.createProperty(this, new WidgetColor(0, 0, 0, 255)); knob_color = knobColor_descriptor.createProperty(this, new WidgetColor(0, 0, 0, 255)); - normalStatusColor = propNormalStatusColor_descriptor.createProperty(this, new WidgetColor(194,198,195)); + normalStatusColor = propNormalStatusColor_descriptor.createProperty(this, new WidgetColor(194, 198, 195)); minorWarningColor = minorWarningColor_descriptor.createProperty(this, new WidgetColor(242, 148, 141)); majorWarningColor = majorWarningColor_descriptor.createProperty(this, new WidgetColor(240, 60, 46)); isGradientEnabled = isGradientEnabled_descriptor.createProperty(this, false); isHighlightingOfInactiveRegionsEnabled = isHighlightingOfInactiveRegionsEnabled_descriptor.createProperty(this, true); properties.add(needleWidth = needleWidth_descriptor.createProperty(this, 1)); List> colorSelectionWidgets = Arrays.asList(foreground, - background, - needle_color, - knob_color, - normalStatusColor, - minorWarningColor, - majorWarningColor, - isGradientEnabled, - isHighlightingOfInactiveRegionsEnabled); + background, + needle_color, + knob_color, + normalStatusColor, + minorWarningColor, + majorWarningColor, + isGradientEnabled, + isHighlightingOfInactiveRegionsEnabled); properties.add(colorsStructuredWidget = colorsStructuredWidget_descriptor.createProperty(this, - colorSelectionWidgets)); + colorSelectionWidgets)); properties.add(level_lolo = propLevelLoLo.createProperty(this, 10.0)); properties.add(level_low = propLevelLow.createProperty(this, 20.0)); properties.add(level_high = propLevelHigh.createProperty(this, 80.0)); @@ -302,113 +291,130 @@ protected void defineProperties(List> properties) properties.add(minMaxTolerance = propMinMaxTolerance.createProperty(this, 0.0)); } - /** @return 'foreground_color' property */ - public WidgetProperty propForeground() - { + /** + * @return 'foreground_color' property + */ + public WidgetProperty propForeground() { return foreground; } - /** @return 'background_color' property */ - public WidgetProperty propBackground() - { + /** + * @return 'background_color' property + */ + public WidgetProperty propBackground() { return background; } - /** @return 'font' property */ - public WidgetProperty propFont() - { + /** + * @return 'font' property + */ + public WidgetProperty propFont() { return font; } - /** @return 'format' property */ - public WidgetProperty propFormat() - { + /** + * @return 'format' property + */ + public WidgetProperty propFormat() { return format; } - /** @return 'show_units' property */ - public WidgetProperty propShowUnits() - { + /** + * @return 'show_units' property + */ + public WidgetProperty propShowUnits() { return show_units; } - /** @return 'scale_visible' property */ - public WidgetProperty propScaleVisible() - { + /** + * @return 'scale_visible' property + */ + public WidgetProperty propScaleVisible() { return scale_visible; } - /** @return 'show_limits' property */ - public WidgetProperty propShowLimits() - { + /** + * @return 'show_limits' property + */ + public WidgetProperty propShowLimits() { return show_limits; } - /** @return 'show_warnings' property */ - public WidgetProperty propShowWarnings() - { + /** + * @return 'show_warnings' property + */ + public WidgetProperty propShowWarnings() { return show_warnings; } - /** @return 'needle_color' property */ - public WidgetProperty propNeedleColor() - { + /** + * @return 'needle_color' property + */ + public WidgetProperty propNeedleColor() { return needle_color; } - /** @return 'knob_color' property */ - public WidgetProperty propKnobColor() - { + /** + * @return 'knob_color' property + */ + public WidgetProperty propKnobColor() { return knob_color; } - public WidgetProperty propKnobSize() { return knobSize; } + public WidgetProperty propKnobSize() { + return knobSize; + } - /** @return 'limits_from_pv' property */ - public WidgetProperty propLimitsFromPV() - { + /** + * @return 'limits_from_pv' property + */ + public WidgetProperty propLimitsFromPV() { return limits_from_pv; } - /** @return 'minimum' property */ - public WidgetProperty propMinimum() - { + /** + * @return 'minimum' property + */ + public WidgetProperty propMinimum() { return minimum; } - /** @return 'maximum' property */ - public WidgetProperty propMaximum() - { + /** + * @return 'maximum' property + */ + public WidgetProperty propMaximum() { return maximum; } - public WidgetProperty propDisplayHorizontal() { return displayHorizontal; } + public WidgetProperty propDisplayHorizontal() { + return displayHorizontal; + } - public WidgetProperty propLevelHiHi ( ) { + public WidgetProperty propLevelHiHi() { return level_hihi; } - public WidgetProperty propLevelHigh ( ) { + public WidgetProperty propLevelHigh() { return level_high; } - public WidgetProperty propLevelLoLo ( ) { + public WidgetProperty propLevelLoLo() { return level_lolo; } - public WidgetProperty propLevelLow ( ) { + public WidgetProperty propLevelLow() { return level_low; } - public WidgetProperty propMinMaxTolerance ( ) { + public WidgetProperty propMinMaxTolerance() { return minMaxTolerance; } - public WidgetProperty propIsGradientEnabled () { + public WidgetProperty propIsGradientEnabled() { return isGradientEnabled; } - public WidgetProperty propIsHighlightActiveRegionEnabled () { + public WidgetProperty propIsHighlightActiveRegionEnabled() { return isHighlightingOfInactiveRegionsEnabled; } @@ -419,15 +425,17 @@ public WidgetProperty propNormalStatusColor() { public WidgetProperty propMinorWarningColor() { return minorWarningColor; } - + public WidgetProperty propMajorWarningColor() { return majorWarningColor; } - public WidgetProperty propNeedleWidth() { return needleWidth; } + public WidgetProperty propNeedleWidth() { + return needleWidth; + } public WidgetProperty propDisplayMode() { return display_mode; -} + } } diff --git a/app/display/model/src/main/java/org/csstudio/display/builder/model/Messages.java b/app/display/model/src/main/java/org/csstudio/display/builder/model/Messages.java index 7e0295fcbc..feff995562 100644 --- a/app/display/model/src/main/java/org/csstudio/display/builder/model/Messages.java +++ b/app/display/model/src/main/java/org/csstudio/display/builder/model/Messages.java @@ -18,6 +18,8 @@ public class Messages public static String Actions_N_Fmt, ActiveTab, + AlarmLimitsFromPV, + AllLimitsFromPV, ArrayWidget_Description, ArrayWidget_Name, ArrowLength, @@ -39,19 +41,23 @@ public class Messages Bottom, Checkbox_Label, Center, + Colors, ComboWidget_Item, ComboWidget_Items, Confirm_NONE, Confirm_BOTH, Confirm_PUSH, Confirm_RELEASE, + DisplayMode, EmbeddedDisplayWidget_GroupName, + EnableGradient, FontStyle_Bold, FontStyle_BoldItalic, FontStyle_Italic, FontStyle_Regular, GroupWidget_Description, GroupWidget_Name, + HighlightActiveRegion, InformativeTooltipActions, InformativeTooltipAlarmBorder, InformativeTooltipHeight, @@ -68,15 +74,23 @@ public class Messages Interpolation_Automatic, Interpolation_Interpolate, Interpolation_None, + KnobSize, LabelWidget_Text, Left, + LinearMeterDescription, LineStyle, LineStyle_Solid, LineStyle_Dash, LineStyle_DashDot, LineStyle_DashDotDot, LineStyle_Dot, + LoLoAndHiHiWarningColor, + LowAndHighWarningColor, + NeedleWidth, Middle, + MinAndMaxFromPV, + NoLimitsFromPV, + NormalStatusColor, PlotWidget_AutoScale, PlotWidget_Color, PlotWidget_ErrorPV, diff --git a/app/display/model/src/main/resources/org/csstudio/display/builder/model/messages.properties b/app/display/model/src/main/resources/org/csstudio/display/builder/model/messages.properties index 121766405e..289f8061b2 100644 --- a/app/display/model/src/main/resources/org/csstudio/display/builder/model/messages.properties +++ b/app/display/model/src/main/resources/org/csstudio/display/builder/model/messages.properties @@ -1,5 +1,7 @@ Actions_N_Fmt={0,choice,0#No actions|1#1 action|1<{0,number,integer} actions} ActiveTab=Active Tab +AlarmLimitsFromPV=Alarm limits from PV +AllLimitsFromPV=All limits from PV ArrayWidget_Name=Array ArrayWidget_Description=Array of widgets ArrowLength=Arrow Length @@ -21,19 +23,23 @@ ByteMonitor_NumBits=Number of bits (1-32) ByteMonitor_StartBit=Start bit (0-31) Checkbox_Label=Label Center=Center +Colors=Colors ComboWidget_Item=Item ComboWidget_Items=Items Confirm_NONE=No Confirm_BOTH=On Both Confirm_PUSH=On Set Confirm_RELEASE=On Clear +DisplayMode=Display Mode EmbeddedDisplayWidget_GroupName=Group name +EnableGradient=Enable Gradient FontStyle_Bold=Bold FontStyle_BoldItalic=Bold & Italic FontStyle_Italic=Italic FontStyle_Regular=Regular GroupWidget_Description=Group of widgets GroupWidget_Name=Group +HighlightActiveRegion=Highlight Active Region InformativeTooltipActions=Actions to execute when the widget is clicked on. InformativeTooltopAlarmBorder=Should an alarm border be shown around the widget when the displayed PV signals an alarm? InformativeTooltipHeight=The height of the widget in pixels. @@ -50,15 +56,23 @@ InformativeTooltipY=The y-coordinate of the widget counted in pixels from the to Interpolation_Automatic=Automatic Interpolation_Interpolate=Interpolate Interpolation_None=None +KnobSize=Knob Size LabelWidget_Text=Label text Left=Left +LinearMeterDescription=Compact monitor widget for the value of a PV. LineStyle=Line Style LineStyle_Solid=Solid LineStyle_Dash=Dashed LineStyle_DashDot=Dash-Dot LineStyle_DashDotDot=Dash-Dot-Dot LineStyle_Dot=Dot +LoLoAndHiHiWarningColor=LoLo & HiHi Warning Color +LowAndHighWarningColor=Low & High Warning Color Middle=Middle +MinAndMaxFromPV=Min & max from PV +NeedleWidth=Needle Width +NoLimitsFromPV=No limits from PV +NormalStatusColor=Normal Status Color PlotWidget_AutoScale=Auto-scale PlotWidget_Color=Color PlotWidget_ErrorPV=Error PV From a30028ddbb2bde625d8a19de0a3514cac60a2296 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Tue, 19 Aug 2025 10:03:25 +0200 Subject: [PATCH 25/35] Improve drawing of the Linear Meter widget. --- .../widgets/linearmeter/RTLinearMeter.java | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index cadfb70adc..359c7c1298 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -614,10 +614,12 @@ private void redrawIndicator(double value, WARNING warning) { DisplayMode displayMode = this.displayMode; if (displayMode.equals(DisplayMode.NEEDLE)) { + redrawBorderAroundLinearMeter(gc); // When drawing a needle, redraw the border *before* drawing the needle. drawNeedle(gc, value); } else if (displayMode.equals(DisplayMode.BAR)) { drawBar(gc, value); + redrawBorderAroundLinearMeter(gc); // When drawing a bar, redraw the border *after* drawing the bar. } else { throw new RuntimeException("Unhandled case"); @@ -643,6 +645,28 @@ else if (displayMode.equals(DisplayMode.BAR)) { }); } + private void redrawBorderAroundLinearMeter(Graphics2D gc) { + { + int width; + int height; + + // Re-draw border around widget: + if (linearMeterScale.isVisible()) { + width = linearMeterScale.getBounds().width - marginLeft - marginRight; + height = linearMeterScale.getBounds().height - marginAbove - marginBelow; + } else { + width = linearMeterScale.getBounds().width - marginLeft - marginRight - 1; + height = linearMeterScale.getBounds().height - marginAbove - marginBelow - 1; + } + paintRectangle(gc, + new Rectangle(marginLeft, + marginAbove, + width, + height), + TRANSPARENT); + } + } + /** Call to update size of meter * * @param width @@ -944,13 +968,6 @@ else if (value >= high && value < hiHi) { } } } - // Re-draw border around widget: - paintRectangle(gc, - new Rectangle(marginLeft, - marginAbove, - linearMeterScale.getBounds().width - marginLeft - marginRight - 1, - linearMeterScale.getBounds().height - marginAbove - marginBelow - 1), - TRANSPARENT); if (linearMeterScale.isHorizontal()) { if (value >= linearMeterScale.getValueRange().getLow() && value <= linearMeterScale.getValueRange().getHigh()) { @@ -1100,14 +1117,6 @@ else if (value >= high && value < hiHi) { gc.fillRect(marginLeft, currentIndicatorPosition, meterBreadth, linearMeterScale.getBounds().height-currentIndicatorPosition-marginBelow); } } - - // Re-draw the border of the linear meter, since it may have been covered by the bar (if the border is transparent): - paintRectangle(gc, - new Rectangle(marginLeft, - marginAbove, - linearMeterScale.getBounds().width - marginLeft - marginRight - 1, - linearMeterScale.getBounds().height - marginAbove - marginBelow - 1), - TRANSPARENT); } gc.setRenderingHints(oldrenderingHints); @@ -1339,7 +1348,7 @@ private void layout() { hiHiRectangle = new Rectangle((int) Math.round(x_hiHiRectangle), marginAbove, - (int) (Math.round(pixelsPerScaleUnit * (linearMeterScale.getValueRange().getHigh() - displayedHiHi))), + (int) (Math.ceil(pixelsPerScaleUnit * (linearMeterScale.getValueRange().getHigh() - displayedHiHi))), meterBreadth); } else { From 041e094387810a7510a9d22149fca613e25df2c6 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Tue, 19 Aug 2025 10:43:43 +0200 Subject: [PATCH 26/35] Bugfix: Set new value after possibly updating alarm ranges. --- .../extra/widgets/linearmeter/LinearMeterRepresentation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java index 73fe8441ff..a20f0ff2de 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java @@ -290,7 +290,6 @@ private void valueChanged(WidgetProperty property, Object old_value, Object n if (new_value instanceof VDouble) { VDouble vDouble = ((VDouble) new_value); double newValue = vDouble.getValue(); - meter.setCurrentValue(newValue); if (!Double.isNaN(newValue)) { if (Double.isNaN(observedMin) || newValue < observedMin) { @@ -368,6 +367,7 @@ private void valueChanged(WidgetProperty property, Object old_value, Object n } } } + meter.setCurrentValue(newValue); } } From c46f05da9a0a1468e75f331696dab8488b58377e Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Tue, 19 Aug 2025 11:29:46 +0200 Subject: [PATCH 27/35] Improve drawing of the Linear Meter widget. --- .../widgets/linearmeter/RTLinearMeter.java | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index 359c7c1298..ffcfbe82f2 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -651,13 +651,8 @@ private void redrawBorderAroundLinearMeter(Graphics2D gc) { int height; // Re-draw border around widget: - if (linearMeterScale.isVisible()) { - width = linearMeterScale.getBounds().width - marginLeft - marginRight; - height = linearMeterScale.getBounds().height - marginAbove - marginBelow; - } else { - width = linearMeterScale.getBounds().width - marginLeft - marginRight - 1; - height = linearMeterScale.getBounds().height - marginAbove - marginBelow - 1; - } + width = linearMeterScale.getBounds().width - marginLeft - marginRight - 1; + height = linearMeterScale.getBounds().height - marginAbove - marginBelow - 1; paintRectangle(gc, new Rectangle(marginLeft, marginAbove, @@ -997,7 +992,7 @@ else if (value >= high && value < hiHi) { gc.setPaint(needleColor); int y1 = marginAbove + needleWidth / 2 + 1; - int y2 = linearMeterScale.getBounds().height - marginBelow - (needleWidth - 1) / 2 - 1; + int y2 = linearMeterScale.getBounds().height - marginBelow - (needleWidth - 1) / 2 - 2; gc.drawLine(currentIndicatorPosition, y1, currentIndicatorPosition, y2); } @@ -1030,7 +1025,7 @@ else if (value >= high && value < hiHi) { gc.setPaint(needleColor); int x1 = marginLeft + (needleWidth)/2 + 1; - int x2 = linearMeterScale.getBounds().width - marginRight - (needleWidth+1)/2; + int x2 = linearMeterScale.getBounds().width - marginRight - (needleWidth+1)/2 - 1; gc.drawLine(x1, currentIndicatorPosition, x2, currentIndicatorPosition); } @@ -1114,7 +1109,7 @@ else if (value >= high && value < hiHi) { gc.setPaint(needleColor); } // Draw the bar: - gc.fillRect(marginLeft, currentIndicatorPosition, meterBreadth, linearMeterScale.getBounds().height-currentIndicatorPosition-marginBelow); + gc.fillRect(marginLeft, currentIndicatorPosition, meterBreadth - 1, linearMeterScale.getBounds().height-currentIndicatorPosition-marginBelow); } } } @@ -1318,7 +1313,7 @@ private void layout() { } pixelsPerScaleUnit = (linearMeterScale.getBounds().width - marginLeft - marginRight - 1) / (linearMeterScale.getValueRange().getHigh() - linearMeterScale.getValueRange().getLow()); - meterBreadth = Math.round(linearMeterScale.getBounds().height - marginAbove - marginBelow); + meterBreadth = Math.round(linearMeterScale.getBounds().height - marginAbove - marginBelow) - 1; double x_loLoRectangle = marginLeft; double x_lowRectangle = marginLeft + pixelsPerScaleUnit * (displayedLoLo - linearMeterScale.getValueRange().getLow()); @@ -1385,27 +1380,27 @@ private void layout() { loLoRectangle = new Rectangle(marginLeft, (int) Math.round(y_loLoRectangle), - meterBreadth, - (int) (Math.round(pixelsPerScaleUnit * (displayedLoLo - linearMeterScale.getValueRange().getLow()) ))); + meterBreadth - 1, + (int) (Math.round(pixelsPerScaleUnit * (displayedLoLo - linearMeterScale.getValueRange().getLow())))); lowRectangle = new Rectangle(marginLeft, (int) Math.round(y_lowRectangle), - meterBreadth, + meterBreadth - 1, (int) (Math.round(y_loLoRectangle) - Math.round(y_lowRectangle))); normalRectangle = new Rectangle(marginLeft, (int) Math.round(y_normalRectangle), - meterBreadth, + meterBreadth - 1, (int) (Math.round(y_lowRectangle) - Math.round(y_normalRectangle))); highRectangle = new Rectangle(marginLeft, (int) Math.round(y_highRectangle), - meterBreadth, + meterBreadth - 1, (int) (Math.round(y_normalRectangle) - Math.round(y_highRectangle))); hiHiRectangle = new Rectangle(marginLeft, marginAbove, - meterBreadth, + meterBreadth - 1, (int) Math.round(y_highRectangle) - marginAbove); } }); From 729bfa87bd4289644c85ef79736b002460d5e8e1 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Tue, 19 Aug 2025 13:09:42 +0200 Subject: [PATCH 28/35] Update comments. --- .../display/extra/widgets/linearmeter/RTLinearMeter.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index ffcfbe82f2..a1f99a45e2 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -42,10 +42,11 @@ /** * @author European Spallation Source ERIC - * @version 1.1 + * @version 1.2 * * Version 1.0 implemented by Fredrik Söderberg. * Version 1.1 (some fixes and improvements) by Abraham Wolk. + * Version 1.2 ("Bar" mode in addition to "Needle" mode, more fine-grained concurrency, and some improvements) by Abraham Wolk. * * The Linear Meter graphics design is by Dirk Nordt and Fredrik Söderberg. * @@ -54,9 +55,9 @@ @SuppressWarnings("nls") public class RTLinearMeter extends ImageView { - // Note: All methods that are called from different threads must ensure thread-safety by running - // relevant code on the JavaFX application thread. (The helper function runOnJavaFXThread() can - // be used for this.) + // Note: All methods must ensure thread-safety by acquiring + // readWriteLock.readLock() when reading fields, and by acquiring + // readWriteLock.writeLock() when writing to one or more fields. public RTLinearMeter(double initialValue, int width, From 39ff1fef9516240092ff6310d526cb3cfaf18c5e Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Tue, 19 Aug 2025 14:48:11 +0200 Subject: [PATCH 29/35] Improve comment. --- .../extra/widgets/linearmeter/RTLinearMeter.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index a1f99a45e2..9fd7f3ba20 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -58,6 +58,16 @@ public class RTLinearMeter extends ImageView // Note: All methods must ensure thread-safety by acquiring // readWriteLock.readLock() when reading fields, and by acquiring // readWriteLock.writeLock() when writing to one or more fields. + // + // The helper functions + // + // - void withReadLock(Runnable runnable) + // - T withReadLock(Supplier supplier) + // - void withWriteLock(Runnable runnable) + // + // have been implemented to facilitate the acquiring and releasing + // of locks. When a read-lock is held, care must be taken not + // to try to acquire the write-lock, since it leads to a deadlock. public RTLinearMeter(double initialValue, int width, From fb5ad0970d3e8551436b8942ebf6005d1fb70f30 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Tue, 19 Aug 2025 14:57:24 +0200 Subject: [PATCH 30/35] Add spaces around calls to minus. --- .../display/extra/widgets/linearmeter/RTLinearMeter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index 9fd7f3ba20..a9063db20f 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -1094,7 +1094,7 @@ else if (value >= high && value < hiHi) { gc.setPaint(needleColor); } // Draw the bar: - gc.fillRect(marginLeft, marginAbove, currentIndicatorPosition-marginLeft, meterBreadth); + gc.fillRect(marginLeft, marginAbove, currentIndicatorPosition - marginLeft, meterBreadth); } } else { int currentIndicatorPosition = maybeCurrentIndicatorPosition.get() - 1; @@ -1120,7 +1120,7 @@ else if (value >= high && value < hiHi) { gc.setPaint(needleColor); } // Draw the bar: - gc.fillRect(marginLeft, currentIndicatorPosition, meterBreadth - 1, linearMeterScale.getBounds().height-currentIndicatorPosition-marginBelow); + gc.fillRect(marginLeft, currentIndicatorPosition, meterBreadth - 1, linearMeterScale.getBounds().height - currentIndicatorPosition - marginBelow); } } } From 0222e708fc776f5957197d5dd8484096cfaa1724 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Tue, 19 Aug 2025 15:07:56 +0200 Subject: [PATCH 31/35] Set minimum width and height to 2. --- .../extra/widgets/linearmeter/LinearMeterRepresentation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java index a20f0ff2de..14d60b18a6 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java @@ -242,7 +242,7 @@ protected void unregisterListeners() super.unregisterListeners(); } - int minimumSize = 10; + int minimumSize = 2; private void widthChanged(WidgetProperty prop, Integer old_value, Integer new_value) { if (new_value < minimumSize) { From 1faf078cddb7cceb77d8e196885a1c595f9efc24 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Tue, 19 Aug 2025 15:16:45 +0200 Subject: [PATCH 32/35] Increment LinearMeterWidget.METER_VERSION to 3.0.0. --- .../display/extra/widgets/linearmeter/LinearMeterWidget.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterWidget.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterWidget.java index 2da4b62b60..736a949023 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterWidget.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterWidget.java @@ -41,8 +41,9 @@ public Widget createWidget() { /** * 1.0.0: Linear meter by Claudio Rosatti based on 3rd party library * 2.0.0: Simple linear meter, based on meter v 3.0.0, drawn in background + * 3.0.0: Improvements to the Linear Meter and the introduction of a "Bar" mode. */ - public static Version METER_VERSION = new Version(2, 0, 0); + public static Version METER_VERSION = new Version(3, 0, 0); /** * Custom configurator to read legacy files From 4b1efd893bc3c4a7219444edc316e96eba65497a Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Tue, 19 Aug 2025 15:26:55 +0200 Subject: [PATCH 33/35] Update widget property label. --- .../org/csstudio/display/builder/model/messages.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/display/model/src/main/resources/org/csstudio/display/builder/model/messages.properties b/app/display/model/src/main/resources/org/csstudio/display/builder/model/messages.properties index 289f8061b2..9f2619903c 100644 --- a/app/display/model/src/main/resources/org/csstudio/display/builder/model/messages.properties +++ b/app/display/model/src/main/resources/org/csstudio/display/builder/model/messages.properties @@ -261,7 +261,7 @@ WidgetProperties_MinuteTickMarkColor=Minute Tick Mark Color WidgetProperties_MinuteTickMarkVisible=Minute Tick Mark Visible WidgetProperties_MultiLine=Multi-Line WidgetProperties_Name=Name -WidgetProperties_NeedleColor=Needle Color +WidgetProperties_NeedleColor=Needle/Bar Color WidgetProperties_OffColor='Off' Color WidgetProperties_OffImage='Off' Image WidgetProperties_OffLabel='Off' Label From 052baf32a3bd9314fb411d676c2c9dc730fcb91c Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Tue, 19 Aug 2025 15:38:54 +0200 Subject: [PATCH 34/35] Use Math.round() instead of Math.ceil(). --- .../display/extra/widgets/linearmeter/RTLinearMeter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java index a9063db20f..95b3a21190 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/RTLinearMeter.java @@ -1354,7 +1354,7 @@ private void layout() { hiHiRectangle = new Rectangle((int) Math.round(x_hiHiRectangle), marginAbove, - (int) (Math.ceil(pixelsPerScaleUnit * (linearMeterScale.getValueRange().getHigh() - displayedHiHi))), + (int) (Math.round(pixelsPerScaleUnit * (linearMeterScale.getValueRange().getHigh() - displayedHiHi))), meterBreadth); } else { From 279e10805bcd7f850f224d41abe6a439d702ec83 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Wed, 20 Aug 2025 08:32:44 +0200 Subject: [PATCH 35/35] CSSTUDIO-3347 Acquire the write-lock in LinearMeterRepresentation.valueChanged(). --- .../LinearMeterRepresentation.java | 132 +++++++++--------- 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java index 14d60b18a6..375c09a1bc 100644 --- a/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java +++ b/app/display/linearmeter/src/main/java/org/csstudio/display/extra/widgets/linearmeter/LinearMeterRepresentation.java @@ -288,86 +288,88 @@ private void layoutChanged(WidgetProperty property, Object old_value, Object private void valueChanged(WidgetProperty property, Object old_value, Object new_value) { if (new_value instanceof VDouble) { - VDouble vDouble = ((VDouble) new_value); - double newValue = vDouble.getValue(); + meter.withWriteLock(() -> { + VDouble vDouble = ((VDouble) new_value); + double newValue = vDouble.getValue(); + + if (!Double.isNaN(newValue)) { + if (Double.isNaN(observedMin) || newValue < observedMin) { + observedMin = newValue; + newObservedMinAndMaxValues = true; + } - if (!Double.isNaN(newValue)) { - if (Double.isNaN(observedMin) || newValue < observedMin) { - observedMin = newValue; - newObservedMinAndMaxValues = true; + if (Double.isNaN(observedMax) || newValue > observedMax) { + observedMax = newValue; + newObservedMinAndMaxValues = true; + } } - if (Double.isNaN(observedMax) || newValue > observedMax) { - observedMax = newValue; - newObservedMinAndMaxValues = true; + Display display = vDouble.getDisplay(); + + // Set the units: + if (model_widget != null && model_widget.propShowUnits().getValue()) { + meter.setUnits(display.getUnit()); } - } - - Display display = vDouble.getDisplay(); - - // Set the units: - if (model_widget != null && model_widget.propShowUnits().getValue()) { - meter.setUnits(display.getUnit()); - } - if (model_widget != null) { - LinearMeterWidget.LimitsFromPV limitsFromPVSetting = model_widget.propLimitsFromPV().getValue(); - if (limitsFromPVSetting.equals(LinearMeterWidget.LimitsFromPV.LimitsFromPV) || - limitsFromPVSetting.equals(LinearMeterWidget.LimitsFromPV.MinAndMaxFromPV)) { - Range displayRange = display.getDisplayRange(); - if (displayRange != null - && Double.isFinite(displayRange.getMinimum()) - && Double.isFinite(displayRange.getMaximum()) - && displayRange.getMaximum() - displayRange.getMinimum() > 0.0) { - if (meter.linearMeterScale.getValueRange().getLow() != displayRange.getMinimum() || meter.linearMeterScale.getValueRange().getHigh() != displayRange.getMaximum() || !meter.getValidRange()) { - meter.setRange(displayRange.getMinimum(), displayRange.getMaximum(), true); - } - } else { - Range controlRange = display.getControlRange(); - if (controlRange != null - && Double.isFinite(controlRange.getMinimum()) - && Double.isFinite(controlRange.getMaximum()) - && controlRange.getMaximum() - controlRange.getMinimum() > 0.0) { - if (meter.linearMeterScale.getValueRange().getLow() != controlRange.getMinimum() || meter.linearMeterScale.getValueRange().getHigh() != controlRange.getMaximum() || !meter.getValidRange()) { - meter.setRange(controlRange.getMinimum(), controlRange.getMaximum(), true); + if (model_widget != null) { + LinearMeterWidget.LimitsFromPV limitsFromPVSetting = model_widget.propLimitsFromPV().getValue(); + if (limitsFromPVSetting.equals(LinearMeterWidget.LimitsFromPV.LimitsFromPV) || + limitsFromPVSetting.equals(LinearMeterWidget.LimitsFromPV.MinAndMaxFromPV)) { + Range displayRange = display.getDisplayRange(); + if (displayRange != null + && Double.isFinite(displayRange.getMinimum()) + && Double.isFinite(displayRange.getMaximum()) + && displayRange.getMaximum() - displayRange.getMinimum() > 0.0) { + if (meter.linearMeterScale.getValueRange().getLow() != displayRange.getMinimum() || meter.linearMeterScale.getValueRange().getHigh() != displayRange.getMaximum() || !meter.getValidRange()) { + meter.setRange(displayRange.getMinimum(), displayRange.getMaximum(), true); + } + } else { + Range controlRange = display.getControlRange(); + if (controlRange != null + && Double.isFinite(controlRange.getMinimum()) + && Double.isFinite(controlRange.getMaximum()) + && controlRange.getMaximum() - controlRange.getMinimum() > 0.0) { + if (meter.linearMeterScale.getValueRange().getLow() != controlRange.getMinimum() || meter.linearMeterScale.getValueRange().getHigh() != controlRange.getMaximum() || !meter.getValidRange()) { + meter.setRange(controlRange.getMinimum(), controlRange.getMaximum(), true); + } + } else if (newObservedMinAndMaxValues && !Double.isNaN(observedMin) && !Double.isNaN(observedMax)) { + meter.setRange(observedMin - 1, observedMax + 1, false); + newObservedMinAndMaxValues = false; + } else if (meter.linearMeterScale.getValueRange().getLow() != 0.0 || meter.linearMeterScale.getValueRange().getHigh() != 100) { + meter.setRange(0.0, 100.0, false); } - } else if (newObservedMinAndMaxValues && !Double.isNaN(observedMin) && !Double.isNaN(observedMax)) { - meter.setRange(observedMin - 1, observedMax + 1, false); - newObservedMinAndMaxValues = false; - } else if (meter.linearMeterScale.getValueRange().getLow() != 0.0 || meter.linearMeterScale.getValueRange().getHigh() != 100) { - meter.setRange(0.0, 100.0, false); } } - } - if (limitsFromPVSetting.equals(LinearMeterWidget.LimitsFromPV.LimitsFromPV) || - limitsFromPVSetting.equals(LinearMeterWidget.LimitsFromPV.AlarmLimitsFromPV)) { - { - Range warningRange = display.getWarningRange(); - if (warningRange != null) { - if (!Double.isNaN(warningRange.getMinimum()) && meter.getLow() != warningRange.getMinimum()) { - meter.setLow(warningRange.getMinimum()); - } - - if (!Double.isNaN(warningRange.getMaximum()) && meter.getHigh() != warningRange.getMaximum()) { - meter.setHigh(warningRange.getMaximum()); + if (limitsFromPVSetting.equals(LinearMeterWidget.LimitsFromPV.LimitsFromPV) || + limitsFromPVSetting.equals(LinearMeterWidget.LimitsFromPV.AlarmLimitsFromPV)) { + { + Range warningRange = display.getWarningRange(); + if (warningRange != null) { + if (!Double.isNaN(warningRange.getMinimum()) && meter.getLow() != warningRange.getMinimum()) { + meter.setLow(warningRange.getMinimum()); + } + + if (!Double.isNaN(warningRange.getMaximum()) && meter.getHigh() != warningRange.getMaximum()) { + meter.setHigh(warningRange.getMaximum()); + } } } - } - { - Range alarmRange = display.getAlarmRange(); - if (alarmRange != null) { - if (!Double.isNaN(alarmRange.getMinimum()) && meter.getLoLo() != alarmRange.getMinimum()) { - meter.setLoLo(alarmRange.getMinimum()); - } + { + Range alarmRange = display.getAlarmRange(); + if (alarmRange != null) { + if (!Double.isNaN(alarmRange.getMinimum()) && meter.getLoLo() != alarmRange.getMinimum()) { + meter.setLoLo(alarmRange.getMinimum()); + } - if (!Double.isNaN(alarmRange.getMaximum()) && meter.getHiHi() != alarmRange.getMaximum()) { - meter.setHiHi(alarmRange.getMaximum()); + if (!Double.isNaN(alarmRange.getMaximum()) && meter.getHiHi() != alarmRange.getMaximum()) { + meter.setHiHi(alarmRange.getMaximum()); + } } } } } - } - meter.setCurrentValue(newValue); + meter.setCurrentValue(newValue); + }); } }