From b85e91d103e402ee34c749bb855e12bc8cf38f03 Mon Sep 17 00:00:00 2001 From: rusEfi Date: Sun, 1 Sep 2019 18:00:52 -0400 Subject: [PATCH] improving mock voltage slider response time by aggregating commands --- .../io/src/com/rusefi/io/CommandQueue.java | 32 ++++++++--- .../src/com/rusefi/io/IMethodInvocation.java | 14 +++++ .../com/rusefi/ui/widgets/DetachedSensor.java | 53 +++++++++++++------ 3 files changed, 77 insertions(+), 22 deletions(-) create mode 100644 java_console/io/src/com/rusefi/io/IMethodInvocation.java diff --git a/java_console/io/src/com/rusefi/io/CommandQueue.java b/java_console/io/src/com/rusefi/io/CommandQueue.java index b6290029df..56d64f4ef9 100644 --- a/java_console/io/src/com/rusefi/io/CommandQueue.java +++ b/java_console/io/src/com/rusefi/io/CommandQueue.java @@ -14,7 +14,7 @@ import java.util.concurrent.LinkedBlockingQueue; * *

* Date: 1/7/13 - * (c) Andrey Belomutskiy + * (c) Andrey Belomutskiy 2013-2019 */ @SuppressWarnings("FieldCanBeLocal") public class CommandQueue { @@ -32,7 +32,7 @@ public class CommandQueue { private Set pendingConfirmations = Collections.synchronizedSet(new HashSet()); private static final CommandQueue instance = new CommandQueue(); - private final BlockingQueue pendingCommands = new LinkedBlockingQueue<>(); + private final BlockingQueue pendingCommands = new LinkedBlockingQueue<>(); private final List commandListeners = new ArrayList<>(); private final Runnable runnable = new Runnable() { @@ -74,7 +74,7 @@ public class CommandQueue { * here we block in case there is no command to send */ @NotNull - final MethodInvocation command = pendingCommands.take(); + final IMethodInvocation command = pendingCommands.take(); // got a command? let's send it! sendCommand(command); } @@ -82,14 +82,14 @@ public class CommandQueue { /** * this method keeps retrying till a confirmation is received */ - private void sendCommand(final MethodInvocation commandRequest) throws InterruptedException { + private void sendCommand(final IMethodInvocation commandRequest) throws InterruptedException { int counter = 0; String command = commandRequest.getCommand(); while (!pendingConfirmations.contains(command)) { counter++; // FileLog.MAIN.logLine("templog sending " + command + " " + System.currentTimeMillis() + " " + new Date()); - LinkManager.send(command, commandRequest.fireEvent); + LinkManager.send(command, commandRequest.isFireEvent()); long now = System.currentTimeMillis(); synchronized (lock) { lock.wait(commandRequest.getTimeout()); @@ -106,7 +106,7 @@ public class CommandQueue { } } if (pendingConfirmations.contains(command)) { - commandRequest.listener.onCommandConfirmation(); + commandRequest.getListener().onCommandConfirmation(); pendingConfirmations.remove(command); } @@ -178,7 +178,13 @@ public class CommandQueue { pendingCommands.add(new MethodInvocation(command, timeoutMs, listener, fireEvent)); } - static class MethodInvocation { + public void addIfNotPresent(IMethodInvocation commandSender) { + // technically this should be a critical locked section but for our use-case we do not care + if (!pendingCommands.contains(commandSender)) + pendingCommands.add(commandSender); + } + + static class MethodInvocation implements IMethodInvocation { private final String command; private final int timeoutMs; private final InvocationConfirmationListener listener; @@ -191,14 +197,26 @@ public class CommandQueue { this.fireEvent = fireEvent; } + @Override public String getCommand() { return command; } + @Override public int getTimeout() { return timeoutMs; } + @Override + public InvocationConfirmationListener getListener() { + return listener; + } + + @Override + public boolean isFireEvent() { + return fireEvent; + } + @Override public String toString() { return "MethodInvocation{" + diff --git a/java_console/io/src/com/rusefi/io/IMethodInvocation.java b/java_console/io/src/com/rusefi/io/IMethodInvocation.java new file mode 100644 index 0000000000..72bc28a299 --- /dev/null +++ b/java_console/io/src/com/rusefi/io/IMethodInvocation.java @@ -0,0 +1,14 @@ +package com.rusefi.io; + +/** + * (c) Andrey Belomutskiy 2013-2019 + */ +public interface IMethodInvocation { + String getCommand(); + + int getTimeout(); + + InvocationConfirmationListener getListener(); + + boolean isFireEvent(); +} diff --git a/java_console/ui/src/com/rusefi/ui/widgets/DetachedSensor.java b/java_console/ui/src/com/rusefi/ui/widgets/DetachedSensor.java index 4b1f8c083b..5b6fbf4e17 100644 --- a/java_console/ui/src/com/rusefi/ui/widgets/DetachedSensor.java +++ b/java_console/ui/src/com/rusefi/ui/widgets/DetachedSensor.java @@ -3,14 +3,14 @@ package com.rusefi.ui.widgets; import com.rusefi.config.generated.Fields; import com.rusefi.core.Sensor; import com.rusefi.io.CommandQueue; +import com.rusefi.io.IMethodInvocation; +import com.rusefi.io.InvocationConfirmationListener; import com.rusefi.io.LinkManager; import com.rusefi.ui.GaugesPanel; import com.rusefi.ui.storage.Node; import com.rusefi.ui.util.UiUtils; import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; @@ -20,9 +20,10 @@ import java.text.Format; import java.util.Arrays; import java.util.Collection; import java.util.Hashtable; +import java.util.concurrent.atomic.AtomicReference; /** - * (c) Andrey Belomutskiy + * (c) Andrey Belomutskiy 2013-2019 * 11/2/14 */ public class DetachedSensor { @@ -82,12 +83,7 @@ public class DetachedSensor { } void create() { - SensorGauge.GaugeChangeListener listener = new SensorGauge.GaugeChangeListener() { - @Override - public void onSensorChange(Sensor sensor) { - onChange(sensor); - } - }; + SensorGauge.GaugeChangeListener listener = this::onChange; content.add(SensorGauge.createGauge(sensor, listener, null), BorderLayout.CENTER); content.add(mockControlPanel, BorderLayout.SOUTH); @@ -123,8 +119,6 @@ public class DetachedSensor { } public static Component createMockVoltageSlider(final Sensor sensor) { - /** - */ final JSlider slider = new JSlider(0, _5_VOLTS_WITH_DECIMAL); slider.setLabelTable(SLIDER_LABELS); slider.setPaintLabels(true); @@ -132,12 +126,41 @@ public class DetachedSensor { slider.setMajorTickSpacing(10); slider.setMinorTickSpacing(5); - slider.addChangeListener(new ChangeListener() { + AtomicReference pendingValue = new AtomicReference<>(); + + IMethodInvocation commandSender = new IMethodInvocation() { @Override - public void stateChanged(ChangeEvent e) { - double value = slider.getValue() / 10.0; - CommandQueue.getInstance().write("set mock_" + sensor.name().toLowerCase() + "_voltage " + value); + public String getCommand() { + return "set mock_" + sensor.name().toLowerCase() + "_voltage " + pendingValue.get(); } + + @Override + public int getTimeout() { + return CommandQueue.DEFAULT_TIMEOUT; + } + + @Override + public InvocationConfirmationListener getListener() { + return InvocationConfirmationListener.VOID; + } + + @Override + public boolean isFireEvent() { + return false; + } + }; + + + slider.addChangeListener(e -> { + double value = slider.getValue() / 10.0; + CommandQueue commandQueue = CommandQueue.getInstance(); + pendingValue.set(value); + + /* + * User might be changing slider faster than commands are being send + * We only add commandSender into the queue only if not already pending in order to only send one command with latest requested value and not a sequence of commands. + */ + commandQueue.addIfNotPresent(commandSender); }); return slider;