mirror of https://github.com/rusefi/rusefi.git
253 lines
9.0 KiB
Java
253 lines
9.0 KiB
Java
package com.rusefi.ui.widgets;
|
|
|
|
import com.rusefi.FileLog;
|
|
import com.rusefi.NamedThreadFactory;
|
|
import com.rusefi.core.MessagesCentral;
|
|
import com.rusefi.io.CommandQueue;
|
|
import com.rusefi.io.LinkManager;
|
|
import com.rusefi.ui.RecentCommands;
|
|
import com.rusefi.ui.UIContext;
|
|
import com.rusefi.core.preferences.storage.Node;
|
|
import com.rusefi.ui.util.JTextFieldWithWidth;
|
|
|
|
import javax.swing.*;
|
|
import javax.swing.event.DocumentEvent;
|
|
import javax.swing.event.DocumentListener;
|
|
import javax.swing.text.JTextComponent;
|
|
import java.awt.*;
|
|
import java.awt.event.ActionEvent;
|
|
import java.awt.event.ActionListener;
|
|
import java.awt.event.KeyAdapter;
|
|
import java.awt.event.KeyEvent;
|
|
import java.util.concurrent.ThreadFactory;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import java.util.function.Function;
|
|
|
|
/**
|
|
* Date: 3/20/13
|
|
* Andrey Belomutskiy, (c) 2013-2020
|
|
* @see com.rusefi.CommandControl for hard-coded commands
|
|
*/
|
|
public class AnyCommand {
|
|
private final static ThreadFactory THREAD_FACTORY = new NamedThreadFactory("AnyCommand");
|
|
public static final String KEY = "last_value";
|
|
|
|
private final UIContext uiContext;
|
|
private final JTextComponent text;
|
|
private JPanel content = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
|
private boolean reentrant;
|
|
private Listener listener;
|
|
|
|
private AnyCommand(UIContext uiContext, final JTextComponent text, final Node config, String defaultCommand, final boolean listenToCommands, boolean withCommandCaption) {
|
|
this.uiContext = uiContext;
|
|
this.text = text;
|
|
installCtrlEnterAction();
|
|
text.setText(defaultCommand);
|
|
content.setBorder(BorderFactory.createLineBorder(Color.PINK));
|
|
if (withCommandCaption) {
|
|
content.add(new JLabel("Command: "));
|
|
}
|
|
content.add(text);
|
|
JButton go = new JButton("Go");
|
|
go.setContentAreaFilled(false);
|
|
go.addActionListener(e -> send());
|
|
content.add(go);
|
|
|
|
uiContext.getCommandQueue().addListener(command -> {
|
|
if (listenToCommands && !reentrant && !RecentCommands.isBoringCommand(command))
|
|
text.setText(command);
|
|
});
|
|
|
|
text.getDocument().addDocumentListener(new DocumentListener() {
|
|
@Override
|
|
public void insertUpdate(DocumentEvent e) {
|
|
changedUpdate(null);
|
|
}
|
|
|
|
@Override
|
|
public void removeUpdate(DocumentEvent e) {
|
|
}
|
|
|
|
@Override
|
|
public void changedUpdate(DocumentEvent e) {
|
|
boolean isOk = isValidInput(text.getText());
|
|
text.setBorder(isOk ? null : BorderFactory.createLineBorder(Color.red));
|
|
config.setProperty(KEY, text.getText());
|
|
}
|
|
});
|
|
|
|
// text.setInputVerifier(new InputVerifier() {
|
|
// @Override
|
|
// public boolean verify(JComponent input) {
|
|
// boolean isOk = isValidInput(text);
|
|
// text.setBorder(isOk ? null : BorderFactory.createLineBorder(Color.red));
|
|
// return isOk;
|
|
// }
|
|
// });
|
|
// todo: limit the length of text in the text field
|
|
}
|
|
|
|
private void installCtrlEnterAction() {
|
|
text.setToolTipText("Ctrl-Enter to send");
|
|
text.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,
|
|
java.awt.event.InputEvent.CTRL_DOWN_MASK),
|
|
"sendKey");
|
|
text.getActionMap().put("sendKey", new AbstractAction() {
|
|
@Override
|
|
public void actionPerformed(ActionEvent e) {
|
|
send();
|
|
}
|
|
});
|
|
}
|
|
|
|
private void send() {
|
|
String multiLine = text.getText();
|
|
for (String line : multiLine.split("\n")) {
|
|
sendCommand(line);
|
|
}
|
|
}
|
|
|
|
private void sendCommand(String rawCommand) {
|
|
if (!isValidInput(rawCommand))
|
|
return;
|
|
String cmd = prepareCommand(rawCommand, uiContext.getLinkManager());
|
|
if (cmd == null) {
|
|
/**
|
|
* {@link #DECODE_RPN} for example does not send out anything
|
|
*/
|
|
return;
|
|
}
|
|
if (listener != null)
|
|
listener.onSend();
|
|
int timeout = CommandQueue.getTimeout(cmd);
|
|
reentrant = true;
|
|
uiContext.getCommandQueue().write(cmd, timeout);
|
|
reentrant = false;
|
|
}
|
|
|
|
public static String prepareCommand(String rawCommand, LinkManager linkManager) {
|
|
try {
|
|
if (rawCommand.toLowerCase().startsWith("stim_check" + " ")) {
|
|
handleStimulationSelfCheck(rawCommand, linkManager);
|
|
return null;
|
|
} else {
|
|
return rawCommand;
|
|
}
|
|
} catch (Throwable e) {
|
|
FileLog.MAIN.log(e);
|
|
return rawCommand;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* stim_check 3000 5 30
|
|
* would set RPM to 3000, give it 5 seconds to settle, and test for 30 seconds
|
|
*/
|
|
private static void handleStimulationSelfCheck(String rawCommand, LinkManager linkManager) {
|
|
String[] parts = rawCommand.split(" ", 4);
|
|
if (parts.length != 4) {
|
|
MessagesCentral.getInstance().postMessage(AnyCommand.class, "Invalid command length " + parts);
|
|
return; // let's ignore invalid command
|
|
}
|
|
int rpm = Integer.parseInt(parts[1]);
|
|
int settleTime = Integer.parseInt(parts[2]);
|
|
int durationTime = Integer.parseInt(parts[3]);
|
|
THREAD_FACTORY.newThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
MessagesCentral.getInstance().postMessage(AnyCommand.class, "Will test with RPM " + rpm + ", settle time" + settleTime + "s and duration" + durationTime + "s");
|
|
Function<String, Object> callback = new Function<String, Object>() {
|
|
@Override
|
|
public Object apply(String status) {
|
|
if (status == null) {
|
|
MessagesCentral.getInstance().postMessage(AnyCommand.class, rpm + " worked!");
|
|
} else {
|
|
MessagesCentral.getInstance().postMessage(AnyCommand.class, rpm + " failed " + status);
|
|
}
|
|
return null;
|
|
}
|
|
};
|
|
EcuTestHelper.assertRpmDoesNotJump(rpm, settleTime, durationTime, callback, linkManager.getCommandQueue());
|
|
}
|
|
}).start();
|
|
}
|
|
|
|
private static boolean isValidInput(String text) {
|
|
boolean isOk = true;
|
|
for (char c : text.toCharArray()) {
|
|
if (c > 127) {
|
|
/**
|
|
* https://sourceforge.net/p/rusefi/tickets/63/
|
|
* only English characters are accepted - we need to reject two-byte unicode stuff
|
|
*/
|
|
isOk = false;
|
|
break;
|
|
}
|
|
}
|
|
return isOk;
|
|
}
|
|
|
|
public JComponent getContent() {
|
|
return content;
|
|
}
|
|
|
|
public void setContent(JPanel content) {
|
|
this.content = content;
|
|
}
|
|
|
|
public void requestFocus() {
|
|
text.requestFocus();
|
|
}
|
|
|
|
interface Listener {
|
|
void onSend();
|
|
}
|
|
|
|
public static AnyCommand createField(UIContext uiContext, Node config, boolean listenToCommands, boolean withCommandCaption) {
|
|
return createField(uiContext, config, config.getProperty(KEY), listenToCommands, withCommandCaption);
|
|
}
|
|
|
|
public static AnyCommand createField(UIContext uiContext, Node config, String defaultCommand, boolean listenToCommands, boolean withCommandCaption) {
|
|
final JTextField text = new JTextFieldWithWidth(200);
|
|
|
|
final AnyCommand command = new AnyCommand(uiContext, text, config, defaultCommand, listenToCommands, withCommandCaption);
|
|
text.addActionListener(new ActionListener() {
|
|
@Override
|
|
public void actionPerformed(ActionEvent e) {
|
|
command.send();
|
|
}
|
|
});
|
|
|
|
final AtomicInteger index = new AtomicInteger();
|
|
command.listener = () -> index.set(0);
|
|
|
|
text.addKeyListener(new KeyAdapter() {
|
|
@Override
|
|
public void keyPressed(KeyEvent e) {
|
|
if (e.getKeyCode() == KeyEvent.VK_UP) {
|
|
String command = RecentCommands.getRecent(index.incrementAndGet());
|
|
text.setText(command);
|
|
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
|
|
String command = RecentCommands.getRecent(index.decrementAndGet());
|
|
text.setText(command);
|
|
}
|
|
}
|
|
});
|
|
|
|
return command;
|
|
}
|
|
|
|
public static AnyCommand createArea(UIContext uiContext, Node config, String defaultCommand, boolean listenToCommands, boolean withCommandCaption) {
|
|
final JTextArea text = new JTextArea(3, 20) {
|
|
@Override
|
|
public Dimension getPreferredSize() {
|
|
Dimension size = super.getPreferredSize();
|
|
return new Dimension(200, size.height);
|
|
}
|
|
};
|
|
// text.setMax
|
|
|
|
return new AnyCommand(uiContext, text, config, defaultCommand, listenToCommands, withCommandCaption);
|
|
}
|
|
}
|