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:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user