Compare commits
4 Commits
64cc14239d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
420fb93a05 | ||
|
|
890abc303d | ||
|
|
67e707eac8 | ||
|
|
c6602315ba |
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
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -2,6 +2,9 @@
|
|||||||
# Compiled class file
|
# Compiled class file
|
||||||
*.class
|
*.class
|
||||||
|
|
||||||
|
# ---> CSV
|
||||||
|
*.csv
|
||||||
|
|
||||||
# Log file
|
# Log file
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
@@ -64,3 +67,4 @@ target/maven-archiver/pom.properties
|
|||||||
target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
|
target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
|
||||||
target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
|
target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
|
||||||
>>>>>>> f462d53 (yessssssssss!)
|
>>>>>>> f462d53 (yessssssssss!)
|
||||||
|
logs/dyno_2025-06-10_19-41-50.csv
|
||||||
|
|||||||
@@ -1,10 +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() {
|
void setup() {
|
||||||
Serial.begin(9600); // Ensure this matches the baud rate in Main.java
|
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() {
|
void loop() {
|
||||||
// Replace this with your actual RPM measurement logic
|
unsigned long currentTime = millis();
|
||||||
double currentRPM = 1500.0; // Example RPM value
|
|
||||||
Serial.println(currentRPM);
|
// Update simulation state
|
||||||
delay(100); // Send data every 100ms, adjust as needed
|
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."
|
||||||
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()
|
||||||
@@ -16,7 +16,7 @@ public class AnalogMeter extends JPanel {
|
|||||||
this.maxValue = maxValue;
|
this.maxValue = maxValue;
|
||||||
this.currentValue = 0.0;
|
this.currentValue = 0.0;
|
||||||
this.needleColor = needleColor;
|
this.needleColor = needleColor;
|
||||||
setPreferredSize(new Dimension(200, 200));
|
setPreferredSize(new Dimension(250, 250));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValue(double value) {
|
public void setValue(double value) {
|
||||||
@@ -41,8 +41,9 @@ public class AnalogMeter extends JPanel {
|
|||||||
int centerY = getHeight() / 2;
|
int centerY = getHeight() / 2;
|
||||||
int radius = Math.min(centerX, centerY) - 10;
|
int radius = Math.min(centerX, centerY) - 10;
|
||||||
|
|
||||||
// Draw meter background
|
// Draw meter background with gradient
|
||||||
g2d.setColor(Color.LIGHT_GRAY);
|
GradientPaint gradient = new GradientPaint(0, 0, Color.LIGHT_GRAY, 0, getHeight(), Color.WHITE);
|
||||||
|
g2d.setPaint(gradient);
|
||||||
g2d.fillOval(centerX - radius, centerY - radius, radius * 2, radius * 2);
|
g2d.fillOval(centerX - radius, centerY - radius, radius * 2, radius * 2);
|
||||||
|
|
||||||
// Draw scale and ticks
|
// Draw scale and ticks
|
||||||
@@ -73,7 +74,7 @@ public class AnalogMeter extends JPanel {
|
|||||||
int needleX = (int) (centerX + needleLength * Math.cos(angle));
|
int needleX = (int) (centerX + needleLength * Math.cos(angle));
|
||||||
int needleY = (int) (centerY - needleLength * Math.sin(angle));
|
int needleY = (int) (centerY - needleLength * Math.sin(angle));
|
||||||
|
|
||||||
g2d.setStroke(new BasicStroke(3));
|
g2d.setStroke(new BasicStroke(4)); // Thicker needle
|
||||||
g2d.drawLine(centerX, centerY, needleX, needleY);
|
g2d.drawLine(centerX, centerY, needleX, needleY);
|
||||||
g2d.fillOval(centerX - 5, centerY - 5, 10, 10);
|
g2d.fillOval(centerX - 5, centerY - 5, 10, 10);
|
||||||
|
|
||||||
@@ -82,6 +83,6 @@ public class AnalogMeter extends JPanel {
|
|||||||
g2d.setFont(new Font("Arial", Font.BOLD, 16));
|
g2d.setFont(new Font("Arial", Font.BOLD, 16));
|
||||||
FontMetrics fm = g2d.getFontMetrics();
|
FontMetrics fm = g2d.getFontMetrics();
|
||||||
int labelWidth = fm.stringWidth(label);
|
int labelWidth = fm.stringWidth(label);
|
||||||
g2d.drawString(label, centerX - labelWidth / 2, centerY + radius + 20);
|
g2d.drawString(label, centerX - labelWidth / 2, centerY + radius + 30); // Adjusted position
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,99 +2,186 @@ package com.puchdyno;
|
|||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import org.jfree.chart.ChartFactory;
|
import java.time.LocalDate;
|
||||||
import org.jfree.chart.ChartPanel;
|
import java.time.format.DateTimeFormatter;
|
||||||
import org.jfree.chart.JFreeChart;
|
|
||||||
import org.jfree.chart.plot.PlotOrientation;
|
import org.jfree.chart.*;
|
||||||
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.axis.NumberAxis;
|
||||||
|
import org.jfree.chart.plot.*;
|
||||||
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
|
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 {
|
public class DynoGUI extends JFrame {
|
||||||
private JLabel leistungLabel;
|
private final JLabel leistungLabel;
|
||||||
private JLabel drehmomentLabel;
|
private final JLabel drehmomentLabel;
|
||||||
private AnalogMeter rpmMeter;
|
private final AnalogMeter rpmMeter;
|
||||||
private AnalogMeter hpMeter;
|
private final AnalogMeter hpMeter;
|
||||||
private XYSeries powerSeries;
|
private final XYSeries powerSeries;
|
||||||
private XYSeries torqueSeries;
|
private final XYSeries torqueSeries;
|
||||||
private ChartPanel chartPanel;
|
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() {
|
public DynoGUI() {
|
||||||
setTitle("Puch Maxi Dyno");
|
setTitle("Puch Maxi Dyno");
|
||||||
setSize(1000, 700); // Increased size for more elements
|
setSize(1000, 700);
|
||||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout(10, 10));
|
||||||
|
|
||||||
// Top panel for analog meters
|
// Top panel: analog meters
|
||||||
JPanel meterPanel = new JPanel(new GridLayout(1, 2));
|
JPanel meterPanel = new JPanel(new GridLayout(1, 2, 20, 20));
|
||||||
rpmMeter = new AnalogMeter("RPM", 12000, Color.BLUE); // Max RPM, adjusted for new test range
|
meterPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
hpMeter = new AnalogMeter("Horse Power", 15, Color.BLUE); // Max HP, e.g., 15
|
rpmMeter = new AnalogMeter("RPM", 12000, Color.BLUE);
|
||||||
meterPanel.add(rpmMeter);
|
hpMeter = new AnalogMeter("Horse Power", 15, Color.RED);
|
||||||
meterPanel.add(hpMeter);
|
|
||||||
|
rpmMeter.setToolTipText("Motordrehzahl in U/min");
|
||||||
|
hpMeter.setToolTipText("Motorleistung in PS");
|
||||||
|
|
||||||
|
JPanel rpmWrapper = new JPanel(new BorderLayout());
|
||||||
|
rpmWrapper.setBorder(BorderFactory.createTitledBorder("Drehzahl [RPM]"));
|
||||||
|
rpmWrapper.add(rpmMeter, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
JPanel hpWrapper = new JPanel(new BorderLayout());
|
||||||
|
hpWrapper.setBorder(BorderFactory.createTitledBorder("Leistung [PS]"));
|
||||||
|
hpWrapper.add(hpMeter, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
meterPanel.add(rpmWrapper);
|
||||||
|
meterPanel.add(hpWrapper);
|
||||||
add(meterPanel, BorderLayout.NORTH);
|
add(meterPanel, BorderLayout.NORTH);
|
||||||
|
|
||||||
// Center panel for labels and chart
|
// Center panel: chart and labels
|
||||||
JPanel centerPanel = new JPanel(new BorderLayout());
|
JPanel centerPanel = new JPanel(new BorderLayout(10, 10));
|
||||||
|
centerPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
JPanel labelPanel = new JPanel(new GridLayout(2, 1));
|
|
||||||
leistungLabel = new JLabel("Leistung: 0.00 PS", SwingConstants.CENTER);
|
// Labels panel with BoxLayout for better scaling
|
||||||
drehmomentLabel = new JLabel("Drehmoment: 0.00 Nm", SwingConstants.CENTER);
|
JPanel labelPanel = new JPanel();
|
||||||
leistungLabel.setFont(new Font("Arial", Font.BOLD, 24));
|
labelPanel.setLayout(new BoxLayout(labelPanel, BoxLayout.Y_AXIS));
|
||||||
drehmomentLabel.setFont(new Font("Arial", Font.BOLD, 24));
|
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(leistungLabel);
|
||||||
labelPanel.add(drehmomentLabel);
|
labelPanel.add(drehmomentLabel);
|
||||||
|
labelPanel.add(Box.createVerticalStrut(5));
|
||||||
|
labelPanel.add(maxLeistungLabel);
|
||||||
|
labelPanel.add(Box.createVerticalStrut(5));
|
||||||
|
labelPanel.add(maxDrehmomentLabel);
|
||||||
centerPanel.add(labelPanel, BorderLayout.NORTH);
|
centerPanel.add(labelPanel, BorderLayout.NORTH);
|
||||||
|
|
||||||
// Chart setup
|
// Data series
|
||||||
powerSeries = new XYSeries("Power (PS)");
|
powerSeries = new XYSeries("Power (PS)");
|
||||||
torqueSeries = new XYSeries("Torque (Nm)");
|
torqueSeries = new XYSeries("Torque (Nm)");
|
||||||
XYSeriesCollection dataset = new XYSeriesCollection();
|
efficiencySeries = new XYSeries("Efficiency");
|
||||||
dataset.addSeries(powerSeries);
|
|
||||||
dataset.addSeries(torqueSeries);
|
|
||||||
|
|
||||||
|
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(
|
JFreeChart chart = ChartFactory.createXYLineChart(
|
||||||
"Leistungsdiagramm - Motorleistung",
|
"LeistungsPrüfstand - Puch Maxi (" + dateStr + ")",
|
||||||
"U/min",
|
"U/min",
|
||||||
"Leistung [PS]",
|
"Leistung [PS]",
|
||||||
dataset,
|
powerDataset,
|
||||||
PlotOrientation.VERTICAL,
|
PlotOrientation.VERTICAL,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
XYPlot plot = chart.getXYPlot();
|
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();
|
NumberAxis powerAxis = (NumberAxis) plot.getRangeAxis();
|
||||||
powerAxis.setRange(0.0, 10.0);
|
powerAxis.setRange(0.0, 10.0);
|
||||||
powerAxis.setLabel("Leistung [PS]");
|
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]");
|
NumberAxis torqueAxis = new NumberAxis("Drehmoment [Nm]");
|
||||||
torqueAxis.setRange(0.0, 15.0);
|
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.setRangeAxis(1, torqueAxis);
|
||||||
|
plot.setDataset(1, torqueDataset);
|
||||||
plot.mapDatasetToRangeAxis(1, 1);
|
plot.mapDatasetToRangeAxis(1, 1);
|
||||||
|
|
||||||
|
// Efficiency axis (additional, right side 2)
|
||||||
|
NumberAxis effAxis = new NumberAxis("Effizienz [arb.u.]");
|
||||||
|
effAxis.setAutoRangeIncludesZero(false);
|
||||||
|
effAxis.setTickLabelFont(new Font("SansSerif", Font.PLAIN, 12));
|
||||||
|
effAxis.setLabelFont(new Font("SansSerif", Font.BOLD, 14));
|
||||||
|
plot.setRangeAxis(2, effAxis);
|
||||||
|
plot.setDataset(2, efficiencyDataset);
|
||||||
|
plot.mapDatasetToRangeAxis(2, 2);
|
||||||
|
|
||||||
|
// X axis
|
||||||
NumberAxis xAxis = (NumberAxis) plot.getDomainAxis();
|
NumberAxis xAxis = (NumberAxis) plot.getDomainAxis();
|
||||||
xAxis.setRange(0.0, 12000.0);
|
xAxis.setRange(0.0, 12000.0);
|
||||||
xAxis.setLabel("U/min");
|
xAxis.setLabel("U/min");
|
||||||
|
xAxis.setTickLabelFont(new Font("SansSerif", Font.PLAIN, 12));
|
||||||
|
xAxis.setLabelFont(new Font("SansSerif", Font.BOLD, 14));
|
||||||
|
|
||||||
XYLineAndShapeRenderer renderer1 = new XYLineAndShapeRenderer();
|
// Renderers
|
||||||
renderer1.setSeriesPaint(0, Color.RED);
|
org.jfree.chart.renderer.xy.XYSplineRenderer powerRenderer = new org.jfree.chart.renderer.xy.XYSplineRenderer();
|
||||||
renderer1.setSeriesShapesVisible(0, false);
|
powerRenderer.setSeriesPaint(0, new Color(220, 50, 47)); // Red
|
||||||
plot.setRenderer(0, renderer1);
|
powerRenderer.setSeriesStroke(0, new BasicStroke(2f));
|
||||||
|
plot.setRenderer(0, powerRenderer);
|
||||||
|
|
||||||
XYLineAndShapeRenderer renderer2 = new XYLineAndShapeRenderer();
|
org.jfree.chart.renderer.xy.XYSplineRenderer torqueRenderer = new org.jfree.chart.renderer.xy.XYSplineRenderer();
|
||||||
renderer2.setSeriesPaint(0, Color.BLUE);
|
torqueRenderer.setSeriesPaint(0, new Color(38, 139, 210)); // Blue
|
||||||
renderer2.setSeriesShapesVisible(0, false);
|
torqueRenderer.setSeriesStroke(0, new BasicStroke(2f));
|
||||||
plot.setRenderer(1, renderer2);
|
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 = new ChartPanel(chart);
|
||||||
chartPanel.setPreferredSize(new Dimension(600, 400));
|
chartPanel.setPreferredSize(new Dimension(800, 400));
|
||||||
|
chartPanel.setMouseWheelEnabled(true);
|
||||||
|
chartPanel.setPopupMenu(null);
|
||||||
centerPanel.add(chartPanel, BorderLayout.CENTER);
|
centerPanel.add(chartPanel, BorderLayout.CENTER);
|
||||||
add(centerPanel, BorderLayout.CENTER);
|
|
||||||
|
|
||||||
|
add(centerPanel, BorderLayout.CENTER);
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +189,6 @@ public class DynoGUI extends JFrame {
|
|||||||
rpmMeter.setValue(rpm);
|
rpmMeter.setValue(rpm);
|
||||||
hpMeter.setValue(leistung);
|
hpMeter.setValue(leistung);
|
||||||
|
|
||||||
// Change HP meter needle color based on performance
|
|
||||||
if (leistung >= 15.0) {
|
if (leistung >= 15.0) {
|
||||||
hpMeter.setNeedleColor(Color.RED);
|
hpMeter.setNeedleColor(Color.RED);
|
||||||
} else if (leistung >= 10.0) {
|
} else if (leistung >= 10.0) {
|
||||||
@@ -114,17 +200,65 @@ public class DynoGUI extends JFrame {
|
|||||||
leistungLabel.setText(String.format("Leistung: %.2f PS", leistung));
|
leistungLabel.setText(String.format("Leistung: %.2f PS", leistung));
|
||||||
drehmomentLabel.setText(String.format("Drehmoment: %.2f Nm", drehmoment));
|
drehmomentLabel.setText(String.format("Drehmoment: %.2f Nm", drehmoment));
|
||||||
|
|
||||||
// Add data to chart
|
// keine Plot-Aktualisierung hier – siehe addPlotData()
|
||||||
powerSeries.add(rpm, leistung);
|
|
||||||
torqueSeries.add(rpm, drehmoment);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetChart() {
|
public void resetChart() {
|
||||||
powerSeries.clear();
|
powerSeries.clear();
|
||||||
torqueSeries.clear();
|
torqueSeries.clear();
|
||||||
|
efficiencySeries.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetMaxHold() {
|
||||||
|
maxLeistung = 0.0;
|
||||||
|
maxDrehmoment = 0.0;
|
||||||
|
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() {
|
public JFreeChart getChart() {
|
||||||
return chartPanel.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,66 +29,36 @@ public class Main {
|
|||||||
private static LeistungBerechner leistungBerechner;
|
private static LeistungBerechner leistungBerechner;
|
||||||
private static long lastTimestamp = 0;
|
private static long lastTimestamp = 0;
|
||||||
private static double lastOmega = 0.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 final double WHEEL_RADIUS_METERS = 0.25;
|
||||||
|
|
||||||
private static boolean testMode = false;
|
private static boolean testMode = false;
|
||||||
private static ScheduledExecutorService testDataExecutor;
|
private static ScheduledExecutorService testDataExecutor;
|
||||||
private static int testRpmCounter = 1000;
|
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 SerialPort currentSerialPort;
|
||||||
private static double testRpmCurrentValue = 0.0;
|
private static ScheduledExecutorService serialExecutor;
|
||||||
private static final double TEST_RPM_INCREMENT_STEP = 100.0; // Increased for faster simulation
|
private static BufferedReader serialReader;
|
||||||
|
|
||||||
private static java.util.List<Double> smoothRpmData;
|
private static CSVLogger csvLogger;
|
||||||
private static int smoothRpmDataIndex = 0;
|
private static boolean measurementActive = false;
|
||||||
private static final double TEST_SIMULATION_DELTA_TIME = 0.05; // 50 milliseconds
|
|
||||||
|
|
||||||
private static TreeMap<Double, Double> simulatedPowerCurve;
|
private static double peakRpmDuringRun = 0.0;
|
||||||
private static TreeMap<Double, Double> simulatedTorqueCurve;
|
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) {
|
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(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
dynoGUI = new DynoGUI();
|
dynoGUI = new DynoGUI();
|
||||||
leistungBerechner = new LeistungBerechner();
|
leistungBerechner = new LeistungBerechner();
|
||||||
addControlPanel();
|
addControlPanel();
|
||||||
// Optionally start in test mode for development
|
startSerialPortListener(); // Start listening for serial data immediately
|
||||||
// toggleTestMode();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addControlPanel() {
|
private static void addControlPanel() {
|
||||||
JPanel controlPanel = new JPanel();
|
JPanel controlPanel = new JPanel();
|
||||||
|
|
||||||
JButton toggleModeButton = new JButton("Toggle Test Mode");
|
JButton toggleModeButton = new JButton("Toggle Test Mode");
|
||||||
toggleModeButton.addActionListener(e -> toggleTestMode());
|
toggleModeButton.addActionListener(e -> toggleTestMode());
|
||||||
controlPanel.add(toggleModeButton);
|
controlPanel.add(toggleModeButton);
|
||||||
@@ -105,11 +75,32 @@ public class Main {
|
|||||||
resetChartButton.addActionListener(e -> dynoGUI.resetChart());
|
resetChartButton.addActionListener(e -> dynoGUI.resetChart());
|
||||||
controlPanel.add(resetChartButton);
|
controlPanel.add(resetChartButton);
|
||||||
|
|
||||||
|
JButton resetMaxButton = new JButton("Reset Max-Hold");
|
||||||
|
resetMaxButton.addActionListener(e -> dynoGUI.resetMaxHold());
|
||||||
|
controlPanel.add(resetMaxButton);
|
||||||
|
|
||||||
|
JButton reconnectButton = new JButton("Reconnect Serial");
|
||||||
|
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.add(controlPanel, BorderLayout.SOUTH);
|
||||||
dynoGUI.revalidate();
|
dynoGUI.revalidate();
|
||||||
dynoGUI.repaint();
|
dynoGUI.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void reconnectSerial() {
|
||||||
|
stopSerialPortListener();
|
||||||
|
startSerialPortListener();
|
||||||
|
}
|
||||||
|
|
||||||
private static void toggleTestMode() {
|
private static void toggleTestMode() {
|
||||||
testMode = !testMode;
|
testMode = !testMode;
|
||||||
if (testMode) {
|
if (testMode) {
|
||||||
@@ -133,69 +124,46 @@ public class Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dynoGUI.resetChart();
|
dynoGUI.resetChart();
|
||||||
lastTimestamp = 0; // Reset for a new run
|
measurementActive = true;
|
||||||
|
lastTimestamp = 0;
|
||||||
lastOmega = 0.0;
|
lastOmega = 0.0;
|
||||||
testRpmCurrentIndex = 0;
|
peakRpmDuringRun = 0.0;
|
||||||
testRpmCurrentValue = TEST_RPM_VALUES[0]; // Start at the first RPM value
|
belowThresholdCounter = 0;
|
||||||
|
|
||||||
// Pre-generate smooth RPM data
|
if (csvLogger != null) { try { csvLogger.close(); } catch (Exception ex) {} }
|
||||||
smoothRpmData = new ArrayList<>();
|
try { csvLogger = new CSVLogger(new java.io.File("logs")); } catch(Exception ex) { csvLogger = null; }
|
||||||
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++) {
|
final long simStartTime = System.currentTimeMillis();
|
||||||
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 = Executors.newSingleThreadScheduledExecutor();
|
||||||
testDataExecutor.scheduleAtFixedRate(() -> {
|
testDataExecutor.scheduleAtFixedRate(() -> {
|
||||||
simulateSmoothRpmRun();
|
// Simulate a realistic dyno run pattern
|
||||||
}, 0, 1, TimeUnit.MILLISECONDS); // Update RPM every 10ms for faster animation
|
long currentTime = System.currentTimeMillis();
|
||||||
}
|
double elapsedSeconds = (currentTime - simStartTime) / 1000.0;
|
||||||
|
|
||||||
private static void simulateSmoothRpmRun() {
|
// Simulate acceleration, hold, then deceleration
|
||||||
if (smoothRpmDataIndex >= smoothRpmData.size()) {
|
double simulatedRpm;
|
||||||
stopTestRun();
|
if (elapsedSeconds < 5.0) {
|
||||||
JOptionPane.showMessageDialog(null, "Test run finished.", "Info", JOptionPane.INFORMATION_MESSAGE);
|
// Acceleration phase (0-5 seconds)
|
||||||
showPrintableChart();
|
simulatedRpm = 800 + (11000 - 800) * (elapsedSeconds / 5.0);
|
||||||
return;
|
} 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();
|
||||||
|
}
|
||||||
|
|
||||||
double currentSimulatedRpm = smoothRpmData.get(smoothRpmDataIndex);
|
// Add some realistic noise
|
||||||
processRPMData(currentSimulatedRpm, TEST_SIMULATION_DELTA_TIME);
|
simulatedRpm += (Math.random() * 200 - 100);
|
||||||
smoothRpmDataIndex++;
|
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() {
|
private static void stopTestRun() {
|
||||||
@@ -203,14 +171,17 @@ public class Main {
|
|||||||
testDataExecutor.shutdownNow();
|
testDataExecutor.shutdownNow();
|
||||||
testDataExecutor = null;
|
testDataExecutor = null;
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
dynoGUI.updateWerte(0, 0, 0); // Reset gauges
|
dynoGUI.updateWerte(0, 0, 0);
|
||||||
|
if (csvLogger != null) {
|
||||||
|
try { csvLogger.close(); } catch (Exception ex) { ex.printStackTrace(); }
|
||||||
|
csvLogger = null;
|
||||||
|
}
|
||||||
|
showPrintableChart();
|
||||||
});
|
});
|
||||||
|
measurementActive = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SerialPort currentSerialPort;
|
|
||||||
private static ScheduledExecutorService serialExecutor;
|
|
||||||
|
|
||||||
private static void startSerialPortListener() {
|
private static void startSerialPortListener() {
|
||||||
SerialPort[] comPorts = SerialPort.getCommPorts();
|
SerialPort[] comPorts = SerialPort.getCommPorts();
|
||||||
if (comPorts.length == 0) {
|
if (comPorts.length == 0) {
|
||||||
@@ -250,23 +221,29 @@ public class Main {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final SerialPort finalChosenPort = chosenPort;
|
currentSerialPort = chosenPort;
|
||||||
currentSerialPort = finalChosenPort; // Store for later closing
|
chosenPort.setBaudRate(9600);
|
||||||
|
chosenPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 100, 0);
|
||||||
|
|
||||||
finalChosenPort.setBaudRate(9600); // Common baud rate, can be adjusted
|
if (chosenPort.openPort()) {
|
||||||
if (finalChosenPort.openPort()) {
|
System.out.println("Serial port " + chosenPort.getSystemPortName() + " opened successfully.");
|
||||||
System.out.println("Serial port " + finalChosenPort.getSystemPortName() + " opened successfully.");
|
serialReader = new BufferedReader(new InputStreamReader(chosenPort.getInputStream()));
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(finalChosenPort.getInputStream()));
|
|
||||||
|
|
||||||
serialExecutor = Executors.newSingleThreadScheduledExecutor();
|
serialExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||||
serialExecutor.scheduleAtFixedRate(() -> {
|
serialExecutor.scheduleAtFixedRate(() -> {
|
||||||
try {
|
try {
|
||||||
if (reader.ready()) {
|
if (serialReader.ready()) {
|
||||||
String line = reader.readLine();
|
String line = serialReader.readLine();
|
||||||
if (line != null && !line.trim().isEmpty()) {
|
if (line != null && !line.trim().isEmpty()) {
|
||||||
try {
|
try {
|
||||||
double rpm = Double.parseDouble(line.trim());
|
// Handle potential incomplete lines or multiple values
|
||||||
processRPMData(rpm); // Call the main processRPMData for live data
|
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) {
|
} catch (NumberFormatException e) {
|
||||||
System.err.println("Received non-numeric data: " + line);
|
System.err.println("Received non-numeric data: " + line);
|
||||||
}
|
}
|
||||||
@@ -276,124 +253,159 @@ public class Main {
|
|||||||
System.err.println("Error reading from serial port: " + e.getMessage());
|
System.err.println("Error reading from serial port: " + e.getMessage());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
stopSerialPortListener();
|
stopSerialPortListener();
|
||||||
JOptionPane.showMessageDialog(null, "Serial port error: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
JOptionPane.showMessageDialog(null,
|
||||||
|
"Serial port error: " + e.getMessage(),
|
||||||
|
"Error", JOptionPane.ERROR_MESSAGE);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, 0, 100, TimeUnit.MILLISECONDS); // Read every 100ms
|
}, 0, 10, TimeUnit.MILLISECONDS); // Check more frequently for data
|
||||||
} else {
|
} else {
|
||||||
JOptionPane.showMessageDialog(null, "Failed to open serial port " + finalChosenPort.getSystemPortName(), "Error", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(null,
|
||||||
|
"Failed to open serial port " + chosenPort.getSystemPortName(),
|
||||||
|
"Error", JOptionPane.ERROR_MESSAGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void stopSerialPortListener() {
|
private static void stopSerialPortListener() {
|
||||||
if (serialExecutor != null) {
|
if (serialExecutor != null) {
|
||||||
serialExecutor.shutdownNow();
|
serialExecutor.shutdownNow();
|
||||||
|
try {
|
||||||
|
if (!serialExecutor.awaitTermination(500, TimeUnit.MILLISECONDS)) {
|
||||||
|
serialExecutor.shutdownNow();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
serialExecutor.shutdownNow();
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
serialExecutor = null;
|
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()) {
|
if (currentSerialPort != null && currentSerialPort.isOpen()) {
|
||||||
currentSerialPort.closePort();
|
currentSerialPort.closePort();
|
||||||
System.out.println("Serial port " + currentSerialPort.getSystemPortName() + " closed.");
|
System.out.println("Serial port " + currentSerialPort.getSystemPortName() + " closed.");
|
||||||
currentSerialPort = null;
|
currentSerialPort = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (csvLogger != null) {
|
||||||
|
try { csvLogger.close(); } catch (Exception ex) { ex.printStackTrace(); }
|
||||||
|
csvLogger = null;
|
||||||
|
}
|
||||||
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
dynoGUI.updateWerte(0, 0, 0); // Reset gauges
|
dynoGUI.updateWerte(0, 0, 0); // Reset gauges
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overloaded processRPMData for live mode, calculates deltaZeit from system time
|
|
||||||
private static void processRPMData(double rpm) {
|
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();
|
long currentTimestamp = System.currentTimeMillis();
|
||||||
|
|
||||||
double effectiveDeltaZeit;
|
if (lastTimestamp == 0) {
|
||||||
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;
|
lastTimestamp = currentTimestamp;
|
||||||
lastOmega = currentOmega;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Linear interpolation method
|
private static void processRPMData(double rpm, double forcedDeltaTime) {
|
||||||
private static double interpolate(TreeMap<Double, Double> curve, double rpm) {
|
// For test mode with forced delta time
|
||||||
if (curve.containsKey(rpm)) {
|
double currentOmega = rpmToOmega(rpm);
|
||||||
return curve.get(rpm);
|
double drehmoment = leistungBerechner.berechneDrehmoment(lastOmega, currentOmega, forcedDeltaTime);
|
||||||
}
|
double leistung = leistungBerechner.berechneLeistung(drehmoment, currentOmega);
|
||||||
Map.Entry<Double, Double> lower = curve.floorEntry(rpm);
|
|
||||||
Map.Entry<Double, Double> upper = curve.ceilingEntry(rpm);
|
|
||||||
|
|
||||||
if (lower == null) {
|
if (measurementActive) {
|
||||||
return upper.getValue(); // RPM is below lowest defined point, return lowest point's value
|
if (rpm > peakRpmDuringRun) {
|
||||||
}
|
peakRpmDuringRun = rpm;
|
||||||
if (upper == null) {
|
}
|
||||||
return lower.getValue(); // RPM is above highest defined point, return highest point's value
|
if (peakRpmDuringRun > 0 && rpm < peakRpmDuringRun * RPM_DROP_FACTOR) {
|
||||||
|
belowThresholdCounter++;
|
||||||
|
} else {
|
||||||
|
belowThresholdCounter = 0;
|
||||||
|
}
|
||||||
|
if (belowThresholdCounter >= BELOW_THRESHOLD_LIMIT) {
|
||||||
|
SwingUtilities.invokeLater(() -> stopMeasurement());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double x1 = lower.getKey();
|
SwingUtilities.invokeLater(() -> {
|
||||||
double y1 = lower.getValue();
|
dynoGUI.updateWerte(leistung, drehmoment, rpm);
|
||||||
double x2 = upper.getKey();
|
if (measurementActive) {
|
||||||
double y2 = upper.getValue();
|
dynoGUI.addPlotData(leistung, drehmoment, rpm);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Linear interpolation formula: y = y1 + ((y2 - y1) / (x2 - x1)) * (x - x1)
|
lastOmega = currentOmega;
|
||||||
if (x2 == x1) { // Avoid division by zero if two points have the same RPM (shouldn't happen with proper data)
|
lastTimestamp = System.currentTimeMillis();
|
||||||
return y1;
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return y1 + ((y2 - y1) / (x2 - x1)) * (rpm - x1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double rpmToOmega(double rpm) {
|
private static double rpmToOmega(double rpm) {
|
||||||
@@ -418,7 +430,9 @@ public class Main {
|
|||||||
try {
|
try {
|
||||||
job.print();
|
job.print();
|
||||||
} catch (PrinterException ex) {
|
} catch (PrinterException ex) {
|
||||||
JOptionPane.showMessageDialog(chartFrame, "Printing error: " + ex.getMessage(), "Print Error", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(chartFrame,
|
||||||
|
"Printing error: " + ex.getMessage(),
|
||||||
|
"Print Error", JOptionPane.ERROR_MESSAGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -426,9 +440,13 @@ public class Main {
|
|||||||
JButton savePdfButton = new JButton("Save as PDF");
|
JButton savePdfButton = new JButton("Save as PDF");
|
||||||
savePdfButton.addActionListener(e -> saveChartAsPdf(printChartPanel));
|
savePdfButton.addActionListener(e -> saveChartAsPdf(printChartPanel));
|
||||||
|
|
||||||
|
JButton savePngButton = new JButton("Save as PNG");
|
||||||
|
savePngButton.addActionListener(e -> saveChartAsPng(printChartPanel));
|
||||||
|
|
||||||
JPanel controlPanel = new JPanel();
|
JPanel controlPanel = new JPanel();
|
||||||
controlPanel.add(printButton);
|
controlPanel.add(printButton);
|
||||||
controlPanel.add(savePdfButton);
|
controlPanel.add(savePdfButton);
|
||||||
|
controlPanel.add(savePngButton);
|
||||||
chartFrame.add(controlPanel, BorderLayout.SOUTH);
|
chartFrame.add(controlPanel, BorderLayout.SOUTH);
|
||||||
|
|
||||||
chartFrame.setLocationRelativeTo(dynoGUI);
|
chartFrame.setLocationRelativeTo(dynoGUI);
|
||||||
@@ -451,27 +469,92 @@ public class Main {
|
|||||||
File finalFile = new File(filePath);
|
File finalFile = new File(filePath);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use the dimensions of the ChartPanel to create the PDF document
|
|
||||||
Document document = new Document(new Rectangle(chartPanel.getWidth(), chartPanel.getHeight()));
|
Document document = new Document(new Rectangle(chartPanel.getWidth(), chartPanel.getHeight()));
|
||||||
PdfWriter.getInstance(document, new FileOutputStream(finalFile));
|
PdfWriter.getInstance(document, new FileOutputStream(finalFile));
|
||||||
document.open();
|
document.open();
|
||||||
|
|
||||||
// Create BufferedImage from the ChartPanel's chart
|
java.awt.Image awtImage = chartPanel.getChart().createBufferedImage(
|
||||||
java.awt.Image awtImage = chartPanel.getChart().createBufferedImage(chartPanel.getWidth(), chartPanel.getHeight());
|
chartPanel.getWidth(), chartPanel.getHeight());
|
||||||
Image pdfImage = Image.getInstance(awtImage, null);
|
Image pdfImage = Image.getInstance(awtImage, null);
|
||||||
|
|
||||||
// Scale image to fit document
|
pdfImage.scaleToFit(document.getPageSize().getWidth() - 20,
|
||||||
pdfImage.scaleToFit(document.getPageSize().getWidth() - 20, document.getPageSize().getHeight() - 20); // Add some padding
|
document.getPageSize().getHeight() - 20);
|
||||||
pdfImage.setAlignment(Image.ALIGN_CENTER);
|
pdfImage.setAlignment(Image.ALIGN_CENTER);
|
||||||
|
|
||||||
document.add(pdfImage);
|
document.add(pdfImage);
|
||||||
document.close();
|
document.close();
|
||||||
|
|
||||||
JOptionPane.showMessageDialog(null, "Chart saved as PDF successfully!\n" + finalFile.getAbsolutePath(), "Save Successful", JOptionPane.INFORMATION_MESSAGE);
|
JOptionPane.showMessageDialog(null,
|
||||||
|
"Chart saved as PDF successfully!\n" + finalFile.getAbsolutePath(),
|
||||||
|
"Save Successful", JOptionPane.INFORMATION_MESSAGE);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
JOptionPane.showMessageDialog(null, "Error saving chart as PDF: " + ex.getMessage(), "Save Error", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(null,
|
||||||
|
"Error saving chart as PDF: " + ex.getMessage(),
|
||||||
|
"Save Error", JOptionPane.ERROR_MESSAGE);
|
||||||
ex.printStackTrace();
|
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