Engine sniffer refinements (#3905)

* don't need 2 digits of angle

* engine sniffer panel clarity

* refactor UpDownImage

* start at +1

* leave space on left side

* every 20ms

* happy test happy developer
This commit is contained in:
Matthew Kennedy 2022-02-09 16:04:20 -08:00 committed by GitHub
parent 4df1467a3a
commit dbcf27a2e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 123 additions and 104 deletions

View File

@ -82,8 +82,14 @@ public class EngineReport {
@Override
public int timeToScreen(int time, int width) {
double translated = (time - minTime) * 1.0 / getDuration();
return (int) (width * translated);
// 0 = left side
// 1 = right side
double fraction = (time - minTime) * 1.0 / getDuration();
// Space to leave on the left side to avoid overlap
int offset = 150;
return (int) (offset + (width - offset) * fraction);
}
@Override

View File

@ -38,7 +38,7 @@ public class RevolutionLog {
}
public static String angle2string(double angle) {
return Double.isNaN(angle) ? "n/a" : String.format("%.2f", angle);
return Double.isNaN(angle) ? "n/a" : String.format("%.1f", angle);
}
public double getCrankAngleByTime(double time) {

View File

@ -20,6 +20,6 @@ public class EngineReportTest {
assertEquals(14679, wr.getTimeAxisTranslator().getMinTime());
assertEquals(43849, wr.getTimeAxisTranslator().getMaxTime());
assertEquals(59, wr.getTimeAxisTranslator().timeToScreen(18134, 500));
assertEquals(191, wr.getTimeAxisTranslator().timeToScreen(18134, 500));
}
}

View File

@ -195,14 +195,13 @@ public class TriggerImage {
triggerPanel.removeAll();
UpDownImage upDownImage0 = new UpDownImage(re0, "trigger");
upDownImage0.showMouseOverText = false;
triggerPanel.add(upDownImage0);
upDownImage0.setRenderText(false);
UpDownImage upDownImage1 = new UpDownImage(re1, "trigger");
upDownImage1.showMouseOverText = false;
upDownImage1.setRenderText(false);
UpDownImage upDownImage2 = new UpDownImage(re2, "trigger");
upDownImage2.showMouseOverText = false;
upDownImage2.setRenderText(false);
boolean isSingleSensor = re1.getList().isEmpty();
boolean isThirdVisible = !re2.getList().isEmpty();
@ -218,8 +217,12 @@ public class TriggerImage {
triggerPanel.setLayout(new GridLayout(height, 1));
// always render the first channel
triggerPanel.add(upDownImage0);
if (!isSingleSensor)
triggerPanel.add(upDownImage1);
if (isThirdVisible)
triggerPanel.add(upDownImage2);

View File

@ -206,12 +206,12 @@ public class EngineSnifferPanel {
statusPanel.setRevolutions(revolutions);
/**
* First let's create images for new keys
*/
for (String imageName : map.getMap().keySet())
// 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)
@ -237,8 +237,15 @@ public class EngineSnifferPanel {
}
private void createSecondaryImage(String name) {
if (images.containsKey(name) || Fields.TOP_DEAD_CENTER_MESSAGE.equalsIgnoreCase(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());
@ -275,6 +282,7 @@ public class EngineSnifferPanel {
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")) {

View File

@ -73,7 +73,7 @@ public class EngineSnifferStatusPanel {
green.setForeground(UpDownImage.ENGINE_CYCLE_COLOR);
infoPanel.add(green);
JLabel red = new JLabel(" Red line is time scale");
JLabel red = new JLabel(" Red line is every 20ms");
red.setForeground(UpDownImage.TIME_SCALE_COLOR);
infoPanel.add(red);
}

View File

@ -29,7 +29,7 @@ import java.util.Date;
* @see EngineReport
*/
public class UpDownImage extends JPanel {
private static final int TIMESCALE_MULT = (int) (100 * EngineReport.ENGINE_SNIFFER_TICKS_PER_MS); // 100ms
private static final int TIMESCALE_MULT = (int) (20 * EngineReport.ENGINE_SNIFFER_TICKS_PER_MS); // 20ms
private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss");
private static final int LINE_SIZE = 20;
public static final Color TIME_SCALE_COLOR = Color.red;
@ -39,7 +39,6 @@ public class UpDownImage extends JPanel {
private static final BasicStroke ENGINE_CYCLE_STROKE = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10.0f,
new float[]{21.0f, 7.0f}, 0.0f);
private long lastUpdateTime;
private EngineReport engineReport;
private StringBuilder revolutions;
private final String name;
@ -49,12 +48,12 @@ public class UpDownImage extends JPanel {
* firmware is sending {@link Fields#PROTOCOL_OUTPIN}
*/
private String pin = "NO PIN";
private long mouseEnterTime;
/**
* we have variable color depending on signal name
*/
private Color signalBody = Color.lightGray;
private Color signalBorder = Color.blue;
private Color signalBorder = Color.GRAY;
private final Timer repaintTimer = new Timer(1000, new ActionListener() {
@Override
@ -62,8 +61,12 @@ public class UpDownImage extends JPanel {
UiUtils.trueRepaint(UpDownImage.this);
}
});
public boolean showMouseOverText = true;
private int currentMouseX = -100;
private boolean renderText = true;
public void setRenderText(boolean renderText) {
this.renderText = renderText;
}
public UpDownImage(final String name) {
this(EngineReport.MOCK, name);
@ -92,21 +95,6 @@ public class UpDownImage extends JPanel {
setWaveReport(wr, null);
setOpaque(true);
translator = createTranslator();
addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
currentMouseX = e.getX();
UiUtils.trueRepaint(UpDownImage.this);
}
});
addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
mouseEnterTime = System.currentTimeMillis();
UiUtils.trueRepaint(UpDownImage.this);
repaintTimer.restart();
}
});
}
public UpDownImage setTranslator(TimeAxisTranslator translator) {
@ -114,8 +102,7 @@ public class UpDownImage extends JPanel {
return this;
}
public TimeAxisTranslator createTranslator() {
return new TimeAxisTranslator() {
private final TimeAxisTranslator _translator = new TimeAxisTranslator() {
@Override
public int timeToScreen(int time, int width) {
return UpDownImage.this.engineReport.getTimeAxisTranslator().timeToScreen(time, width);
@ -141,13 +128,15 @@ public class UpDownImage extends JPanel {
return "TimeAxisTranslator";
}
};
public TimeAxisTranslator createTranslator() {
return this._translator;
}
public void setWaveReport(EngineReport wr, StringBuilder revolutions) {
this.engineReport = wr;
propagateDwellIntoSensor(wr);
this.revolutions = revolutions;
lastUpdateTime = System.currentTimeMillis();
UiUtils.trueRepaint(this);
}
@ -171,43 +160,39 @@ public class UpDownImage extends JPanel {
g.setColor(getBackground());
g.fillRect(0, 0, d.width, d.height);
if (showMouseOverText)
if (this.renderText) {
paintScaleLines(g2, d);
}
drawStartOfRevolution(g2, d);
d.height = (int)(0.95 * d.height);
for (EngineReport.UpDown upDown : engineReport.getList())
paintUpDown(d, upDown, g);
g2.setColor(Color.black);
int line = 0;
boolean justEntered = System.currentTimeMillis() - mouseEnterTime < 1000;
Font f = getFont();
if (justEntered) {
g.setFont(f.deriveFont(Font.BOLD, f.getSize() * 3));
g.setColor(Color.red);
}
if (showMouseOverText) {
String mouseOverText = NameUtil.getUiName(name);
if (pin != null)
mouseOverText += "/" + pin;
g.drawString(mouseOverText, 5, ++line * LINE_SIZE + (justEntered ? 30 : 0));
}
if (justEntered) {
// revert font & color
g.setFont(f);
g.setColor(Color.black);
if (!this.renderText) {
return;
}
if (showMouseOverText) {
g.drawString("Showing " + engineReport.getList().size() + " events", 5, ++line * LINE_SIZE);
// todo: this has to be broken in case of real engine since 'SYS_TICKS_PER_MS' here is not correct?
// g.drawString("Total seconds: " + (duration / EngineReport.SYS_TICKS_PER_MS / 1000.0), 5, ++line * LINE_SIZE);
g.drawString(FORMAT.format(new Date(lastUpdateTime)), 5, ++line * LINE_SIZE);
String mouseOverText = NameUtil.getUiName(name);
// if we have a pin name, append that
if (pin != null) {
mouseOverText += "/" + pin;
}
g.drawString(mouseOverText, 5, ++line * LINE_SIZE);
// When the row gets small, omit event count
if (d.height > 40) {
g.drawString(engineReport.getList().size() + " events", 5, ++line * LINE_SIZE);
}
}
private void drawStartOfRevolution(Graphics2D g2, Dimension d) {
@ -216,6 +201,8 @@ public class UpDownImage extends JPanel {
RevolutionLog time2rpm = RevolutionLog.parseRevolutions(revolutions);
Stroke oldStroke = g2.getStroke();
g2.setStroke(ENGINE_CYCLE_STROKE);
for (int time : time2rpm.keySet()) {
int x = translator.timeToScreen(time, d.width);
@ -228,6 +215,8 @@ public class UpDownImage extends JPanel {
g2.rotate(-Math.PI / 2);
}
}
g2.setStroke(oldStroke);
}
protected boolean isShowTdcLabel() {
@ -239,9 +228,6 @@ public class UpDownImage extends JPanel {
*/
private void paintScaleLines(Graphics2D g2, Dimension d) {
int fromMs = translator.getMinTime() / TIMESCALE_MULT;
g2.setStroke(TIME_SCALE_STROKE);
g2.setColor(TIME_SCALE_COLOR);
int toMs = translator.getMaxTime() / TIMESCALE_MULT;
if (toMs - fromMs > d.getWidth() / 5) {
@ -253,62 +239,78 @@ public class UpDownImage extends JPanel {
return;
}
for (int ms = fromMs; ms <= toMs; ms++) {
Stroke oldStroke = g2.getStroke();
g2.setStroke(TIME_SCALE_STROKE);
g2.setColor(TIME_SCALE_COLOR);
// start at +1 so we don't render a line at the left edge
for (int ms = fromMs + 1; ms <= toMs; ms++) {
int tick = ms * TIMESCALE_MULT;
int x = translator.timeToScreen(tick, d.width);
g2.drawLine(x, 0, x, d.height);
}
g2.setStroke(oldStroke);
}
private void paintUpDown(Dimension d, EngineReport.UpDown upDown, Graphics g) {
int x1 = translator.timeToScreen(upDown.upTime, d.width);
int x2 = translator.timeToScreen(upDown.downTime, d.width);
int y = (int) (0.2 * d.height);
// Draw the filled in rectangle body
g.setColor(signalBody);
g.fillRect(x1, y, x2 - x1, d.height - y);
g.fillRect(x1, 0, x2 - x1, d.height);
// Draw the outline box
g.setColor(signalBorder);
g.drawLine(x1, y, x2, y);
g.drawLine(x1, y, x1, d.height);
g.drawLine(x2, y, x2, d.height);
g.drawLine(x1, 0, x2, 0);
g.drawLine(x1, 0, x1, d.height);
g.drawLine(x2, 0, x2, d.height);
g.drawLine(x1, d.height, x2, d.height);
if (showMouseOverText) {
g.setColor(Color.red);
String durationString = String.format(" %.2fms", upDown.getDuration() / EngineReport.ENGINE_SNIFFER_TICKS_PER_MS);
g.drawString(durationString, x1, (int) (0.5 * d.height));
double fromAngle = time2rpm.getCrankAngleByTime(upDown.upTime);
double toAngle = time2rpm.getCrankAngleByTime(upDown.downTime);
String fromAngleStr = RevolutionLog.angle2string(fromAngle);
g.setColor(Color.darkGray);
if (upDown.upTriggerCycleIndex != -1) {
g.drawString("" + upDown.upTriggerCycleIndex, x1, (int) (0.25 * d.height));
// System.out.println("digital_event," + upDown.upIndex + "," + fromAngleStr);
}
if (upDown.downTriggerCycleIndex != -1) {
g.drawString("" + upDown.downTriggerCycleIndex, x2, (int) (0.25 * d.height));
// System.out.println("digital_event," + upDown.downIndex + "," + toAngleStr);
}
int offset = 3;
g.setColor(Color.black);
g.drawString(fromAngleStr, x1 + offset, (int) (0.75 * d.height));
g.setColor(Color.black);
if (Math.abs(x1 - currentMouseX) < 5) {
double angleDuration = toAngle - fromAngle;
String durationStr = RevolutionLog.angle2string(angleDuration);
g.drawString(durationStr, x1 + offset, (int) (1.0 * d.height));
} else {
String toAngleStr = RevolutionLog.angle2string(toAngle);
g.drawString(toAngleStr, x1 + offset, (int) (1.0 * d.height));
}
// No text if shorter than 25px
if (d.height < 25) {
return;
}
if (!this.renderText) {
return;
}
int duration = upDown.getDuration();
// don't render duration for zero duration or for trigger
if (duration != 0 && upDown.upTriggerCycleIndex == -1) {
g.setColor(Color.red);
String durationString = String.format(" %.2fms", duration / EngineReport.ENGINE_SNIFFER_TICKS_PER_MS);
g.drawString(durationString, x1, 15);
}
g.setColor(Color.darkGray);
if (upDown.upTriggerCycleIndex != -1) {
g.drawString("" + upDown.upTriggerCycleIndex, x1, (int) (0.25 * d.height));
}
// Skip second index if invalid or equal to start index
if (upDown.downTriggerCycleIndex != -1 && upDown.upTriggerCycleIndex != upDown.downTriggerCycleIndex) {
g.drawString("" + upDown.downTriggerCycleIndex, x2, (int) (0.25 * d.height));
}
// No angle text if shorter than 50px
if (d.height < 50) {
return;
}
int offset = 3;
g.setColor(Color.black);
double fromAngle = time2rpm.getCrankAngleByTime(upDown.upTime);
String fromAngleStr = RevolutionLog.angle2string(fromAngle);
g.drawString(fromAngleStr, x1 + offset, (int) (0.5 * d.height));
double toAngle = time2rpm.getCrankAngleByTime(upDown.downTime);
String toAngleStr = RevolutionLog.angle2string(toAngle);
g.drawString(toAngleStr, x1 + offset, (int) (0.75 * d.height));
}
public void setRevolutions(StringBuilder revolutions) {