feat(web): multiple feature
This commit is contained in:
@@ -19,7 +19,7 @@ RUN apt-get update && apt-get install -y \
|
||||
libglib2.0-0 \
|
||||
libgtk-3-0 \
|
||||
libdbus-1-3 \
|
||||
libwebkit2gtk-4.1-0 \
|
||||
libwebkit2gtk-4.0-37 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install OrcaSlicer
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id 'org.springframework.boot' version '3.4.1'
|
||||
id 'io.spring.dependency-management' version '1.1.7'
|
||||
}
|
||||
@@ -13,6 +14,10 @@ java {
|
||||
}
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass = 'com.printcalculator.BackendApplication'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
@@ -27,3 +32,11 @@ dependencies {
|
||||
tasks.named('test') {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
tasks.named('bootRun') {
|
||||
args = ["--spring.profiles.active=local"]
|
||||
}
|
||||
|
||||
application {
|
||||
applicationDefaultJvmArgs = ["-Dspring.profiles.active=local"]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.printcalculator.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
@Profile("local")
|
||||
public class CorsConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedOrigins("*")
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
||||
.allowedHeaders("*")
|
||||
.allowCredentials(false);
|
||||
}
|
||||
}
|
||||
@@ -13,16 +13,15 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
@RestController
|
||||
@CrossOrigin(origins = "*") // Allow all for development
|
||||
public class QuoteController {
|
||||
|
||||
private final SlicerService slicerService;
|
||||
private final QuoteCalculator quoteCalculator;
|
||||
|
||||
// Defaults
|
||||
private static final String DEFAULT_MACHINE = "Bambu_Lab_A1_machine";
|
||||
private static final String DEFAULT_FILAMENT = "Bambu_PLA_Basic";
|
||||
private static final String DEFAULT_PROCESS = "Bambu_Process_0.20_Standard";
|
||||
// Defaults (using aliases defined in ProfileManager)
|
||||
private static final String DEFAULT_MACHINE = "bambu_a1";
|
||||
private static final String DEFAULT_FILAMENT = "pla_basic";
|
||||
private static final String DEFAULT_PROCESS = "standard";
|
||||
|
||||
public QuoteController(SlicerService slicerService, QuoteCalculator quoteCalculator) {
|
||||
this.slicerService = slicerService;
|
||||
@@ -32,12 +31,24 @@ public class QuoteController {
|
||||
@PostMapping("/api/quote")
|
||||
public ResponseEntity<QuoteResult> calculateQuote(
|
||||
@RequestParam("file") MultipartFile file,
|
||||
@RequestParam(value = "machine", defaultValue = DEFAULT_MACHINE) String machine,
|
||||
@RequestParam(value = "filament", defaultValue = DEFAULT_FILAMENT) String filament,
|
||||
@RequestParam(value = "process", defaultValue = DEFAULT_PROCESS) String process
|
||||
@RequestParam(value = "machine", required = false, defaultValue = DEFAULT_MACHINE) String machine,
|
||||
@RequestParam(value = "filament", required = false, defaultValue = DEFAULT_FILAMENT) String filament,
|
||||
@RequestParam(value = "process", required = false) String process,
|
||||
@RequestParam(value = "quality", required = false) String quality
|
||||
) throws IOException {
|
||||
|
||||
return processRequest(file, machine, filament, process);
|
||||
// Frontend sends 'quality', backend expects 'process'.
|
||||
// If process is missing, try quality. If both missing, use default.
|
||||
String actualProcess = process;
|
||||
if (actualProcess == null || actualProcess.isEmpty()) {
|
||||
if (quality != null && !quality.isEmpty()) {
|
||||
actualProcess = quality;
|
||||
} else {
|
||||
actualProcess = DEFAULT_PROCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return processRequest(file, machine, filament, actualProcess);
|
||||
}
|
||||
|
||||
@PostMapping("/calculate/stl")
|
||||
|
||||
@@ -13,9 +13,13 @@ import java.util.regex.Pattern;
|
||||
@Service
|
||||
public class GCodeParser {
|
||||
|
||||
private static final Pattern TIME_PATTERN = Pattern.compile("estimated printing time = (.*)");
|
||||
private static final Pattern FILAMENT_G_PATTERN = Pattern.compile("filament used \\[g\\] = (.*)");
|
||||
private static final Pattern FILAMENT_MM_PATTERN = Pattern.compile("filament used \\[mm\\] = (.*)");
|
||||
// OrcaSlicer/BambuStudio format
|
||||
// ; estimated printing time = 1h 2m 3s
|
||||
// ; filament used [g] = 12.34
|
||||
// ; filament used [mm] = 1234.56
|
||||
private static final Pattern TIME_PATTERN = Pattern.compile(";\\s*estimated printing time\\s*=\\s*(.*)");
|
||||
private static final Pattern FILAMENT_G_PATTERN = Pattern.compile(";\\s*filament used \\[g\\]\\s*=\\s*(.*)");
|
||||
private static final Pattern FILAMENT_MM_PATTERN = Pattern.compile(";\\s*filament used \\[mm\\]\\s*=\\s*(.*)");
|
||||
|
||||
public PrintStats parse(File gcodeFile) throws IOException {
|
||||
long seconds = 0;
|
||||
@@ -25,9 +29,9 @@ public class GCodeParser {
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(gcodeFile))) {
|
||||
String line;
|
||||
// Scan first 500 lines for efficiency
|
||||
// Scan first 5000 lines for efficiency (metadata might be further down)
|
||||
int count = 0;
|
||||
while ((line = reader.readLine()) != null && count < 500) {
|
||||
while ((line = reader.readLine()) != null && count < 5000) {
|
||||
line = line.trim();
|
||||
if (!line.startsWith(";")) {
|
||||
count++;
|
||||
|
||||
@@ -14,6 +14,8 @@ import java.util.Iterator;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
@Service
|
||||
public class ProfileManager {
|
||||
@@ -22,9 +24,31 @@ public class ProfileManager {
|
||||
private final String profilesRoot;
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
private final Map<String, String> profileAliases;
|
||||
|
||||
public ProfileManager(@Value("${profiles.root:profiles}") String profilesRoot, ObjectMapper mapper) {
|
||||
this.profilesRoot = profilesRoot;
|
||||
this.mapper = mapper;
|
||||
this.profileAliases = new HashMap<>();
|
||||
initializeAliases();
|
||||
}
|
||||
|
||||
private void initializeAliases() {
|
||||
// Machine Aliases
|
||||
profileAliases.put("bambu_a1", "Bambu Lab A1 0.4 nozzle");
|
||||
|
||||
// Material Aliases
|
||||
profileAliases.put("pla_basic", "Bambu PLA Basic @BBL A1");
|
||||
profileAliases.put("petg_basic", "Bambu PETG Basic @BBL A1");
|
||||
profileAliases.put("tpu_95a", "Bambu TPU 95A @BBL A1");
|
||||
|
||||
// Quality/Process Aliases
|
||||
profileAliases.put("draft", "0.24mm Draft @BBL A1");
|
||||
profileAliases.put("standard", "0.20mm Standard @BBL A1"); // or 0.20mm Standard @BBL A1
|
||||
profileAliases.put("extra_fine", "0.08mm High Quality @BBL A1");
|
||||
|
||||
// Additional aliases from error logs
|
||||
profileAliases.put("Bambu_Process_0.20_Standard", "0.20mm Standard @BBL A1");
|
||||
}
|
||||
|
||||
public ObjectNode getMergedProfile(String profileName, String type) throws IOException {
|
||||
@@ -36,9 +60,12 @@ public class ProfileManager {
|
||||
}
|
||||
|
||||
private Path findProfileFile(String name, String type) {
|
||||
// Check aliases first
|
||||
String resolvedName = profileAliases.getOrDefault(name, name);
|
||||
|
||||
// Simple search: look for name.json in the profiles_root recursively
|
||||
// Type could be "machine", "process", "filament" to narrow down, but for now global search
|
||||
String filename = name.endsWith(".json") ? name : name + ".json";
|
||||
String filename = resolvedName.endsWith(".json") ? resolvedName : resolvedName + ".json";
|
||||
|
||||
try (Stream<Path> stream = Files.walk(Paths.get(profilesRoot))) {
|
||||
Optional<Path> found = stream
|
||||
|
||||
@@ -52,8 +52,8 @@ public class QuoteCalculator {
|
||||
);
|
||||
|
||||
List<String> notes = new ArrayList<>();
|
||||
notes.add("Generated via Dynamic Slicer (Java Backend)");
|
||||
// notes.add("Generated via Dynamic Slicer (Java Backend)");
|
||||
|
||||
return new QuoteResult(totalPrice, "EUR", stats, breakdown, notes);
|
||||
return new QuoteResult(totalPrice, "CHF", stats, breakdown, notes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,12 +55,16 @@ public class SlicerService {
|
||||
|
||||
// 3. Build Command
|
||||
// --load-settings "machine.json;process.json" --load-filaments "filament.json"
|
||||
String settingsArg = mFile.getAbsolutePath() + ";" + pFile.getAbsolutePath();
|
||||
|
||||
List<String> command = new ArrayList<>();
|
||||
command.add(slicerPath);
|
||||
|
||||
// Load machine settings
|
||||
command.add("--load-settings");
|
||||
command.add(settingsArg);
|
||||
command.add(mFile.getAbsolutePath());
|
||||
|
||||
// Load process settings
|
||||
command.add("--load-settings");
|
||||
command.add(pFile.getAbsolutePath());
|
||||
command.add("--load-filaments");
|
||||
command.add(fFile.getAbsolutePath());
|
||||
command.add("--ensure-on-bed");
|
||||
|
||||
Reference in New Issue
Block a user