diff --git a/MainWindow.xaml b/MainWindow.xaml index 44a20ef..5e1ebba 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -153,6 +153,23 @@ + diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index 19d130c..2fc80dc 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -20,20 +20,40 @@ namespace DwgExtractorManual private DispatcherTimer _timer; private ExportExcel? _exportExcel; private SqlDatas? _sqlDatas; + // μžλ™ 처리 λͺ¨λ“œ ν”Œλž˜κ·Έ + private bool isAutoProcessing = false; public MainWindow() { InitializeComponent(); InitializeDefaultPaths(); InitializeTimer(); + + // μ•± μ’…λ£Œ μ‹œ Teigha λ¦¬μ†ŒμŠ€ 정리 + this.Closed += MainWindow_Closed; + LogMessage("πŸš€ DWG 정보 μΆ”μΆœκΈ°κ°€ μ‹œμž‘λ˜μ—ˆμŠ΅λ‹ˆλ‹€."); } + private void MainWindow_Closed(object sender, EventArgs e) + { + try + { + LogMessage("πŸ”„ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ’…λ£Œ μ‹œ Teigha λ¦¬μ†ŒμŠ€ 정리 쀑..."); + TeighaServicesManager.Instance.ForceDisposeServices(); + LogMessage("βœ… Teigha λ¦¬μ†ŒμŠ€ 정리 μ™„λ£Œ"); + } + catch (Exception ex) + { + LogMessage($"⚠️ μ•± μ’…λ£Œ μ‹œ Teigha 정리 쀑 였λ₯˜: {ex.Message}"); + } + } + private void InitializeDefaultPaths() { // κΈ°λ³Έ 경둜 μ„€μ • - μ‹€μ œ ν™˜κ²½μ— 맞게 μˆ˜μ • - txtSourceFolder.Text = @"D:\dwgpdfcompare\test2"; - txtResultFolder.Text = @"D:\dwgpdfcompare\result"; + txtSourceFolder.Text = @"C:\WorkProjects\dwfpdfCompare"; + txtResultFolder.Text = @"C:\WorkProjects\dwfpdfCompare"; // κ²½λ‘œκ°€ μ‘΄μž¬ν•˜μ§€ μ•ŠμœΌλ©΄ κΈ°λ³Έκ°’μœΌλ‘œ μ„€μ • if (!Directory.Exists(txtSourceFolder.Text)) @@ -157,17 +177,15 @@ namespace DwgExtractorManual private async void BtnExtract_Click(object sender, RoutedEventArgs e) { // μž…λ ₯ μœ νš¨μ„± 검사 - if (string.IsNullOrEmpty(txtSourceFolder.Text) || !Directory.Exists(txtSourceFolder.Text)) + if (string.IsNullOrEmpty(txtSourceFolder.Text) || !Directory.Exists(txtSourceFolder.Text)) { - System.Windows.MessageBox.Show("μœ νš¨ν•œ λ³€ν™˜ν•  폴더λ₯Ό μ„ νƒν•˜μ„Έμš”.", "였λ₯˜", - MessageBoxButton.OK, MessageBoxImage.Warning); + ShowMessageBox("μœ νš¨ν•œ λ³€ν™˜ν•  폴더λ₯Ό μ„ νƒν•˜μ„Έμš”.", "였λ₯˜", MessageBoxButton.OK, MessageBoxImage.Warning); return; } - + if (string.IsNullOrEmpty(txtResultFolder.Text) || !Directory.Exists(txtResultFolder.Text)) { - System.Windows.MessageBox.Show("μœ νš¨ν•œ κ²°κ³Ό μ €μž₯ 폴더λ₯Ό μ„ νƒν•˜μ„Έμš”.", "였λ₯˜", - MessageBoxButton.OK, MessageBoxImage.Warning); + ShowMessageBox("μœ νš¨ν•œ κ²°κ³Ό μ €μž₯ 폴더λ₯Ό μ„ νƒν•˜μ„Έμš”.", "였λ₯˜", MessageBoxButton.OK, MessageBoxImage.Warning); return; } @@ -181,11 +199,11 @@ namespace DwgExtractorManual { if (!testSql.TestConnection()) { - System.Windows.MessageBox.Show( - "PostgreSQL λ°μ΄ν„°λ² μ΄μŠ€μ— μ—°κ²°ν•  수 μ—†μŠ΅λ‹ˆλ‹€.\n\n" + - "μ—°κ²° 섀정을 ν™•μΈν•˜κ³  λ°μ΄ν„°λ² μ΄μŠ€ μ„œλ²„κ°€ μ‹€ν–‰ 쀑인지 ν™•μΈν•˜μ„Έμš”.", - "λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° 였λ₯˜", - MessageBoxButton.OK, MessageBoxImage.Error); + ShowMessageBox( + "PostgreSQL λ°μ΄ν„°λ² μ΄μŠ€μ— μ—°κ²°ν•  수 μ—†μŠ΅λ‹ˆλ‹€.\n\n" + + "μ—°κ²° 섀정을 ν™•μΈν•˜κ³  λ°μ΄ν„°λ² μ΄μŠ€ μ„œλ²„κ°€ μ‹€ν–‰ 쀑인지 ν™•μΈν•˜μ„Έμš”.", + "λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° 였λ₯˜", + MessageBoxButton.OK, MessageBoxImage.Error); LogMessage("❌ λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° μ‹€νŒ¨"); return; } @@ -194,7 +212,7 @@ namespace DwgExtractorManual } catch (Exception ex) { - System.Windows.MessageBox.Show( + ShowMessageBox( $"λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° ν…ŒμŠ€νŠΈ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€:\n\n{ex.Message}", "였λ₯˜", MessageBoxButton.OK, MessageBoxImage.Error); LogMessage($"❌ λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° 였λ₯˜: {ex.Message}"); @@ -206,6 +224,7 @@ namespace DwgExtractorManual btnExtract.IsEnabled = false; btnPdfExtract.IsEnabled = false; btnMerge.IsEnabled = false; + btnAuto.IsEnabled = false; btnBrowseSource.IsEnabled = false; btnBrowseResult.IsEnabled = false; rbExcel.IsEnabled = false; @@ -225,8 +244,7 @@ namespace DwgExtractorManual { LogMessage($"❌ 치λͺ…적 였λ₯˜ λ°œμƒ: {ex.Message}"); UpdateStatus("였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€."); - System.Windows.MessageBox.Show($"μž‘μ—… 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€:\n\n{ex.Message}", - "였λ₯˜", MessageBoxButton.OK, MessageBoxImage.Error); + ShowMessageBox($"μž‘μ—… 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€:\n\n{ex.Message}", "였λ₯˜", MessageBoxButton.OK, MessageBoxImage.Error); } finally { @@ -234,6 +252,7 @@ namespace DwgExtractorManual btnExtract.IsEnabled = true; btnPdfExtract.IsEnabled = true; btnMerge.IsEnabled = true; + btnAuto.IsEnabled = true; btnBrowseSource.IsEnabled = true; btnBrowseResult.IsEnabled = true; rbExcel.IsChecked = true; @@ -242,9 +261,280 @@ namespace DwgExtractorManual LogMessage(new string('=', 50)); LogMessage("🏁 μž‘μ—… μ™„λ£Œ"); LogMessage(new string('=', 50)); // βœ… 정상 μž‘λ™ + + // μžλ™ 처리 λͺ¨λ“œ λΉ„ν™œμ„±ν™” + isAutoProcessing = false; } } + /// + /// MessageBox ν‘œμ‹œ (μžλ™ λͺ¨λ“œμ—μ„œλŠ” 둜그만 좜λ ₯) + /// + private void ShowMessageBox(string message, string title, MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage image = MessageBoxImage.Information) + { + if (isAutoProcessing) + { + LogMessage($"πŸ“’ {title}: {message}"); + } + else + { + System.Windows.MessageBox.Show(message, title, button, image); + } + } + + /// + /// 확인 λŒ€ν™”μƒμž ν‘œμ‹œ (μžλ™ λͺ¨λ“œμ—μ„œλŠ” μžλ™μœΌλ‘œ Yes λ°˜ν™˜) + /// + private MessageBoxResult ShowConfirmationDialog(string message, string title) + { + if (isAutoProcessing) + { + LogMessage($"πŸ“’ {title}: {message} - μžλ™ 승인됨"); + return MessageBoxResult.Yes; + } + else + { + return System.Windows.MessageBox.Show(message, title, MessageBoxButton.YesNo, MessageBoxImage.Question); + } + } + + /// + /// DWG 파일 처리 - μžλ™ λͺ¨λ“œμš© (폴더 경둜 직접 μ§€μ •) + /// + /// μ²˜λ¦¬ν•  폴더 경둜 + private async Task ProcessFiles(string sourceFolderPath) + { + var stopwatch = Stopwatch.StartNew(); + var targetDir = new DirectoryInfo(sourceFolderPath); + var files = targetDir.GetFiles("*.dwg", SearchOption.AllDirectories); + + if (files.Length == 0) + { + UpdateStatus("μ„ νƒν•œ 폴더에 DWG 파일이 μ—†μŠ΅λ‹ˆλ‹€."); + ShowMessageBox("μ„ νƒν•œ 폴더에 DWG 파일이 μ—†μŠ΅λ‹ˆλ‹€.", "정보", MessageBoxButton.OK, MessageBoxImage.Information); + return; + } + + // κ²°κ³Ό μ €μž₯ ν΄λ”λŠ” UIμ—μ„œ κ°€μ Έμ˜΄ (λ³€κ²½ν•˜μ§€ μ•ŠμŒ) + string resultDir = txtResultFolder.Text; + + LogMessage($"πŸ“Š μ²˜λ¦¬ν•  파일 수: {files.Length}개"); + LogMessage($"πŸ“‹ 좜λ ₯ λͺ¨λ“œ: {(rbExcel.IsChecked == true ? "Excel" : "Database")}"); + LogMessage($"πŸ“‚ μ†ŒμŠ€ 폴더: {sourceFolderPath}"); + LogMessage($"πŸ’Ύ κ²°κ³Ό 폴더: {resultDir}"); + + try + { + if (rbExcel.IsChecked == true) + { + LogMessage("πŸ“Š Excel 내보내기 λͺ¨λ“œλ‘œ μ‹œμž‘ν•©λ‹ˆλ‹€..."); + + ExportExcel? _exportExcel = null; + var successCount = 0; + var failureCount = 0; + var totalProcessingTime = 0.0; + + try + { + _exportExcel = new ExportExcel(); + LogMessage("πŸ“ Excel μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ΄ˆκΈ°ν™” 쀑..."); + LogMessage("βœ… Excel μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ΄ˆκΈ°ν™” μ™„λ£Œ"); + + for (int i = 0; i < files.Length; i++) + { + var file = files[i]; + var fileStopwatch = Stopwatch.StartNew(); + + UpdateStatus($"처리 쀑: {file.Name} ({i + 1}/{files.Length})"); + LogMessage($"πŸ”„ [{i + 1}/{files.Length}] 처리 쀑: {file.Name}"); + + try + { + // μ‹€μ œ DWG 처리 둜직 + var progress = new Progress(value => + { + // νŒŒμΌλ³„ μ§„ν–‰λ₯ μ„ 전체 μ§„ν–‰λ₯ μ— 반영 + var overallProgress = ((i * 100.0 + value) / files.Length); + progressBar.Value = overallProgress; + }); + + bool success = _exportExcel.ExportDwgToExcel(file.FullName, progress); + + fileStopwatch.Stop(); + + if (success) + { + successCount++; + totalProcessingTime += fileStopwatch.ElapsedMilliseconds; + LogMessage($"βœ… {file.Name} 처리 μ™„λ£Œ ({fileStopwatch.ElapsedMilliseconds}ms)"); + } + else + { + failureCount++; + LogMessage($"❌ {file.Name} 처리 μ‹€νŒ¨ ({fileStopwatch.ElapsedMilliseconds}ms)"); + } + } + catch (Exception ex) + { + failureCount++; + fileStopwatch.Stop(); + LogMessage($"πŸ’₯ {file.Name} 처리 쀑 μ˜ˆμ™Έ λ°œμƒ: {ex.Message}"); + } + + // μ§„ν–‰λ₯  μ—…λ°μ΄νŠΈ + progressBar.Value = ((i + 1) * 100.0) / files.Length; + + // UI 응닡성을 μœ„ν•œ 짧은 μ§€μ—° + await Task.Delay(10); + } + + // Excel 파일 및 λ§€ν•‘ 데이터 μ €μž₯ + var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss"); + var excelFileName = Path.Combine(resultDir, $"{timestamp}_DwgToExcel.xlsx"); + var mappingDataFile = Path.Combine(resultDir, $"{timestamp}_mapping_data.json"); + + LogMessage("πŸ’Ύ Excel 파일과 λ§€ν•‘ 데이터λ₯Ό μ €μž₯ν•©λ‹ˆλ‹€..."); + + // λ§€ν•‘ λ”•μ…”λ„ˆλ¦¬λ₯Ό JSON 파일둜 μ €μž₯ (PDF 데이터 λ³‘ν•©μš©) + _exportExcel.SaveMappingDictionary(mappingDataFile); + LogMessage($"βœ… λ§€ν•‘ 데이터 μ €μž₯ μ™„λ£Œ: {Path.GetFileName(mappingDataFile)}"); + + // Excel 파일 μ €μž₯ + _exportExcel.SaveAndCloseExcel(excelFileName); + LogMessage($"βœ… Excel 파일 μ €μž₯ μ™„λ£Œ: {Path.GetFileName(excelFileName)}"); + + var elapsed = stopwatch.Elapsed; + LogMessage($"βœ… Excel 내보내기 μ™„λ£Œ! μ†Œμš”μ‹œκ°„: {elapsed.TotalSeconds:F1}초"); + UpdateStatus($"βœ… {files.Length}개 파일 처리 μ™„λ£Œ"); + LogMessage($"πŸ“ˆ 성곡: {successCount}개, μ‹€νŒ¨: {failureCount}개"); + LogMessage($"⏱️ 평균 처리 μ‹œκ°„: {(successCount > 0 ? totalProcessingTime / successCount : 0):F1}ms"); + + if (!isAutoProcessing) + { + ShowMessageBox($"Excel 내보내기가 μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€!\n\n" + + $"βœ… 성곡: {successCount}개\n" + + $"❌ μ‹€νŒ¨: {failureCount}개\n" + + $"⏱️ 총 μ†Œμš”μ‹œκ°„: {elapsed:mm\\:ss}\n\n" + + $"πŸ“„ κ²°κ³Ό 파일이 μ €μž₯ 폴더에 μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", + "μ™„λ£Œ", MessageBoxButton.OK, MessageBoxImage.Information); + } + } + catch (Exception ex) + { + LogMessage($"❌ Excel 처리 쀑 치λͺ…적 였λ₯˜: {ex.Message}"); + UpdateStatus("❌ Excel 처리 μ‹€νŒ¨"); + if (!isAutoProcessing) + { + ShowMessageBox("Excel 처리 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€. 둜그λ₯Ό ν™•μΈν•΄μ£Όμ„Έμš”.", "였λ₯˜", MessageBoxButton.OK, MessageBoxImage.Error); + } + throw; + } + finally + { + _exportExcel?.Dispose(); + _exportExcel = null; + } + } + else if (rbDatabase.IsChecked == true) + { + LogMessage("πŸ—„οΈ λ°μ΄ν„°λ² μ΄μŠ€ λͺ¨λ“œλ‘œ μ‹œμž‘ν•©λ‹ˆλ‹€..."); + LogMessage("πŸ”— λ°μ΄ν„°λ² μ΄μŠ€ 연결을 μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€..."); + + SqlDatas? _sqlDatas = null; + var successCount = 0; + var failureCount = 0; + var totalProcessingTime = 0.0; + + try + { + _sqlDatas = new SqlDatas(); + LogMessage("βœ… λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° μ΄ˆκΈ°ν™” μ™„λ£Œ"); + + for (int i = 0; i < files.Length; i++) + { + var file = files[i]; + var fileStopwatch = Stopwatch.StartNew(); + + UpdateStatus($"처리 쀑: {file.Name} ({i + 1}/{files.Length})"); + LogMessage($"πŸ”„ [{i + 1}/{files.Length}] 처리 쀑: {file.Name}"); + + try + { + // μ‹€μ œ DWG 처리 둜직 (DwgToDBλŠ” μ‹€νŒ¨μ‹œ true λ°˜ν™˜) + bool failed = _sqlDatas.DwgToDB(file.FullName); + bool success = !failed; + + fileStopwatch.Stop(); + + if (success) + { + successCount++; + totalProcessingTime += fileStopwatch.ElapsedMilliseconds; + LogMessage($"βœ… {file.Name} DB μ €μž₯ μ™„λ£Œ ({fileStopwatch.ElapsedMilliseconds}ms)"); + } + else + { + failureCount++; + LogMessage($"❌ {file.Name} DB μ €μž₯ μ‹€νŒ¨ ({fileStopwatch.ElapsedMilliseconds}ms)"); + } + } + catch (Exception ex) + { + failureCount++; + fileStopwatch.Stop(); + LogMessage($"πŸ’₯ {file.Name} 처리 쀑 μ˜ˆμ™Έ λ°œμƒ: {ex.Message}"); + } + + // μ§„ν–‰λ₯  μ—…λ°μ΄νŠΈ + progressBar.Value = ((i + 1) * 100.0) / files.Length; + + // UI 응닡성을 μœ„ν•œ 짧은 μ§€μ—° + await Task.Delay(10); + } + + var elapsed = stopwatch.Elapsed; + LogMessage($"βœ… λ°μ΄ν„°λ² μ΄μŠ€ μ €μž₯ μ™„λ£Œ! μ†Œμš”μ‹œκ°„: {elapsed.TotalSeconds:F1}초"); + UpdateStatus($"βœ… {files.Length}개 파일 처리 μ™„λ£Œ"); + LogMessage($"πŸ“ˆ 성곡: {successCount}개, μ‹€νŒ¨: {failureCount}개"); + LogMessage($"⏱️ 평균 처리 μ‹œκ°„: {(successCount > 0 ? totalProcessingTime / successCount : 0):F1}ms"); + + if (!isAutoProcessing) + { + ShowMessageBox($"λ°μ΄ν„°λ² μ΄μŠ€ μ €μž₯이 μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€!\n\n" + + $"βœ… 성곡: {successCount}개\n" + + $"❌ μ‹€νŒ¨: {failureCount}개\n" + + $"⏱️ 총 μ†Œμš”μ‹œκ°„: {elapsed:mm\\:ss}", + "μ™„λ£Œ", MessageBoxButton.OK, MessageBoxImage.Information); + } + } + catch (Exception ex) + { + LogMessage($"❌ λ°μ΄ν„°λ² μ΄μŠ€ 처리 쀑 치λͺ…적 였λ₯˜: {ex.Message}"); + UpdateStatus("❌ λ°μ΄ν„°λ² μ΄μŠ€ 처리 μ‹€νŒ¨"); + if (!isAutoProcessing) + { + ShowMessageBox("λ°μ΄ν„°λ² μ΄μŠ€ 처리 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€. 둜그λ₯Ό ν™•μΈν•΄μ£Όμ„Έμš”.", "였λ₯˜", MessageBoxButton.OK, MessageBoxImage.Error); + } + throw; + } + finally + { + _sqlDatas?.Dispose(); + _sqlDatas = null; + } + } + } + catch (Exception ex) + { + LogMessage($"❌ 처리 쀑 였λ₯˜ λ°œμƒ: {ex.Message}"); + UpdateStatus("❌ 처리 μ‹€νŒ¨"); + throw; + } + } + + /// + /// DWG 파일 처리 - κΈ°μ‘΄ λ©”μ„œλ“œ + /// private async Task ProcessFiles() { var stopwatch = Stopwatch.StartNew(); @@ -254,8 +544,7 @@ namespace DwgExtractorManual if (files.Length == 0) { UpdateStatus("μ„ νƒν•œ 폴더에 DWG 파일이 μ—†μŠ΅λ‹ˆλ‹€."); - System.Windows.MessageBox.Show("μ„ νƒν•œ 폴더에 DWG 파일이 μ—†μŠ΅λ‹ˆλ‹€.", - "정보", MessageBoxButton.OK, MessageBoxImage.Information); + ShowMessageBox("μ„ νƒν•œ 폴더에 DWG 파일이 μ—†μŠ΅λ‹ˆλ‹€.", "정보", MessageBoxButton.OK, MessageBoxImage.Information); return; } @@ -356,6 +645,22 @@ namespace DwgExtractorManual // Excel 파일 μ €μž₯ _exportExcel.SaveAndCloseExcel(excelFileName); LogMessage($"βœ… Excel 파일 μ €μž₯ μ™„λ£Œ: {Path.GetFileName(excelFileName)}"); + + + LogMessage($"βœ… Excel 내보내기 μ™„λ£Œ!"); + UpdateStatus($"βœ… {files.Length}개 파일 처리 μ™„λ£Œ"); + LogMessage($"πŸ“ˆ 성곡: {successCount}개, μ‹€νŒ¨: {failureCount}개"); + LogMessage($"⏱️ 평균 처리 μ‹œκ°„: {(successCount > 0 ? totalProcessingTime / successCount : 0):F1}ms"); + + if (!isAutoProcessing) + { + ShowMessageBox($"Excel 내보내기가 μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€!\n\n" + + $"βœ… 성곡: {successCount}개\n" + + $"❌ μ‹€νŒ¨: {failureCount}개\n" + + $"βœ… Excel 내보내기 μ™„λ£Œ!" + + $"πŸ“„ κ²°κ³Ό 파일이 μ €μž₯ 폴더에 μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", + "μ™„λ£Œ", MessageBoxButton.OK, MessageBoxImage.Information); + } } catch (Exception ex) { @@ -376,7 +681,7 @@ namespace DwgExtractorManual LogMessage($"⏱️ 평균 처리 μ‹œκ°„: {(successCount > 0 ? totalProcessingTime / successCount : 0):F1}ms"); LogMessage($"πŸ• 총 μ†Œμš” μ‹œκ°„: {totalStopwatch.ElapsedMilliseconds}ms"); - System.Windows.MessageBox.Show( + ShowMessageBox( $"Excel 내보내기가 μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€!\n\n" + $"βœ… 성곡: {successCount}개\n" + $"❌ μ‹€νŒ¨: {failureCount}개\n" + @@ -461,7 +766,7 @@ namespace DwgExtractorManual LogMessage($"⏱️ 평균 처리 μ‹œκ°„: {(successCount > 0 ? totalProcessingTime / successCount : 0):F1}ms"); LogMessage($"πŸ• 총 μ†Œμš” μ‹œκ°„: {totalStopwatch.ElapsedMilliseconds}ms"); - System.Windows.MessageBox.Show( + ShowMessageBox( $"λ°μ΄ν„°λ² μ΄μŠ€ 내보내기가 μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€!\n\n" + $"βœ… 성곡: {successCount}개\n" + $"❌ μ‹€νŒ¨: {failureCount}개\n" + @@ -475,15 +780,13 @@ namespace DwgExtractorManual // μž…λ ₯ μœ νš¨μ„± 검사 if (string.IsNullOrEmpty(txtSourceFolder.Text) || !Directory.Exists(txtSourceFolder.Text)) { - System.Windows.MessageBox.Show("μœ νš¨ν•œ λ³€ν™˜ν•  폴더λ₯Ό μ„ νƒν•˜μ„Έμš”.", "였λ₯˜", - MessageBoxButton.OK, MessageBoxImage.Warning); + ShowMessageBox("μœ νš¨ν•œ λ³€ν™˜ν•  폴더λ₯Ό μ„ νƒν•˜μ„Έμš”.", "였λ₯˜", MessageBoxButton.OK, MessageBoxImage.Warning); return; } if (string.IsNullOrEmpty(txtResultFolder.Text) || !Directory.Exists(txtResultFolder.Text)) { - System.Windows.MessageBox.Show("μœ νš¨ν•œ κ²°κ³Ό μ €μž₯ 폴더λ₯Ό μ„ νƒν•˜μ„Έμš”.", "였λ₯˜", - MessageBoxButton.OK, MessageBoxImage.Warning); + ShowMessageBox("μœ νš¨ν•œ κ²°κ³Ό μ €μž₯ 폴더λ₯Ό μ„ νƒν•˜μ„Έμš”.", "였λ₯˜", MessageBoxButton.OK, MessageBoxImage.Warning); return; } @@ -493,8 +796,7 @@ namespace DwgExtractorManual if (pdfFiles.Length == 0) { - System.Windows.MessageBox.Show("μ„ νƒν•œ 폴더에 PDF 파일이 μ—†μŠ΅λ‹ˆλ‹€.", "정보", - MessageBoxButton.OK, MessageBoxImage.Information); + ShowMessageBox("μ„ νƒν•œ 폴더에 PDF 파일이 μ—†μŠ΅λ‹ˆλ‹€.", "정보", MessageBoxButton.OK, MessageBoxImage.Information); return; } @@ -502,6 +804,7 @@ namespace DwgExtractorManual btnExtract.IsEnabled = false; btnPdfExtract.IsEnabled = false; btnMerge.IsEnabled = false; + btnAuto.IsEnabled = false; btnBrowseSource.IsEnabled = false; btnBrowseResult.IsEnabled = false; rbExcel.IsEnabled = false; @@ -521,8 +824,7 @@ namespace DwgExtractorManual { LogMessage($"❌ PDF μΆ”μΆœ 쀑 치λͺ…적 였λ₯˜ λ°œμƒ: {ex.Message}"); UpdateStatus("PDF μΆ”μΆœ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€."); - System.Windows.MessageBox.Show($"PDF μΆ”μΆœ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€:\n\n{ex.Message}", - "였λ₯˜", MessageBoxButton.OK, MessageBoxImage.Error); + ShowMessageBox($"PDF μΆ”μΆœ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€:\n\n{ex.Message}", "였λ₯˜", MessageBoxButton.OK, MessageBoxImage.Error); } finally { @@ -530,6 +832,7 @@ namespace DwgExtractorManual btnExtract.IsEnabled = true; btnPdfExtract.IsEnabled = true; btnMerge.IsEnabled = true; + btnAuto.IsEnabled = true; btnBrowseSource.IsEnabled = true; btnBrowseResult.IsEnabled = true; rbExcel.IsEnabled = true; @@ -579,7 +882,7 @@ namespace DwgExtractorManual $"--include-errors {includeErrors.ToString().ToLower()} " + $"--output \"{outputPath}\""; - LogMessage($"🐍 Python 슀크립트 μ‹€ν–‰ μ€€λΉ„..."); + LogMessage("🐍 Python 슀크립트 μ‹€ν–‰ μ€€λΉ„..."); LogMessage($"πŸ“ Python 경둜: {pythonPath}"); LogMessage($"πŸ“ 슀크립트 경둜: {scriptPath}"); LogMessage($"πŸ“„ 파일 리슀트: {Path.GetFileName(tempFileList)}"); @@ -648,7 +951,7 @@ namespace DwgExtractorManual // μžλ™ Excel λ§€ν•‘ μ—…λ°μ΄νŠΈ (μƒˆλ‘œμš΄ 효율적 방식) await AutoUpdateExcelMappingWithPdfResults(outputPath); - System.Windows.MessageBox.Show( + ShowMessageBox( $"PDF μΆ”μΆœμ΄ μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€!\n\n" + $"πŸ“„ 처리된 파일: {pdfFiles.Length}개\n" + $"⏱️ 총 μ†Œμš”μ‹œκ°„: {stopwatch.Elapsed:mm\\:ss}\n\n" + @@ -996,8 +1299,7 @@ namespace DwgExtractorManual // μž…λ ₯ μœ νš¨μ„± 검사 if (string.IsNullOrEmpty(txtResultFolder.Text) || !Directory.Exists(txtResultFolder.Text)) { - System.Windows.MessageBox.Show("μœ νš¨ν•œ κ²°κ³Ό μ €μž₯ 폴더λ₯Ό μ„ νƒν•˜μ„Έμš”.", "였λ₯˜", - MessageBoxButton.OK, MessageBoxImage.Warning); + ShowMessageBox("μœ νš¨ν•œ κ²°κ³Ό μ €μž₯ 폴더λ₯Ό μ„ νƒν•˜μ„Έμš”.", "였λ₯˜", MessageBoxButton.OK, MessageBoxImage.Warning); return; } @@ -1005,6 +1307,7 @@ namespace DwgExtractorManual btnExtract.IsEnabled = false; btnPdfExtract.IsEnabled = false; btnMerge.IsEnabled = false; + btnAuto.IsEnabled = false; btnBrowseSource.IsEnabled = false; btnBrowseResult.IsEnabled = false; rbExcel.IsEnabled = false; @@ -1024,8 +1327,7 @@ namespace DwgExtractorManual { LogMessage($"❌ λ§€ν•‘ 병합 쀑 치λͺ…적 였λ₯˜ λ°œμƒ: {ex.Message}"); UpdateStatus("λ§€ν•‘ 병합 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€."); - System.Windows.MessageBox.Show($"λ§€ν•‘ 병합 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€:\n\n{ex.Message}", - "였λ₯˜", MessageBoxButton.OK, MessageBoxImage.Error); + ShowMessageBox($"λ§€ν•‘ 병합 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€:\n\n{ex.Message}", "였λ₯˜", MessageBoxButton.OK, MessageBoxImage.Error); } finally { @@ -1033,6 +1335,7 @@ namespace DwgExtractorManual btnExtract.IsEnabled = true; btnPdfExtract.IsEnabled = true; btnMerge.IsEnabled = true; + btnAuto.IsEnabled = true; btnBrowseSource.IsEnabled = true; btnBrowseResult.IsEnabled = true; rbExcel.IsEnabled = true; @@ -1065,7 +1368,7 @@ namespace DwgExtractorManual LogMessage("❌ PDF μΆ”μΆœ JSON νŒŒμΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€."); LogMessage("πŸ’‘ λ¨Όμ € 'PDF μΆ”μΆœ' λ²„νŠΌμ„ μ‹€ν–‰ν•˜μ—¬ JSON νŒŒμΌμ„ μƒμ„±ν•˜μ„Έμš”."); - System.Windows.MessageBox.Show( + ShowMessageBox( "PDF μΆ”μΆœ JSON νŒŒμΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.\n\n" + "λ¨Όμ € 'PDF μΆ”μΆœ' λ²„νŠΌμ„ μ‹€ν–‰ν•˜μ—¬ JSON νŒŒμΌμ„ μƒμ„±ν•˜μ„Έμš”.", "파일 μ—†μŒ", MessageBoxButton.OK, MessageBoxImage.Information); @@ -1082,7 +1385,7 @@ namespace DwgExtractorManual LogMessage("❌ λ§€ν•‘ 데이터 νŒŒμΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€."); LogMessage("πŸ’‘ λ¨Όμ € 'DWG 정보 μΆ”μΆœ' λ²„νŠΌμ„ μ‹€ν–‰ν•˜μ—¬ λ§€ν•‘ 데이터λ₯Ό μƒμ„±ν•˜μ„Έμš”."); - System.Windows.MessageBox.Show( + ShowMessageBox( "λ§€ν•‘ 데이터 νŒŒμΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.\n\n" + "λ¨Όμ € 'DWG 정보 μΆ”μΆœ' λ²„νŠΌμ„ μ‹€ν–‰ν•˜μ—¬ λ§€ν•‘ 데이터λ₯Ό μƒμ„±ν•˜μ„Έμš”.", "파일 μ—†μŒ", MessageBoxButton.OK, MessageBoxImage.Information); @@ -1100,15 +1403,13 @@ namespace DwgExtractorManual await ShowJsonPreview(latestJsonFile); // μ‚¬μš©μž 확인 - var result = System.Windows.MessageBox.Show( + var result = ShowConfirmationDialog( $"λ‹€μŒ νŒŒμΌλ“€λ‘œ Excel 맀핑을 μ—…λ°μ΄νŠΈν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?\n\n" + $"πŸ“„ PDF JSON: {Path.GetFileName(latestJsonFile)}\n" + $"πŸ“Š λ§€ν•‘ 데이터: {Path.GetFileName(latestMappingDataFile)}\n" + $"πŸ“… 생성: {File.GetCreationTime(latestJsonFile):yyyy-MM-dd HH:mm:ss}\n" + $"πŸ“ 크기: {new FileInfo(latestJsonFile).Length / 1024:N0} KB", - "λ§€ν•‘ μ—…λ°μ΄νŠΈ 확인", - MessageBoxButton.YesNo, - MessageBoxImage.Question); + "λ§€ν•‘ μ—…λ°μ΄νŠΈ 확인"); if (result != MessageBoxResult.Yes) { @@ -1165,7 +1466,7 @@ namespace DwgExtractorManual LogMessage($"βœ… Excel λ§€ν•‘ 병합 μ™„λ£Œ: {Path.GetFileName(completeExcelPath)}"); LogMessage("πŸ“Š DWG κ°’κ³Ό PDF 값이 λͺ¨λ‘ ν¬ν•¨λœ 톡합 Excel 파일이 μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€."); - System.Windows.MessageBox.Show( + ShowMessageBox( $"Excel λ§€ν•‘ 병합이 μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€!\n\n" + $"πŸ“„ μ‚¬μš©λœ JSON: {Path.GetFileName(latestJsonFile)}\n" + $"πŸ“Š μƒμ„±λœ 파일: {Path.GetFileName(completeExcelPath)}\n" + @@ -1254,6 +1555,302 @@ namespace DwgExtractorManual } } + /// + /// μžλ™ 처리 λ²„νŠΌ 클릭 이벀트 + /// + private async void BtnAuto_Click(object sender, RoutedEventArgs e) + { + try + { + // μž…λ ₯ 검증 + if (string.IsNullOrEmpty(txtSourceFolder.Text)) + { + ShowMessageBox("μ†ŒμŠ€ 폴더λ₯Ό μ„ νƒν•΄μ£Όμ„Έμš”.", "였λ₯˜", MessageBoxButton.OK, MessageBoxImage.Warning); + return; + } + + if (string.IsNullOrEmpty(txtResultFolder.Text)) + { + ShowMessageBox("κ²°κ³Ό μ €μž₯ 폴더λ₯Ό μ„ νƒν•΄μ£Όμ„Έμš”.", "였λ₯˜", MessageBoxButton.OK, MessageBoxImage.Warning); + return; + } + + string rootFolder = txtSourceFolder.Text; + string resultFolder = txtResultFolder.Text; + + if (!Directory.Exists(rootFolder)) + { + ShowMessageBox("μ†ŒμŠ€ 폴더가 μ‘΄μž¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.", "였λ₯˜", MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + + if (!Directory.Exists(resultFolder)) + { + ShowMessageBox("κ²°κ³Ό μ €μž₯ 폴더가 μ‘΄μž¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.", "였λ₯˜", MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + + // 리프 폴더듀 μ°ΎκΈ° + LogMessage("πŸ” 리프 폴더 검색 쀑..."); + var leafFolders = FindLeafFolders(rootFolder); + + if (leafFolders.Count == 0) + { + ShowMessageBox("μ²˜λ¦¬ν•  리프 폴더가 μ—†μŠ΅λ‹ˆλ‹€.", "정보", MessageBoxButton.OK, MessageBoxImage.Information); + return; + } + + LogMessage($"πŸ“ 발견된 리프 폴더: {leafFolders.Count}개"); + foreach (var folder in leafFolders) + { + LogMessage($" └─ {folder}"); + } + + // μ‚¬μš©μž 확인 + var result = ShowConfirmationDialog( + $"총 {leafFolders.Count}개의 리프 폴더λ₯Ό μžλ™ μ²˜λ¦¬ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?\n\n" + + "각 ν΄λ”λ§ˆλ‹€ DWG μΆ”μΆœ β†’ PDF μΆ”μΆœ β†’ ν•©μΉ˜κΈ°κ°€ 순차적으둜 μ‹€ν–‰λ©λ‹ˆλ‹€.", + "μžλ™ 처리 확인"); + + if (result != MessageBoxResult.Yes) + { + return; + } + + // UI μƒνƒœ λ³€κ²½ + SetButtonsEnabled(false); + progressBar.Value = 0; + UpdateStatus("πŸ€– μžλ™ 처리 μ‹œμž‘..."); + + // μžλ™ 처리 λͺ¨λ“œ ν™œμ„±ν™” + isAutoProcessing = true; + + await ProcessLeafFoldersAutomatically(leafFolders, resultFolder); + + ShowMessageBox( + $"μžλ™ μ²˜λ¦¬κ°€ μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€!\n\n" + + $"처리된 폴더: {leafFolders.Count}개\n" + + $"κ²°κ³Ό μ €μž₯ μœ„μΉ˜: {resultFolder}", + "μ™„λ£Œ", + MessageBoxButton.OK, + MessageBoxImage.Information); + } + catch (Exception ex) + { + LogMessage($"❌ μžλ™ 처리 쀑 였λ₯˜: {ex.Message}"); + ShowMessageBox($"μžλ™ 처리 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€:\n{ex.Message}", "였λ₯˜", MessageBoxButton.OK, MessageBoxImage.Error); + } + finally + { + SetButtonsEnabled(true); + progressBar.Value = 0; + UpdateStatus("βœ… μ€€λΉ„"); + + // μžλ™ 처리 λͺ¨λ“œ λΉ„ν™œμ„±ν™” + isAutoProcessing = false; + } + } + + /// + /// ν΄λ”μ—μ„œ 리프 폴더듀(ν•˜μœ„ 폴더가 μ—†λŠ” 폴더)을 μž¬κ·€μ μœΌλ‘œ μ°ΎμŠ΅λ‹ˆλ‹€. + /// + /// 루트 폴더 경둜 + /// 리프 폴더 경둜 λͺ©λ‘ + private List FindLeafFolders(string rootPath) + { + var leafFolders = new List(); + + try + { + FindLeafFoldersRecursive(rootPath, leafFolders); + } + catch (Exception ex) + { + LogMessage($"❌ 리프 폴더 검색 쀑 였λ₯˜: {ex.Message}"); + } + + return leafFolders; + } + + /// + /// μž¬κ·€μ μœΌλ‘œ 리프 폴더λ₯Ό μ°ΎλŠ” 헬퍼 λ©”μ„œλ“œ + /// + /// ν˜„μž¬ 폴더 경둜 + /// 리프 폴더 λͺ©λ‘ + private void FindLeafFoldersRecursive(string currentPath, List leafFolders) + { + try + { + var subDirectories = Directory.GetDirectories(currentPath); + + if (subDirectories.Length == 0) + { + // ν•˜μœ„ 폴더가 μ—†μœΌλ©΄ 리프 폴더 + leafFolders.Add(currentPath); + } + else + { + // ν•˜μœ„ 폴더가 있으면 μž¬κ·€ 호좜 + foreach (var subDir in subDirectories) + { + FindLeafFoldersRecursive(subDir, leafFolders); + } + } + } + catch (UnauthorizedAccessException) + { + LogMessage($"⚠️ μ ‘κ·Ό κΆŒν•œ μ—†μŒ: {currentPath}"); + } + catch (Exception ex) + { + LogMessage($"❌ 폴더 처리 쀑 였λ₯˜ ({currentPath}): {ex.Message}"); + } + } + + /// + /// 리프 폴더듀을 μžλ™μœΌλ‘œ μ²˜λ¦¬ν•©λ‹ˆλ‹€. + /// + /// μ²˜λ¦¬ν•  리프 폴더 λͺ©λ‘ + /// κ²°κ³Ό μ €μž₯ κΈ°λ³Έ 폴더 + private async Task ProcessLeafFoldersAutomatically(List leafFolders, string resultBaseFolder) + { + int totalFolders = leafFolders.Count; + int currentFolderIndex = 0; + + foreach (var leafFolder in leafFolders) + { + currentFolderIndex++; + try + { + LogMessage($"πŸ“ [{currentFolderIndex}/{totalFolders}] 처리 μ‹œμž‘: {leafFolder}"); + + // 폴더 경둜λ₯Ό 파일λͺ…μœΌλ‘œ λ³€ν™˜ (경둜 κ΅¬λΆ„μžλ₯Ό '_'둜 λ³€κ²½) + string rootFolderPath = txtSourceFolder.Text.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + string relativePath = Path.GetRelativePath(rootFolderPath, leafFolder); + string excelFileName = relativePath.Replace(Path.DirectorySeparatorChar, '_').Replace(Path.AltDirectorySeparatorChar, '_') + ".xlsx"; + string excelFilePath = Path.Combine(resultBaseFolder, excelFileName); + + // μ§„ν–‰λ₯  μ—…λ°μ΄νŠΈ + double baseProgress = ((double)(currentFolderIndex - 1) / totalFolders) * 100; + progressBar.Value = baseProgress; + + // 1. DWG μΆ”μΆœ + LogMessage($"πŸ”§ [{currentFolderIndex}/{totalFolders}] DWG μΆ”μΆœ μ‹œμž‘..."); + UpdateStatus($"πŸ”§ DWG μΆ”μΆœ 쀑... ({currentFolderIndex}/{totalFolders})"); + + await ProcessFiles(leafFolder); + + progressBar.Value = baseProgress + (100.0 / totalFolders) * 0.33; + LogMessage($"βœ… [{currentFolderIndex}/{totalFolders}] DWG μΆ”μΆœ μ™„λ£Œ"); + + // 2초 λŒ€κΈ° (μ›λž˜ 타이밍 볡원 - 싱글톀 Services둜 좩돌 해결됨) + await Task.Delay(2000); + + // 2. PDF μΆ”μΆœ + LogMessage($"πŸ“„ [{currentFolderIndex}/{totalFolders}] PDF μΆ”μΆœ μ‹œμž‘..."); + UpdateStatus($"πŸ“„ PDF μΆ”μΆœ 쀑... ({currentFolderIndex}/{totalFolders})"); + + // ν˜„μž¬ ν΄λ”μ—μ„œ PDF 파일 μ°ΎκΈ° + var pdfFiles = new DirectoryInfo(leafFolder) + .GetFiles("*.pdf", SearchOption.AllDirectories) + .ToArray(); + + if (pdfFiles.Length == 0) + { + LogMessage($"⚠️ [{currentFolderIndex}/{totalFolders}] PDF 파일이 μ—†μŒ - PDF μΆ”μΆœ κ±΄λ„ˆλœ€"); + } + else + { + await ProcessPdfFiles(pdfFiles); + } + + progressBar.Value = baseProgress + (100.0 / totalFolders) * 0.66; + LogMessage($"βœ… [{currentFolderIndex}/{totalFolders}] PDF μΆ”μΆœ μ™„λ£Œ"); + + // 3초 λŒ€κΈ° + await Task.Delay(3000); + + // 3. ν•©μΉ˜κΈ° 및 Excel 파일 이름 λ³€κ²½ + LogMessage($"πŸ”— [{currentFolderIndex}/{totalFolders}] ν•©μΉ˜κΈ° μ‹œμž‘..."); + UpdateStatus($"πŸ”— ν•©μΉ˜κΈ° 쀑... ({currentFolderIndex}/{totalFolders})"); + + await MergeLatestPdfResults(); + + // μƒμ„±λœ Excel νŒŒμΌμ„ λͺ©ν‘œ μ΄λ¦„μœΌλ‘œ λ³€κ²½ + await RenameLatestExcelFile(resultBaseFolder, excelFilePath); + + progressBar.Value = baseProgress + (100.0 / totalFolders); + LogMessage($"βœ… [{currentFolderIndex}/{totalFolders}] 처리 μ™„λ£Œ: {excelFileName}"); + + // 2초 λŒ€κΈ° ν›„ λ‹€μŒ 폴더 처리 + if (currentFolderIndex < totalFolders) + { + await Task.Delay(2000); + } + } + catch (Exception ex) + { + LogMessage($"❌ [{currentFolderIndex}/{totalFolders}] 폴더 처리 μ‹€νŒ¨ ({leafFolder}): {ex.Message}"); + LogMessage($"⏭️ [{currentFolderIndex}/{totalFolders}] λ‹€μŒ ν΄λ”λ‘œ 계속 μ§„ν–‰"); + + // 였λ₯˜ λ°œμƒ μ‹œ μž μ‹œ λŒ€κΈ° ν›„ λ‹€μŒ 폴더 처리 + await Task.Delay(2000); + continue; + } + } + + LogMessage($"πŸŽ‰ μžλ™ 처리 μ™„λ£Œ! 총 {totalFolders}개 폴더 처리됨"); + UpdateStatus("βœ… μžλ™ 처리 μ™„λ£Œ"); + } + + /// + /// μ΅œμ‹  Excel νŒŒμΌμ„ λͺ©ν‘œ 경둜둜 이름 λ³€κ²½ + /// + /// κ²°κ³Ό 폴더 + /// λͺ©ν‘œ 파일 경둜 + private async Task RenameLatestExcelFile(string resultFolder, string targetPath) + { + try + { + // μ΅œμ‹  *_Complete_Mapping_Merged.xlsx 파일 μ°ΎκΈ° + var excelFiles = Directory.GetFiles(resultFolder, "*_Complete_Mapping_Merged.xlsx") + .Where(f => !Path.GetFileName(f).StartsWith("~$")) + .OrderByDescending(f => File.GetCreationTime(f)) + .ToList(); + + if (excelFiles.Any()) + { + string latestFile = excelFiles.First(); + + // λͺ©ν‘œ 파일이 이미 μ‘΄μž¬ν•˜λ©΄ μ‚­μ œ + if (File.Exists(targetPath)) + { + File.Delete(targetPath); + } + + // 파일 이름 λ³€κ²½ + File.Move(latestFile, targetPath); + LogMessage($"πŸ“‹ Excel 파일 이름 변경됨: {Path.GetFileName(targetPath)}"); + } + } + catch (Exception ex) + { + LogMessage($"⚠️ Excel 파일 이름 λ³€κ²½ μ‹€νŒ¨: {ex.Message}"); + } + } + + /// + /// λ²„νŠΌλ“€μ˜ ν™œμ„±ν™” μƒνƒœλ₯Ό μ„€μ •ν•©λ‹ˆλ‹€. + /// + /// ν™œμ„±ν™” μ—¬λΆ€ + private void SetButtonsEnabled(bool enabled) + { + btnExtract.IsEnabled = enabled; + btnPdfExtract.IsEnabled = enabled; + btnMerge.IsEnabled = enabled; + btnAuto.IsEnabled = enabled; + } + protected override void OnClosed(EventArgs e) { _timer?.Stop(); diff --git a/Models/ExportExcel.cs b/Models/ExportExcel.cs index 5f1e922..8c0c8ce 100644 --- a/Models/ExportExcel.cs +++ b/Models/ExportExcel.cs @@ -21,7 +21,7 @@ namespace DwgExtractorManual.Models /// internal class ExportExcel : IDisposable { - // ODA μ„œλΉ„μŠ€ 객체 + // ODA μ„œλΉ„μŠ€ 객체 (managed by singleton) private Services appServices; // Excel COM 객체듀 @@ -72,7 +72,7 @@ namespace DwgExtractorManual.Models } Debug.WriteLine("πŸ”„ ODA μ΄ˆκΈ°ν™” 쀑..."); - ActivateAndInitializeODA(); + InitializeTeighaServices(); Debug.WriteLine("πŸ”„ Excel μ΄ˆκΈ°ν™” 쀑..."); InitializeExcel(); } @@ -93,13 +93,20 @@ namespace DwgExtractorManual.Models } } - // ODA μ œν’ˆ ν™œμ„±ν™” 및 μ΄ˆκΈ°ν™” - private void ActivateAndInitializeODA() + // Teigha μ„œλΉ„μŠ€ μ΄ˆκΈ°ν™” (싱글톀 μ‚¬μš©) + private void InitializeTeighaServices() { - var userInfo = "c2FtYW4gZW5naW5lZXJpbmc="; - var userSignature = "F0kuQTmtVpHtvl/TgaFVGE92/YqGmYR9SLoXckEjnOk8NoAQh7Sg6GQruVC04JqD4C/IipxJYqpqvMfMc2auRMG+cAJCKqKUE2djIMdkUdb+C5IVx2c97fcK5ba3n8DDvB54Upokajl+6j12yD8h8MKGOR3Z3zysObeXD62bFpQgp00GCYTqlxEZtTIRjHIPAfJfix8Y0jtXWWYyVJ3LYOu86as5xtx+hY1aakpYIJiQk/6pGd84qSn/9K1w8nxR7UrFzieDeQ/xM58BHSD4u/ZxVJwvv6Uy10tsdBFBTvfJMAFp05Y7yeyeCNr100tA3iOfmWoXAVRHfxnkPfiYR54aK04QI+R6OGkI+yd1oR5BtmN6BdDt3z8KYK5EpFGJGiJIGoUy5PvkYdJ2VV6xe9JWBiIJuI/tDn1Y+uyTQFA9qaDHnOURriXsRGfy8reDPf1eejybSJxWKkpilG6RXhq3xHlCkjZzh1Q45S+xYXNGatcWMm9nkn20M8Ke5JEVaI9w/p2GE36CHRtRQbt8kfPmsbWNXJCFr4svHW2MPbCKWoyn5XEyMWBnuAKi74zvczB13DKjf29SqSIgF5k/hwy2QrgvnaKzY1k8bw8w2/k0vJXcS3GKOB/ZYDle1tf/lkAD1HtnF9zE18TiXhVnqwAVjwg4ui1RPLn/LMs6b5Y="; - Services.odActivate(userInfo, userSignature); - appServices = new Services(); + try + { + Debug.WriteLine("[DEBUG] TeighaServicesManagerλ₯Ό ν†΅ν•œ Services νšλ“ 쀑..."); + appServices = TeighaServicesManager.Instance.AcquireServices(); + Debug.WriteLine($"[DEBUG] Services νšλ“ 성곡. Reference Count: {TeighaServicesManager.Instance.ReferenceCount}"); + } + catch (Teigha.Runtime.Exception ex) + { + Debug.WriteLine($"[DEBUG] Teigha Services μ΄ˆκΈ°ν™” μ‹€νŒ¨: {ex.Message}"); + throw; + } } // Excel μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 및 μ›Œν¬μ‹œνŠΈ μ΄ˆκΈ°ν™” @@ -603,14 +610,31 @@ namespace DwgExtractorManual.Models return false; } - // JSON 파일 읽기 - string jsonContent = File.ReadAllText(jsonFilePath); - JObject jsonData = JObject.Parse(jsonContent); + // JSON 파일 읽기 및 정리 + string jsonContent = File.ReadAllText(jsonFilePath, System.Text.Encoding.UTF8); + Debug.WriteLine($"[DEBUG] JSON 파일 크기: {jsonContent.Length} bytes"); + + // JSON λ‚΄μš© 정리 (주석 제거 λ“±) + jsonContent = CleanJsonContent(jsonContent); + + JObject jsonData; + try + { + jsonData = JObject.Parse(jsonContent); + } + catch (Newtonsoft.Json.JsonReaderException jsonEx) + { + Debug.WriteLine($"❌ JSON νŒŒμ‹± 였λ₯˜: {jsonEx.Message}"); + Debug.WriteLine($"❌ JSON λ‚΄μš© 미리보기 (첫 500자):"); + Debug.WriteLine(jsonContent.Length > 500 ? jsonContent.Substring(0, 500) + "..." : jsonContent); + throw new System.Exception($"PDF 뢄석 JSON 파일 νŒŒμ‹± μ‹€νŒ¨: {jsonEx.Message}\n파일: {jsonFilePath}"); + } var results = jsonData["results"] as JArray; if (results == null) { Debug.WriteLine("❌ JSONμ—μ„œ 'results' 배열을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€."); + Debug.WriteLine($"❌ JSON 루트 ν‚€λ“€: {string.Join(", ", jsonData.Properties().Select(p => p.Name))}"); return false; } @@ -1032,7 +1056,21 @@ namespace DwgExtractorManual.Models string jsonContent = File.ReadAllText(filePath, System.Text.Encoding.UTF8); Debug.WriteLine($"[DEBUG] JSON λ‚΄μš© 길이: {jsonContent.Length}"); - var deserializedData = JsonConvert.DeserializeObject>>(jsonContent); + // JSON λ‚΄μš© 정리 (주석 제거 λ“±) + jsonContent = CleanJsonContent(jsonContent); + + Dictionary> deserializedData; + try + { + deserializedData = JsonConvert.DeserializeObject>>(jsonContent); + } + catch (Newtonsoft.Json.JsonReaderException jsonEx) + { + Debug.WriteLine($"❌ JSON νŒŒμ‹± 였λ₯˜: {jsonEx.Message}"); + Debug.WriteLine($"❌ JSON λ‚΄μš© 미리보기 (첫 500자):"); + Debug.WriteLine(jsonContent.Length > 500 ? jsonContent.Substring(0, 500) + "..." : jsonContent); + throw new System.Exception($"λ§€ν•‘ JSON 파일 νŒŒμ‹± μ‹€νŒ¨: {jsonEx.Message}\n파일: {filePath}"); + } // μƒˆλ‘œμš΄ λ”•μ…”λ„ˆλ¦¬ μ΄ˆκΈ°ν™” FileToMapkeyToLabelTagValuePdf.Clear(); @@ -1146,14 +1184,31 @@ namespace DwgExtractorManual.Models return; } - // JSON 파일 읽기 - string jsonContent = File.ReadAllText(jsonFilePath); - JObject jsonData = JObject.Parse(jsonContent); + // JSON 파일 읽기 및 정리 + string jsonContent = File.ReadAllText(jsonFilePath, System.Text.Encoding.UTF8); + Debug.WriteLine($"[DEBUG] JSON 파일 크기: {jsonContent.Length} bytes"); + + // JSON λ‚΄μš© 정리 (주석 제거 λ“±) + jsonContent = CleanJsonContent(jsonContent); + + JObject jsonData; + try + { + jsonData = JObject.Parse(jsonContent); + } + catch (Newtonsoft.Json.JsonReaderException jsonEx) + { + Debug.WriteLine($"❌ JSON νŒŒμ‹± 였λ₯˜: {jsonEx.Message}"); + Debug.WriteLine($"❌ JSON λ‚΄μš© 미리보기 (첫 500자):"); + Debug.WriteLine(jsonContent.Length > 500 ? jsonContent.Substring(0, 500) + "..." : jsonContent); + throw new System.Exception($"PDF 뢄석 JSON 파일 νŒŒμ‹± μ‹€νŒ¨: {jsonEx.Message}\n파일: {jsonFilePath}"); + } var results = jsonData["results"] as JArray; if (results == null) { Debug.WriteLine("❌ JSONμ—μ„œ 'results' 배열을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€."); + Debug.WriteLine($"❌ JSON 루트 ν‚€λ“€: {string.Join(", ", jsonData.Properties().Select(p => p.Name))}"); return; } @@ -1229,17 +1284,158 @@ namespace DwgExtractorManual.Models } } + /// + /// JSON λ‚΄μš©μ„ μ •λ¦¬ν•˜μ—¬ νŒŒμ‹± κ°€λŠ₯ν•œ μƒνƒœλ‘œ λ§Œλ“­λ‹ˆλ‹€. + /// 주석 제거 및 기타 λ¬΄νš¨ν•œ 문자 처리 + /// + /// 원본 JSON λ‚΄μš© + /// μ •λ¦¬λœ JSON λ‚΄μš© + private string CleanJsonContent(string jsonContent) + { + if (string.IsNullOrEmpty(jsonContent)) + return jsonContent; + + try + { + // μ€„λ³„λ‘œ μ²˜λ¦¬ν•˜μ—¬ 주석 제거 + var lines = jsonContent.Split('\n'); + var cleanedLines = new List(); + + bool inMultiLineComment = false; + + foreach (string line in lines) + { + string processedLine = line; + + // 멀티라인 주석 처리 (/* */) + if (inMultiLineComment) + { + int endIndex = processedLine.IndexOf("*/"); + if (endIndex >= 0) + { + processedLine = processedLine.Substring(endIndex + 2); + inMultiLineComment = false; + } + else + { + continue; // 전체 라인이 주석 + } + } + + // 멀티라인 주석 μ‹œμž‘ 확인 + int multiLineStart = processedLine.IndexOf("/*"); + if (multiLineStart >= 0) + { + int multiLineEnd = processedLine.IndexOf("*/", multiLineStart + 2); + if (multiLineEnd >= 0) + { + // 같은 λΌμΈμ—μ„œ μ‹œμž‘ν•˜κ³  λλ‚˜λŠ” 주석 + processedLine = processedLine.Substring(0, multiLineStart) + + processedLine.Substring(multiLineEnd + 2); + } + else + { + // 멀티라인 주석 μ‹œμž‘ + processedLine = processedLine.Substring(0, multiLineStart); + inMultiLineComment = true; + } + } + + // 싱글라인 주석 제거 (//) - λ¬Έμžμ—΄ λ‚΄λΆ€μ˜ //λŠ” μ œμ™Έ + bool inString = false; + bool escaped = false; + int commentIndex = -1; + + for (int i = 0; i < processedLine.Length - 1; i++) + { + char current = processedLine[i]; + char next = processedLine[i + 1]; + + if (escaped) + { + escaped = false; + continue; + } + + if (current == '\\') + { + escaped = true; + continue; + } + + if (current == '"') + { + inString = !inString; + continue; + } + + if (!inString && current == '/' && next == '/') + { + commentIndex = i; + break; + } + } + + if (commentIndex >= 0) + { + processedLine = processedLine.Substring(0, commentIndex); + } + + // 빈 라인이 μ•„λ‹ˆλ©΄ μΆ”κ°€ + if (!string.IsNullOrWhiteSpace(processedLine)) + { + cleanedLines.Add(processedLine); + } + } + + string result = string.Join("\n", cleanedLines); + Debug.WriteLine($"[DEBUG] JSON 정리 μ™„λ£Œ: {jsonContent.Length} -> {result.Length} bytes"); + return result; + } + catch (System.Exception ex) + { + Debug.WriteLine($"❌ JSON 정리 쀑 였λ₯˜: {ex.Message}"); + // 정리 μ‹€νŒ¨μ‹œ 원본 λ°˜ν™˜ + return jsonContent; + } + } + public void Dispose() { - if (excelApplication != null) + try { - CloseExcelObjects(); - } + Debug.WriteLine("[DEBUG] ExportExcel Dispose μ‹œμž‘"); + + if (excelApplication != null) + { + Debug.WriteLine("[DEBUG] Excel 객체 정리 쀑..."); + CloseExcelObjects(); + } - if (appServices != null) + if (appServices != null) + { + Debug.WriteLine("[DEBUG] Teigha Services ν•΄μ œ 쀑..."); + try + { + TeighaServicesManager.Instance.ReleaseServices(); + Debug.WriteLine($"[DEBUG] Teigha Services ν•΄μ œ μ™„λ£Œ. Remaining ref count: {TeighaServicesManager.Instance.ReferenceCount}"); + } + catch (System.Exception ex) + { + Debug.WriteLine($"[DEBUG] Teigha Services ν•΄μ œ 쀑 였λ₯˜ (λ¬΄μ‹œλ¨): {ex.Message}"); + } + finally + { + appServices = null; + } + } + + Debug.WriteLine("[DEBUG] ExportExcel Dispose μ™„λ£Œ"); + } + catch (System.Exception ex) { - appServices.Dispose(); - appServices = null; + Debug.WriteLine($"[DEBUG] ExportExcel Dispose 쀑 μ „μ—­ 였λ₯˜: {ex.Message}"); + // Disposal 였λ₯˜λŠ” 둜그만 남기고 계속 μ§„ν–‰ } } } diff --git a/Models/SqlDatas.cs b/Models/SqlDatas.cs index 085ef34..881225a 100644 --- a/Models/SqlDatas.cs +++ b/Models/SqlDatas.cs @@ -15,15 +15,22 @@ namespace DwgExtractorManual.Models /// internal sealed class SqlDatas : IDisposable { - Services appServices; // ODA μ œν’ˆ ν™œμ„±ν™”μš© + Services appServices; // ODA μ œν’ˆ ν™œμ„±ν™”μš© (managed by singleton) readonly string connectionString = "Host=localhost;Database=postgres;Username=postgres;Password=Qwer1234"; - void ActivateAndInitializeODA() + void InitializeTeighaServices() { - var userInfo = "c2FtYW4gZW5naW5lZXJpbmc="; - var userSignature = "F0kuQTmtVpHtvl/TgaFVGE92/YqGmYR9SLoXckEjnOk8NoAQh7Sg6GQruVC04JqD4C/IipxJYqpqvMfMc2auRMG+cAJCKqKUE2djIMdkUdb+C5IVx2c97fcK5ba3n8DDvB54Upokajl+6j12yD8h8MKGOR3Z3zysObeXD62bFpQgp00GCYTqlxEZtTIRjHIPAfJfix8Y0jtXWWYyVJ3LYOu86as5xtx+hY1aakpYIJiQk/6pGd84qSn/9K1w8nxR7UrFzieDeQ/xM58BHSD4u/ZxVJwvv6Uy10tsdBFBTvfJMAFp05Y7yeyeCNr100tA3iOfmWoXAVRHfxnkPfiYR54aK04QI+R6OGkI+yd1oR5BtmN6BdDt3z8KYK5EpFGJGiJIGoUy5PvkYdJ2VV6xe9JWBiIJuI/tDn1Y+uyTQFA9qaDHnOURriXsRGfy8reDPf1eejybSJxWKkpilG6RXhq3xHlCkjZzh1Q45S+xYXNGatcWMm9nkn20M8Ke5JEVaI9w/p2GE36CHRtRQbt8kfPmsbWNXJCFr4svHW2MPbCKWoyn5XEyMWBnuAKi74zvczB13DKjf29SqSIgF5k/hwy2QrgvnaKzY1k8bw8w2/k0vJXcS3GKOB/ZYDle1tf/lkAD1HtnF9zE18TiXhVnqwAVjwg4ui1RPLn/LMs6b5Y="; - Services.odActivate(userInfo, userSignature); - appServices = new Services(); + try + { + Debug.WriteLine("[SqlDatas] TeighaServicesManagerλ₯Ό ν†΅ν•œ Services νšλ“ 쀑..."); + appServices = TeighaServicesManager.Instance.AcquireServices(); + Debug.WriteLine($"[SqlDatas] Services νšλ“ 성곡. Reference Count: {TeighaServicesManager.Instance.ReferenceCount}"); + } + catch (Teigha.Runtime.Exception ex) + { + Debug.WriteLine($"[SqlDatas] Teigha Services μ΄ˆκΈ°ν™” μ‹€νŒ¨: {ex.Message}"); + throw; + } } void CreateTables() @@ -78,7 +85,7 @@ namespace DwgExtractorManual.Models public SqlDatas() { - ActivateAndInitializeODA(); + InitializeTeighaServices(); CreateTables(); } @@ -301,8 +308,20 @@ namespace DwgExtractorManual.Models { if (appServices != null) { - appServices.Dispose(); - appServices = null; + try + { + Debug.WriteLine("[SqlDatas] Teigha Services ν•΄μ œ 쀑..."); + TeighaServicesManager.Instance.ReleaseServices(); + Debug.WriteLine($"[SqlDatas] Teigha Services ν•΄μ œ μ™„λ£Œ. Remaining ref count: {TeighaServicesManager.Instance.ReferenceCount}"); + } + catch (Teigha.Runtime.Exception ex) + { + Debug.WriteLine($"[SqlDatas] Teigha Services ν•΄μ œ 쀑 였λ₯˜ (λ¬΄μ‹œλ¨): {ex.Message}"); + } + finally + { + appServices = null; + } } } } diff --git a/Models/TeighaServicesManager.cs b/Models/TeighaServicesManager.cs new file mode 100644 index 0000000..201deb1 --- /dev/null +++ b/Models/TeighaServicesManager.cs @@ -0,0 +1,192 @@ +using System; +using System.Diagnostics; +using Teigha.Runtime; + +namespace DwgExtractorManual.Models +{ + /// + /// Singleton class to manage Teigha Services lifecycle and prevent disposal conflicts + /// + public sealed class TeighaServicesManager + { + private static readonly object _lock = new object(); + private static TeighaServicesManager _instance = null; + private static Services _services = null; + private static int _referenceCount = 0; + private static bool _isActivated = false; + + private TeighaServicesManager() + { + // Private constructor for singleton + } + + public static TeighaServicesManager Instance + { + get + { + if (_instance == null) + { + lock (_lock) + { + if (_instance == null) + { + _instance = new TeighaServicesManager(); + } + } + } + return _instance; + } + } + + /// + /// Acquires Teigha Services (creates if needed, increments reference count) + /// + /// The Services instance + public Services AcquireServices() + { + lock (_lock) + { + try + { + Debug.WriteLine($"[TeighaManager] AcquireServices - Current ref count: {_referenceCount}"); + + if (!_isActivated) + { + Debug.WriteLine("[TeighaManager] Activating ODA for first time..."); + ActivateODA(); + _isActivated = true; + } + + if (_services == null) + { + Debug.WriteLine("[TeighaManager] Creating new Services instance..."); + _services = new Services(); + Debug.WriteLine("[TeighaManager] Services instance created successfully"); + } + + _referenceCount++; + Debug.WriteLine($"[TeighaManager] Services acquired - New ref count: {_referenceCount}"); + return _services; + } + catch (Teigha.Runtime.Exception ex) + { + Debug.WriteLine($"[TeighaManager] Error acquiring services: {ex.Message}"); + throw; + } + } + } + + /// + /// Releases Teigha Services (decrements reference count, disposes when count reaches 0) + /// + public void ReleaseServices() + { + lock (_lock) + { + try + { + Debug.WriteLine($"[TeighaManager] ReleaseServices - Current ref count: {_referenceCount}"); + + if (_referenceCount > 0) + { + _referenceCount--; + Debug.WriteLine($"[TeighaManager] Services released - New ref count: {_referenceCount}"); + } + + // Don't dispose Services until app shutdown to prevent conflicts + // Just track reference count for debugging + if (_referenceCount == 0) + { + Debug.WriteLine("[TeighaManager] All references released (Services kept alive for app lifetime)"); + } + } + catch (Teigha.Runtime.Exception ex) + { + Debug.WriteLine($"[TeighaManager] Error releasing services: {ex.Message}"); + // Don't throw on release to prevent cascade failures + } + } + } + + /// + /// Force dispose Services (only call on application shutdown) + /// + public void ForceDisposeServices() + { + lock (_lock) + { + try + { + Debug.WriteLine("[TeighaManager] Force disposing Services..."); + + if (_services != null) + { + _services.Dispose(); + Debug.WriteLine("[TeighaManager] Services disposed successfully"); + } + + _services = null; + _referenceCount = 0; + _isActivated = false; + + Debug.WriteLine("[TeighaManager] Force dispose completed"); + } + catch (Teigha.Runtime.Exception ex) + { + Debug.WriteLine($"[TeighaManager] Error during force dispose: {ex.Message}"); + // Reset state even if disposal fails + _services = null; + _referenceCount = 0; + _isActivated = false; + } + } + } + + /// + /// Get current reference count (for debugging) + /// + public int ReferenceCount + { + get + { + lock (_lock) + { + return _referenceCount; + } + } + } + + /// + /// Check if Services is active and valid + /// + public bool IsServicesActive + { + get + { + lock (_lock) + { + return _services != null && _isActivated; + } + } + } + + private void ActivateODA() + { + try + { + Debug.WriteLine("[TeighaManager] Activating ODA..."); + + var userInfo = "c2FtYW4gZW5naW5lZXJpbmc="; + var userSignature = "F0kuQTmtVpHtvl/TgaFVGE92/YqGmYR9SLoXckEjnOk8NoAQh7Sg6GQruVC04JqD4C/IipxJYqpqvMfMc2auRMG+cAJCKqKUE2djIMdkUdb+C5IVx2c97fcK5ba3n8DDvB54Upokajl+6j12yD8h8MKGOR3Z3zysObeXD62bFpQgp00GCYTqlxEZtTIRjHIPAfJfix8Y0jtXWWYyVJ3LYOu86as5xtx+hY1aakpYIJiQk/6pGd84qSn/9K1w8nxR7UrFzieDeQ/xM58BHSD4u/ZxVJwvv6Uy10tsdBFBTvfJMAFp05Y7yeyeCNr100tA3iOfmWoXAVRHfxnkPfiYR54aK04QI+R6OGkI+yd1oR5BtmN6BdDt3z8KYK5EpFGJGiJIGoUy5PvkYdJ2VV6xe9JWBiIJuI/tDn1Y+uyTQFA9qaDHnOURriXsRGfy8reDPf1eejybSJxWKkpilG6RXhq3xHlCkjZzh1Q45S+xYXNGatcWMm9nkn20M8Ke5JEVaI9w/p2GE36CHRtRQbt8kfPmsbWNXJCFr4svHW2MPbCKWoyn5XEyMWBnuAKi74zvczB13DKjf29SqSIgF5k/hwy2QrgvnaKzY1k8bw8w2/k0vJXcS3GKOB/ZYDle1tf/lkAD1HtnF9zE18TiXhVnqwAVjwg4ui1RPLn/LMs6b5Y="; + + Services.odActivate(userInfo, userSignature); + Debug.WriteLine("[TeighaManager] ODA activation successful"); + } + catch (Teigha.Runtime.Exception ex) + { + Debug.WriteLine($"[TeighaManager] ODA activation failed: {ex.Message}"); + throw; + } + } + } +} \ No newline at end of file diff --git a/csharp_mapping_usage.cs b/csharp_mapping_usage.cs index 8c427d0..8e639c1 100644 --- a/csharp_mapping_usage.cs +++ b/csharp_mapping_usage.cs @@ -56,15 +56,158 @@ public class FieldMapper /// public static FieldMapper LoadFromFile(string jsonFilePath) { - string jsonContent = File.ReadAllText(jsonFilePath); - var options = new JsonSerializerOptions + try { - PropertyNameCaseInsensitive = true, - Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping - }; - - var mappingData = JsonSerializer.Deserialize(jsonContent, options); - return new FieldMapper(mappingData); + string jsonContent = File.ReadAllText(jsonFilePath, System.Text.Encoding.UTF8); + Console.WriteLine($"[DEBUG] λ§€ν•‘ ν…Œμ΄λΈ” JSON 파일 크기: {jsonContent.Length} bytes"); + + // JSON λ‚΄μš© 정리 (주석 제거 λ“±) + jsonContent = CleanJsonContent(jsonContent); + + var options = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true, + Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + AllowTrailingCommas = true + }; + + var mappingData = JsonSerializer.Deserialize(jsonContent, options); + Console.WriteLine($"[DEBUG] λ§€ν•‘ ν…Œμ΄λΈ” λ‘œλ“œ 성곡: {mappingData?.MappingTable?.AilabelToSystems?.Count ?? 0}개 ν•­λͺ©"); + return new FieldMapper(mappingData); + } + catch (JsonException jsonEx) + { + Console.WriteLine($"❌ λ§€ν•‘ ν…Œμ΄λΈ” JSON νŒŒμ‹± 였λ₯˜: {jsonEx.Message}"); + Console.WriteLine($"❌ 파일: {jsonFilePath}"); + if (File.Exists(jsonFilePath)) + { + string content = File.ReadAllText(jsonFilePath); + Console.WriteLine($"❌ JSON λ‚΄μš© 미리보기 (첫 500자):"); + Console.WriteLine(content.Length > 500 ? content.Substring(0, 500) + "..." : content); + } + throw new Exception($"λ§€ν•‘ ν…Œμ΄λΈ” JSON 파일 νŒŒμ‹± μ‹€νŒ¨: {jsonEx.Message}\n파일: {jsonFilePath}"); + } + catch (Exception ex) + { + Console.WriteLine($"❌ λ§€ν•‘ ν…Œμ΄λΈ” λ‘œλ“œ 쀑 였λ₯˜: {ex.Message}"); + throw; + } + } + + /// + /// JSON λ‚΄μš©μ„ μ •λ¦¬ν•˜μ—¬ νŒŒμ‹± κ°€λŠ₯ν•œ μƒνƒœλ‘œ λ§Œλ“­λ‹ˆλ‹€. + /// 주석 제거 및 기타 λ¬΄νš¨ν•œ 문자 처리 + /// + /// 원본 JSON λ‚΄μš© + /// μ •λ¦¬λœ JSON λ‚΄μš© + private static string CleanJsonContent(string jsonContent) + { + if (string.IsNullOrEmpty(jsonContent)) + return jsonContent; + + try + { + // μ€„λ³„λ‘œ μ²˜λ¦¬ν•˜μ—¬ 주석 제거 + var lines = jsonContent.Split('\n'); + var cleanedLines = new List(); + + bool inMultiLineComment = false; + + foreach (string line in lines) + { + string processedLine = line; + + // 멀티라인 주석 처리 (/* */) + if (inMultiLineComment) + { + int endIndex = processedLine.IndexOf("*/"); + if (endIndex >= 0) + { + processedLine = processedLine.Substring(endIndex + 2); + inMultiLineComment = false; + } + else + { + continue; // 전체 라인이 주석 + } + } + + // 멀티라인 주석 μ‹œμž‘ 확인 + int multiLineStart = processedLine.IndexOf("/*"); + if (multiLineStart >= 0) + { + int multiLineEnd = processedLine.IndexOf("*/", multiLineStart + 2); + if (multiLineEnd >= 0) + { + // 같은 λΌμΈμ—μ„œ μ‹œμž‘ν•˜κ³  λλ‚˜λŠ” 주석 + processedLine = processedLine.Substring(0, multiLineStart) + + processedLine.Substring(multiLineEnd + 2); + } + else + { + // 멀티라인 주석 μ‹œμž‘ + processedLine = processedLine.Substring(0, multiLineStart); + inMultiLineComment = true; + } + } + + // 싱글라인 주석 제거 (//) - λ¬Έμžμ—΄ λ‚΄λΆ€μ˜ //λŠ” μ œμ™Έ + bool inString = false; + bool escaped = false; + int commentIndex = -1; + + for (int i = 0; i < processedLine.Length - 1; i++) + { + char current = processedLine[i]; + char next = processedLine[i + 1]; + + if (escaped) + { + escaped = false; + continue; + } + + if (current == '\\') + { + escaped = true; + continue; + } + + if (current == '"') + { + inString = !inString; + continue; + } + + if (!inString && current == '/' && next == '/') + { + commentIndex = i; + break; + } + } + + if (commentIndex >= 0) + { + processedLine = processedLine.Substring(0, commentIndex); + } + + // 빈 라인이 μ•„λ‹ˆλ©΄ μΆ”κ°€ + if (!string.IsNullOrWhiteSpace(processedLine)) + { + cleanedLines.Add(processedLine); + } + } + + string result = string.Join("\n", cleanedLines); + Console.WriteLine($"[DEBUG] λ§€ν•‘ ν…Œμ΄λΈ” JSON 정리 μ™„λ£Œ: {jsonContent.Length} -> {result.Length} bytes"); + return result; + } + catch (Exception ex) + { + Console.WriteLine($"❌ λ§€ν•‘ ν…Œμ΄λΈ” JSON 정리 쀑 였λ₯˜: {ex.Message}"); + // 정리 μ‹€νŒ¨μ‹œ 원본 λ°˜ν™˜ + return jsonContent; + } } /// diff --git a/fletimageanalysis/mapping_table_json.json b/fletimageanalysis/mapping_table_json.json index 787ed17..1db153d 100644 --- a/fletimageanalysis/mapping_table_json.json +++ b/fletimageanalysis/mapping_table_json.json @@ -73,13 +73,7 @@ "railway": "", "docaikey": "CSCOP" }, - //"사업λͺ…_bot": { - // "molit": "", - // "expressway": "TD_CNAME", - // "railway": "TD_CNAME", - // "docaikey": "TDCNAME" - //}, - "섀계곡ꡬ_곡ꡬλͺ…": { + "섀계곡ꡬ_곡ꡬλͺ…": { "molit": "", "expressway": "TD_DSECT", "railway": "",