rusefi/java_console/ui/src/main/java/com/rusefi/ui/widgets/AnyCommand.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);
}
}