improving mock voltage slider response time by aggregating commands

This commit is contained in:
rusEfi 2019-09-01 18:00:52 -04:00
parent 8f10e85d03
commit b023353457
3 changed files with 77 additions and 22 deletions

View File

@ -14,7 +14,7 @@ import java.util.concurrent.LinkedBlockingQueue;
* *
* <p/> * <p/>
* Date: 1/7/13 * Date: 1/7/13
* (c) Andrey Belomutskiy * (c) Andrey Belomutskiy 2013-2019
*/ */
@SuppressWarnings("FieldCanBeLocal") @SuppressWarnings("FieldCanBeLocal")
public class CommandQueue { public class CommandQueue {
@ -32,7 +32,7 @@ public class CommandQueue {
private Set<String> pendingConfirmations = Collections.synchronizedSet(new HashSet<String>()); private Set<String> pendingConfirmations = Collections.synchronizedSet(new HashSet<String>());
private static final CommandQueue instance = new CommandQueue(); private static final CommandQueue instance = new CommandQueue();
private final BlockingQueue<MethodInvocation> pendingCommands = new LinkedBlockingQueue<>(); private final BlockingQueue<IMethodInvocation> pendingCommands = new LinkedBlockingQueue<>();
private final List<CommandQueueListener> commandListeners = new ArrayList<>(); private final List<CommandQueueListener> commandListeners = new ArrayList<>();
private final Runnable runnable = new Runnable() { private final Runnable runnable = new Runnable() {
@ -74,7 +74,7 @@ public class CommandQueue {
* here we block in case there is no command to send * here we block in case there is no command to send
*/ */
@NotNull @NotNull
final MethodInvocation command = pendingCommands.take(); final IMethodInvocation command = pendingCommands.take();
// got a command? let's send it! // got a command? let's send it!
sendCommand(command); sendCommand(command);
} }
@ -82,14 +82,14 @@ public class CommandQueue {
/** /**
* this method keeps retrying till a confirmation is received * 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; int counter = 0;
String command = commandRequest.getCommand(); String command = commandRequest.getCommand();
while (!pendingConfirmations.contains(command)) { while (!pendingConfirmations.contains(command)) {
counter++; counter++;
// FileLog.MAIN.logLine("templog sending " + command + " " + System.currentTimeMillis() + " " + new Date()); // FileLog.MAIN.logLine("templog sending " + command + " " + System.currentTimeMillis() + " " + new Date());
LinkManager.send(command, commandRequest.fireEvent); LinkManager.send(command, commandRequest.isFireEvent());
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
synchronized (lock) { synchronized (lock) {
lock.wait(commandRequest.getTimeout()); lock.wait(commandRequest.getTimeout());
@ -106,7 +106,7 @@ public class CommandQueue {
} }
} }
if (pendingConfirmations.contains(command)) { if (pendingConfirmations.contains(command)) {
commandRequest.listener.onCommandConfirmation(); commandRequest.getListener().onCommandConfirmation();
pendingConfirmations.remove(command); pendingConfirmations.remove(command);
} }
@ -178,7 +178,13 @@ public class CommandQueue {
pendingCommands.add(new MethodInvocation(command, timeoutMs, listener, fireEvent)); 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 String command;
private final int timeoutMs; private final int timeoutMs;
private final InvocationConfirmationListener listener; private final InvocationConfirmationListener listener;
@ -191,14 +197,26 @@ public class CommandQueue {
this.fireEvent = fireEvent; this.fireEvent = fireEvent;
} }
@Override
public String getCommand() { public String getCommand() {
return command; return command;
} }
@Override
public int getTimeout() { public int getTimeout() {
return timeoutMs; return timeoutMs;
} }
@Override
public InvocationConfirmationListener getListener() {
return listener;
}
@Override
public boolean isFireEvent() {
return fireEvent;
}
@Override @Override
public String toString() { public String toString() {
return "MethodInvocation{" + return "MethodInvocation{" +

View File

@ -0,0 +1,14 @@
package com.rusefi.io;
/**
* (c) Andrey Belomutskiy 2013-2019
*/
public interface IMethodInvocation {
String getCommand();
int getTimeout();
InvocationConfirmationListener getListener();
boolean isFireEvent();
}

View File

@ -3,14 +3,14 @@ package com.rusefi.ui.widgets;
import com.rusefi.config.generated.Fields; import com.rusefi.config.generated.Fields;
import com.rusefi.core.Sensor; import com.rusefi.core.Sensor;
import com.rusefi.io.CommandQueue; import com.rusefi.io.CommandQueue;
import com.rusefi.io.IMethodInvocation;
import com.rusefi.io.InvocationConfirmationListener;
import com.rusefi.io.LinkManager; import com.rusefi.io.LinkManager;
import com.rusefi.ui.GaugesPanel; import com.rusefi.ui.GaugesPanel;
import com.rusefi.ui.storage.Node; import com.rusefi.ui.storage.Node;
import com.rusefi.ui.util.UiUtils; import com.rusefi.ui.util.UiUtils;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*; import java.awt.*;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter; import java.awt.event.WindowAdapter;
@ -20,9 +20,10 @@ import java.text.Format;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.concurrent.atomic.AtomicReference;
/** /**
* (c) Andrey Belomutskiy * (c) Andrey Belomutskiy 2013-2019
* 11/2/14 * 11/2/14
*/ */
public class DetachedSensor { public class DetachedSensor {
@ -82,12 +83,7 @@ public class DetachedSensor {
} }
void create() { void create() {
SensorGauge.GaugeChangeListener listener = new SensorGauge.GaugeChangeListener() { SensorGauge.GaugeChangeListener listener = this::onChange;
@Override
public void onSensorChange(Sensor sensor) {
onChange(sensor);
}
};
content.add(SensorGauge.createGauge(sensor, listener, null), BorderLayout.CENTER); content.add(SensorGauge.createGauge(sensor, listener, null), BorderLayout.CENTER);
content.add(mockControlPanel, BorderLayout.SOUTH); content.add(mockControlPanel, BorderLayout.SOUTH);
@ -123,8 +119,6 @@ public class DetachedSensor {
} }
public static Component createMockVoltageSlider(final Sensor sensor) { public static Component createMockVoltageSlider(final Sensor sensor) {
/**
*/
final JSlider slider = new JSlider(0, _5_VOLTS_WITH_DECIMAL); final JSlider slider = new JSlider(0, _5_VOLTS_WITH_DECIMAL);
slider.setLabelTable(SLIDER_LABELS); slider.setLabelTable(SLIDER_LABELS);
slider.setPaintLabels(true); slider.setPaintLabels(true);
@ -132,12 +126,41 @@ public class DetachedSensor {
slider.setMajorTickSpacing(10); slider.setMajorTickSpacing(10);
slider.setMinorTickSpacing(5); slider.setMinorTickSpacing(5);
slider.addChangeListener(new ChangeListener() { AtomicReference<Double> pendingValue = new AtomicReference<>();
IMethodInvocation commandSender = new IMethodInvocation() {
@Override @Override
public void stateChanged(ChangeEvent e) { public String getCommand() {
double value = slider.getValue() / 10.0; return "set mock_" + sensor.name().toLowerCase() + "_voltage " + pendingValue.get();
CommandQueue.getInstance().write("set mock_" + sensor.name().toLowerCase() + "_voltage " + value);
} }
@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; return slider;