yessssssssss!
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -58,3 +58,9 @@ replay_pid*
|
||||
*.out
|
||||
*.app
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
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/inputFiles.lst
|
||||
>>>>>>> f462d53 (yessssssssss!)
|
||||
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"java.configuration.updateBuildConfiguration": "interactive"
|
||||
}
|
||||
9
LICENCE
Normal file
9
LICENCE
Normal file
@@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 rattatwinko
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
10
arduinosrc/main.ino
Normal file
10
arduinosrc/main.ino
Normal file
@@ -0,0 +1,10 @@
|
||||
void setup() {
|
||||
Serial.begin(9600); // Ensure this matches the baud rate in Main.java
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Replace this with your actual RPM measurement logic
|
||||
double currentRPM = 1500.0; // Example RPM value
|
||||
Serial.println(currentRPM);
|
||||
delay(100); // Send data every 100ms, adjust as needed
|
||||
}
|
||||
83
pom.xml
Normal file
83
pom.xml
Normal file
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.puchdyno</groupId>
|
||||
<artifactId>PuchDyno</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- jSerialComm for serial port communication -->
|
||||
<dependency>
|
||||
<groupId>com.fazecast</groupId>
|
||||
<artifactId>jSerialComm</artifactId>
|
||||
<version>2.10.0</version>
|
||||
</dependency>
|
||||
<!-- JFreeChart for plotting -->
|
||||
<dependency>
|
||||
<groupId>org.jfree</groupId>
|
||||
<artifactId>jfreechart</artifactId>
|
||||
<version>1.5.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>11</source>
|
||||
<target>11</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addClasspath>true</addClasspath>
|
||||
<mainClass>com.puchdyno.Main</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>
|
||||
com.puchdyno.Main
|
||||
</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
2
run.sh
Executable file
2
run.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
mvn clean install
|
||||
java -jar target/PuchDyno-1.0-SNAPSHOT-jar-with-dependencies.jar
|
||||
87
src/main/java/com/puchdyno/AnalogMeter.java
Normal file
87
src/main/java/com/puchdyno/AnalogMeter.java
Normal file
@@ -0,0 +1,87 @@
|
||||
package com.puchdyno;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
|
||||
public class AnalogMeter extends JPanel {
|
||||
|
||||
private String label;
|
||||
private double maxValue;
|
||||
private double currentValue;
|
||||
private Color needleColor;
|
||||
|
||||
public AnalogMeter(String label, double maxValue, Color needleColor) {
|
||||
this.label = label;
|
||||
this.maxValue = maxValue;
|
||||
this.currentValue = 0.0;
|
||||
this.needleColor = needleColor;
|
||||
setPreferredSize(new Dimension(200, 200));
|
||||
}
|
||||
|
||||
public void setValue(double value) {
|
||||
if (value < 0) value = 0;
|
||||
if (value > maxValue) value = maxValue;
|
||||
this.currentValue = value;
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void setNeedleColor(Color color) {
|
||||
this.needleColor = color;
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
int centerX = getWidth() / 2;
|
||||
int centerY = getHeight() / 2;
|
||||
int radius = Math.min(centerX, centerY) - 10;
|
||||
|
||||
// Draw meter background
|
||||
g2d.setColor(Color.LIGHT_GRAY);
|
||||
g2d.fillOval(centerX - radius, centerY - radius, radius * 2, radius * 2);
|
||||
|
||||
// Draw scale and ticks
|
||||
g2d.setColor(Color.BLACK);
|
||||
g2d.drawOval(centerX - radius, centerY - radius, radius * 2, radius * 2);
|
||||
|
||||
for (int i = 0; i <= 10; i++) {
|
||||
double angle = Math.toRadians(210 - (i * 24)); // Start at 210 degrees, end at -30 degrees
|
||||
int x1 = (int) (centerX + radius * 0.8 * Math.cos(angle));
|
||||
int y1 = (int) (centerY - radius * 0.8 * Math.sin(angle));
|
||||
int x2 = (int) (centerX + radius * 0.9 * Math.cos(angle));
|
||||
int y2 = (int) (centerY - radius * 0.9 * Math.sin(angle));
|
||||
g2d.drawLine(x1, y1, x2, y2);
|
||||
|
||||
String tickLabel = String.valueOf((int) (maxValue / 10 * i));
|
||||
FontMetrics fm = g2d.getFontMetrics();
|
||||
int labelWidth = fm.stringWidth(tickLabel);
|
||||
int labelHeight = fm.getHeight();
|
||||
int labelX = (int) (centerX + radius * 0.7 * Math.cos(angle) - labelWidth / 2);
|
||||
int labelY = (int) (centerY - radius * 0.7 * Math.sin(angle) + labelHeight / 4);
|
||||
g2d.drawString(tickLabel, labelX, labelY);
|
||||
}
|
||||
|
||||
// Draw needle
|
||||
g2d.setColor(needleColor);
|
||||
double angle = Math.toRadians(210 - (currentValue / maxValue) * 240); // 240 degrees for the full sweep (210 to -30)
|
||||
int needleLength = (int) (radius * 0.75);
|
||||
int needleX = (int) (centerX + needleLength * Math.cos(angle));
|
||||
int needleY = (int) (centerY - needleLength * Math.sin(angle));
|
||||
|
||||
g2d.setStroke(new BasicStroke(3));
|
||||
g2d.drawLine(centerX, centerY, needleX, needleY);
|
||||
g2d.fillOval(centerX - 5, centerY - 5, 10, 10);
|
||||
|
||||
// Draw label
|
||||
g2d.setColor(Color.BLACK);
|
||||
g2d.setFont(new Font("Arial", Font.BOLD, 16));
|
||||
FontMetrics fm = g2d.getFontMetrics();
|
||||
int labelWidth = fm.stringWidth(label);
|
||||
g2d.drawString(label, centerX - labelWidth / 2, centerY + radius + 20);
|
||||
}
|
||||
}
|
||||
130
src/main/java/com/puchdyno/DynoGUI.java
Normal file
130
src/main/java/com/puchdyno/DynoGUI.java
Normal file
@@ -0,0 +1,130 @@
|
||||
package com.puchdyno;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import org.jfree.chart.ChartFactory;
|
||||
import org.jfree.chart.ChartPanel;
|
||||
import org.jfree.chart.JFreeChart;
|
||||
import org.jfree.chart.plot.PlotOrientation;
|
||||
import org.jfree.data.xy.XYSeries;
|
||||
import org.jfree.data.xy.XYSeriesCollection;
|
||||
import org.jfree.chart.plot.XYPlot;
|
||||
import org.jfree.chart.axis.NumberAxis;
|
||||
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
|
||||
|
||||
public class DynoGUI extends JFrame {
|
||||
private JLabel leistungLabel;
|
||||
private JLabel drehmomentLabel;
|
||||
private AnalogMeter rpmMeter;
|
||||
private AnalogMeter hpMeter;
|
||||
private XYSeries powerSeries;
|
||||
private XYSeries torqueSeries;
|
||||
private ChartPanel chartPanel;
|
||||
|
||||
public DynoGUI() {
|
||||
setTitle("Puch Maxi Dyno");
|
||||
setSize(1000, 700); // Increased size for more elements
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
// Top panel for analog meters
|
||||
JPanel meterPanel = new JPanel(new GridLayout(1, 2));
|
||||
rpmMeter = new AnalogMeter("RPM", 12000, Color.BLUE); // Max RPM, adjusted for new test range
|
||||
hpMeter = new AnalogMeter("Horse Power", 15, Color.BLUE); // Max HP, e.g., 15
|
||||
meterPanel.add(rpmMeter);
|
||||
meterPanel.add(hpMeter);
|
||||
add(meterPanel, BorderLayout.NORTH);
|
||||
|
||||
// Center panel for labels and chart
|
||||
JPanel centerPanel = new JPanel(new BorderLayout());
|
||||
|
||||
JPanel labelPanel = new JPanel(new GridLayout(2, 1));
|
||||
leistungLabel = new JLabel("Leistung: 0.00 PS", SwingConstants.CENTER);
|
||||
drehmomentLabel = new JLabel("Drehmoment: 0.00 Nm", SwingConstants.CENTER);
|
||||
leistungLabel.setFont(new Font("Arial", Font.BOLD, 24));
|
||||
drehmomentLabel.setFont(new Font("Arial", Font.BOLD, 24));
|
||||
labelPanel.add(leistungLabel);
|
||||
labelPanel.add(drehmomentLabel);
|
||||
centerPanel.add(labelPanel, BorderLayout.NORTH);
|
||||
|
||||
// Chart setup
|
||||
powerSeries = new XYSeries("Power (PS)");
|
||||
torqueSeries = new XYSeries("Torque (Nm)");
|
||||
XYSeriesCollection dataset = new XYSeriesCollection();
|
||||
dataset.addSeries(powerSeries);
|
||||
dataset.addSeries(torqueSeries);
|
||||
|
||||
JFreeChart chart = ChartFactory.createXYLineChart(
|
||||
"Leistungsdiagramm - Motorleistung",
|
||||
"U/min",
|
||||
"Leistung [PS]",
|
||||
dataset,
|
||||
PlotOrientation.VERTICAL,
|
||||
true,
|
||||
true,
|
||||
false
|
||||
);
|
||||
|
||||
XYPlot plot = chart.getXYPlot();
|
||||
|
||||
NumberAxis powerAxis = (NumberAxis) plot.getRangeAxis();
|
||||
powerAxis.setRange(0.0, 10.0);
|
||||
powerAxis.setLabel("Leistung [PS]");
|
||||
|
||||
NumberAxis torqueAxis = new NumberAxis("Drehmoment [Nm]");
|
||||
torqueAxis.setRange(0.0, 15.0);
|
||||
plot.setRangeAxis(1, torqueAxis);
|
||||
plot.mapDatasetToRangeAxis(1, 1);
|
||||
|
||||
NumberAxis xAxis = (NumberAxis) plot.getDomainAxis();
|
||||
xAxis.setRange(4000.0, 12000.0);
|
||||
xAxis.setLabel("U/min");
|
||||
|
||||
XYLineAndShapeRenderer renderer1 = new XYLineAndShapeRenderer();
|
||||
renderer1.setSeriesPaint(0, Color.RED);
|
||||
renderer1.setSeriesShapesVisible(0, false);
|
||||
plot.setRenderer(0, renderer1);
|
||||
|
||||
XYLineAndShapeRenderer renderer2 = new XYLineAndShapeRenderer();
|
||||
renderer2.setSeriesPaint(0, Color.BLUE);
|
||||
renderer2.setSeriesShapesVisible(0, false);
|
||||
plot.setRenderer(1, renderer2);
|
||||
|
||||
chartPanel = new ChartPanel(chart);
|
||||
chartPanel.setPreferredSize(new Dimension(600, 400));
|
||||
centerPanel.add(chartPanel, BorderLayout.CENTER);
|
||||
add(centerPanel, BorderLayout.CENTER);
|
||||
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
public void updateWerte(double leistung, double drehmoment, double rpm) {
|
||||
rpmMeter.setValue(rpm);
|
||||
hpMeter.setValue(leistung);
|
||||
|
||||
// Change HP meter needle color based on performance
|
||||
if (leistung >= 15.0) {
|
||||
hpMeter.setNeedleColor(Color.RED);
|
||||
} else if (leistung >= 10.0) {
|
||||
hpMeter.setNeedleColor(Color.ORANGE);
|
||||
} else {
|
||||
hpMeter.setNeedleColor(Color.BLUE);
|
||||
}
|
||||
|
||||
leistungLabel.setText(String.format("Leistung: %.2f PS", leistung));
|
||||
drehmomentLabel.setText(String.format("Drehmoment: %.2f Nm", drehmoment));
|
||||
|
||||
// Add data to chart
|
||||
powerSeries.add(rpm, leistung);
|
||||
torqueSeries.add(rpm, drehmoment);
|
||||
}
|
||||
|
||||
public void resetChart() {
|
||||
powerSeries.clear();
|
||||
torqueSeries.clear();
|
||||
}
|
||||
|
||||
public JFreeChart getChart() {
|
||||
return chartPanel.getChart();
|
||||
}
|
||||
}
|
||||
15
src/main/java/com/puchdyno/LeistungBerechner.java
Normal file
15
src/main/java/com/puchdyno/LeistungBerechner.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.puchdyno;
|
||||
|
||||
public class LeistungBerechner {
|
||||
private double J = 0.0285; // Trägheitsmoment in kg*m², adjusted for realistic values
|
||||
|
||||
public double berechneDrehmoment(double omegaAlt, double omegaNeu, double deltaZeit) {
|
||||
double alpha = (omegaNeu - omegaAlt) / deltaZeit;
|
||||
return J * Math.abs(alpha);
|
||||
}
|
||||
|
||||
public double berechneLeistung(double drehmoment, double omega) {
|
||||
double leistungWatt = drehmoment * omega;
|
||||
return leistungWatt / 735.5; // in PS
|
||||
}
|
||||
}
|
||||
428
src/main/java/com/puchdyno/Main.java
Normal file
428
src/main/java/com/puchdyno/Main.java
Normal file
@@ -0,0 +1,428 @@
|
||||
package com.puchdyno;
|
||||
|
||||
import com.fazecast.jSerialComm.SerialPort;
|
||||
import javax.swing.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.print.PrinterException;
|
||||
import java.awt.print.PrinterJob;
|
||||
import org.jfree.chart.ChartPanel;
|
||||
import org.jfree.chart.JFreeChart;
|
||||
import java.util.ArrayList;
|
||||
import java.util.TreeMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class Main {
|
||||
|
||||
private static DynoGUI dynoGUI;
|
||||
private static LeistungBerechner leistungBerechner;
|
||||
private static long lastTimestamp = 0;
|
||||
private static double lastOmega = 0.0;
|
||||
private static final double WHEEL_RADIUS_METERS = 0.25; // Example: 25 cm radius for the dyno wheel
|
||||
|
||||
private static boolean testMode = false;
|
||||
private static ScheduledExecutorService testDataExecutor;
|
||||
private static int testRpmCounter = 1000;
|
||||
private static final double[] TEST_RPM_VALUES = {
|
||||
0, 500, 1000, 1500, 2000, 2500, 3000, 3500,
|
||||
4000, 4500, 5000, 5500, 6000, 6500, 7000, 7500, 8000, 8500,
|
||||
9000, 9500, 10000, 10500, 11000, 11500, 11000, 10500, 10000, 9500,
|
||||
9000, 8500, 8000, 7500, 7000, 6500, 6000, 5500, 5000, 4500,
|
||||
4000, 3500, 3000, 2500, 2000, 1500, 1000, 500, 0
|
||||
};
|
||||
|
||||
private static int testRpmCurrentIndex = 0;
|
||||
private static double testRpmCurrentValue = 0.0;
|
||||
private static final double TEST_RPM_INCREMENT_STEP = 1.0; // Very small steps for extremely smooth simulation
|
||||
|
||||
private static java.util.List<Double> smoothRpmData;
|
||||
private static int smoothRpmDataIndex = 0;
|
||||
private static final double TEST_SIMULATION_DELTA_TIME = 0.05; // 50 milliseconds
|
||||
|
||||
private static TreeMap<Double, Double> simulatedPowerCurve;
|
||||
private static TreeMap<Double, Double> simulatedTorqueCurve;
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Initialize simulated curves
|
||||
simulatedPowerCurve = new TreeMap<>();
|
||||
simulatedPowerCurve.put(0.0, 0.0);
|
||||
simulatedPowerCurve.put(4200.0, 2.0);
|
||||
simulatedPowerCurve.put(6000.0, 5.0);
|
||||
simulatedPowerCurve.put(7000.0, 6.0);
|
||||
simulatedPowerCurve.put(8000.0, 7.5);
|
||||
simulatedPowerCurve.put(8945.0, 8.5); // Peak Power
|
||||
simulatedPowerCurve.put(9500.0, 8.0);
|
||||
simulatedPowerCurve.put(10500.0, 7.0);
|
||||
simulatedPowerCurve.put(11400.0, 6.5);
|
||||
simulatedPowerCurve.put(12000.0, 6.0);
|
||||
|
||||
simulatedTorqueCurve = new TreeMap<>();
|
||||
simulatedTorqueCurve.put(0.0, 0.0);
|
||||
simulatedTorqueCurve.put(4200.0, 3.0);
|
||||
simulatedTorqueCurve.put(5000.0, 4.0);
|
||||
simulatedTorqueCurve.put(6000.0, 5.0);
|
||||
simulatedTorqueCurve.put(8379.0, 6.8); // Peak Torque
|
||||
simulatedTorqueCurve.put(9000.0, 6.0);
|
||||
simulatedTorqueCurve.put(10000.0, 5.0);
|
||||
simulatedTorqueCurve.put(11400.0, 4.0);
|
||||
simulatedTorqueCurve.put(12000.0, 3.5);
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI = new DynoGUI();
|
||||
leistungBerechner = new LeistungBerechner();
|
||||
addControlPanel();
|
||||
// Optionally start in test mode for development
|
||||
// toggleTestMode();
|
||||
});
|
||||
}
|
||||
|
||||
private static void addControlPanel() {
|
||||
JPanel controlPanel = new JPanel();
|
||||
JButton toggleModeButton = new JButton("Toggle Test Mode");
|
||||
toggleModeButton.addActionListener(e -> toggleTestMode());
|
||||
controlPanel.add(toggleModeButton);
|
||||
|
||||
JButton startTestButton = new JButton("Start Test Run");
|
||||
startTestButton.addActionListener(e -> startTestRun());
|
||||
controlPanel.add(startTestButton);
|
||||
|
||||
JButton stopTestButton = new JButton("Stop Test Run");
|
||||
stopTestButton.addActionListener(e -> stopTestRun());
|
||||
controlPanel.add(stopTestButton);
|
||||
|
||||
JButton resetChartButton = new JButton("Reset Chart");
|
||||
resetChartButton.addActionListener(e -> dynoGUI.resetChart());
|
||||
controlPanel.add(resetChartButton);
|
||||
|
||||
dynoGUI.add(controlPanel, BorderLayout.SOUTH);
|
||||
dynoGUI.revalidate();
|
||||
dynoGUI.repaint();
|
||||
}
|
||||
|
||||
private static void toggleTestMode() {
|
||||
testMode = !testMode;
|
||||
if (testMode) {
|
||||
stopSerialPortListener();
|
||||
JOptionPane.showMessageDialog(null, "Switched to Test Mode. Click 'Start Test Run' to begin.", "Mode Changed", JOptionPane.INFORMATION_MESSAGE);
|
||||
} else {
|
||||
stopTestRun();
|
||||
startSerialPortListener();
|
||||
JOptionPane.showMessageDialog(null, "Switched to Live Mode. Attempting to connect to serial port.", "Mode Changed", JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private static void startTestRun() {
|
||||
if (!testMode) {
|
||||
JOptionPane.showMessageDialog(null, "Please switch to Test Mode first.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
if (testDataExecutor != null && !testDataExecutor.isShutdown()) {
|
||||
JOptionPane.showMessageDialog(null, "Test run already in progress.", "Info", JOptionPane.INFORMATION_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
dynoGUI.resetChart();
|
||||
lastTimestamp = 0; // Reset for a new run
|
||||
lastOmega = 0.0;
|
||||
testRpmCurrentIndex = 0;
|
||||
testRpmCurrentValue = TEST_RPM_VALUES[0]; // Start at the first RPM value
|
||||
|
||||
// Pre-generate smooth RPM data
|
||||
smoothRpmData = new ArrayList<>();
|
||||
if (TEST_RPM_VALUES.length > 0) {
|
||||
smoothRpmData.add(TEST_RPM_VALUES[0]); // Start with the first point
|
||||
}
|
||||
|
||||
for (int i = 0; i < TEST_RPM_VALUES.length - 1; i++) {
|
||||
double startRpm = TEST_RPM_VALUES[i];
|
||||
double endRpm = TEST_RPM_VALUES[i+1];
|
||||
|
||||
if (startRpm == endRpm) {
|
||||
// If start and end RPM are the same, just add it if it's not already the last element
|
||||
if (smoothRpmData.isEmpty() || smoothRpmData.get(smoothRpmData.size() - 1).doubleValue() != endRpm) {
|
||||
smoothRpmData.add(endRpm);
|
||||
}
|
||||
continue; // Move to the next segment
|
||||
}
|
||||
|
||||
// Determine the direction of change
|
||||
int direction = (endRpm > startRpm) ? 1 : -1;
|
||||
double currentRpm = startRpm;
|
||||
|
||||
// Generate intermediate points
|
||||
while (true) {
|
||||
double nextRpm = currentRpm + direction * TEST_RPM_INCREMENT_STEP;
|
||||
|
||||
// Check if we would overshoot the endRpm
|
||||
if ((direction > 0 && nextRpm > endRpm) || (direction < 0 && nextRpm < endRpm)) {
|
||||
// Add the exact endRpm if it's not already the last one
|
||||
if (smoothRpmData.isEmpty() || smoothRpmData.get(smoothRpmData.size() - 1).doubleValue() != endRpm) {
|
||||
smoothRpmData.add(endRpm);
|
||||
}
|
||||
break; // Segment finished
|
||||
} else {
|
||||
smoothRpmData.add(nextRpm);
|
||||
currentRpm = nextRpm;
|
||||
}
|
||||
}
|
||||
}
|
||||
smoothRpmDataIndex = 0;
|
||||
|
||||
testDataExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||
testDataExecutor.scheduleAtFixedRate(() -> {
|
||||
simulateSmoothRpmRun();
|
||||
}, 0, 50, TimeUnit.MILLISECONDS); // Update RPM every 50ms
|
||||
}
|
||||
|
||||
private static void simulateSmoothRpmRun() {
|
||||
if (smoothRpmDataIndex >= smoothRpmData.size()) {
|
||||
stopTestRun();
|
||||
JOptionPane.showMessageDialog(null, "Test run finished.", "Info", JOptionPane.INFORMATION_MESSAGE);
|
||||
showPrintableChart();
|
||||
return;
|
||||
}
|
||||
|
||||
double currentSimulatedRpm = smoothRpmData.get(smoothRpmDataIndex);
|
||||
processRPMData(currentSimulatedRpm, TEST_SIMULATION_DELTA_TIME);
|
||||
smoothRpmDataIndex++;
|
||||
}
|
||||
|
||||
private static void stopTestRun() {
|
||||
if (testDataExecutor != null) {
|
||||
testDataExecutor.shutdownNow();
|
||||
testDataExecutor = null;
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI.updateWerte(0, 0, 0); // Reset gauges
|
||||
dynoGUI.resetChart(); // Clear chart on stop
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static SerialPort currentSerialPort;
|
||||
private static ScheduledExecutorService serialExecutor;
|
||||
|
||||
private static void startSerialPortListener() {
|
||||
SerialPort[] comPorts = SerialPort.getCommPorts();
|
||||
if (comPorts.length == 0) {
|
||||
JOptionPane.showMessageDialog(null, "No serial ports found!", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
String[] portNames = new String[comPorts.length];
|
||||
for (int i = 0; i < comPorts.length; i++) {
|
||||
portNames[i] = comPorts[i].getSystemPortName();
|
||||
}
|
||||
|
||||
String selectedPortName = (String) JOptionPane.showInputDialog(
|
||||
null,
|
||||
"Choose a serial port:",
|
||||
"Serial Port Selection",
|
||||
JOptionPane.QUESTION_MESSAGE,
|
||||
null,
|
||||
portNames,
|
||||
portNames[0]);
|
||||
|
||||
if (selectedPortName == null) {
|
||||
JOptionPane.showMessageDialog(null, "No serial port selected. Exiting.", "Info", JOptionPane.INFORMATION_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
SerialPort chosenPort = null;
|
||||
for (SerialPort port : comPorts) {
|
||||
if (port.getSystemPortName().equals(selectedPortName)) {
|
||||
chosenPort = port;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (chosenPort == null) {
|
||||
JOptionPane.showMessageDialog(null, "Invalid serial port selected.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
final SerialPort finalChosenPort = chosenPort;
|
||||
currentSerialPort = finalChosenPort; // Store for later closing
|
||||
|
||||
finalChosenPort.setBaudRate(9600); // Common baud rate, can be adjusted
|
||||
if (finalChosenPort.openPort()) {
|
||||
System.out.println("Serial port " + finalChosenPort.getSystemPortName() + " opened successfully.");
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(finalChosenPort.getInputStream()));
|
||||
|
||||
serialExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||
serialExecutor.scheduleAtFixedRate(() -> {
|
||||
try {
|
||||
if (reader.ready()) {
|
||||
String line = reader.readLine();
|
||||
if (line != null && !line.trim().isEmpty()) {
|
||||
try {
|
||||
double rpm = Double.parseDouble(line.trim());
|
||||
processRPMData(rpm); // Call the main processRPMData for live data
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.println("Received non-numeric data: " + line);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error reading from serial port: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
stopSerialPortListener();
|
||||
JOptionPane.showMessageDialog(null, "Serial port error: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}, 0, 100, TimeUnit.MILLISECONDS); // Read every 100ms
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(null, "Failed to open serial port " + finalChosenPort.getSystemPortName(), "Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private static void stopSerialPortListener() {
|
||||
if (serialExecutor != null) {
|
||||
serialExecutor.shutdownNow();
|
||||
serialExecutor = null;
|
||||
}
|
||||
if (currentSerialPort != null && currentSerialPort.isOpen()) {
|
||||
currentSerialPort.closePort();
|
||||
System.out.println("Serial port " + currentSerialPort.getSystemPortName() + " closed.");
|
||||
currentSerialPort = null;
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI.updateWerte(0, 0, 0); // Reset gauges
|
||||
dynoGUI.resetChart(); // Clear chart on stop
|
||||
});
|
||||
}
|
||||
|
||||
// Overloaded processRPMData for live mode, calculates deltaZeit from system time
|
||||
private static void processRPMData(double rpm) {
|
||||
processRPMData(rpm, 0.0); // Pass 0.0 to indicate no forced delta time, use system time
|
||||
}
|
||||
|
||||
// Main processRPMData for both live and test mode, uses forcedDeltaTime if provided
|
||||
private static void processRPMData(double rpm, double forcedDeltaTime) {
|
||||
long currentTimestamp = System.currentTimeMillis();
|
||||
|
||||
double effectiveDeltaZeit;
|
||||
double leistung;
|
||||
double drehmoment;
|
||||
|
||||
if (forcedDeltaTime > 0) {
|
||||
// Test mode: Use simulated curves with linear interpolation
|
||||
effectiveDeltaZeit = forcedDeltaTime;
|
||||
|
||||
leistung = interpolate(simulatedPowerCurve, rpm);
|
||||
drehmoment = interpolate(simulatedTorqueCurve, rpm);
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI.updateWerte(leistung, drehmoment, rpm);
|
||||
});
|
||||
|
||||
// For test mode, we still need to update lastOmega for the next iteration
|
||||
// in case the next simulated RPM is used for an actual calculation (e.g. if we switch modes).
|
||||
lastOmega = rpmToOmega(rpm);
|
||||
lastTimestamp = currentTimestamp; // Also update timestamp for consistency, though not used in test mode calculation
|
||||
return;
|
||||
|
||||
} else {
|
||||
// Live mode: Calculate from actual sensor data
|
||||
if (lastTimestamp == 0) {
|
||||
lastTimestamp = currentTimestamp;
|
||||
lastOmega = rpmToOmega(rpm);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI.updateWerte(0, 0, rpm); // Update RPM, but no power/torque yet for the first point
|
||||
});
|
||||
return; // Skip calculations for the very first data point
|
||||
}
|
||||
effectiveDeltaZeit = (currentTimestamp - lastTimestamp) / 1000.0;
|
||||
// Ensure effectiveDeltaZeit is always positive and non-zero for live mode
|
||||
if (effectiveDeltaZeit <= 0) {
|
||||
effectiveDeltaZeit = 0.001; // Assign a small positive value to avoid division by zero
|
||||
}
|
||||
|
||||
double currentOmega = rpmToOmega(rpm);
|
||||
|
||||
// If there's no change in RPM, then acceleration is zero. Display zero power/torque.
|
||||
if (currentOmega == lastOmega) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI.updateWerte(0, 0, rpm); // Update RPM, but output zero power/torque
|
||||
});
|
||||
lastTimestamp = currentTimestamp; // Crucially update for the next iteration
|
||||
lastOmega = currentOmega; // Crucially update for the next iteration
|
||||
return;
|
||||
}
|
||||
|
||||
drehmoment = leistungBerechner.berechneDrehmoment(lastOmega, currentOmega, effectiveDeltaZeit);
|
||||
leistung = leistungBerechner.berechneLeistung(drehmoment, currentOmega);
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI.updateWerte(leistung, drehmoment, rpm);
|
||||
});
|
||||
|
||||
lastTimestamp = currentTimestamp;
|
||||
lastOmega = currentOmega;
|
||||
}
|
||||
}
|
||||
|
||||
// Linear interpolation method
|
||||
private static double interpolate(TreeMap<Double, Double> curve, double rpm) {
|
||||
if (curve.containsKey(rpm)) {
|
||||
return curve.get(rpm);
|
||||
}
|
||||
Map.Entry<Double, Double> lower = curve.floorEntry(rpm);
|
||||
Map.Entry<Double, Double> upper = curve.ceilingEntry(rpm);
|
||||
|
||||
if (lower == null) {
|
||||
return upper.getValue(); // RPM is below lowest defined point, return lowest point's value
|
||||
}
|
||||
if (upper == null) {
|
||||
return lower.getValue(); // RPM is above highest defined point, return highest point's value
|
||||
}
|
||||
|
||||
double x1 = lower.getKey();
|
||||
double y1 = lower.getValue();
|
||||
double x2 = upper.getKey();
|
||||
double y2 = upper.getValue();
|
||||
|
||||
// Linear interpolation formula: y = y1 + ((y2 - y1) / (x2 - x1)) * (x - x1)
|
||||
if (x2 == x1) { // Avoid division by zero if two points have the same RPM (shouldn't happen with proper data)
|
||||
return y1;
|
||||
}
|
||||
return y1 + ((y2 - y1) / (x2 - x1)) * (rpm - x1);
|
||||
}
|
||||
|
||||
private static double rpmToOmega(double rpm) {
|
||||
return (rpm * 2 * Math.PI) / 60;
|
||||
}
|
||||
|
||||
private static void showPrintableChart() {
|
||||
JFrame chartFrame = new JFrame("Dyno Run Chart - Printable");
|
||||
chartFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
||||
chartFrame.setSize(800, 600);
|
||||
|
||||
JFreeChart chart = dynoGUI.getChart();
|
||||
ChartPanel printChartPanel = new ChartPanel(chart);
|
||||
printChartPanel.setMouseWheelEnabled(true);
|
||||
chartFrame.add(printChartPanel, BorderLayout.CENTER);
|
||||
|
||||
JButton printButton = new JButton("Print Chart");
|
||||
printButton.addActionListener(e -> {
|
||||
PrinterJob job = PrinterJob.getPrinterJob();
|
||||
job.setPrintable(printChartPanel);
|
||||
if (job.printDialog()) {
|
||||
try {
|
||||
job.print();
|
||||
} catch (PrinterException ex) {
|
||||
JOptionPane.showMessageDialog(chartFrame, "Printing error: " + ex.getMessage(), "Print Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
JPanel controlPanel = new JPanel();
|
||||
controlPanel.add(printButton);
|
||||
chartFrame.add(controlPanel, BorderLayout.SOUTH);
|
||||
|
||||
chartFrame.setLocationRelativeTo(dynoGUI);
|
||||
chartFrame.setVisible(true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user