shit
All checks were successful
Maven Build / build (push) Successful in 10m22s

This commit is contained in:
rattatwinko
2025-06-10 19:47:40 +02:00
parent 67e707eac8
commit 890abc303d
5 changed files with 262 additions and 9 deletions

4
.gitignore vendored
View File

@@ -2,6 +2,9 @@
# Compiled class file # Compiled class file
*.class *.class
# ---> CSV
*.csv
# Log file # Log file
*.log *.log
@@ -64,3 +67,4 @@ target/maven-archiver/pom.properties
target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
>>>>>>> f462d53 (yessssssssss!) >>>>>>> f462d53 (yessssssssss!)
logs/dyno_2025-06-10_19-41-50.csv

View File

@@ -16,7 +16,7 @@ public class AnalogMeter extends JPanel {
this.maxValue = maxValue; this.maxValue = maxValue;
this.currentValue = 0.0; this.currentValue = 0.0;
this.needleColor = needleColor; this.needleColor = needleColor;
setPreferredSize(new Dimension(200, 200)); setPreferredSize(new Dimension(250, 250));
} }
public void setValue(double value) { public void setValue(double value) {
@@ -41,8 +41,9 @@ public class AnalogMeter extends JPanel {
int centerY = getHeight() / 2; int centerY = getHeight() / 2;
int radius = Math.min(centerX, centerY) - 10; int radius = Math.min(centerX, centerY) - 10;
// Draw meter background // Draw meter background with gradient
g2d.setColor(Color.LIGHT_GRAY); GradientPaint gradient = new GradientPaint(0, 0, Color.LIGHT_GRAY, 0, getHeight(), Color.WHITE);
g2d.setPaint(gradient);
g2d.fillOval(centerX - radius, centerY - radius, radius * 2, radius * 2); g2d.fillOval(centerX - radius, centerY - radius, radius * 2, radius * 2);
// Draw scale and ticks // Draw scale and ticks
@@ -73,7 +74,7 @@ public class AnalogMeter extends JPanel {
int needleX = (int) (centerX + needleLength * Math.cos(angle)); int needleX = (int) (centerX + needleLength * Math.cos(angle));
int needleY = (int) (centerY - needleLength * Math.sin(angle)); int needleY = (int) (centerY - needleLength * Math.sin(angle));
g2d.setStroke(new BasicStroke(3)); g2d.setStroke(new BasicStroke(4)); // Thicker needle
g2d.drawLine(centerX, centerY, needleX, needleY); g2d.drawLine(centerX, centerY, needleX, needleY);
g2d.fillOval(centerX - 5, centerY - 5, 10, 10); g2d.fillOval(centerX - 5, centerY - 5, 10, 10);
@@ -82,6 +83,6 @@ public class AnalogMeter extends JPanel {
g2d.setFont(new Font("Arial", Font.BOLD, 16)); g2d.setFont(new Font("Arial", Font.BOLD, 16));
FontMetrics fm = g2d.getFontMetrics(); FontMetrics fm = g2d.getFontMetrics();
int labelWidth = fm.stringWidth(label); int labelWidth = fm.stringWidth(label);
g2d.drawString(label, centerX - labelWidth / 2, centerY + radius + 20); g2d.drawString(label, centerX - labelWidth / 2, centerY + radius + 30); // Adjusted position
} }
} }

View File

@@ -0,0 +1,53 @@
package com.puchdyno;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* Utility class that appends each sample coming from the dyno run to a csv file.
* The file will be created the first time it is needed and automatically closed
* when the logger is closed.
*/
public class CSVLogger implements Closeable {
private static final DateTimeFormatter FILE_TS = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss");
private static final DateTimeFormatter ROW_TS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
private final BufferedWriter writer;
/**
* Creates a new logger that writes to the given directory using the current timestamp as file name.
*/
public CSVLogger(File directory) throws IOException {
if (!directory.exists() && !directory.mkdirs()) {
throw new IOException("Unable to create log directory " + directory);
}
File file = new File(directory, "dyno_" + LocalDateTime.now().format(FILE_TS) + ".csv");
boolean created = file.createNewFile();
writer = new BufferedWriter(new FileWriter(file, true));
if (created) {
writer.write("timestamp,rpm,ps,nm");
writer.newLine();
}
}
public synchronized void log(double rpm, double ps, double nm) {
try {
writer.write(String.format("%s,%.2f,%.2f,%.2f", LocalDateTime.now().format(ROW_TS), rpm, ps, nm));
writer.newLine();
} catch (IOException e) {
// we can't do much more than print the stacktrace at this point
e.printStackTrace();
}
}
@Override
public void close() throws IOException {
writer.flush();
writer.close();
}
}

View File

@@ -18,7 +18,16 @@ public class DynoGUI extends JFrame {
private final AnalogMeter hpMeter; private final AnalogMeter hpMeter;
private final XYSeries powerSeries; private final XYSeries powerSeries;
private final XYSeries torqueSeries; private final XYSeries torqueSeries;
private final XYSeries efficiencySeries;
private final ChartPanel chartPanel; private final ChartPanel chartPanel;
private final JLabel maxLeistungLabel;
private final JLabel maxDrehmomentLabel;
private double maxLeistung = 0.0;
private double maxDrehmoment = 0.0;
private double maxLeistungRpm = 0.0;
private double maxLeistungNmAtHp = 0.0;
private double maxDrehmomentRpm = 0.0;
private double maxDrehmomentPsAtNm = 0.0;
public DynoGUI() { public DynoGUI() {
setTitle("Puch Maxi Dyno"); setTitle("Puch Maxi Dyno");
@@ -27,12 +36,24 @@ public class DynoGUI extends JFrame {
setLayout(new BorderLayout(10, 10)); setLayout(new BorderLayout(10, 10));
// Top panel: analog meters // Top panel: analog meters
JPanel meterPanel = new JPanel(new GridLayout(1, 2, 10, 10)); JPanel meterPanel = new JPanel(new GridLayout(1, 2, 20, 20));
meterPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); meterPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
rpmMeter = new AnalogMeter("RPM", 12000, Color.BLUE); rpmMeter = new AnalogMeter("RPM", 12000, Color.BLUE);
hpMeter = new AnalogMeter("Horse Power", 15, Color.RED); hpMeter = new AnalogMeter("Horse Power", 15, Color.RED);
meterPanel.add(rpmMeter);
meterPanel.add(hpMeter); rpmMeter.setToolTipText("Motordrehzahl in U/min");
hpMeter.setToolTipText("Motorleistung in PS");
JPanel rpmWrapper = new JPanel(new BorderLayout());
rpmWrapper.setBorder(BorderFactory.createTitledBorder("Drehzahl [RPM]"));
rpmWrapper.add(rpmMeter, BorderLayout.CENTER);
JPanel hpWrapper = new JPanel(new BorderLayout());
hpWrapper.setBorder(BorderFactory.createTitledBorder("Leistung [PS]"));
hpWrapper.add(hpMeter, BorderLayout.CENTER);
meterPanel.add(rpmWrapper);
meterPanel.add(hpWrapper);
add(meterPanel, BorderLayout.NORTH); add(meterPanel, BorderLayout.NORTH);
// Center panel: chart and labels // Center panel: chart and labels
@@ -46,24 +67,35 @@ public class DynoGUI extends JFrame {
leistungLabel = new JLabel("Leistung: 0.00 PS"); leistungLabel = new JLabel("Leistung: 0.00 PS");
drehmomentLabel = new JLabel("Drehmoment: 0.00 Nm"); drehmomentLabel = new JLabel("Drehmoment: 0.00 Nm");
maxLeistungLabel = new JLabel("Max Leistung: 0.00 PS");
maxDrehmomentLabel = new JLabel("Max Drehmoment: 0.00 Nm");
leistungLabel.setFont(new Font("SansSerif", Font.BOLD, 24)); leistungLabel.setFont(new Font("SansSerif", Font.BOLD, 24));
drehmomentLabel.setFont(new Font("SansSerif", Font.BOLD, 24)); drehmomentLabel.setFont(new Font("SansSerif", Font.BOLD, 24));
maxLeistungLabel.setFont(new Font("SansSerif", Font.PLAIN, 14));
maxDrehmomentLabel.setFont(new Font("SansSerif", Font.PLAIN, 14));
leistungLabel.setAlignmentX(Component.CENTER_ALIGNMENT); leistungLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
drehmomentLabel.setAlignmentX(Component.CENTER_ALIGNMENT); drehmomentLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
maxLeistungLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
maxDrehmomentLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
labelPanel.add(leistungLabel); labelPanel.add(leistungLabel);
labelPanel.add(Box.createVerticalStrut(10));
labelPanel.add(drehmomentLabel); labelPanel.add(drehmomentLabel);
labelPanel.add(Box.createVerticalStrut(5));
labelPanel.add(maxLeistungLabel);
labelPanel.add(Box.createVerticalStrut(5));
labelPanel.add(maxDrehmomentLabel);
centerPanel.add(labelPanel, BorderLayout.NORTH); centerPanel.add(labelPanel, BorderLayout.NORTH);
// Data series // Data series
powerSeries = new XYSeries("Power (PS)"); powerSeries = new XYSeries("Power (PS)");
torqueSeries = new XYSeries("Torque (Nm)"); torqueSeries = new XYSeries("Torque (Nm)");
efficiencySeries = new XYSeries("Efficiency");
XYSeriesCollection powerDataset = new XYSeriesCollection(powerSeries); XYSeriesCollection powerDataset = new XYSeriesCollection(powerSeries);
XYSeriesCollection torqueDataset = new XYSeriesCollection(torqueSeries); XYSeriesCollection torqueDataset = new XYSeriesCollection(torqueSeries);
XYSeriesCollection efficiencyDataset = new XYSeriesCollection(efficiencySeries);
// Date for chart title // Date for chart title
String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy")); String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy"));
@@ -107,6 +139,15 @@ public class DynoGUI extends JFrame {
plot.setDataset(1, torqueDataset); plot.setDataset(1, torqueDataset);
plot.mapDatasetToRangeAxis(1, 1); plot.mapDatasetToRangeAxis(1, 1);
// Efficiency axis (additional, right side 2)
NumberAxis effAxis = new NumberAxis("Effizienz [arb.u.]");
effAxis.setAutoRangeIncludesZero(false);
effAxis.setTickLabelFont(new Font("SansSerif", Font.PLAIN, 12));
effAxis.setLabelFont(new Font("SansSerif", Font.BOLD, 14));
plot.setRangeAxis(2, effAxis);
plot.setDataset(2, efficiencyDataset);
plot.mapDatasetToRangeAxis(2, 2);
// X axis // X axis
NumberAxis xAxis = (NumberAxis) plot.getDomainAxis(); NumberAxis xAxis = (NumberAxis) plot.getDomainAxis();
xAxis.setRange(0.0, 12000.0); xAxis.setRange(0.0, 12000.0);
@@ -123,6 +164,12 @@ public class DynoGUI extends JFrame {
torqueRenderer.setSeriesPaint(0, new Color(38, 139, 210)); // Blue torqueRenderer.setSeriesPaint(0, new Color(38, 139, 210)); // Blue
plot.setRenderer(1, torqueRenderer); plot.setRenderer(1, torqueRenderer);
XYLineAndShapeRenderer effRenderer = new XYLineAndShapeRenderer(false, true);
effRenderer.setSeriesPaint(0, new Color(133, 153, 0)); // green
effRenderer.setSeriesShapesVisible(0, true);
effRenderer.setSeriesShape(0, new java.awt.geom.Ellipse2D.Double(-4, -4, 8, 8));
plot.setRenderer(2, effRenderer);
// Chart panel setup // Chart panel setup
chartPanel = new ChartPanel(chart); chartPanel = new ChartPanel(chart);
chartPanel.setPreferredSize(new Dimension(800, 400)); chartPanel.setPreferredSize(new Dimension(800, 400));
@@ -149,6 +196,36 @@ public class DynoGUI extends JFrame {
leistungLabel.setText(String.format("Leistung: %.2f PS", leistung)); leistungLabel.setText(String.format("Leistung: %.2f PS", leistung));
drehmomentLabel.setText(String.format("Drehmoment: %.2f Nm", drehmoment)); drehmomentLabel.setText(String.format("Drehmoment: %.2f Nm", drehmoment));
// update max hold values
boolean effUpdated = false;
if (leistung > maxLeistung) {
maxLeistung = leistung;
maxLeistungLabel.setText(String.format("Max Leistung: %.2f PS", maxLeistung));
maxLeistungRpm = rpm;
maxLeistungNmAtHp = drehmoment;
effUpdated = true;
}
if (drehmoment > maxDrehmoment) {
maxDrehmoment = drehmoment;
maxDrehmomentLabel.setText(String.format("Max Drehmoment: %.2f Nm", maxDrehmoment));
maxDrehmomentRpm = rpm;
maxDrehmomentPsAtNm = leistung;
effUpdated = true;
}
if (effUpdated) {
efficiencySeries.clear();
if (maxLeistungNmAtHp > 0) {
efficiencySeries.add(maxLeistungRpm, maxLeistung / maxLeistungNmAtHp);
}
if (maxDrehmoment > 0) {
double effNm = maxDrehmomentPsAtNm == 0 ? 0.0 : maxDrehmomentPsAtNm / maxDrehmoment;
// avoid duplicate point if same rpm
if (maxDrehmomentRpm != maxLeistungRpm)
efficiencySeries.add(maxDrehmomentRpm, effNm);
}
}
powerSeries.add(rpm, leistung); powerSeries.add(rpm, leistung);
torqueSeries.add(rpm, drehmoment); torqueSeries.add(rpm, drehmoment);
} }
@@ -156,6 +233,14 @@ public class DynoGUI extends JFrame {
public void resetChart() { public void resetChart() {
powerSeries.clear(); powerSeries.clear();
torqueSeries.clear(); torqueSeries.clear();
efficiencySeries.clear();
}
public void resetMaxHold() {
maxLeistung = 0.0;
maxDrehmoment = 0.0;
maxLeistungLabel.setText("Max Leistung: 0.00 PS");
maxDrehmomentLabel.setText("Max Drehmoment: 0.00 Nm");
} }
public JFreeChart getChart() { public JFreeChart getChart() {

View File

@@ -39,6 +39,9 @@ public class Main {
private static ScheduledExecutorService serialExecutor; private static ScheduledExecutorService serialExecutor;
private static BufferedReader serialReader; private static BufferedReader serialReader;
private static CSVLogger csvLogger;
private static boolean measurementActive = false;
public static void main(String[] args) { public static void main(String[] args) {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
dynoGUI = new DynoGUI(); dynoGUI = new DynoGUI();
@@ -67,10 +70,22 @@ public class Main {
resetChartButton.addActionListener(e -> dynoGUI.resetChart()); resetChartButton.addActionListener(e -> dynoGUI.resetChart());
controlPanel.add(resetChartButton); controlPanel.add(resetChartButton);
JButton resetMaxButton = new JButton("Reset Max-Hold");
resetMaxButton.addActionListener(e -> dynoGUI.resetMaxHold());
controlPanel.add(resetMaxButton);
JButton reconnectButton = new JButton("Reconnect Serial"); JButton reconnectButton = new JButton("Reconnect Serial");
reconnectButton.addActionListener(e -> reconnectSerial()); reconnectButton.addActionListener(e -> reconnectSerial());
controlPanel.add(reconnectButton); controlPanel.add(reconnectButton);
JButton startMeasButton = new JButton("Start Messung");
startMeasButton.addActionListener(e -> startMeasurement());
controlPanel.add(startMeasButton);
JButton stopMeasButton = new JButton("Stop Messung");
stopMeasButton.addActionListener(e -> stopMeasurement());
controlPanel.add(stopMeasButton);
dynoGUI.add(controlPanel, BorderLayout.SOUTH); dynoGUI.add(controlPanel, BorderLayout.SOUTH);
dynoGUI.revalidate(); dynoGUI.revalidate();
dynoGUI.repaint(); dynoGUI.repaint();
@@ -144,6 +159,10 @@ public class Main {
testDataExecutor = null; testDataExecutor = null;
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
dynoGUI.updateWerte(0, 0, 0); // Reset gauges dynoGUI.updateWerte(0, 0, 0); // Reset gauges
if (csvLogger != null) {
try { csvLogger.close(); } catch (Exception ex) { ex.printStackTrace(); }
csvLogger = null;
}
}); });
} }
} }
@@ -262,6 +281,11 @@ public class Main {
currentSerialPort = null; currentSerialPort = null;
} }
if (csvLogger != null) {
try { csvLogger.close(); } catch (Exception ex) { ex.printStackTrace(); }
csvLogger = null;
}
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
dynoGUI.updateWerte(0, 0, 0); // Reset gauges dynoGUI.updateWerte(0, 0, 0); // Reset gauges
}); });
@@ -292,6 +316,19 @@ public class Main {
lastTimestamp = currentTimestamp; lastTimestamp = currentTimestamp;
lastOmega = currentOmega; lastOmega = currentOmega;
if (measurementActive) {
if (csvLogger == null) {
try {
csvLogger = new CSVLogger(new java.io.File("logs"));
} catch (Exception ex) {
System.err.println("Could not create CSV logger: " + ex.getMessage());
}
}
if (csvLogger != null) {
csvLogger.log(rpm, leistung, drehmoment);
}
}
} }
private static void processRPMData(double rpm, double forcedDeltaTime) { private static void processRPMData(double rpm, double forcedDeltaTime) {
@@ -306,6 +343,19 @@ public class Main {
lastOmega = currentOmega; lastOmega = currentOmega;
lastTimestamp = System.currentTimeMillis(); lastTimestamp = System.currentTimeMillis();
if (measurementActive) {
if (csvLogger == null) {
try {
csvLogger = new CSVLogger(new java.io.File("logs"));
} catch (Exception ex) {
System.err.println("Could not create CSV logger: " + ex.getMessage());
}
}
if (csvLogger != null) {
csvLogger.log(rpm, leistung, drehmoment);
}
}
} }
private static double rpmToOmega(double rpm) { private static double rpmToOmega(double rpm) {
@@ -340,9 +390,13 @@ public class Main {
JButton savePdfButton = new JButton("Save as PDF"); JButton savePdfButton = new JButton("Save as PDF");
savePdfButton.addActionListener(e -> saveChartAsPdf(printChartPanel)); savePdfButton.addActionListener(e -> saveChartAsPdf(printChartPanel));
JButton savePngButton = new JButton("Save as PNG");
savePngButton.addActionListener(e -> saveChartAsPng(printChartPanel));
JPanel controlPanel = new JPanel(); JPanel controlPanel = new JPanel();
controlPanel.add(printButton); controlPanel.add(printButton);
controlPanel.add(savePdfButton); controlPanel.add(savePdfButton);
controlPanel.add(savePngButton);
chartFrame.add(controlPanel, BorderLayout.SOUTH); chartFrame.add(controlPanel, BorderLayout.SOUTH);
chartFrame.setLocationRelativeTo(dynoGUI); chartFrame.setLocationRelativeTo(dynoGUI);
@@ -391,4 +445,60 @@ public class Main {
} }
} }
} }
private static void saveChartAsPng(ChartPanel chartPanel) {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle("Save Chart as PNG");
fileChooser.setFileFilter(new FileNameExtensionFilter("PNG Image (*.png)", "png"));
int userSelection = fileChooser.showSaveDialog(null);
if (userSelection == JFileChooser.APPROVE_OPTION) {
File fileToSave = fileChooser.getSelectedFile();
String filePath = fileToSave.getAbsolutePath();
if (!filePath.toLowerCase().endsWith(".png")) {
filePath += ".png";
}
File finalFile = new File(filePath);
try {
org.jfree.chart.ChartUtils.saveChartAsPNG(finalFile, chartPanel.getChart(), chartPanel.getWidth(), chartPanel.getHeight());
JOptionPane.showMessageDialog(null, "Chart saved as PNG successfully!\n" + finalFile.getAbsolutePath(), "Save Successful", JOptionPane.INFORMATION_MESSAGE);
} catch (Exception ex) {
JOptionPane.showMessageDialog(null, "Error saving chart as PNG: " + ex.getMessage(), "Save Error", JOptionPane.ERROR_MESSAGE);
ex.printStackTrace();
}
}
}
private static void startMeasurement() {
if (measurementActive) {
JOptionPane.showMessageDialog(null, "Messung läuft bereits.", "Info", JOptionPane.INFORMATION_MESSAGE);
return;
}
measurementActive = true;
dynoGUI.resetChart();
dynoGUI.resetMaxHold();
if (csvLogger != null) {
try { csvLogger.close(); } catch (Exception ex) { ex.printStackTrace(); }
}
try {
csvLogger = new CSVLogger(new java.io.File("logs"));
} catch (Exception ex) {
csvLogger = null;
System.err.println("CSV Logger konnte nicht erstellt werden: " + ex.getMessage());
}
}
private static void stopMeasurement() {
if (!measurementActive) {
JOptionPane.showMessageDialog(null, "Keine laufende Messung.", "Info", JOptionPane.INFORMATION_MESSAGE);
return;
}
measurementActive = false;
if (csvLogger != null) {
try { csvLogger.close(); } catch (Exception ex) { ex.printStackTrace(); }
csvLogger = null;
}
JOptionPane.showMessageDialog(null, "Messung gestoppt.", "Info", JOptionPane.INFORMATION_MESSAGE);
}
} }