refactor: rewrite DxfExportService for local file export with revision tracking

- Replace API client with ExcelExportService and LogFileService
- Export DXFs to Templates folder with template-based naming
- Content hash comparison against existing xlsx for revision tracking
- Changed DXFs get revision suffix (e.g., PT03 Rev2.dxf)
- Unchanged DXFs are skipped
- Raw BOM table copied from SolidWorks drawing to Excel
- PDF exported directly to output folder
- Update PartExporter to remove FilePrefix dependency

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-13 22:16:47 -04:00
parent c6dde6e217
commit 9bc29e98c8
2 changed files with 212 additions and 324 deletions

View File

@@ -1,9 +1,7 @@
using ExportDXF.ApiClient;
using ExportDXF.Extensions;
using ExportDXF.ItemExtractors;
using ExportDXF.Models;
using ExportDXF.Utilities;
using ExportDXF;
using SolidWorks.Interop.sldworks;
using System;
using System.Collections.Generic;
@@ -15,42 +13,34 @@ namespace ExportDXF.Services
{
public interface IDxfExportService
{
/// <summary>
/// Exports the document specified in the context to DXF format.
/// </summary>
/// <param name="context">The export context containing all necessary information.</param>
Task ExportAsync(ExportContext context);
}
/// <summary>
/// Service responsible for orchestrating the export of SolidWorks documents to DXF format.
/// Files are generated locally in a temp directory, then uploaded to the API for storage and versioning.
/// </summary>
public class DxfExportService : IDxfExportService
{
private readonly ISolidWorksService _solidWorksService;
private readonly IBomExtractor _bomExtractor;
private readonly IPartExporter _partExporter;
private readonly IDrawingExporter _drawingExporter;
private readonly IFabWorksApiClient _apiClient;
private readonly ExcelExportService _excelExportService;
private readonly LogFileService _logFileService;
public DxfExportService(
ISolidWorksService solidWorksService,
IBomExtractor bomExtractor,
IPartExporter partExporter,
IDrawingExporter drawingExporter,
IFabWorksApiClient apiClient)
ExcelExportService excelExportService,
LogFileService logFileService)
{
_solidWorksService = solidWorksService ?? throw new ArgumentNullException(nameof(solidWorksService));
_bomExtractor = bomExtractor ?? throw new ArgumentNullException(nameof(bomExtractor));
_partExporter = partExporter ?? throw new ArgumentNullException(nameof(partExporter));
_drawingExporter = drawingExporter ?? throw new ArgumentNullException(nameof(drawingExporter));
_apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
_excelExportService = excelExportService ?? throw new ArgumentNullException(nameof(excelExportService));
_logFileService = logFileService ?? throw new ArgumentNullException(nameof(logFileService));
}
/// <summary>
/// Exports the document specified in the context to DXF format.
/// </summary>
public async Task ExportAsync(ExportContext context)
{
if (context == null)
@@ -59,6 +49,28 @@ namespace ExportDXF.Services
ValidateContext(context);
SetupExportContext(context);
var outputFolder = context.OutputFolder;
if (!Directory.Exists(outputFolder))
Directory.CreateDirectory(outputFolder);
var prefix = FilenameTemplateParser.GetPrefix(
context.FilenameTemplate,
context.ActiveDocument.Title);
var xlsxPath = Path.Combine(outputFolder, $"{prefix}.xlsx");
var logPath = Path.Combine(outputFolder, $"{prefix}.log");
_logFileService.StartExportLog(logPath);
_logFileService.LogInfo($"Export started: {context.ActiveDocument.FilePath}");
_logFileService.LogInfo($"Template: {context.FilenameTemplate}");
_logFileService.LogInfo($"Output: {outputFolder}");
// Read existing cut templates for revision comparison
var existingTemplates = _excelExportService.ReadExistingCutTemplates(xlsxPath);
var bomItems = new List<BomItem>();
List<Dictionary<string, string>> rawBomTable = null;
var startTime = DateTime.Now;
var tempDir = CreateTempWorkDir();
@@ -66,26 +78,39 @@ namespace ExportDXF.Services
{
_solidWorksService.EnableUserControl(false);
var drawingNumber = ParseDrawingNumber(context);
switch (context.ActiveDocument.DocumentType)
{
case DocumentType.Part:
await ExportPartAsync(context, tempDir, drawingNumber);
await ExportPartAsync(context, tempDir, outputFolder, existingTemplates, bomItems);
break;
case DocumentType.Assembly:
await ExportAssemblyAsync(context, tempDir, drawingNumber);
await ExportAssemblyAsync(context, tempDir, outputFolder, existingTemplates, bomItems);
break;
case DocumentType.Drawing:
await ExportDrawingAsync(context, drawingNumber, tempDir);
rawBomTable = await ExportDrawingAsync(context, tempDir, outputFolder, existingTemplates, bomItems);
break;
default:
LogProgress(context, "Unknown document type.", LogLevel.Error);
break;
}
// Write Excel file
_excelExportService.Write(xlsxPath, rawBomTable, bomItems);
_logFileService.LogInfo($"Wrote {Path.GetFileName(xlsxPath)}");
LogProgress(context, $"Saved {Path.GetFileName(xlsxPath)}");
}
catch (OperationCanceledException)
{
_logFileService.LogWarning("Export cancelled by user");
throw;
}
catch (Exception ex)
{
_logFileService.LogError($"Export failed: {ex.Message}");
throw;
}
finally
{
@@ -94,13 +119,19 @@ namespace ExportDXF.Services
CleanupTempDir(tempDir);
var duration = DateTime.Now - startTime;
_logFileService.LogInfo($"Run time: {duration.ToReadableFormat()}");
LogProgress(context, $"Run time: {duration.ToReadableFormat()}");
}
}
#region Export Methods by Document Type
private async Task ExportPartAsync(ExportContext context, string tempDir, string drawingNumber)
private async Task ExportPartAsync(
ExportContext context,
string tempDir,
string outputFolder,
Dictionary<string, (string ContentHash, int Revision, string FileName)> existingTemplates,
List<BomItem> bomItems)
{
LogProgress(context, "Active document is a Part");
@@ -111,54 +142,25 @@ namespace ExportDXF.Services
return;
}
var exportRecord = await CreateExportRecordAsync(context, drawingNumber);
var item = _partExporter.ExportSinglePart(part, tempDir, context);
if (item != null)
{
// Check if this part+config already has a BOM item for this drawing
var existingItemNo = await FindExistingItemNoAsync(exportRecord?.Id, item.PartName, item.Configuration);
item.ItemNo = existingItemNo ?? await GetNextItemNumberAsync(drawingNumber);
var bomItem = new BomItem
{
ExportRecordId = exportRecord?.Id ?? 0,
ItemNo = item.ItemNo,
PartNo = item.FileName ?? item.PartName ?? "",
SortOrder = 0,
Qty = item.Quantity,
TotalQty = item.Quantity,
Description = item.Description ?? "",
PartName = item.PartName ?? "",
ConfigurationName = item.Configuration ?? "",
Material = item.Material ?? ""
};
// Upload DXF to API and get stored path
if (!string.IsNullOrEmpty(item.LocalTempPath))
{
var uploadResult = await UploadDxfAsync(item, context);
if (uploadResult != null)
{
bomItem.CutTemplate = new CutTemplate
{
DxfFilePath = uploadResult.StoredFilePath,
ContentHash = item.ContentHash,
Thickness = item.Thickness > 0 ? item.Thickness : null,
KFactor = item.KFactor > 0 ? item.KFactor : null,
DefaultBendRadius = item.BendRadius > 0 ? item.BendRadius : null
};
}
}
item.ItemNo = "1";
var bomItem = CreateBomItem(item);
PlaceDxfFile(item, context, outputFolder, existingTemplates, bomItem);
bomItems.Add(bomItem);
context.BomItemCallback?.Invoke(bomItem);
if (exportRecord != null)
await SaveBomItemAsync(exportRecord.Id, bomItem, context);
}
}
private async Task ExportAssemblyAsync(ExportContext context, string tempDir, string drawingNumber)
private async Task ExportAssemblyAsync(
ExportContext context,
string tempDir,
string outputFolder,
Dictionary<string, (string ContentHash, int Revision, string FileName)> existingTemplates,
List<BomItem> bomItems)
{
LogProgress(context, "Active document is an Assembly");
LogProgress(context, "Fetching components...");
@@ -180,31 +182,26 @@ namespace ExportDXF.Services
LogProgress(context, $"Found {items.Count} item(s).");
var exportRecord = await CreateExportRecordAsync(context, drawingNumber);
// Check existing BOM items and reuse item numbers, or assign new ones
var nextNum = int.Parse(await GetNextItemNumberAsync(drawingNumber));
// Assign item numbers
int nextNum = 1;
foreach (var item in items)
{
if (string.IsNullOrWhiteSpace(item.ItemNo))
{
var existingItemNo = await FindExistingItemNoAsync(exportRecord?.Id, item.PartName, item.Configuration);
if (existingItemNo != null)
{
item.ItemNo = existingItemNo;
}
else
{
item.ItemNo = nextNum.ToString();
nextNum++;
}
item.ItemNo = nextNum.ToString();
nextNum++;
}
}
await ExportItemsAsync(items, tempDir, context, exportRecord?.Id);
await ExportItemsAsync(items, tempDir, outputFolder, context, existingTemplates, bomItems);
}
private async Task ExportDrawingAsync(ExportContext context, string drawingNumber, string tempDir)
private async Task<List<Dictionary<string, string>>> ExportDrawingAsync(
ExportContext context,
string tempDir,
string outputFolder,
Dictionary<string, (string ContentHash, int Revision, string FileName)> existingTemplates,
List<BomItem> bomItems)
{
LogProgress(context, "Active document is a Drawing");
LogProgress(context, "Finding BOM tables...");
@@ -213,59 +210,35 @@ namespace ExportDXF.Services
if (drawing == null)
{
LogProgress(context, "Failed to get drawing document.", LogLevel.Error);
return;
return null;
}
// Read raw BOM table for Excel output
var rawBomTable = new List<Dictionary<string, string>>();
var bomTables = drawing.GetBomTables();
foreach (var table in bomTables)
{
var rows = RawBomTableReader.Read(table);
rawBomTable.AddRange(rows);
}
// Extract items for DXF export
var items = _bomExtractor.ExtractFromDrawing(drawing, context.ProgressCallback);
if (items == null || items.Count == 0)
{
LogProgress(context, "Error: Bill of materials not found.", LogLevel.Error);
return;
return rawBomTable;
}
LogProgress(context, $"Found {items.Count} component(s)");
// Export drawing to PDF in temp dir
_drawingExporter.ExportToPdf(drawing, tempDir, context);
// Export drawing to PDF in output folder
_drawingExporter.ExportToPdf(drawing, outputFolder, context);
// Create export record via API
var exportRecord = await CreateExportRecordAsync(context, drawingNumber);
await ExportItemsAsync(items, tempDir, outputFolder, context, existingTemplates, bomItems);
// Upload PDF to API with versioning
try
{
var pdfs = Directory.GetFiles(tempDir, "*.pdf");
if (pdfs.Length > 0)
{
var pdfTempPath = pdfs[0];
var pdfHash = ContentHasher.ComputeFileHash(pdfTempPath);
var uploadResult = await _apiClient.UploadPdfAsync(
pdfTempPath,
context.Equipment,
context.DrawingNo,
pdfHash,
exportRecord?.Id);
if (uploadResult != null)
{
if (uploadResult.WasUnchanged)
LogProgress(context, $"PDF unchanged: {uploadResult.FileName}", LogLevel.Info);
else if (uploadResult.IsNewFile)
LogProgress(context, $"Saved PDF: {uploadResult.FileName}", LogLevel.Info);
else
LogProgress(context, $"PDF updated: {uploadResult.FileName}", LogLevel.Info);
}
}
}
catch (Exception ex)
{
LogProgress(context, $"PDF upload error: {ex.Message}", LogLevel.Error);
}
// Export parts to DXF and save BOM items
await ExportItemsAsync(items, tempDir, context, exportRecord?.Id);
return rawBomTable;
}
#endregion
@@ -274,17 +247,12 @@ namespace ExportDXF.Services
private void SetupExportContext(ExportContext context)
{
// Set up SolidWorks application reference
context.SolidWorksApp = _solidWorksService.GetNativeSldWorks();
if (context.SolidWorksApp == null)
{
throw new InvalidOperationException("SolidWorks service is not connected.");
}
// Set up drawing template path
context.TemplateDrawing = null;
LogProgress(context, "Export context initialized");
}
@@ -292,13 +260,11 @@ namespace ExportDXF.Services
{
try
{
// Clean up template drawing if it was created
context.CleanupTemplateDrawing();
}
catch (Exception ex)
{
LogProgress(context, $"Warning: Failed to cleanup template drawing: {ex.Message}", LogLevel.Warning);
// Don't throw - this is cleanup code
}
}
@@ -314,7 +280,6 @@ namespace ExportDXF.Services
{
TopLevelOnly = false
};
return extractor.GetItems();
}
catch (Exception ex)
@@ -324,10 +289,17 @@ namespace ExportDXF.Services
}
}
private async Task ExportItemsAsync(List<Item> items, string tempDir, ExportContext context, int? exportRecordId = null)
private async Task ExportItemsAsync(
List<Item> items,
string tempDir,
string outputFolder,
ExportContext context,
Dictionary<string, (string ContentHash, int Revision, string FileName)> existingTemplates,
List<BomItem> bomItems)
{
int successCount = 0;
int skippedCount = 0;
int unchangedCount = 0;
int failureCount = 0;
int sortOrder = 0;
@@ -341,215 +313,150 @@ namespace ExportDXF.Services
try
{
// PartExporter will handle template drawing creation through context
_partExporter.ExportItem(item, tempDir, context);
// Always create BomItem for every item (sheet metal or not)
var bomItem = new BomItem
{
ExportRecordId = exportRecordId ?? 0,
ItemNo = item.ItemNo ?? "",
PartNo = item.FileName ?? item.PartName ?? "",
SortOrder = sortOrder++,
Qty = item.Quantity,
TotalQty = item.Quantity,
Description = item.Description ?? "",
PartName = item.PartName ?? "",
ConfigurationName = item.Configuration ?? "",
Material = item.Material ?? ""
};
var bomItem = CreateBomItem(item);
bomItem.SortOrder = sortOrder++;
// Only upload and create CutTemplate if DXF was exported successfully
if (!string.IsNullOrEmpty(item.LocalTempPath))
{
successCount++;
var uploadResult = await UploadDxfAsync(item, context);
if (uploadResult != null)
{
bomItem.CutTemplate = new CutTemplate
{
DxfFilePath = uploadResult.StoredFilePath,
ContentHash = item.ContentHash,
Thickness = item.Thickness > 0 ? item.Thickness : null,
KFactor = item.KFactor > 0 ? item.KFactor : null,
DefaultBendRadius = item.BendRadius > 0 ? item.BendRadius : null
};
}
var wasPlaced = PlaceDxfFile(item, context, outputFolder, existingTemplates, bomItem);
if (wasPlaced)
successCount++;
else
unchangedCount++;
}
else
{
skippedCount++;
}
// Add to UI
bomItems.Add(bomItem);
context.BomItemCallback?.Invoke(bomItem);
// Save BOM item via API if we have an export record
if (exportRecordId.HasValue)
{
await SaveBomItemAsync(exportRecordId.Value, bomItem, context);
}
}
catch (Exception ex)
{
LogProgress(context, $"Error exporting item {item.ItemNo}: {ex.Message}", LogLevel.Error);
_logFileService.LogError($"Item {item.ItemNo}: {ex.Message}");
failureCount++;
}
}
var summary = $"Export complete: {successCount} exported, {skippedCount} skipped";
var summary = $"Export complete: {successCount} exported";
if (unchangedCount > 0)
summary += $", {unchangedCount} unchanged";
if (skippedCount > 0)
summary += $", {skippedCount} skipped (non-sheet-metal)";
if (failureCount > 0)
summary += $", {failureCount} failed";
LogProgress(context, summary, failureCount > 0 ? LogLevel.Warning : LogLevel.Info);
if (exportRecordId.HasValue)
{
LogProgress(context, $"BOM items saved (ExportRecord ID: {exportRecordId.Value})", LogLevel.Info);
}
_logFileService.LogInfo(summary);
}
#endregion
#region File Upload
private async Task<ApiFileUploadResponse> UploadDxfAsync(Item item, ExportContext context)
/// <summary>
/// Places a DXF file in the output folder with revision tracking.
/// Returns true if a new file was written, false if unchanged.
/// </summary>
private bool PlaceDxfFile(
Item item,
ExportContext context,
string outputFolder,
Dictionary<string, (string ContentHash, int Revision, string FileName)> existingTemplates,
BomItem bomItem)
{
try
var baseName = FilenameTemplateParser.Evaluate(context.FilenameTemplate, item);
var contentHash = item.ContentHash;
int revision = 1;
string dxfFileName;
// Check existing templates for revision comparison
if (existingTemplates.TryGetValue(item.ItemNo, out var existing))
{
var result = await _apiClient.UploadDxfAsync(
item.LocalTempPath,
context.Equipment,
context.DrawingNo,
item.ItemNo,
item.ContentHash);
if (result.WasUnchanged)
LogProgress(context, $"DXF unchanged: {result.FileName}", LogLevel.Info);
else if (result.IsNewFile)
LogProgress(context, $"Exported: {result.FileName}", LogLevel.Info);
else
LogProgress(context, $"DXF updated: {result.FileName}", LogLevel.Info);
return result;
}
catch (Exception ex)
{
LogProgress(context, $"DXF upload failed for {item.FileName}: {ex.Message}", LogLevel.Warning);
return null;
}
}
#endregion
#region API Helpers
private async Task<ExportRecord> CreateExportRecordAsync(ExportContext context, string drawingNumber)
{
try
{
var dto = await _apiClient.CreateExportAsync(
drawingNumber ?? context.ActiveDocument.Title,
context.Equipment ?? "",
context.DrawingNo ?? "",
context.ActiveDocument.FilePath,
"", // Output folder is now managed by the API
context.Title);
var record = new ExportRecord
if (existing.ContentHash == contentHash)
{
Id = dto.Id,
DrawingNumber = dto.DrawingNumber,
EquipmentNo = dto.EquipmentNo,
DrawingNo = dto.DrawingNo,
SourceFilePath = dto.SourceFilePath,
OutputFolder = dto.OutputFolder,
ExportedAt = dto.ExportedAt,
ExportedBy = dto.ExportedBy
};
// Unchanged — skip file write, keep existing
dxfFileName = existing.FileName;
revision = existing.Revision;
LogProgress(context, $"Created export record (ID: {record.Id})", LogLevel.Info);
return record;
}
catch (Exception ex)
{
LogProgress(context, $"API error creating export record: {ex.Message}", LogLevel.Error);
return null;
}
}
LogProgress(context, $"Unchanged: {dxfFileName}", LogLevel.Info, item.PartName);
_logFileService.LogInfo($"Unchanged: {dxfFileName}");
private async Task<string> FindExistingItemNoAsync(int? exportRecordId, string partName, string configurationName)
{
if (!exportRecordId.HasValue)
return null;
try
{
var existing = await _apiClient.FindExistingBomItemAsync(exportRecordId.Value, partName, configurationName);
return existing?.ItemNo;
}
catch
{
return null;
}
}
private async Task<string> GetNextItemNumberAsync(string drawingNumber)
{
if (string.IsNullOrEmpty(drawingNumber))
return "1";
try
{
return await _apiClient.GetNextItemNumberAsync(drawingNumber);
}
catch
{
return "1";
}
}
private async Task SaveBomItemAsync(int exportRecordId, BomItem bomItem, ExportContext context)
{
try
{
var apiBomItem = new ApiBomItem
{
ItemNo = bomItem.ItemNo,
PartNo = bomItem.PartNo,
SortOrder = bomItem.SortOrder,
Qty = bomItem.Qty,
TotalQty = bomItem.TotalQty,
Description = bomItem.Description,
PartName = bomItem.PartName,
ConfigurationName = bomItem.ConfigurationName,
Material = bomItem.Material
};
if (bomItem.CutTemplate != null)
{
apiBomItem.CutTemplate = new ApiCutTemplate
bomItem.CutTemplate = new CutTemplate
{
DxfFilePath = bomItem.CutTemplate.DxfFilePath,
ContentHash = bomItem.CutTemplate.ContentHash,
Thickness = bomItem.CutTemplate.Thickness,
KFactor = bomItem.CutTemplate.KFactor,
DefaultBendRadius = bomItem.CutTemplate.DefaultBendRadius
DxfFilePath = dxfFileName,
ContentHash = contentHash,
Revision = revision,
Thickness = item.Thickness > 0 ? item.Thickness : (double?)null,
KFactor = item.KFactor > 0 ? item.KFactor : (double?)null,
DefaultBendRadius = item.BendRadius > 0 ? item.BendRadius : (double?)null
};
}
await _apiClient.CreateBomItemAsync(exportRecordId, apiBomItem);
return false;
}
else
{
// Changed — increment revision
revision = existing.Revision + 1;
dxfFileName = GetRevisionFileName(baseName, revision);
LogProgress(context, $"Updated: {dxfFileName} (Rev{revision})", LogLevel.Info, item.PartName);
_logFileService.LogInfo($"Updated: {dxfFileName} (was {existing.FileName})");
}
}
catch (Exception ex)
else
{
LogProgress(context, $"API error saving BOM item: {ex.Message}", LogLevel.Error);
// New item
dxfFileName = $"{baseName}.dxf";
LogProgress(context, $"Exported: {dxfFileName}", LogLevel.Info, item.PartName);
_logFileService.LogInfo($"Exported: {dxfFileName}");
}
// Copy from temp to output
var destPath = Path.Combine(outputFolder, dxfFileName);
File.Copy(item.LocalTempPath, destPath, overwrite: true);
bomItem.CutTemplate = new CutTemplate
{
DxfFilePath = Path.GetFileNameWithoutExtension(dxfFileName),
ContentHash = contentHash,
Revision = revision,
Thickness = item.Thickness > 0 ? item.Thickness : (double?)null,
KFactor = item.KFactor > 0 ? item.KFactor : (double?)null,
DefaultBendRadius = item.BendRadius > 0 ? item.BendRadius : (double?)null
};
return true;
}
private BomItem CreateBomItem(Item item)
{
return new BomItem
{
ItemNo = item.ItemNo ?? "",
PartNo = item.FileName ?? item.PartName ?? "",
SortOrder = 0,
Qty = item.Quantity,
TotalQty = item.Quantity,
Description = item.Description ?? "",
PartName = item.PartName ?? "",
ConfigurationName = item.Configuration ?? "",
Material = item.Material ?? ""
};
}
#endregion
#region Helper Methods
private string GetRevisionFileName(string baseName, int revision)
{
if (revision <= 1)
return $"{baseName}.dxf";
return $"{baseName} Rev{revision}.dxf";
}
private string CreateTempWorkDir()
{
var path = Path.Combine(Path.GetTempPath(), "ExportDXF-" + Guid.NewGuid().ToString("N"));
@@ -570,27 +477,6 @@ namespace ExportDXF.Services
}
}
private string ParseDrawingNumber(ExportContext context)
{
// Use explicit Equipment/DrawingNo from the UI when available
if (!string.IsNullOrWhiteSpace(context?.Equipment))
{
return !string.IsNullOrWhiteSpace(context?.DrawingNo)
? $"{context.Equipment} {context.DrawingNo}"
: context.Equipment;
}
// Fallback: parse from prefix or document title
var candidate = context?.FilePrefix;
var info = string.IsNullOrWhiteSpace(candidate) ? null : DrawingInfo.Parse(candidate);
if (info == null)
{
var title = context?.ActiveDocument?.Title;
info = string.IsNullOrWhiteSpace(title) ? null : DrawingInfo.Parse(title);
}
return info?.ToString();
}
private void ValidateContext(ExportContext context)
{
if (context.ActiveDocument == null)
@@ -598,6 +484,12 @@ namespace ExportDXF.Services
if (context.ProgressCallback == null)
throw new ArgumentException("ProgressCallback cannot be null.", nameof(context));
if (string.IsNullOrWhiteSpace(context.FilenameTemplate))
throw new ArgumentException("FilenameTemplate cannot be null or empty.", nameof(context));
if (string.IsNullOrWhiteSpace(context.OutputFolder))
throw new ArgumentException("OutputFolder cannot be null or empty.", nameof(context));
}
private void LogProgress(ExportContext context, string message, LogLevel level = LogLevel.Info, string file = null)

View File

@@ -54,7 +54,7 @@ namespace ExportDXF.Services
try
{
var fileName = GetSinglePartFileName(model, context.FilePrefix);
var fileName = GetSinglePartFileName(model);
var savePath = Path.Combine(saveDirectory, fileName + ".dxf");
// Build result item with metadata
@@ -139,7 +139,7 @@ namespace ExportDXF.Services
EnrichItemWithMetadata(item, model, part);
var fileName = GetItemFileName(item, context.FilePrefix);
var fileName = GetItemFileName(item);
var savePath = Path.Combine(saveDirectory, fileName + ".dxf");
var templateDrawing = context.GetOrCreateTemplateDrawing();
@@ -332,7 +332,7 @@ namespace ExportDXF.Services
}
}
private string GetSinglePartFileName(ModelDoc2 model, string prefix)
private string GetSinglePartFileName(ModelDoc2 model)
{
var title = model.GetTitle().Replace(".SLDPRT", "");
var config = model.ConfigurationManager.ActiveConfiguration.Name;
@@ -341,17 +341,13 @@ namespace ExportDXF.Services
return isDefaultConfig ? title : $"{title} [{config}]";
}
private string GetItemFileName(Item item, string prefix)
private string GetItemFileName(Item item)
{
if (string.IsNullOrWhiteSpace(item.ItemNo))
return item.PartName;
return item.PartName ?? "unknown";
prefix = prefix?.Replace("\"", "''") ?? string.Empty;
var num = item.ItemNo.PadLeft(2, '0');
// Expected format: {DrawingNo} PT{ItemNo}
return string.IsNullOrWhiteSpace(prefix)
? $"PT{num}"
: $"{prefix} PT{num}";
return $"PT{num}";
}
private void LogExportFailure(Item item, ExportContext context)