package com.rusefi.ui; import com.devexperts.logging.Logging; import com.rusefi.AverageAnglesUtil; import com.rusefi.core.ui.AutoupdateUtil; import com.rusefi.config.generated.Fields; import com.rusefi.core.MessagesCentral; import com.rusefi.io.CommandQueue; import com.rusefi.ui.util.UiUtils; import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.util.*; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import static com.rusefi.IoUtil.*; import static com.rusefi.config.generated.Fields.CMD_DATE; import static com.rusefi.config.generated.Fields.CMD_TRIGGERINFO; import static com.rusefi.core.preferences.storage.PersistentConfiguration.getConfig; /** * This UI element displays an array of buttons, each for one of the recently used commands */ public class RecentCommands { private final static Logging log = Logging.getLogging(RecentCommands.class); private final static int NUMBER_OF_COMMANDS = 40; private static final String KEY = "recent_commands"; private static final String DELIMETER = "|"; private static final String STOPENGINE = "stopengine"; private static final String SHOWCONFIG = "showconfig"; private static final String HELP = "help"; private static final String SENSORINFO = "sensorinfo"; private static final String IDLEINFO = "idleinfo"; private static final String ALTINFO = "altinfo"; private static final String TPSINFO = "tpsinfo"; private static final String MAPINFO = "mapinfo"; private static final String ACCELINFO = "accelinfo"; private static final String CANINFO = "caninfo"; private static final String TSINFO = "tsinfo"; private static final String SPEEDINFO = "speedinfo"; private static final String joystickINFO = "joystickinfo"; private static final String FUELINFO = "fuelinfo"; private static final String HIPINFO = "hipinfo"; private static final String SDINFO = "sdinfo"; private static final String FSIOINFO = "fsioinfo"; private static final String PINS = "pins"; private final static Map COMMAND_ICONS = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); static { COMMAND_ICONS.put(STOPENGINE, AutoupdateUtil.loadIcon("stop.jpg")); ImageIcon infoIcon = AutoupdateUtil.loadIcon("info.png"); COMMAND_ICONS.put(SHOWCONFIG, infoIcon); COMMAND_ICONS.put(HELP, AutoupdateUtil.loadIcon("help.jpg")); COMMAND_ICONS.put(SENSORINFO, infoIcon); COMMAND_ICONS.put(CMD_TRIGGERINFO, AutoupdateUtil.loadIcon("trigger.jpg")); COMMAND_ICONS.put(IDLEINFO, infoIcon); COMMAND_ICONS.put(ALTINFO, AutoupdateUtil.loadIcon("alternator.jpg")); COMMAND_ICONS.put(ACCELINFO, infoIcon); COMMAND_ICONS.put(TSINFO, infoIcon); COMMAND_ICONS.put(TPSINFO, infoIcon); COMMAND_ICONS.put(MAPINFO, infoIcon); COMMAND_ICONS.put(joystickINFO, AutoupdateUtil.loadIcon("joystick.png")); COMMAND_ICONS.put(CANINFO, infoIcon); COMMAND_ICONS.put(FUELINFO, infoIcon); COMMAND_ICONS.put(HIPINFO, AutoupdateUtil.loadIcon("knock.jpg")); COMMAND_ICONS.put(SDINFO, AutoupdateUtil.loadIcon("sdinfo.jpg")); COMMAND_ICONS.put(FSIOINFO, infoIcon); COMMAND_ICONS.put(PINS, infoIcon); COMMAND_ICONS.put(Fields.CMD_WRITECONFIG, AutoupdateUtil.loadIcon("writeconfig.jpg")); COMMAND_ICONS.put(SPEEDINFO, AutoupdateUtil.loadIcon("speedinfo.jpg")); } private final JPanel content = new JPanel(new GridLayout(NUMBER_OF_COMMANDS + 1, 1)); private static final LinkedHashMap entries = new LinkedHashMap() { @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > NUMBER_OF_COMMANDS; } }; private final AtomicBoolean reentrant = new AtomicBoolean(); private final JScrollPane messagesScroll = new JScrollPane(content, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); private final UIContext uiContext; public RecentCommands(UIContext uiContext) { this.uiContext = uiContext; uiContext.getCommandQueue().addListener(command -> { if (!reentrant.get()) add(command); } ); String value = getConfig().getRoot().getProperty(KEY, null); if (value != null && value.trim().length() > 5) { unpack(value); } else { addDefaults(); } } private void unpack(String value) { entries.clear(); for (String command : value.split("\\" + DELIMETER)) add(command); } private void addDefaults() { add(HELP); add(SHOWCONFIG); add(STOPENGINE); add(SENSORINFO); add(CMD_TRIGGERINFO); add(TSINFO); add(SPEEDINFO); add(joystickINFO); add(CANINFO); add(Fields.CMD_WRITECONFIG); add("rewriteconfig"); add(getEnableCommand("injection")); add(getDisableCommand("injection")); add(getEnableCommand("ignition")); add(getDisableCommand("ignition")); add(getEnableCommand("self_stimulation")); add(getDisableCommand("self_stimulation")); add("blipidle 80 2000"); add("set_idle_position 50"); add("sparkbench 5 400 2"); add("fuelbench 5 400 2"); add(IDLEINFO); add(ALTINFO); add(TPSINFO); add(MAPINFO); add(ACCELINFO); add(FUELINFO); add(HIPINFO); add(SDINFO); add(FSIOINFO); add(PINS); } public void add(String command) { if (isBoringCommand(command)) { // not useful to remember this one return; } synchronized (entries) { entries.put(new Entry(command), null); } SwingUtilities.invokeLater(new Runnable() { @Override public void run() { content.removeAll(); if (uiContext.getLinkManager().isLogViewer()) content.add(createButton(uiContext)); JButton reset = new JButton(AutoupdateUtil.loadIcon("undo.jpg")); reset.setContentAreaFilled(false); reset.setFocusPainted(false); reset.setBorder(BorderFactory.createEmptyBorder()); content.add(UiUtils.wrap(reset)); reset.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { entries.clear(); addDefaults(); } }); synchronized (entries) { Set sorted = new TreeSet<>(entries.keySet()); for (Entry entry : sorted) { content.add(createButton(uiContext, reentrant, entry.command)); } } UiUtils.trueLayout(content.getParent()); } }); getConfig().getRoot().setProperty(KEY, pack()); } public static boolean isBoringCommand(String command) { return command.startsWith(getSetCommand(CMD_DATE)); } public static JComponent createButton(UIContext uiContext, final AtomicBoolean reentrant, final String command) { JButton button = new JButton(command); Icon icon = COMMAND_ICONS.get(command); if (icon != null) button.setIcon(icon); button.setBorder(BorderFactory.createEmptyBorder(3, 5, 0, 5)); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { reentrant.set(true); int timeout = CommandQueue.getTimeout(command); uiContext.getCommandQueue().write(command, timeout); reentrant.set(false); } }); return UiUtils.wrap(button); } public Component getContent() { return messagesScroll; } public String pack() { StringBuilder sb = new StringBuilder(); for (Entry command : entries.keySet()) { if (sb.length() != 0) sb.append(DELIMETER); sb.append(command.command); } return sb.toString(); } static class Entry implements Comparable { private final String command; Entry(String command) { this.command = command; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Entry entry = (Entry) o; return !(command != null ? !command.equals(entry.command) : entry.command != null); } @Override public int hashCode() { return command != null ? command.hashCode() : 0; } @Override public String toString() { return "{" + "command='" + command + '\'' + '}'; } @Override public int compareTo(Entry o) { return command.compareTo(o.command); } } public static String getRecent(int index) { List elements; index = Math.max(0, index); synchronized (entries) { elements = new ArrayList<>(entries.keySet()); } if (index >= elements.size()) return elements.get(0).command; return elements.get(elements.size() - 1 - index).command; } public static JButton createButton(UIContext uiContext) { JButton button = new JButton("Read trigger log"); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { JFileChooser fc = UiUtils.getFileChooser(new FileNameExtensionFilter("CSV files", "csv")); if (fc.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { String fileName = fc.getSelectedFile().getAbsolutePath(); String report; try { report = AverageAnglesUtil.runUtil(fileName); } catch (IOException e) { throw new IllegalStateException(e); } MessagesCentral.getInstance().postMessage(AverageAnglesUtil.class, report); } } }); return button; } }