Compare commits
6 Commits
cbbe868c8c
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
420fb93a05 | ||
|
|
890abc303d | ||
|
|
67e707eac8 | ||
|
|
c6602315ba | ||
|
|
64cc14239d | ||
|
|
e80a94ff94 |
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.py linguist-vendored
|
||||
55
.gitea/workflows/maven_build.yaml
Normal file
55
.gitea/workflows/maven_build.yaml
Normal file
@@ -0,0 +1,55 @@
|
||||
name: Maven Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, master, dev ]
|
||||
pull_request:
|
||||
branches: [ main, master ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
cache: 'maven'
|
||||
|
||||
- name: Install Maven
|
||||
run: |
|
||||
if ! command -v mvn &> /dev/null; then
|
||||
echo "Maven not found, installing..."
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y maven
|
||||
fi
|
||||
mvn --version
|
||||
|
||||
- name: Debug Info
|
||||
run: |
|
||||
echo "Current workspace directory: $GITHUB_WORKSPACE"
|
||||
echo "Current directory: $(pwd)"
|
||||
echo "Project structure:"
|
||||
find . -type f -name "*.kt" | sort
|
||||
find . -type f -name "pom.xml"
|
||||
echo "Maven version: $(mvn --version)"
|
||||
|
||||
- name: Build PuchDyno (PuchDyno)
|
||||
run: |
|
||||
echo "Building PuchDyno"
|
||||
echo "Current directory: $(pwd)"
|
||||
# Run Maven build directly using the POM file path
|
||||
mvn -B clean package -f "$GITHUB_WORKSPACE/pom.xml" -Dmaven.compiler.failOnError=true
|
||||
|
||||
- name: Upload PuchDyno artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PuchDyno
|
||||
path: target/PuchDyno-*.jar
|
||||
if-no-files-found: error
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -2,6 +2,9 @@
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# ---> CSV
|
||||
*.csv
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
@@ -58,3 +61,10 @@ 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!)
|
||||
logs/dyno_2025-06-10_19-41-50.csv
|
||||
|
||||
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.
|
||||
201
arduinosrc/main.ino
Normal file
201
arduinosrc/main.ino
Normal file
@@ -0,0 +1,201 @@
|
||||
#include <WiFi.h>
|
||||
|
||||
// Mock dyno simulation parameters
|
||||
const int SERIAL_BAUD_RATE = 9600;
|
||||
const int DATA_SEND_INTERVAL_MS = 100; // Send data every 100ms
|
||||
const int SIMULATION_STEP_MS = 50; // Update simulation every 50ms
|
||||
|
||||
// Simulation state
|
||||
enum SimulationState {
|
||||
IDLE,
|
||||
ACCELERATING,
|
||||
HOLDING,
|
||||
DECELERATING
|
||||
};
|
||||
|
||||
SimulationState currentState = IDLE;
|
||||
unsigned long lastDataSend = 0;
|
||||
unsigned long lastSimulationUpdate = 0;
|
||||
unsigned long stateStartTime = 0;
|
||||
|
||||
// RPM simulation variables
|
||||
float currentRPM = 0.0;
|
||||
float targetRPM = 0.0;
|
||||
float rpmAcceleration = 0.0;
|
||||
float maxRPM = 11000.0;
|
||||
float minRPM = 800.0; // Idle RPM
|
||||
float idleRPM = 800.0;
|
||||
|
||||
// Dyno run parameters
|
||||
const float ACCELERATION_RATE = 25.0; // RPM per 100ms during acceleration
|
||||
const float DECELERATION_RATE = 30.0; // RPM per 100ms during deceleration
|
||||
const int HOLD_DURATION_MS = 2000; // Hold at max RPM for 2 seconds
|
||||
const int IDLE_DURATION_MS = 3000; // Stay at idle for 3 seconds
|
||||
|
||||
// Add some realistic noise and variations
|
||||
float rpmNoise = 0.0;
|
||||
int noiseCounter = 0;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(SERIAL_BAUD_RATE);
|
||||
|
||||
// Initialize random seed
|
||||
randomSeed(analogRead(0));
|
||||
|
||||
// Wait for serial to initialize
|
||||
delay(2000);
|
||||
|
||||
Serial.println("ESP32 Mock Dyno Data Generator");
|
||||
Serial.println("Sending RPM data for dyno simulation...");
|
||||
Serial.println("==================================");
|
||||
|
||||
// Start at idle
|
||||
currentRPM = idleRPM;
|
||||
targetRPM = idleRPM;
|
||||
currentState = IDLE;
|
||||
stateStartTime = millis();
|
||||
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
unsigned long currentTime = millis();
|
||||
|
||||
// Update simulation state
|
||||
if (currentTime - lastSimulationUpdate >= SIMULATION_STEP_MS) {
|
||||
updateSimulation();
|
||||
lastSimulationUpdate = currentTime;
|
||||
}
|
||||
|
||||
// Send data to Java application
|
||||
if (currentTime - lastDataSend >= DATA_SEND_INTERVAL_MS) {
|
||||
sendRPMData();
|
||||
lastDataSend = currentTime;
|
||||
}
|
||||
|
||||
delay(10); // Small delay to prevent excessive CPU usage
|
||||
}
|
||||
|
||||
void updateSimulation() {
|
||||
unsigned long currentTime = millis();
|
||||
unsigned long stateElapsed = currentTime - stateStartTime;
|
||||
|
||||
switch (currentState) {
|
||||
case IDLE:
|
||||
// Stay at idle RPM for a while, then start accelerating
|
||||
targetRPM = idleRPM + random(-50, 51); // Add some idle variation
|
||||
if (stateElapsed > IDLE_DURATION_MS) {
|
||||
currentState = ACCELERATING;
|
||||
targetRPM = maxRPM;
|
||||
stateStartTime = currentTime;
|
||||
Serial.println("Starting dyno run - ACCELERATING");
|
||||
}
|
||||
break;
|
||||
|
||||
case ACCELERATING:
|
||||
// Accelerate towards max RPM
|
||||
if (currentRPM < maxRPM - 100) {
|
||||
currentRPM += ACCELERATION_RATE * (SIMULATION_STEP_MS / 100.0);
|
||||
// Add some realistic acceleration curve (not perfectly linear)
|
||||
float accelerationFactor = 1.0 - (currentRPM / maxRPM) * 0.3;
|
||||
currentRPM = currentRPM * accelerationFactor + currentRPM * (1.0 - accelerationFactor);
|
||||
} else {
|
||||
currentState = HOLDING;
|
||||
stateStartTime = currentTime;
|
||||
Serial.println("Reached max RPM - HOLDING");
|
||||
}
|
||||
break;
|
||||
|
||||
case HOLDING:
|
||||
// Hold at max RPM with some variation
|
||||
targetRPM = maxRPM + random(-100, 101);
|
||||
if (stateElapsed > HOLD_DURATION_MS) {
|
||||
currentState = DECELERATING;
|
||||
targetRPM = idleRPM;
|
||||
stateStartTime = currentTime;
|
||||
Serial.println("Starting deceleration - DECELERATING");
|
||||
}
|
||||
break;
|
||||
|
||||
case DECELERATING:
|
||||
// Decelerate back to idle
|
||||
if (currentRPM > idleRPM + 100) {
|
||||
currentRPM -= DECELERATION_RATE * (SIMULATION_STEP_MS / 100.0);
|
||||
// Add engine braking effect (faster deceleration at higher RPM)
|
||||
float brakingFactor = 1.0 + (currentRPM / maxRPM) * 0.5;
|
||||
currentRPM -= (DECELERATION_RATE * brakingFactor - DECELERATION_RATE) * (SIMULATION_STEP_MS / 100.0);
|
||||
} else {
|
||||
currentRPM = idleRPM;
|
||||
currentState = IDLE;
|
||||
stateStartTime = currentTime;
|
||||
Serial.println("Returned to idle - IDLE");
|
||||
Serial.println("==================================");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Add realistic noise and fluctuations
|
||||
addRealisticNoise();
|
||||
|
||||
// Ensure RPM doesn't go below 0 or above reasonable limits
|
||||
currentRPM = constrain(currentRPM, 0, maxRPM + 500);
|
||||
}
|
||||
|
||||
void addRealisticNoise() {
|
||||
// Add different types of noise based on RPM range
|
||||
float baseNoise = random(-10, 11); // Basic noise
|
||||
|
||||
// Engine vibration increases with RPM
|
||||
float vibrationNoise = (currentRPM / 1000.0) * random(-5, 6);
|
||||
|
||||
// Periodic fluctuations (simulating engine cycles)
|
||||
noiseCounter++;
|
||||
float periodicNoise = sin(noiseCounter * 0.1) * (currentRPM / 2000.0) * 3.0;
|
||||
|
||||
// Combine all noise sources
|
||||
rpmNoise = baseNoise + vibrationNoise + periodicNoise;
|
||||
|
||||
// Apply noise with some smoothing
|
||||
currentRPM += rpmNoise * 0.3; // Reduce noise impact
|
||||
}
|
||||
|
||||
void sendRPMData() {
|
||||
// Send just the RPM value as your Java code expects
|
||||
Serial.println(currentRPM, 0); // Send as integer (no decimal places)
|
||||
|
||||
// Optional: Send debug info to Serial Monitor (comment out for production)
|
||||
// Serial.print("DEBUG - State: ");
|
||||
// Serial.print(getStateString());
|
||||
// Serial.print(", RPM: ");
|
||||
// Serial.print(currentRPM, 1);
|
||||
// Serial.print(", Noise: ");
|
||||
// Serial.println(rpmNoise, 1);
|
||||
}
|
||||
|
||||
String getStateString() {
|
||||
switch (currentState) {
|
||||
case IDLE: return "IDLE";
|
||||
case ACCELERATING: return "ACCELERATING";
|
||||
case HOLDING: return "HOLDING";
|
||||
case DECELERATING: return "DECELERATING";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
// Function to manually trigger a dyno run (call this if you want manual control)
|
||||
void triggerDynoRun() {
|
||||
if (currentState == IDLE) {
|
||||
currentState = ACCELERATING;
|
||||
stateStartTime = millis();
|
||||
Serial.println("Manual dyno run triggered!");
|
||||
}
|
||||
}
|
||||
|
||||
// Function to reset to idle (emergency stop)
|
||||
void resetToIdle() {
|
||||
currentState = IDLE;
|
||||
currentRPM = idleRPM;
|
||||
targetRPM = idleRPM;
|
||||
stateStartTime = millis();
|
||||
Serial.println("Reset to idle!");
|
||||
}
|
||||
10
build.sh
Executable file
10
build.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
# Direct build script for Maven project
|
||||
|
||||
echo "Current directory: $(pwd)"
|
||||
echo "Building project with Maven..."
|
||||
|
||||
# Run Maven build using the exact pom.xml location
|
||||
mvn clean package -f "$(pwd)/pom.xml"
|
||||
|
||||
echo "Build complete. JAR file should be in target/ directory."
|
||||
89
pom.xml
Normal file
89
pom.xml
Normal file
@@ -0,0 +1,89 @@
|
||||
<?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>
|
||||
<!-- iText for PDF generation -->
|
||||
<dependency>
|
||||
<groupId>com.itextpdf</groupId>
|
||||
<artifactId>itextpdf</artifactId>
|
||||
<version>5.5.13.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>
|
||||
291
pommer.py
vendored
Normal file
291
pommer.py
vendored
Normal file
@@ -0,0 +1,291 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
POMMER.PY
|
||||
|
||||
THIS IS PROPRIETARY SOFTWARE DO NOT DISTRIBUTE TO OUTSIDERS!
|
||||
|
||||
This Python File is distributed with every Kotlin Plugin Repository!
|
||||
If you find this to be confusing to use look at the Documentation in "rattatwinko/pommer"
|
||||
|
||||
Run this Script with Python 3.11 ; 3.9
|
||||
|
||||
This YET only works with Maven!
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
import xml.etree.ElementTree as ET
|
||||
import re
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import glob
|
||||
|
||||
|
||||
def parse_pom_xml(pom_path):
|
||||
"""
|
||||
Parse a pom.xml file and extract relevant information
|
||||
"""
|
||||
try:
|
||||
print(f"Parsing POM file: {pom_path}")
|
||||
|
||||
# Register the default namespace
|
||||
ET.register_namespace('', "http://maven.apache.org/POM/4.0.0")
|
||||
|
||||
# Parse the XML file
|
||||
tree = ET.parse(pom_path)
|
||||
root = tree.getroot()
|
||||
|
||||
# Define namespace for easier XPath queries
|
||||
ns = {'mvn': "http://maven.apache.org/POM/4.0.0"}
|
||||
|
||||
# Extract project info
|
||||
artifact_id = root.find('./mvn:artifactId', ns).text
|
||||
group_id = root.find('./mvn:groupId', ns).text if root.find('./mvn:groupId', ns) is not None else "unknown"
|
||||
version = root.find('./mvn:version', ns).text if root.find('./mvn:version', ns) is not None else "unknown"
|
||||
name = root.find('./mvn:name', ns).text if root.find('./mvn:name', ns) is not None else artifact_id
|
||||
|
||||
# Extract Java version
|
||||
java_version_elem = root.find('./mvn:properties/mvn:java.version', ns)
|
||||
java_version = java_version_elem.text if java_version_elem is not None else "17" # Default to Java 17 if not specified
|
||||
|
||||
# Extract packaging type (default to jar if not specified)
|
||||
packaging = root.find('./mvn:packaging', ns)
|
||||
packaging = packaging.text if packaging is not None else "jar"
|
||||
|
||||
# Check if Kotlin is used
|
||||
kotlin_version_elem = root.find('./mvn:properties/mvn:kotlin.version', ns)
|
||||
kotlin_version = kotlin_version_elem.text if kotlin_version_elem is not None else None
|
||||
|
||||
# Check for Kotlin plugin or dependency
|
||||
kotlin_plugin = None
|
||||
kotlin_dep = None
|
||||
|
||||
# Check for Kotlin plugin
|
||||
plugins = root.findall('.//mvn:plugin', ns)
|
||||
for plugin in plugins:
|
||||
plugin_group = plugin.find('./mvn:groupId', ns)
|
||||
if plugin_group is not None and plugin_group.text == 'org.jetbrains.kotlin':
|
||||
kotlin_plugin = plugin
|
||||
break
|
||||
|
||||
# Check for Kotlin dependency
|
||||
deps = root.findall('.//mvn:dependency', ns)
|
||||
for dep in deps:
|
||||
dep_group = dep.find('./mvn:groupId', ns)
|
||||
dep_artifact = dep.find('./mvn:artifactId', ns)
|
||||
if (dep_group is not None and dep_group.text == 'org.jetbrains.kotlin' and
|
||||
dep_artifact is not None and 'kotlin-stdlib' in dep_artifact.text):
|
||||
kotlin_dep = dep
|
||||
break
|
||||
|
||||
# Determine if this is a Kotlin project
|
||||
is_kotlin = kotlin_version is not None or kotlin_plugin is not None or kotlin_dep is not None
|
||||
|
||||
# Check for source directories
|
||||
source_dir = None
|
||||
source_dirs = root.findall('.//mvn:sourceDirectory', ns)
|
||||
if source_dirs:
|
||||
source_dir = source_dirs[0].text
|
||||
|
||||
# Check for default goal (to use the same command as IntelliJ)
|
||||
default_goal = None
|
||||
default_goal_elem = root.find('./mvn:build/mvn:defaultGoal', ns)
|
||||
if default_goal_elem is not None:
|
||||
default_goal = default_goal_elem.text
|
||||
|
||||
return {
|
||||
"artifact_id": artifact_id,
|
||||
"group_id": group_id,
|
||||
"version": version,
|
||||
"name": name,
|
||||
"java_version": java_version,
|
||||
"packaging": packaging,
|
||||
"is_kotlin": is_kotlin,
|
||||
"kotlin_version": kotlin_version,
|
||||
"source_dir": source_dir,
|
||||
"default_goal": default_goal,
|
||||
"pom_path": pom_path
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"Error parsing {pom_path}: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def generate_gitea_workflow(pom_infos):
|
||||
"""
|
||||
Generate a Gitea workflow YAML file based on multiple POM information
|
||||
"""
|
||||
if not pom_infos:
|
||||
print("No valid POM files found")
|
||||
return None
|
||||
|
||||
# Get the highest Java version required
|
||||
java_version = max([info["java_version"] for info in pom_infos])
|
||||
|
||||
# Check if any project uses Kotlin
|
||||
uses_kotlin = any(info["is_kotlin"] for info in pom_infos)
|
||||
|
||||
# Kotlin version (if any)
|
||||
kotlin_version = None
|
||||
for info in pom_infos:
|
||||
if info["kotlin_version"]:
|
||||
kotlin_version = info["kotlin_version"]
|
||||
break
|
||||
|
||||
# Construct the workflow content
|
||||
workflow_content = f"""name: Maven Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, master, dev ]
|
||||
pull_request:
|
||||
branches: [ main, master ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up JDK {java_version}
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '{java_version}'
|
||||
cache: 'maven'
|
||||
|
||||
- name: Install Maven
|
||||
run: |
|
||||
if ! command -v mvn &> /dev/null; then
|
||||
echo "Maven not found, installing..."
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y maven
|
||||
fi
|
||||
mvn --version
|
||||
|
||||
- name: Debug Info
|
||||
run: |
|
||||
echo "Current workspace directory: $GITHUB_WORKSPACE"
|
||||
echo "Current directory: $(pwd)"
|
||||
echo "Project structure:"
|
||||
find . -type f -name "*.kt" | sort
|
||||
find . -type f -name "pom.xml"
|
||||
echo "Maven version: $(mvn --version)"
|
||||
"""
|
||||
|
||||
# Add individual build steps for each POM
|
||||
for i, info in enumerate(pom_infos):
|
||||
# Determine the Maven command to use (same as the default goal if specified)
|
||||
maven_command = "clean package"
|
||||
if info["default_goal"]:
|
||||
maven_command = info["default_goal"]
|
||||
|
||||
workflow_content += f"""
|
||||
- name: Build {info["name"]} ({info["artifact_id"]})
|
||||
run: |
|
||||
echo "Building {info["artifact_id"]}"
|
||||
echo "Current directory: $(pwd)"
|
||||
# Run Maven build directly using the POM file path
|
||||
mvn -B {maven_command} -f "$GITHUB_WORKSPACE/pom.xml" -Dmaven.compiler.failOnError=true
|
||||
"""
|
||||
|
||||
# Add artifact upload step
|
||||
workflow_content += f"""
|
||||
- name: Upload {info["artifact_id"]} artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: {info["artifact_id"]}
|
||||
path: target/{info['artifact_id']}-*.jar
|
||||
if-no-files-found: error
|
||||
"""
|
||||
|
||||
return workflow_content
|
||||
|
||||
|
||||
def find_pom_files(base_dir="."):
|
||||
"""Find all pom.xml files in the given directory and subdirectories"""
|
||||
return glob.glob(f"{base_dir}/**/pom.xml", recursive=True)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Generate Gitea workflow for Maven/Kotlin projects')
|
||||
parser.add_argument('--dir', '-d', default='.', help='Base directory to search for pom.xml files')
|
||||
parser.add_argument('--specific-pom', '-p', help='Path to a specific pom.xml file to process')
|
||||
args = parser.parse_args()
|
||||
|
||||
pom_files = []
|
||||
if args.specific_pom:
|
||||
pom_files = [args.specific_pom]
|
||||
else:
|
||||
pom_files = find_pom_files(args.dir)
|
||||
|
||||
if not pom_files:
|
||||
print(f"No pom.xml files found in {args.dir}")
|
||||
return
|
||||
|
||||
print(f"Found {len(pom_files)} pom.xml files")
|
||||
|
||||
# Parse all POM files
|
||||
pom_infos = []
|
||||
for pom_file in pom_files:
|
||||
info = parse_pom_xml(pom_file)
|
||||
if info:
|
||||
pom_infos.append(info)
|
||||
|
||||
if not pom_infos:
|
||||
print("No valid POM files could be parsed")
|
||||
return
|
||||
|
||||
# Generate the workflow content
|
||||
workflow_content = generate_gitea_workflow(pom_infos)
|
||||
|
||||
if not workflow_content:
|
||||
return
|
||||
|
||||
# Create the .gitea/workflows directory if it doesn't exist
|
||||
workflow_dir = Path(".gitea/workflows")
|
||||
workflow_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Write the workflow file
|
||||
workflow_file = workflow_dir / "maven_build.yaml"
|
||||
with open(workflow_file, "w") as f:
|
||||
f.write(workflow_content)
|
||||
|
||||
print(f"Gitea workflow generated at: {workflow_file}")
|
||||
print(f"This workflow will build {len(pom_infos)} Maven projects")
|
||||
|
||||
# Print summary of detected projects
|
||||
print("\nDetected projects:")
|
||||
for info in pom_infos:
|
||||
kotlin_info = "with Kotlin" if info["is_kotlin"] else "Java only"
|
||||
build_command = info["default_goal"] if info["default_goal"] else "clean package"
|
||||
print(
|
||||
f"- {info['name']} ({info['artifact_id']}): {kotlin_info}, Java {info['java_version']}, build: {build_command}")
|
||||
|
||||
# Create a simple direct build script as fallback
|
||||
with open("build.sh", "w") as f:
|
||||
f.write("""#!/bin/bash
|
||||
# Direct build script for Maven project
|
||||
|
||||
echo "Current directory: $(pwd)"
|
||||
echo "Building project with Maven..."
|
||||
|
||||
# Run Maven build using the exact pom.xml location
|
||||
mvn clean package -f "$(pwd)/pom.xml"
|
||||
|
||||
echo "Build complete. JAR file should be in target/ directory."
|
||||
""")
|
||||
|
||||
# Make it executable
|
||||
os.chmod("build.sh", 0o755)
|
||||
print(f"Simple build script generated at: build.sh")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
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
|
||||
88
src/main/java/com/puchdyno/AnalogMeter.java
Normal file
88
src/main/java/com/puchdyno/AnalogMeter.java
Normal file
@@ -0,0 +1,88 @@
|
||||
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(250, 250));
|
||||
}
|
||||
|
||||
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 with gradient
|
||||
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);
|
||||
|
||||
// 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(4)); // Thicker needle
|
||||
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 + 30); // Adjusted position
|
||||
}
|
||||
}
|
||||
53
src/main/java/com/puchdyno/CSVLogger.java
Normal file
53
src/main/java/com/puchdyno/CSVLogger.java
Normal 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();
|
||||
}
|
||||
}
|
||||
264
src/main/java/com/puchdyno/DynoGUI.java
Normal file
264
src/main/java/com/puchdyno/DynoGUI.java
Normal file
@@ -0,0 +1,264 @@
|
||||
package com.puchdyno;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import org.jfree.chart.*;
|
||||
import org.jfree.chart.axis.NumberAxis;
|
||||
import org.jfree.chart.plot.*;
|
||||
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
|
||||
import org.jfree.data.xy.*;
|
||||
import org.jfree.chart.renderer.xy.XYSplineRenderer;
|
||||
import java.awt.BasicStroke;
|
||||
|
||||
public class DynoGUI extends JFrame {
|
||||
private final JLabel leistungLabel;
|
||||
private final JLabel drehmomentLabel;
|
||||
private final AnalogMeter rpmMeter;
|
||||
private final AnalogMeter hpMeter;
|
||||
private final XYSeries powerSeries;
|
||||
private final XYSeries torqueSeries;
|
||||
private final XYSeries efficiencySeries;
|
||||
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() {
|
||||
setTitle("Puch Maxi Dyno");
|
||||
setSize(1000, 700);
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setLayout(new BorderLayout(10, 10));
|
||||
|
||||
// Top panel: analog meters
|
||||
JPanel meterPanel = new JPanel(new GridLayout(1, 2, 20, 20));
|
||||
meterPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
rpmMeter = new AnalogMeter("RPM", 12000, Color.BLUE);
|
||||
hpMeter = new AnalogMeter("Horse Power", 15, Color.RED);
|
||||
|
||||
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);
|
||||
|
||||
// Center panel: chart and labels
|
||||
JPanel centerPanel = new JPanel(new BorderLayout(10, 10));
|
||||
centerPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
|
||||
// Labels panel with BoxLayout for better scaling
|
||||
JPanel labelPanel = new JPanel();
|
||||
labelPanel.setLayout(new BoxLayout(labelPanel, BoxLayout.Y_AXIS));
|
||||
labelPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
|
||||
leistungLabel = new JLabel("Leistung: 0.00 PS");
|
||||
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));
|
||||
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);
|
||||
drehmomentLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
maxLeistungLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
maxDrehmomentLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
|
||||
labelPanel.add(leistungLabel);
|
||||
labelPanel.add(drehmomentLabel);
|
||||
labelPanel.add(Box.createVerticalStrut(5));
|
||||
labelPanel.add(maxLeistungLabel);
|
||||
labelPanel.add(Box.createVerticalStrut(5));
|
||||
labelPanel.add(maxDrehmomentLabel);
|
||||
centerPanel.add(labelPanel, BorderLayout.NORTH);
|
||||
|
||||
// Data series
|
||||
powerSeries = new XYSeries("Power (PS)");
|
||||
torqueSeries = new XYSeries("Torque (Nm)");
|
||||
efficiencySeries = new XYSeries("Efficiency");
|
||||
|
||||
XYSeriesCollection powerDataset = new XYSeriesCollection(powerSeries);
|
||||
XYSeriesCollection torqueDataset = new XYSeriesCollection(torqueSeries);
|
||||
XYSeriesCollection efficiencyDataset = new XYSeriesCollection(efficiencySeries);
|
||||
|
||||
// Date for chart title
|
||||
String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy"));
|
||||
|
||||
// Create chart
|
||||
JFreeChart chart = ChartFactory.createXYLineChart(
|
||||
"LeistungsPrüfstand - Puch Maxi (" + dateStr + ")",
|
||||
"U/min",
|
||||
"Leistung [PS]",
|
||||
powerDataset,
|
||||
PlotOrientation.VERTICAL,
|
||||
true,
|
||||
true,
|
||||
false
|
||||
);
|
||||
|
||||
chart.setBackgroundPaint(Color.WHITE);
|
||||
chart.getTitle().setFont(new Font("SansSerif", Font.BOLD, 18));
|
||||
|
||||
XYPlot plot = chart.getXYPlot();
|
||||
plot.setBackgroundPaint(new Color(245, 245, 245));
|
||||
plot.setDomainGridlinePaint(Color.LIGHT_GRAY);
|
||||
plot.setRangeGridlinePaint(Color.LIGHT_GRAY);
|
||||
plot.setOutlineVisible(false);
|
||||
plot.setDomainCrosshairVisible(true);
|
||||
plot.setRangeCrosshairVisible(true);
|
||||
|
||||
// Power axis (left)
|
||||
NumberAxis powerAxis = (NumberAxis) plot.getRangeAxis();
|
||||
powerAxis.setRange(0.0, 10.0);
|
||||
powerAxis.setLabel("Leistung [PS]");
|
||||
powerAxis.setTickLabelFont(new Font("SansSerif", Font.PLAIN, 12));
|
||||
powerAxis.setLabelFont(new Font("SansSerif", Font.BOLD, 14));
|
||||
|
||||
// Torque axis (right)
|
||||
NumberAxis torqueAxis = new NumberAxis("Drehmoment [Nm]");
|
||||
torqueAxis.setRange(0.0, 15.0);
|
||||
torqueAxis.setTickLabelFont(new Font("SansSerif", Font.PLAIN, 12));
|
||||
torqueAxis.setLabelFont(new Font("SansSerif", Font.BOLD, 14));
|
||||
plot.setRangeAxis(1, torqueAxis);
|
||||
plot.setDataset(1, torqueDataset);
|
||||
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
|
||||
NumberAxis xAxis = (NumberAxis) plot.getDomainAxis();
|
||||
xAxis.setRange(0.0, 12000.0);
|
||||
xAxis.setLabel("U/min");
|
||||
xAxis.setTickLabelFont(new Font("SansSerif", Font.PLAIN, 12));
|
||||
xAxis.setLabelFont(new Font("SansSerif", Font.BOLD, 14));
|
||||
|
||||
// Renderers
|
||||
org.jfree.chart.renderer.xy.XYSplineRenderer powerRenderer = new org.jfree.chart.renderer.xy.XYSplineRenderer();
|
||||
powerRenderer.setSeriesPaint(0, new Color(220, 50, 47)); // Red
|
||||
powerRenderer.setSeriesStroke(0, new BasicStroke(2f));
|
||||
plot.setRenderer(0, powerRenderer);
|
||||
|
||||
org.jfree.chart.renderer.xy.XYSplineRenderer torqueRenderer = new org.jfree.chart.renderer.xy.XYSplineRenderer();
|
||||
torqueRenderer.setSeriesPaint(0, new Color(38, 139, 210)); // Blue
|
||||
torqueRenderer.setSeriesStroke(0, new BasicStroke(2f));
|
||||
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
|
||||
chartPanel = new ChartPanel(chart);
|
||||
chartPanel.setPreferredSize(new Dimension(800, 400));
|
||||
chartPanel.setMouseWheelEnabled(true);
|
||||
chartPanel.setPopupMenu(null);
|
||||
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);
|
||||
|
||||
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));
|
||||
|
||||
// keine Plot-Aktualisierung hier – siehe addPlotData()
|
||||
}
|
||||
|
||||
public void resetChart() {
|
||||
powerSeries.clear();
|
||||
torqueSeries.clear();
|
||||
efficiencySeries.clear();
|
||||
}
|
||||
|
||||
public void resetMaxHold() {
|
||||
maxLeistung = 0.0;
|
||||
maxDrehmoment = 0.0;
|
||||
maxLeistungRpm = 0.0;
|
||||
maxLeistungNmAtHp = 0.0;
|
||||
maxDrehmomentRpm = 0.0;
|
||||
maxDrehmomentPsAtNm = 0.0;
|
||||
efficiencySeries.clear();
|
||||
maxLeistungLabel.setText("Max Leistung: 0.00 PS");
|
||||
maxDrehmomentLabel.setText("Max Drehmoment: 0.00 Nm");
|
||||
}
|
||||
|
||||
public JFreeChart getChart() {
|
||||
return chartPanel.getChart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fügt Datenpunkte in den Diagrammen hinzu. Nur während aktiver Messung aufrufen.
|
||||
*/
|
||||
public void addPlotData(double leistung, double drehmoment, double rpm) {
|
||||
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;
|
||||
if (maxDrehmomentRpm != maxLeistungRpm) {
|
||||
efficiencySeries.add(maxDrehmomentRpm, effNm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
powerSeries.add(rpm, leistung);
|
||||
torqueSeries.add(rpm, drehmoment);
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
560
src/main/java/com/puchdyno/Main.java
Normal file
560
src/main/java/com/puchdyno/Main.java
Normal file
@@ -0,0 +1,560 @@
|
||||
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;
|
||||
import com.itextpdf.text.Document;
|
||||
import com.itextpdf.text.pdf.PdfWriter;
|
||||
import com.itextpdf.text.Image;
|
||||
import com.itextpdf.text.Rectangle;
|
||||
import java.io.FileOutputStream;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import java.io.File;
|
||||
|
||||
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;
|
||||
|
||||
private static boolean testMode = false;
|
||||
private static ScheduledExecutorService testDataExecutor;
|
||||
private static int testRpmCounter = 1000;
|
||||
|
||||
private static SerialPort currentSerialPort;
|
||||
private static ScheduledExecutorService serialExecutor;
|
||||
private static BufferedReader serialReader;
|
||||
|
||||
private static CSVLogger csvLogger;
|
||||
private static boolean measurementActive = false;
|
||||
|
||||
private static double peakRpmDuringRun = 0.0;
|
||||
private static int belowThresholdCounter = 0;
|
||||
private static final int BELOW_THRESHOLD_LIMIT = 20; // 20 * 10ms = 200ms
|
||||
private static final double RPM_DROP_FACTOR = 0.3; // 30% of peak
|
||||
|
||||
public static void main(String[] args) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI = new DynoGUI();
|
||||
leistungBerechner = new LeistungBerechner();
|
||||
addControlPanel();
|
||||
startSerialPortListener(); // Start listening for serial data immediately
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
JButton resetMaxButton = new JButton("Reset Max-Hold");
|
||||
resetMaxButton.addActionListener(e -> dynoGUI.resetMaxHold());
|
||||
controlPanel.add(resetMaxButton);
|
||||
|
||||
JButton reconnectButton = new JButton("Reconnect Serial");
|
||||
reconnectButton.addActionListener(e -> reconnectSerial());
|
||||
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.revalidate();
|
||||
dynoGUI.repaint();
|
||||
}
|
||||
|
||||
private static void reconnectSerial() {
|
||||
stopSerialPortListener();
|
||||
startSerialPortListener();
|
||||
}
|
||||
|
||||
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();
|
||||
measurementActive = true;
|
||||
lastTimestamp = 0;
|
||||
lastOmega = 0.0;
|
||||
peakRpmDuringRun = 0.0;
|
||||
belowThresholdCounter = 0;
|
||||
|
||||
if (csvLogger != null) { try { csvLogger.close(); } catch (Exception ex) {} }
|
||||
try { csvLogger = new CSVLogger(new java.io.File("logs")); } catch(Exception ex) { csvLogger = null; }
|
||||
|
||||
final long simStartTime = System.currentTimeMillis();
|
||||
|
||||
testDataExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||
testDataExecutor.scheduleAtFixedRate(() -> {
|
||||
// Simulate a realistic dyno run pattern
|
||||
long currentTime = System.currentTimeMillis();
|
||||
double elapsedSeconds = (currentTime - simStartTime) / 1000.0;
|
||||
|
||||
// Simulate acceleration, hold, then deceleration
|
||||
double simulatedRpm;
|
||||
if (elapsedSeconds < 5.0) {
|
||||
// Acceleration phase (0-5 seconds)
|
||||
simulatedRpm = 800 + (11000 - 800) * (elapsedSeconds / 5.0);
|
||||
} else if (elapsedSeconds < 7.0) {
|
||||
// Hold at max RPM (5-7 seconds)
|
||||
simulatedRpm = 11000;
|
||||
} else if (elapsedSeconds < 12.0) {
|
||||
// Deceleration phase (7-12 seconds)
|
||||
simulatedRpm = 11000 - (11000 - 800) * ((elapsedSeconds - 7.0) / 5.0);
|
||||
} else {
|
||||
// Return to idle after 12 seconds
|
||||
simulatedRpm = 800;
|
||||
stopTestRun();
|
||||
}
|
||||
|
||||
// Add some realistic noise
|
||||
simulatedRpm += (Math.random() * 200 - 100);
|
||||
simulatedRpm = Math.max(0, simulatedRpm);
|
||||
|
||||
processRPMData(simulatedRpm, 0.05); // Use 50ms delta time for test mode
|
||||
}, 0, 50, TimeUnit.MILLISECONDS); // Update every 50ms
|
||||
}
|
||||
|
||||
private static void stopTestRun() {
|
||||
if (testDataExecutor != null) {
|
||||
testDataExecutor.shutdownNow();
|
||||
testDataExecutor = null;
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI.updateWerte(0, 0, 0);
|
||||
if (csvLogger != null) {
|
||||
try { csvLogger.close(); } catch (Exception ex) { ex.printStackTrace(); }
|
||||
csvLogger = null;
|
||||
}
|
||||
showPrintableChart();
|
||||
});
|
||||
measurementActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
currentSerialPort = chosenPort;
|
||||
chosenPort.setBaudRate(9600);
|
||||
chosenPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 100, 0);
|
||||
|
||||
if (chosenPort.openPort()) {
|
||||
System.out.println("Serial port " + chosenPort.getSystemPortName() + " opened successfully.");
|
||||
serialReader = new BufferedReader(new InputStreamReader(chosenPort.getInputStream()));
|
||||
|
||||
serialExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||
serialExecutor.scheduleAtFixedRate(() -> {
|
||||
try {
|
||||
if (serialReader.ready()) {
|
||||
String line = serialReader.readLine();
|
||||
if (line != null && !line.trim().isEmpty()) {
|
||||
try {
|
||||
// Handle potential incomplete lines or multiple values
|
||||
String[] values = line.trim().split("[\\s,]+");
|
||||
for (String value : values) {
|
||||
if (!value.isEmpty()) {
|
||||
double rpm = Double.parseDouble(value);
|
||||
processRPMData(rpm); // Process each valid RPM value
|
||||
}
|
||||
}
|
||||
} 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();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
"Serial port error: " + e.getMessage(),
|
||||
"Error", JOptionPane.ERROR_MESSAGE);
|
||||
});
|
||||
}
|
||||
}, 0, 10, TimeUnit.MILLISECONDS); // Check more frequently for data
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
"Failed to open serial port " + chosenPort.getSystemPortName(),
|
||||
"Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private static void stopSerialPortListener() {
|
||||
if (serialExecutor != null) {
|
||||
serialExecutor.shutdownNow();
|
||||
try {
|
||||
if (!serialExecutor.awaitTermination(500, TimeUnit.MILLISECONDS)) {
|
||||
serialExecutor.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
serialExecutor.shutdownNow();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
serialExecutor = null;
|
||||
}
|
||||
|
||||
if (serialReader != null) {
|
||||
try {
|
||||
serialReader.close();
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error closing serial reader: " + e.getMessage());
|
||||
}
|
||||
serialReader = null;
|
||||
}
|
||||
|
||||
if (currentSerialPort != null && currentSerialPort.isOpen()) {
|
||||
currentSerialPort.closePort();
|
||||
System.out.println("Serial port " + currentSerialPort.getSystemPortName() + " closed.");
|
||||
currentSerialPort = null;
|
||||
}
|
||||
|
||||
if (csvLogger != null) {
|
||||
try { csvLogger.close(); } catch (Exception ex) { ex.printStackTrace(); }
|
||||
csvLogger = null;
|
||||
}
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI.updateWerte(0, 0, 0); // Reset gauges
|
||||
});
|
||||
}
|
||||
|
||||
private static void processRPMData(double rpm) {
|
||||
long currentTimestamp = System.currentTimeMillis();
|
||||
|
||||
if (lastTimestamp == 0) {
|
||||
lastTimestamp = currentTimestamp;
|
||||
lastOmega = rpmToOmega(rpm);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI.updateWerte(0, 0, rpm);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
double deltaTime = (currentTimestamp - lastTimestamp) / 1000.0;
|
||||
if (deltaTime <= 0) deltaTime = 0.001;
|
||||
|
||||
double currentOmega = rpmToOmega(rpm);
|
||||
double drehmoment = leistungBerechner.berechneDrehmoment(lastOmega, currentOmega, deltaTime);
|
||||
double leistung = leistungBerechner.berechneLeistung(drehmoment, currentOmega);
|
||||
|
||||
if (measurementActive) {
|
||||
if (rpm > peakRpmDuringRun) {
|
||||
peakRpmDuringRun = rpm;
|
||||
}
|
||||
if (peakRpmDuringRun > 0 && rpm < peakRpmDuringRun * RPM_DROP_FACTOR) {
|
||||
belowThresholdCounter++;
|
||||
} else {
|
||||
belowThresholdCounter = 0;
|
||||
}
|
||||
|
||||
if (belowThresholdCounter >= BELOW_THRESHOLD_LIMIT) {
|
||||
SwingUtilities.invokeLater(() -> stopMeasurement());
|
||||
}
|
||||
}
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI.updateWerte(leistung, drehmoment, rpm);
|
||||
if (measurementActive) {
|
||||
dynoGUI.addPlotData(leistung, drehmoment, rpm);
|
||||
}
|
||||
});
|
||||
|
||||
lastTimestamp = currentTimestamp;
|
||||
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) {
|
||||
// For test mode with forced delta time
|
||||
double currentOmega = rpmToOmega(rpm);
|
||||
double drehmoment = leistungBerechner.berechneDrehmoment(lastOmega, currentOmega, forcedDeltaTime);
|
||||
double leistung = leistungBerechner.berechneLeistung(drehmoment, currentOmega);
|
||||
|
||||
if (measurementActive) {
|
||||
if (rpm > peakRpmDuringRun) {
|
||||
peakRpmDuringRun = rpm;
|
||||
}
|
||||
if (peakRpmDuringRun > 0 && rpm < peakRpmDuringRun * RPM_DROP_FACTOR) {
|
||||
belowThresholdCounter++;
|
||||
} else {
|
||||
belowThresholdCounter = 0;
|
||||
}
|
||||
if (belowThresholdCounter >= BELOW_THRESHOLD_LIMIT) {
|
||||
SwingUtilities.invokeLater(() -> stopMeasurement());
|
||||
}
|
||||
}
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
dynoGUI.updateWerte(leistung, drehmoment, rpm);
|
||||
if (measurementActive) {
|
||||
dynoGUI.addPlotData(leistung, drehmoment, rpm);
|
||||
}
|
||||
});
|
||||
|
||||
lastOmega = currentOmega;
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
JButton savePdfButton = new JButton("Save as PDF");
|
||||
savePdfButton.addActionListener(e -> saveChartAsPdf(printChartPanel));
|
||||
|
||||
JButton savePngButton = new JButton("Save as PNG");
|
||||
savePngButton.addActionListener(e -> saveChartAsPng(printChartPanel));
|
||||
|
||||
JPanel controlPanel = new JPanel();
|
||||
controlPanel.add(printButton);
|
||||
controlPanel.add(savePdfButton);
|
||||
controlPanel.add(savePngButton);
|
||||
chartFrame.add(controlPanel, BorderLayout.SOUTH);
|
||||
|
||||
chartFrame.setLocationRelativeTo(dynoGUI);
|
||||
chartFrame.setVisible(true);
|
||||
}
|
||||
|
||||
private static void saveChartAsPdf(ChartPanel chartPanel) {
|
||||
JFileChooser fileChooser = new JFileChooser();
|
||||
fileChooser.setDialogTitle("Save Chart as PDF");
|
||||
fileChooser.setFileFilter(new FileNameExtensionFilter("PDF Documents (*.pdf)", "pdf"));
|
||||
|
||||
int userSelection = fileChooser.showSaveDialog(null);
|
||||
|
||||
if (userSelection == JFileChooser.APPROVE_OPTION) {
|
||||
File fileToSave = fileChooser.getSelectedFile();
|
||||
String filePath = fileToSave.getAbsolutePath();
|
||||
if (!filePath.toLowerCase().endsWith(".pdf")) {
|
||||
filePath += ".pdf";
|
||||
}
|
||||
File finalFile = new File(filePath);
|
||||
|
||||
try {
|
||||
Document document = new Document(new Rectangle(chartPanel.getWidth(), chartPanel.getHeight()));
|
||||
PdfWriter.getInstance(document, new FileOutputStream(finalFile));
|
||||
document.open();
|
||||
|
||||
java.awt.Image awtImage = chartPanel.getChart().createBufferedImage(
|
||||
chartPanel.getWidth(), chartPanel.getHeight());
|
||||
Image pdfImage = Image.getInstance(awtImage, null);
|
||||
|
||||
pdfImage.scaleToFit(document.getPageSize().getWidth() - 20,
|
||||
document.getPageSize().getHeight() - 20);
|
||||
pdfImage.setAlignment(Image.ALIGN_CENTER);
|
||||
|
||||
document.add(pdfImage);
|
||||
document.close();
|
||||
|
||||
JOptionPane.showMessageDialog(null,
|
||||
"Chart saved as PDF successfully!\n" + finalFile.getAbsolutePath(),
|
||||
"Save Successful", JOptionPane.INFORMATION_MESSAGE);
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
"Error saving chart as PDF: " + ex.getMessage(),
|
||||
"Save Error", JOptionPane.ERROR_MESSAGE);
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
peakRpmDuringRun = 0.0;
|
||||
belowThresholdCounter = 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
// Snapshot chart for printing
|
||||
showPrintableChart();
|
||||
peakRpmDuringRun = 0.0;
|
||||
belowThresholdCounter = 0;
|
||||
JOptionPane.showMessageDialog(null, "Messung gestoppt.", "Info", JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user