@fdm-monster/server
Version:
FDM Monster is a bulk OctoPrint, Klipper, PrusaLink and BambuLab manager to set up, configure and monitor 3D printers. Our aim is to provide neat overview over your farm.
136 lines (135 loc) • 4.44 kB
JavaScript
import { GCodeParser } from "../utils/parsers/gcode.parser.js";
import { ThreeMFParser } from "../utils/parsers/3mf.parser.js";
import { BGCodeParser } from "../utils/parsers/bgcode.parser.js";
import { basename, extname } from "node:path";
import { access } from "node:fs/promises";
//#region src/services/file-analysis.service.ts
/**
* Service for analyzing print files and extracting metadata
* Supports G-code, 3MF (including multi-plate), and BGCode formats
*/
var FileAnalysisService = class FileAnalysisService {
logger;
FILE_FORMATS = {
GCODE: "gcode",
THREE_MF: "3mf",
BGCODE: "bgcode"
};
EXTENSIONS = {
THREE_MF: ".3mf",
BGCODE: ".bgcode",
GCODE: ".gcode",
GCODE_SHORT: ".g",
GCODE_ALT: ".gco",
GCODE_THREE_MF: ".gcode.3mf"
};
gcodeParser;
threemfParser;
bgcodeParser;
constructor(loggerFactory) {
this.logger = loggerFactory(FileAnalysisService.name);
this.gcodeParser = new GCodeParser();
this.threemfParser = new ThreeMFParser();
this.bgcodeParser = new BGCodeParser();
this.logger.log("File analysis service initialized with all parsers");
}
/**
* Analyze a file and extract metadata
*/
async analyzeFile(filePath) {
const ext = extname(filePath).toLowerCase();
const fileFormat = this.getFileFormat(ext, filePath);
this.logger.log(`Analyzing file: ${basename(filePath)} (format: ${fileFormat}, ext: ${ext})`);
let result;
try {
switch (fileFormat) {
case this.FILE_FORMATS.GCODE:
result = await this.analyzeGCode(filePath);
break;
case this.FILE_FORMATS.THREE_MF:
result = await this.analyze3MF(filePath);
break;
case this.FILE_FORMATS.BGCODE:
result = await this.analyzeBGCode(filePath);
break;
default: throw new Error(`Unsupported file format: ${fileFormat}`);
}
const thumbnails = this.extractThumbnails(result);
return {
metadata: result.normalized,
thumbnails
};
} catch (error) {
this.logger.error(`Failed to analyze file ${filePath} (ext: "${ext}"): ${error}`);
throw error;
}
}
/**
* Analyze multiple plates from a 3MF file
* Returns array of metadata objects, one per plate
*/
async analyzeMultiPlate3MF(filePath) {
const result = await this.analyze3MF(filePath);
if (!result.normalized.isMultiPlate || !result.plates) return [{
metadata: result.normalized,
thumbnails: this.extractThumbnails(result)
}];
return result.plates.map((plate) => ({
metadata: {
...result.normalized,
plateNumber: plate.plateNumber,
gcodePrintTimeSeconds: plate.gcodePrintTimeSeconds,
filamentUsedGrams: plate.filamentUsedGrams,
totalLayers: plate.totalLayers
},
thumbnails: plate.thumbnails?.map((t) => ({
width: t.width || 0,
height: t.height || 0,
format: t.type || t.format || "PNG"
})) || []
}));
}
async analyzeGCode(filePath) {
return await this.gcodeParser.parse(filePath);
}
async analyze3MF(filePath) {
return await this.threemfParser.parse(filePath);
}
async analyzeBGCode(filePath) {
return await this.bgcodeParser.parse(filePath);
}
extractThumbnails(result) {
const thumbnails = [];
if (result.raw._thumbnails) for (const thumb of result.raw._thumbnails) thumbnails.push({
width: thumb.width,
height: thumb.height,
format: thumb.format,
data: thumb.data
});
return thumbnails;
}
getFileFormat(ext, filePath) {
const lowerPath = filePath.toLowerCase();
if (lowerPath.endsWith(this.EXTENSIONS.GCODE_THREE_MF) || lowerPath.endsWith(this.EXTENSIONS.THREE_MF)) return this.FILE_FORMATS.THREE_MF;
if (lowerPath.endsWith(this.EXTENSIONS.BGCODE)) return this.FILE_FORMATS.BGCODE;
if (lowerPath.endsWith(this.EXTENSIONS.GCODE) || lowerPath.endsWith(this.EXTENSIONS.GCODE_SHORT) || lowerPath.endsWith(this.EXTENSIONS.GCODE_ALT)) return this.FILE_FORMATS.GCODE;
if (ext === this.EXTENSIONS.THREE_MF) return this.FILE_FORMATS.THREE_MF;
if (ext === this.EXTENSIONS.BGCODE) return this.FILE_FORMATS.BGCODE;
if (ext === this.EXTENSIONS.GCODE || ext === this.EXTENSIONS.GCODE_SHORT || ext === this.EXTENSIONS.GCODE_ALT) return this.FILE_FORMATS.GCODE;
throw new Error(`Unknown file extension: "${ext}" (path: "${filePath}")`);
}
/**
* Quick check if file needs analysis
*/
async needsAnalysis(filePath) {
try {
await access(filePath);
return true;
} catch {
return false;
}
}
};
//#endregion
export { FileAnalysisService };
//# sourceMappingURL=file-analysis.service.js.map