UNPKG

@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
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