rusefi/java_console/ui/src/main/java/com/rusefi/ui/engine/EngineSnifferPanel.java

350 lines
13 KiB
Java

package com.rusefi.ui.engine;
import com.devexperts.logging.Logging;
import com.rusefi.FileLog;
import com.rusefi.config.generated.Fields;
import com.rusefi.core.EngineState;
import com.rusefi.core.Sensor;
import com.rusefi.core.SensorCentral;
import com.rusefi.ui.*;
import com.rusefi.ui.config.BitConfigField;
import com.rusefi.ui.config.ConfigField;
import com.rusefi.core.preferences.storage.Node;
import com.rusefi.ui.util.URLLabel;
import com.rusefi.ui.util.UiUtils;
import com.rusefi.ui.widgets.AnyCommand;
import com.rusefi.waves.EngineChart;
import com.rusefi.waves.EngineChartParser;
import com.rusefi.waves.EngineReport;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import java.util.List;
import static com.devexperts.logging.Logging.getLogging;
/**
* Engine Sniffer control consists of a set of {@link UpDownImage}
* <p/>
* <p/>
* Date: 6/23/13
* Andrey Belomutskiy, (c) 2013-2020
*
* @see EngineSnifferStatusPanel status bar
* @see com.rusefi.ui.test.WavePanelSandbox
*/
public class EngineSnifferPanel {
private static final Logging log = getLogging(EngineSnifferPanel.class);
private static final int EFI_DEFAULT_CHART_SIZE = 180;
public static final Comparator<String> INSTANCE = new ImageOrderComparator();
private static final String HELP_URL = "http://rusefi.com/wiki/index.php?title=Manual:DevConsole#Digital_Chart";
public static final String HELP_TEXT = "Click here for online help";
private final JPanel chartPanel = new JPanel(new BorderLayout());
/**
* chartPanel in the center and warning panel on the bottom
*/
private final JPanel mainPanel = new JPanel(new BorderLayout());
/**
* imageName -> UpDownImage
*/
private final TreeMap<String, UpDownImage> images = new TreeMap<>(INSTANCE);
/**
* this is the panel which displays all {@link UpDownImage} using {@link GridLayout}
*/
private final JPanel imagePanel = new JPanel(new GridLayout(1, 1)) {
@Override
public Dimension getPreferredSize() {
Dimension d = chartPanel.getSize();
Dimension s = super.getPreferredSize();
return new Dimension((int) (d.width * zoomControl.getZoomProvider().getZoomValue()), s.height);
}
};
private final ZoomControl zoomControl = new ZoomControl();
private final EngineSnifferStatusPanel statusPanel = new EngineSnifferStatusPanel();
private final UpDownImage crank = createImage(Fields.PROTOCOL_CRANK1);
private final ChartScrollControl scrollControl;
private AnyCommand command;
private boolean isPaused;
public EngineSnifferPanel(UIContext uiContext, Node config) {
statusPanel.setTimeAxisTranslator(crank.createTranslator());
final JButton pauseButton = UiUtils.createPauseButton();
pauseButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setPaused(pauseButton, !isPaused);
}
});
JButton clearButton = UiUtils.createClearButton();
clearButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (UpDownImage image : images.values())
image.setWaveReport(EngineReport.MOCK, null);
setPaused(pauseButton, false);
}
});
JButton saveImageButton = UiUtils.createSaveImageButton();
saveImageButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
saveImage();
}
});
JPanel upperPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0));
upperPanel.add(clearButton);
upperPanel.add(saveImageButton);
upperPanel.add(pauseButton);
upperPanel.add(new RpmLabel(uiContext,2).getContent());
if (!uiContext.getLinkManager().isLogViewer()) {
command = AnyCommand.createField(uiContext, config, "chartsize " + EFI_DEFAULT_CHART_SIZE, true, true);
upperPanel.add(command.getContent());
}
upperPanel.add(zoomControl);
scrollControl = ChartRepository.getInstance().createControls(chart -> displayChart(chart));
if (uiContext.getLinkManager().isLogViewer())
upperPanel.add(scrollControl.getContent());
upperPanel.add(new URLLabel(HELP_TEXT, HELP_URL));
JPanel bottomPanel = new JPanel(new BorderLayout());
if (!uiContext.getLinkManager().isLogViewer()) {
JPanel lowerButtons = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0));
lowerButtons.add(new ConfigField(uiContext, Fields.GLOBALTRIGGERANGLEOFFSET, "Trigger Offset").getContent());
lowerButtons.add(new BitConfigField(uiContext, Fields.VERBOSETRIGGERSYNCHDETAILS, "Verbose trigger Sync").getContent());
lowerButtons.add(new BitConfigField(uiContext, Fields.VERBOSEVVTDECODING, "Verbose VVT Sync").getContent());
lowerButtons.add(new BitConfigField(uiContext, Fields.ENGINESNIFFERFOCUSONINPUTS, "Focus On Inputs").getContent());
lowerButtons.add(new ConfigField(uiContext, Fields.ENGINECHARTSIZE, "Engine Sniffer size").getContent());
lowerButtons.add(new ConfigField(uiContext, Fields.ENGINESNIFFERRPMTHRESHOLD, "RPM threshold").getContent());
lowerButtons.add(new BitConfigField(uiContext, Fields.INVERTPRIMARYTRIGGERSIGNAL, "Invert Primary Input").getContent());
bottomPanel.add(lowerButtons, BorderLayout.NORTH);
}
bottomPanel.add(statusPanel.infoPanel, BorderLayout.SOUTH);
chartPanel.add(upperPanel, BorderLayout.NORTH);
JScrollPane pane = new JScrollPane(imagePanel, JScrollPane.VERTICAL_SCROLLBAR_NEVER, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
chartPanel.add(pane, BorderLayout.CENTER);
chartPanel.add(bottomPanel, BorderLayout.SOUTH);
zoomControl.listener = () -> {
System.out.println("onZoomChange");
/**
* We have scroll pane size which depends on zoom, that's a long chain of dependencies
*/
UiUtils.trueLayout(imagePanel.getParent());
};
resetImagePanel();
uiContext.getLinkManager().getEngineState().registerStringValueAction(EngineReport.ENGINE_CHART, new EngineState.ValueCallback<String>() {
@Override
public void onUpdate(String value) {
if (isPaused)
return;
displayChart(value);
}
});
mainPanel.add(chartPanel, BorderLayout.CENTER);
mainPanel.add(new WarningPanel(config).getPanel(config), BorderLayout.SOUTH);
}
private void setPaused(JButton pauseButton, boolean isPaused) {
this.isPaused = isPaused;
UiUtils.setPauseButtonText(pauseButton, this.isPaused);
}
public void setOutpinListener(EngineState engineState) {
engineState.registerStringValueAction(Fields.PROTOCOL_OUTPIN, new EngineState.ValueCallback<String>() {
@Override
public void onUpdate(String value) {
String[] pinInfo = value.split("@");
if (pinInfo.length != 2)
return;
String channel = pinInfo[0];
String pin = pinInfo[1];
UpDownImage image = images.get(channel);
ChannelNaming.INSTANCE.channelName2PhysicalPin.put(channel, pin);
if (image != null)
image.setPhysicalPin(pin);
}
});
}
private void resetImagePanel() {
imagePanel.removeAll();
imagePanel.add(crank);
images.put(Fields.PROTOCOL_CRANK1, crank);
}
public void displayChart(String value) {
EngineChart map = EngineChartParser.unpackToMap(value);
StringBuilder revolutions = map.get(Fields.TOP_DEAD_CENTER_MESSAGE);
statusPanel.setRevolutions(revolutions);
// Create images for any new keys
for (String imageName : map.getMap().keySet()) {
createSecondaryImage(imageName);
}
// Update existing images
for (String imageName : images.keySet()) {
UpDownImage image = images.get(imageName);
if (image == null)
throw new IllegalStateException("image not found for " + imageName);
StringBuilder sb = map.getMap().get(imageName);
String report = sb == null ? "" : sb.toString();
image.setRevolutions(revolutions);
List<EngineReport.UpDown> list = EngineReport.parse(report);
EngineReport wr = new EngineReport(list);
image.setWaveReport(wr, revolutions);
}
// Repaint now that we've updated state
SwingUtilities.invokeLater(() -> UiUtils.trueRepaint(imagePanel));
}
public JPanel getPanel() {
return mainPanel;
}
private void createSecondaryImage(String name) {
if (images.containsKey(name)) {
// already created, skip
return;
}
// Don't render a row for the TDC mark
if (Fields.TOP_DEAD_CENTER_MESSAGE.equalsIgnoreCase(name)) {
return;
}
int index = getInsertIndex(name, images.keySet());
log.info("Engine sniffer register channel " + name + " at idx " + index);
UpDownImage image = createImage(name);
images.put(name, image);
image.setTranslator(crank.createTranslator());
imagePanel.add(image, index);
imagePanel.setLayout(new GridLayout(images.size(), 1));
}
public static int getInsertIndex(String name, Set<String> strings) {
String[] mapKeys = new String[strings.size()];
int pos = 0;
for (String key : strings)
mapKeys[pos++] = key;
// int index = -Arrays.binarySearch(mapKeys, name) - 1;
int index = -Arrays.binarySearch(mapKeys, 0, mapKeys.length, name, INSTANCE) - 1;
if (index >= strings.size())
index = -1;
return index;
}
private void saveImage() {
int rpm = RpmModel.getInstance().getValue();
double maf = SensorCentral.getInstance().getValue(Sensor.MAF);
String fileName = FileLog.getDate() + "rpm_" + rpm + "_maf_" + maf + ".png";
UiUtils.saveImageWithPrompt(fileName, mainPanel, imagePanel);
}
private UpDownImage createImage(final String name) {
Color signalBody = Color.lightGray;
Color signalBorder = Color.blue;
if (name.startsWith("tach") || name.startsWith("dizzy")) {
signalBody = Color.yellow;
} else if (name.startsWith("t")) {
// trigger
} else if (name.startsWith("r")) {
// trailing coil
signalBody = new Color(0xffa400); // golden yellow
} else if (name.startsWith("c")) {
// coil
signalBody = Color.darkGray;
} else if (name.startsWith("HIP")) {
signalBody = Color.white;
} else if (name.startsWith("i")) {
// injection
signalBody = Color.green;
} else if (name.startsWith("map")) {
signalBody = Color.pink;
} else {
signalBody = Color.gray;
}
UpDownImage image = new UpDownImage(name) {
@Override
protected boolean isShowTdcLabel() {
/**
* TDC label is only displayed on the bottom UpDown image
*/
return name.equals(images.lastKey());
}
};
image.setSignalBody(signalBody);
image.setSignalBorder(signalBorder);
image.addMouseMotionListener(statusPanel.motionAdapter);
return image;
}
public void reloadFile() {
displayChart(ChartRepository.getInstance().getChart(0));
scrollControl.reset();
}
public ActionListener getTabSelectedListener() {
return new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (command != null)
command.requestFocus();
}
};
}
/**
* The job of this comparator is to place Spark charts before Injection charts
*/
static class ImageOrderComparator implements Comparator<String> {
@Override
public int compare(String o1, String o2) {
return fixNameForNicerOrder(o1).compareTo(fixNameForNicerOrder(o2));
}
String fixNameForNicerOrder(String s) {
if (s.toLowerCase().startsWith("t"))
return "a" + s; // let's place this at the top
if (s.toLowerCase().startsWith("hip"))
return "z" + s; // let's place this at the bottom
if (s.toLowerCase().startsWith("spa"))
return "d" + s;
return s;
}
}
}