This commit is contained in:
rusefi 2024-11-11 17:59:44 -05:00
commit ac377803cf
2 changed files with 292 additions and 147 deletions

View File

@ -1,55 +1,69 @@
package com.rusefi.ts_plugin;
import com.efiAnalytics.plugin.ecu.ControllerAccess;
import com.efiAnalytics.plugin.ecu.ControllerException;
import com.efiAnalytics.plugin.ecu.ControllerParameter;
import com.efiAnalytics.plugin.ecu.OutputChannelClient;
import com.efiAnalytics.plugin.ecu.*;
import com.rusefi.core.ui.AutoupdateUtil;
import com.rusefi.models.Utils;
import org.putgemin.VerticalFlowLayout;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class KnockAnalyzerTab {
private enum CanvasType {
CT_ALL,
CT_SENSORS,
CT_CYLINDERS,
}
private final Supplier<ControllerAccess> controllerAccessSupplier;
String ecuControllerName;
private final JComponent content = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 5, 5));
JComponent allDraw = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 5));
JComponent canvasesComponent = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 5, 5));
private final JLabel status = new JLabel();
private final JButton button = new JButton("Start");
private final JButton buttonStartStop = new JButton("Start");
private final JButton buttonAll = new JButton("All");
private final JButton buttonSensors = new JButton("Sensors");
private final JButton buttonCylinders = new JButton("Cylinders");
private boolean started = false;
private boolean flushed = false;
private final int[] line_sum_index = {0};
private int channel = 0;
private int cylinder = 0;
private final KnockCanvas canvas = new KnockCanvas();
private final KnockCanvas canvas2 = new KnockCanvas();
private float[] values = new float[64];
private int cylindersCount = 0;
private CanvasType canvasType = CanvasType.CT_ALL;
private ArrayList<KnockCanvas> canvases = new ArrayList<>();
private final KnockMagnitudeCanvas magnituges = new KnockMagnitudeCanvas();
public KnockAnalyzerTab(Supplier<ControllerAccess> controllerAccessSupplier) {
this.controllerAccessSupplier = controllerAccessSupplier;
this.setStartButtonState();
this.setStartButtonState();
String ecuControllerName = this.controllerAccessSupplier.get().getEcuConfigurationNames()[0];
ecuControllerName = this.controllerAccessSupplier.get().getEcuConfigurationNames()[0];
try {
controllerAccessSupplier.get().getOutputChannelServer().subscribe(ecuControllerName, "m_knockFrequencyStart", new OutputChannelClient() {
@Override
public void setCurrentOutputChannelValue(String name, double v) {
canvas.setFrequencyStart((int)v);
canvas2.setFrequencyStart((int)v);
magnituges.setFrequencyStart((int)v);
//canvas.repaint();
int frequency = (int)v;
canvases.forEach(c -> c.setFrequencyStart(frequency));
magnituges.setFrequencyStart(frequency);
}
});
} catch (ControllerException ee) {
@ -60,16 +74,40 @@ public class KnockAnalyzerTab {
controllerAccessSupplier.get().getOutputChannelServer().subscribe(ecuControllerName, "m_knockFrequencyStep", new OutputChannelClient() {
@Override
public void setCurrentOutputChannelValue(String name, double v) {
canvas.setFrequencyStep((float)v);
canvas2.setFrequencyStep((float)v);
magnituges.setFrequencyStep((float)v);
//canvas.repaint();
float frequencyStep = (float)v;
canvases.forEach(c -> c.setFrequencyStep(frequencyStep));
magnituges.setFrequencyStep(frequencyStep);
}
});
} catch (ControllerException ee) {
System.out.println(ee.getMessage());
}
try {
controllerAccessSupplier.get().getOutputChannelServer().subscribe(ecuControllerName, "m_knockSpectrumChannelCyl", (name, v) -> {
long value = (long)v;
flush();
KnockAnalyzerTab.this.channel = (int)(value >>> 8) & 0xFF;
KnockAnalyzerTab.this.cylinder = (int)(value & 0xFF);
});
} catch (ControllerException ee) {
System.out.println(ee.getMessage());
}
try {
ControllerParameter cylindersCountParameter = controllerAccessSupplier.get().getControllerParameterServer().getControllerParameter(ecuControllerName, "cylindersCount");
if(cylindersCountParameter != null) {
double value = cylindersCountParameter.getScalarValue();
KnockAnalyzerTab.this.cylindersCount = (int)(value);
}
} catch (ControllerException ee) {
System.out.println(ee.getMessage());
}
try {
String[] outputChannelNames = this.controllerAccessSupplier.get().getOutputChannelServer().getOutputChannels(ecuControllerName);
@ -78,10 +116,6 @@ public class KnockAnalyzerTab {
.collect(Collectors.toList())
.toArray(new String[0]);
float[] values = new float[64];
button.setText(this.getEnabled() == "true" ? "Stop" : "Start");
int checksum = 0;
for (int i = 0; i< 16; ++i) {
checksum += i;
@ -92,48 +126,36 @@ public class KnockAnalyzerTab {
String name = spectrums[i];
int finalChecksum = checksum;
controllerAccessSupplier.get().getOutputChannelServer().subscribe(ecuControllerName, name, new OutputChannelClient() {
@Override
public void setCurrentOutputChannelValue(String name, double v) {
//value.setText(String.valueOf(v));
controllerAccessSupplier.get().getOutputChannelServer().subscribe(ecuControllerName, name, (name1, v) -> {
if(!started)
{
// SwingUtilities.invokeLater(() -> AutoupdateUtil.trueLayout(content));
return;
}
if(!started)
{
canvas.getComponent().repaint();
canvas2.getComponent().repaint();
return;
}
flushed = false;
String indexStr = name.substring(15);
int index = Integer.parseInt(indexStr) - 1;
//values[index] = (int)v;
String indexStr = name1.substring(15);
int index = Integer.parseInt(indexStr) - 1;
long value = (long)v;
long value = (long)v;
long a = (value >>> 24) & 0xFF;
long b = (value >>> 16) & 0xFF;
long c = (value >>> 8) & 0xFF;
long d = value & 0xFF;
long a = (value >>> 24) & 0xFF;
long b = (value >>> 16) & 0xFF;
long c = (value >>> 8) & 0xFF;
long d = value & 0xFF;
values[index * 4] = a;
values[(index * 4) + 1] = b;
values[(index * 4) + 2] = c;
values[(index * 4) + 3] = d;
values[index * 4] = a;
values[(index * 4) + 1] = b;
values[(index * 4) + 2] = c;
values[(index * 4) + 3] = d;
line_sum_index[0] += index;
if(line_sum_index[0] >= finalChecksum) {
line_sum_index[0] += index;
if(line_sum_index[0] >= finalChecksum) {
if(channel == 1) {
canvas.processValues(values);
canvas.getComponent().repaint();
}
else {
canvas2.processValues(values);
canvas2.getComponent().repaint();
}
flush();
line_sum_index[0] = 0;
}
line_sum_index[0] = 0;
}
});
} catch (ControllerException ee) {
@ -145,63 +167,98 @@ public class KnockAnalyzerTab {
}
try {
controllerAccessSupplier.get().getOutputChannelServer().subscribe(ecuControllerName, "m_knockSpectrumChannelCyl", new OutputChannelClient() {
@Override
public void setCurrentOutputChannelValue(String name, double v) {
long value = (long)v;
KnockAnalyzerTab.this.channel = (int)(value >>> 8) & 0xFF;
KnockAnalyzerTab.this.cylinder = (int)(value & 0xFF);
}
});
} catch (ControllerException ee) {
System.out.println(ee.getMessage());
}
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
boolean started = KnockAnalyzerTab.this.setStartButtonState();
controllerAccessSupplier.get().getControllerParameterServer().updateParameter(ecuControllerName, "enableKnockSpectrogram", started ? 1.0 : 0.0);
} catch (ControllerException ee) {
System.out.println(ee.getMessage());
}
}
buttonStartStop.addActionListener(e -> {
boolean enabled = this.getEnabledEcu();
KnockAnalyzerTab.this.setStartState(!enabled);
KnockAnalyzerTab.this.setEnabledEcu(!enabled);
});
JComponent buttons = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 5));
content.add(status);
content.add(button);
buttons.add(status);
buttons.add(buttonStartStop);
buttons.add(buttonAll);
buttons.add(buttonSensors);
buttons.add(buttonCylinders);
KnockMouseListener kml = new KnockMouseListener(this.canvas);
KnockMotionListener kmml = new KnockMotionListener(this.canvas, this.magnituges);
KnockKeyListener l = new KnockKeyListener();
initCanvas(kmml, kml, l, canvas.getComponent());
content.add(canvas.getComponent());
buttonAll.addActionListener(e -> {
canvasType = CanvasType.CT_ALL;
createCanvas(canvasType);
buttonStartStop.doClick();
buttonStartStop.doClick();
});
buttonSensors.addActionListener(e -> {
canvasType = CanvasType.CT_SENSORS;
createCanvas(canvasType);
buttonStartStop.doClick();
buttonStartStop.doClick();
});
buttonCylinders.addActionListener(e -> {
canvasType = CanvasType.CT_CYLINDERS;
createCanvas(canvasType);
buttonStartStop.doClick();
buttonStartStop.doClick();
});
content.add(buttons);
KnockMouseListener kml2 = new KnockMouseListener(this.canvas2);
KnockMotionListener kmml2 = new KnockMotionListener(this.canvas2, this.magnituges);
KnockKeyListener l2 = new KnockKeyListener();
initCanvas(kmml2, kml2, l2, canvas2.getComponent());
content.add(canvas2.getComponent());
JScrollPane canvasScroll = new JScrollPane(canvasesComponent, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
canvasScroll.setPreferredSize(new Dimension(840, 800));
canvasScroll.setMinimumSize(new Dimension(840, 800));
canvasScroll.setMaximumSize(new Dimension(840, 800));
allDraw.add(canvasScroll);
content.add(allDraw);
JComponent magnituges = this.magnituges.getComponent();
magnituges.setFocusable(true);
magnituges.setFocusTraversalKeysEnabled(false);
magnituges.setFocusable(true);
magnituges.setDoubleBuffered(true);
magnituges.setPreferredSize(new Dimension(800, 200));
magnituges.setMinimumSize(new Dimension(800, 200));
content.add(magnituges);
magnituges.setPreferredSize(new Dimension(760, 200));
magnituges.setMinimumSize(new Dimension(760, 200));
allDraw.add(magnituges);
AutoupdateUtil.trueLayout(content);
createCanvas(CanvasType.CT_ALL);
boolean enabled = this.getEnabledEcu();
this.setStartState(enabled);
SwingUtilities.invokeLater(() -> AutoupdateUtil.trueLayout(content));
}
private void flush() {
if(!started || flushed)
{
return;
}
switch (canvasType) {
case CT_ALL:
canvases.forEach(canvas -> {
canvas.processValues(values);
canvas.getComponent().repaint();
});
break;
case CT_SENSORS:
assert channel < canvases.size();
canvases.get(channel).processValues(values);
break;
case CT_CYLINDERS:
assert cylinder < canvases.size();
canvases.get(cylinder).processValues(values);
break;
}
canvases.forEach(canvas -> {
canvas.getComponent().repaint();
});
for (int i = 0; i < values.length; ++i) {
values[i] = 0;
}
flushed = true;
}
private void initCanvas(KnockMotionListener kmml, KnockMouseListener kml, KnockKeyListener l, JComponent canvas) {
@ -217,32 +274,98 @@ public class KnockAnalyzerTab {
canvas.setMinimumSize(new Dimension(800, 200));
}
public String getEnabled() {
public void createCanvas(CanvasType canvasType) {
this.clearCanvas();
switch (canvasType) {
case CT_ALL:
createCanvas(1,1);
break;
case CT_SENSORS:
createCanvasSensors();
break;
case CT_CYLINDERS:
createCanvasCylinders();
break;
default:
throw new Error("Unknown CanvasType");
}
refreshCanvases();
}
public void clearCanvas() {
this.canvases.clear();
canvasesComponent.removeAll();
this.refreshCanvases();
}
public void refreshCanvases() {
SwingUtilities.invokeLater(() -> AutoupdateUtil.trueLayout(canvasesComponent));
SwingUtilities.invokeLater(() -> AutoupdateUtil.trueLayout(content));
}
public void createCanvas(int number, int divider) {
KnockCanvas canvas = new KnockCanvas(number, divider);
KnockMouseListener kml = new KnockMouseListener(canvas);
KnockMotionListener kmml = new KnockMotionListener(canvas, this.magnituges);
KnockKeyListener l = new KnockKeyListener();
initCanvas(kmml, kml, l, canvas.getComponent());
canvasesComponent.add(canvas.getComponent());
canvases.add(canvas);
}
public void createCanvasSensors() {
this.createCanvas(1, 2);
this.createCanvas(2, 2);
}
public void createCanvasCylinders() {
for(int i = 0; i < this.cylindersCount; ++i){
this.createCanvas(i + 1, this.cylindersCount);
}
}
public boolean getEnabledEcu() {
try {
String ecuControllerName = this.controllerAccessSupplier.get().getEcuConfigurationNames()[0];
ControllerParameter enable = controllerAccessSupplier.get().getControllerParameterServer().getControllerParameter(ecuControllerName, "enableKnockSpectrogram");
String enabled = enable.getStringValue();
return enabled;
return enabled.indexOf("true") > 0;
} catch (ControllerException ee) {
System.out.println(ee.getMessage());
}
return "false";
return false;
}
public boolean setStartButtonState() {
String enabled = this.getEnabled();
public void setEnabledEcu(boolean enabled) {
try {
String ecuControllerName = this.controllerAccessSupplier.get().getEcuConfigurationNames()[0];
controllerAccessSupplier.get().getControllerParameterServer().updateParameter(ecuControllerName, "enableKnockSpectrogram", enabled ? 1.0 : 0.0);
} catch (ControllerException ee) {
System.out.println(ee.getMessage());
}
}
this.started = enabled.indexOf("true") > 0 ? false : true;
public void setStartState(boolean enabled) {
this.started = enabled;
button.setText(this.started ? "Stop" : "Start");
buttonStartStop.setText(this.started ? "Stop" : "Start");
this.line_sum_index[0] = 0;
if (this.started) {
this.canvas.resetPeak();
if(this.started) {
canvases.forEach(canvas -> {
canvas.resetPeak();
});
}
refreshCanvases();
}
public boolean getStartState() {
return this.started;
}

View File

@ -1,5 +1,7 @@
package com.rusefi.ts_plugin;
import com.rusefi.core.ui.AutoupdateUtil;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
@ -29,28 +31,29 @@ public class KnockCanvas {
null);
g.drawImage(bufferedImage, size.width - offset, 0, size.width, size.height,null);
g.setColor(Color.RED);
//debug helper
/*g.setColor(Color.RED);
int line = (int)(currentIndexXAxis * bx);
g.drawLine(line, 0, line, height);
g.drawLine(line, 0, line, height);*/
Font f = g.getFont();
g.setFont(new Font(f.getName(), Font.CENTER_BASELINE, g.getFont().getSize() - 4));
g.setColor(Color.YELLOW);
for(int i = 0; i < yAxisHz.length; ++i) {
for(int i = 0; i < yAxisHz.length / 8; ++i) {
int y = hzToYScreen(yAxisHz[i], height);
int y = hzToYScreen(yAxisHz[i * 8], height);
g.setColor(Color.orange);
g.fillRect(0, y, 30, 1);
double hz = yAxisHz[i];
double hz = yAxisHz[i * 8];
g.drawString(Double.valueOf(round(hz, 1)).toString(), 35, y);
}
mouseFrequency = (float)YScreenToHz(mouse_y, height);
int mouseSpecX = canvasXToSpectrogramSpace(mouse_x);
int mouseSpecX = canvasXToSpectrogramSpace(mouse_x, false);
int mouseSpecY = canvasYToSpectrogramSpace(mouse_y);
mouseAmplitude = specrtogram[mouseSpecX][mouseSpecY];
@ -74,23 +77,26 @@ public class KnockCanvas {
g.drawString(Float.valueOf(mouseFrequency).toString() + " Hz", currentX * 3, 30);
g.drawString(Float.valueOf(mouseAmplitude).toString() + " Amp", currentX * 3, 50);
g.setFont(new Font(f.getName(), Font.BOLD, g.getFont().getSize() * 5));
g.setColor(Color.WHITE);
g.drawString(Integer.valueOf(number).toString(), 10, 30);
g.setFont(f);
g.setColor(Color.green);
g.fillOval(spectrogramSpaceToCanvasX(peakX)-5, spectrogramSpaceToCanvasY(peakY)-5, 10, 10);
g.setColor(Color.RED);
//g.fillOval(spectrogramSpaceToCanvasX(peakX)-5, spectrogramSpaceToCanvasY(peakY)-5, 10, 10);
int peakYCanvasSpace = spectrogramSpaceToCanvasY(peakY);
g.drawPolygon(new int[]{width, width-5, width}, new int[]{peakYCanvasSpace-3, peakYCanvasSpace, peakYCanvasSpace+3}, 3);
g.setColor(Color.WHITE);
int yy = hzToYScreen(currentFrequency, height);
g.fillRect(0, yy, width, 1);
g.setColor(Color.YELLOW);
int currentYCanvasSpace = hzToYScreen(currentFrequency, height);
g.drawPolygon(new int[]{width, width-5, width}, new int[]{currentYCanvasSpace-3, currentYCanvasSpace, currentYCanvasSpace+3}, 3);
g.setColor(Color.DARK_GRAY);
g.fillRect(0, mouse_y, width, 1);
//for test
//var yy2 = hzToYScreen(8117.68, height);
//g.fillRect(0, yy2, width, 1);
g.dispose();
}
};
@ -105,17 +111,17 @@ public class KnockCanvas {
private BufferedImage bufferedImage;
private Graphics2D bufferedGraphics;
int SPECTROGRAM_X_AXIS_SIZE = 1024 * 4;
int SPECTROGRAM_X_AXIS_SIZE_BASE = 1024 * 4;
int SPECTROGRAM_X_AXIS_SIZE = SPECTROGRAM_X_AXIS_SIZE_BASE;
float[][] specrtogram;
Color[] colorspace;
Color[] colors;
float[] amplitudesInColorSpace;
int spectrogramYAxisSize;
int currentIndexXAxis = 0;
int number = 1;
public double yAxisHz[];
int yAxisFequencyStart = -1;
@ -139,9 +145,12 @@ public class KnockCanvas {
float mouseAmplitude = 0;
public KnockCanvas() {
public KnockCanvas(int number, int divider) {
SwingUtilities.invokeLater(() -> component.repaint());
SwingUtilities.invokeLater(() -> AutoupdateUtil.trueLayout(component));
this.number = number;
SPECTROGRAM_X_AXIS_SIZE = SPECTROGRAM_X_AXIS_SIZE_BASE / divider;
bufferedImage = new BufferedImage(640,480, BufferedImage.TYPE_INT_RGB);
bufferedGraphics = bufferedImage.createGraphics();
@ -167,8 +176,6 @@ public class KnockCanvas {
spectrogramYAxisSize = yAxisHz.length;
specrtogram = new float[SPECTROGRAM_X_AXIS_SIZE][spectrogramYAxisSize];
colors = new Color[spectrogramYAxisSize];
amplitudesInColorSpace = new float[spectrogramYAxisSize];
}
public void setFrequencyStart(int start) {
@ -223,6 +230,7 @@ public class KnockCanvas {
float bx = (float)width / (float)SPECTROGRAM_X_AXIS_SIZE;
int maxYIndex = 0;
float min = Integer.MAX_VALUE;
float max = 0;
float minCurrent = min;
@ -249,11 +257,13 @@ public class KnockCanvas {
if(x == currentIndexXAxis && value > maxCurrent) {
maxCurrent = value;
maxYIndex = y;
}
}
}
currentAmplitude = maxCurrent;
currentFrequency = (float)this.yAxisHz[maxYIndex];
if(peakAmplitudeCol == currentIndexXAxis) {
peakFrequency = 0;
@ -297,9 +307,6 @@ public class KnockCanvas {
Color color = colorspace[color_index];
colors[(spectrogramYAxisSize-1) - y] = color;
amplitudesInColorSpace[y] = ((float)y) / (float) spectrogramYAxisSize;
//simple draw
bufferedGraphics.setColor(color);
int yr = height - (height / spectrogramYAxisSize) * y;
@ -430,19 +437,34 @@ public class KnockCanvas {
return (int)(((float)spectrogramYAxisSize - 1 - (float)y) * by);
}
int canvasXToSpectrogramSpace(int x) {
int canvasXToSpectrogramSpace(int x, boolean reverseOffset) {
int width = bufferedImage.getWidth();
float bx = (float)width / (float)SPECTROGRAM_X_AXIS_SIZE;
int offset = (int)(currentIndexXAxis * bx);
int imageX = 0;
int imageX;
if(offset < x) {
imageX = x - offset;
}
else {
imageX = (width - offset) + x;
if(reverseOffset) {
if(offset < x) {
imageX = x + offset;
if(imageX > width) {
imageX -= width;
}
}
else {
imageX = x - (width - offset);
if(imageX < 0) {
imageX = Math.abs(imageX);
}
}
} else {
if(offset < x) {
imageX = x - offset;
}
else {
imageX = x + (width - offset);
}
}
return (int)((float)imageX / bx);
@ -459,7 +481,7 @@ public class KnockCanvas {
}
float[] getCurrentMouseMagnitudes() {
int spectrogramSpaceX = this.canvasXToSpectrogramSpace(this.mouse_x);
int spectrogramSpaceX = this.canvasXToSpectrogramSpace(this.mouse_x, true);
return specrtogram[spectrogramSpaceX];
}
}