Files
manual_wpf/Models/ExportExcel.cs
2025-07-22 10:45:51 +09:00

1443 lines
66 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices; // COM 객체 해제를 위해 필요
using System.Threading;
using System.Threading.Tasks;
using Teigha.DatabaseServices;
using Teigha.Geometry;
using Teigha.Runtime;
using Excel = Microsoft.Office.Interop.Excel;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace DwgExtractorManual.Models
{
/// <summary>
/// DWG 파일에서 Excel로 데이터 내보내기 클래스
/// AttributeReference, AttributeDefinition, DBText, MText 추출 지원
/// </summary>
internal class ExportExcel : IDisposable
{
// ODA 서비스 객체 (managed by singleton)
private Services appServices;
// Excel COM 객체들
private Excel.Application excelApplication;
private Excel.Workbook workbook1;
private Excel.Worksheet titleBlockSheet; // Title Block용 시트
private Excel.Worksheet textEntitiesSheet; // Text Entities용 시트
private Excel.Workbook mappingWorkbook; // 매핑 데이터용 워크북
private Excel.Worksheet mappingSheet; // 매핑 데이터용 시트
readonly List<string> MapKeys;
// 각 시트의 현재 행 번호
private int titleBlockCurrentRow = 2; // 헤더가 1행이므로 데이터는 2행부터 시작
private int textEntitiesCurrentRow = 2; // 헤더가 1행이므로 데이터는 2행부터 시작
private int mappingDataCurrentRow = 2; // 헤더가 1행이므로 데이터는 2행부터 시작
// 매핑 데이터 저장용 딕셔너리 - (AILabel, DwgTag, DwgValue, PdfValue)
private Dictionary<string, Dictionary<string, (string, string, string, string)>> FileToMapkeyToLabelTagValuePdf = new Dictionary<string, Dictionary<string, (string, string, string, string)>>();
private FieldMapper fieldMapper;
// 생성자: ODA 및 Excel 초기화
public ExportExcel()
{
try
{
Debug.WriteLine("🔄 FieldMapper 로딩 중: mapping_table_json.json...");
fieldMapper = FieldMapper.LoadFromFile("fletimageanalysis/mapping_table_json.json");
Debug.WriteLine("✅ FieldMapper 로딩 성공");
MapKeys = fieldMapper.GetAllDocAiKeys();
Debug.WriteLine($"📊 총 DocAI 키 개수: {MapKeys?.Count ?? 0}");
// 매핑 테스트 (디버깅용)
TestFieldMapper();
}
catch (System.Exception ex)
{
Debug.WriteLine($"❌ FieldMapper 로딩 오류:");
Debug.WriteLine($" 메시지: {ex.Message}");
Debug.WriteLine($" 예외 타입: {ex.GetType().Name}");
if (ex.InnerException != null)
{
Debug.WriteLine($" 내부 예외: {ex.InnerException.Message}");
}
Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
throw;
}
Debug.WriteLine("🔄 ODA 초기화 중...");
InitializeTeighaServices();
Debug.WriteLine("🔄 Excel 초기화 중...");
InitializeExcel();
}
// 필드 매퍼 테스트
private void TestFieldMapper()
{
Debug.WriteLine("[DEBUG] Testing field mapper...");
// 몇 가지 알려진 expressway 필드로 테스트
var testFields = new[] { "TD_DNAME_MAIN", "TD_DWGNO", "TD_DWGCODE", "TB_MTITIL"};
foreach (var field in testFields)
{
var aiLabel = fieldMapper.ExpresswayToAilabel(field);
var docAiKey = fieldMapper.AilabelToDocAiKey(aiLabel);
Debug.WriteLine($"[DEBUG] Field: {field} -> AILabel: {aiLabel} -> DocAiKey: {docAiKey}");
}
}
// Teigha 서비스 초기화 (싱글톤 사용)
private void InitializeTeighaServices()
{
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 애플리케이션 및 워크시트 초기화
private void InitializeExcel()
{
try
{
var excelApp = new Excel.Application();
excelApplication = excelApp;
excelApplication.Visible = false; // WPF에서는 숨김 처리
Excel.Workbook workbook = excelApp.Workbooks.Add();
workbook1 = workbook;
// Title Block Sheet 설정 (기본 Sheet1)
titleBlockSheet = (Excel.Worksheet)workbook.Sheets[1];
titleBlockSheet.Name = "Title Block";
SetupTitleBlockHeaders();
// Text Entities Sheet 추가
textEntitiesSheet = (Excel.Worksheet)workbook.Sheets.Add();
textEntitiesSheet.Name = "Text Entities";
SetupTextEntitiesHeaders();
// 매핑 데이터용 워크북 및 시트 생성
mappingWorkbook = excelApp.Workbooks.Add();
mappingSheet = (Excel.Worksheet)mappingWorkbook.Sheets[1];
mappingSheet.Name = "Mapping Data";
SetupMappingHeaders();
}
catch (System.Exception ex)
{
Debug.WriteLine($"Excel 초기화 중 오류 발생: {ex.Message}");
ReleaseExcelObjects();
throw;
}
}
// Title Block 시트 헤더 설정
private void SetupTitleBlockHeaders()
{
titleBlockSheet.Cells[1, 1] = "Type"; // 예: AttributeReference, AttributeDefinition
titleBlockSheet.Cells[1, 2] = "Name"; // BlockReference 이름 또는 BlockDefinition 이름
titleBlockSheet.Cells[1, 3] = "Tag"; // Attribute Tag
titleBlockSheet.Cells[1, 4] = "Prompt"; // Attribute Prompt
titleBlockSheet.Cells[1, 5] = "Value"; // Attribute 값 (TextString)
titleBlockSheet.Cells[1, 6] = "Path"; // 원본 DWG 파일 전체 경로
titleBlockSheet.Cells[1, 7] = "FileName"; // 원본 DWG 파일 이름만
// 헤더 행 스타일
Excel.Range headerRange = titleBlockSheet.Range["A1:G1"];
headerRange.Font.Bold = true;
headerRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightBlue);
}
// Text Entities 시트 헤더 설정
private void SetupTextEntitiesHeaders()
{
textEntitiesSheet.Cells[1, 1] = "Type"; // DBText, MText
textEntitiesSheet.Cells[1, 2] = "Layer"; // Layer 이름
textEntitiesSheet.Cells[1, 3] = "Text"; // 실제 텍스트 내용
textEntitiesSheet.Cells[1, 4] = "Path"; // 원본 DWG 파일 전체 경로
textEntitiesSheet.Cells[1, 5] = "FileName"; // 원본 DWG 파일 이름만
// 헤더 행 스타일
Excel.Range headerRange = textEntitiesSheet.Range["A1:E1"];
headerRange.Font.Bold = true;
headerRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightGreen);
}
// 매핑 데이터 시트 헤더 설정
private void SetupMappingHeaders()
{
mappingSheet.Cells[1, 1] = "FileName"; // 파일 이름
mappingSheet.Cells[1, 2] = "MapKey"; // 매핑 키
mappingSheet.Cells[1, 3] = "AILabel"; // AI 라벨
mappingSheet.Cells[1, 4] = "DwgTag"; // DWG Tag
mappingSheet.Cells[1, 5] = "Att_value"; // DWG 값
mappingSheet.Cells[1, 6] = "Pdf_value"; // PDF 값 (현재는 빈 값)
// 헤더 행 스타일
Excel.Range headerRange = mappingSheet.Range["A1:F1"];
headerRange.Font.Bold = true;
headerRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightYellow);
}
/// <summary>
/// 단일 DWG 파일에서 AttributeReference/AttributeDefinition 데이터를 추출하여
/// 초기화된 Excel 워크시트에 추가합니다.
/// </summary>
/// <param name="filePath">처리할 DWG 파일 경로</param>
/// <param name="progress">진행 상태 보고를 위한 IProgress 객체</param>
/// <param name="cancellationToken">작업 취소를 위한 CancellationToken</param>
/// <returns>성공 시 true, 실패 시 false 반환</returns>
public bool ExportDwgToExcel(string filePath, IProgress<double> progress = null, CancellationToken cancellationToken = default)
{
Debug.WriteLine($"[DEBUG] ExportDwgToExcel 시작: {filePath}");
if (excelApplication == null)
{
Debug.WriteLine("❌ Excel이 초기화되지 않았습니다.");
return false;
}
if (!File.Exists(filePath))
{
Debug.WriteLine($"❌ 파일이 존재하지 않습니다: {filePath}");
return false;
}
try
{
Debug.WriteLine("[DEBUG] 진행률 0% 보고");
progress?.Report(0);
cancellationToken.ThrowIfCancellationRequested();
Debug.WriteLine("[DEBUG] ODA Database 객체 생성 중...");
// ODA Database 객체 생성 및 DWG 파일 읽기
using (var database = new Database(false, true))
{
Debug.WriteLine($"[DEBUG] DWG 파일 읽기 시도: {filePath}");
database.ReadDwgFile(filePath, FileOpenMode.OpenForReadAndWriteNoShare, false, null);
Debug.WriteLine("[DEBUG] DWG 파일 읽기 성공");
cancellationToken.ThrowIfCancellationRequested();
Debug.WriteLine("[DEBUG] 진행률 10% 보고");
progress?.Report(10);
Debug.WriteLine("[DEBUG] 트랜잭션 시작 중...");
using (var tran = database.TransactionManager.StartTransaction())
{
Debug.WriteLine("[DEBUG] BlockTable 접근 중...");
var bt = tran.GetObject(database.BlockTableId, OpenMode.ForRead) as BlockTable;
Debug.WriteLine("[DEBUG] ModelSpace 접근 중...");
using (var btr = tran.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead) as BlockTableRecord)
{
int totalEntities = btr.Cast<ObjectId>().Count();
Debug.WriteLine($"[DEBUG] 총 엔티티 수: {totalEntities}");
int processedCount = 0;
foreach (ObjectId entId in btr)
{
cancellationToken.ThrowIfCancellationRequested();
using (var ent = tran.GetObject(entId, OpenMode.ForRead) as Entity)
{
// Layer 이름 가져오기 (공통)
string layerName = GetLayerName(ent.LayerId, tran, database);
var fileName = Path.GetFileNameWithoutExtension(database.Filename);
// 파일명 유효성 검사
if (string.IsNullOrEmpty(fileName))
{
fileName = "Unknown_File";
Debug.WriteLine($"[DEBUG] Using default filename: {fileName}");
}
// 파일별 매핑 딕셔너리 초기화
if (!FileToMapkeyToLabelTagValuePdf.ContainsKey(fileName))
{
FileToMapkeyToLabelTagValuePdf[fileName] = new Dictionary<string, (string, string, string, string)>();
}
// AttributeDefinition 추출
if (ent is AttributeDefinition attDef)
{
titleBlockSheet.Cells[titleBlockCurrentRow, 1] = attDef.GetType().Name;
titleBlockSheet.Cells[titleBlockCurrentRow, 2] = attDef.BlockName;
titleBlockSheet.Cells[titleBlockCurrentRow, 3] = attDef.Tag;
titleBlockSheet.Cells[titleBlockCurrentRow, 4] = attDef.Prompt;
titleBlockSheet.Cells[titleBlockCurrentRow, 5] = attDef.TextString;
titleBlockSheet.Cells[titleBlockCurrentRow, 6] = database.Filename;
titleBlockSheet.Cells[titleBlockCurrentRow, 7] = Path.GetFileName(database.Filename);
titleBlockCurrentRow++;
var tag = attDef.Tag;
var aiLabel = fieldMapper.ExpresswayToAilabel(tag);
var mapKey = fieldMapper.AilabelToDocAiKey(aiLabel);
var attValue = attDef.TextString;
Debug.WriteLine($"[DEBUG] AttributeDefinition - Tag: {tag}, AILabel: {aiLabel}, MapKey: {mapKey}");
// 매핑 데이터 저장
if (!string.IsNullOrEmpty(aiLabel))
{
// mapKey가 null이면 aiLabel을 mapKey로 사용
var finalMapKey = mapKey ?? aiLabel;
FileToMapkeyToLabelTagValuePdf[fileName][finalMapKey] = (aiLabel, tag, attValue, "");
Debug.WriteLine($"[DEBUG] Added mapping: {tag} -> {aiLabel} (MapKey: {finalMapKey})");
}
else
{
// aiLabel이 null이면 tag를 사용하여 저장
var finalMapKey = mapKey ?? tag;
if (!string.IsNullOrEmpty(finalMapKey))
{
FileToMapkeyToLabelTagValuePdf[fileName][finalMapKey] = (tag, tag, attValue, "");
Debug.WriteLine($"[DEBUG] Added unmapped tag: {tag} -> {tag} (MapKey: {finalMapKey})");
}
else
{
Debug.WriteLine($"[DEBUG] Skipped empty tag for AttributeDefinition");
}
}
}
// BlockReference 및 그 안의 AttributeReference 추출
else if (ent is BlockReference blr)
{
foreach (ObjectId attId in blr.AttributeCollection)
{
cancellationToken.ThrowIfCancellationRequested();
using (var attRef = tran.GetObject(attId, OpenMode.ForRead) as AttributeReference)
{
if (attRef != null && attRef.TextString.Trim() != "")
{
titleBlockSheet.Cells[titleBlockCurrentRow, 1] = attRef.GetType().Name;
titleBlockSheet.Cells[titleBlockCurrentRow, 2] = blr.Name;
titleBlockSheet.Cells[titleBlockCurrentRow, 3] = attRef.Tag;
titleBlockSheet.Cells[titleBlockCurrentRow, 4] = GetPromptFromAttributeReference(tran, blr, attRef.Tag);
titleBlockSheet.Cells[titleBlockCurrentRow, 5] = attRef.TextString;
titleBlockSheet.Cells[titleBlockCurrentRow, 6] = database.Filename;
titleBlockSheet.Cells[titleBlockCurrentRow, 7] = Path.GetFileName(database.Filename);
titleBlockCurrentRow++;
var tag = attRef.Tag;
var aiLabel = fieldMapper.ExpresswayToAilabel(tag);
if (aiLabel == null) continue;
var mapKey = fieldMapper.AilabelToDocAiKey(aiLabel);
var attValue = attRef.TextString;
Debug.WriteLine($"[DEBUG] AttributeReference - Tag: {tag}, AILabel: {aiLabel}, MapKey: {mapKey}");
// 매핑 데이터 저장
if (!string.IsNullOrEmpty(aiLabel))
{
// mapKey가 null이면 aiLabel을 mapKey로 사용
var finalMapKey = mapKey ?? aiLabel;
FileToMapkeyToLabelTagValuePdf[fileName][finalMapKey] = (aiLabel, tag, attValue, "");
Debug.WriteLine($"[DEBUG] Added mapping: {tag} -> {aiLabel} (MapKey: {finalMapKey})");
}
else
{
// aiLabel이 null이면 tag를 사용하여 저장
var finalMapKey = mapKey ?? tag;
if (!string.IsNullOrEmpty(finalMapKey))
{
FileToMapkeyToLabelTagValuePdf[fileName][finalMapKey] = (tag, tag, attValue, "");
Debug.WriteLine($"[DEBUG] Added unmapped tag: {tag} -> {tag} (MapKey: {finalMapKey})");
}
else
{
Debug.WriteLine($"[DEBUG] Skipped empty tag for AttributeReference");
}
}
}
}
}
}
// DBText 엔티티 추출 (별도 시트)
else if (ent is DBText dbText)
{
textEntitiesSheet.Cells[textEntitiesCurrentRow, 1] = "DBText"; // Type
textEntitiesSheet.Cells[textEntitiesCurrentRow, 2] = layerName; // Layer
textEntitiesSheet.Cells[textEntitiesCurrentRow, 3] = dbText.TextString; // Text
textEntitiesSheet.Cells[textEntitiesCurrentRow, 4] = database.Filename; // Path
textEntitiesSheet.Cells[textEntitiesCurrentRow, 5] = Path.GetFileName(database.Filename); // FileName
textEntitiesCurrentRow++;
}
// MText 엔티티 추출 (별도 시트)
else if (ent is MText mText)
{
textEntitiesSheet.Cells[textEntitiesCurrentRow, 1] = "MText"; // Type
textEntitiesSheet.Cells[textEntitiesCurrentRow, 2] = layerName; // Layer
textEntitiesSheet.Cells[textEntitiesCurrentRow, 3] = mText.Contents; // Text
textEntitiesSheet.Cells[textEntitiesCurrentRow, 4] = database.Filename; // Path
textEntitiesSheet.Cells[textEntitiesCurrentRow, 5] = Path.GetFileName(database.Filename); // FileName
textEntitiesCurrentRow++;
}
}
// write values in new excel file with FileToMapkeyToLabelTag
processedCount++;
double currentProgress = 10.0 + (double)processedCount / totalEntities * 80.0;
progress?.Report(Math.Min(currentProgress, 90.0));
}
}
tran.Commit();
}
Debug.WriteLine($"[DEBUG] Transaction committed. Total mapping entries collected: {FileToMapkeyToLabelTagValuePdf.Sum(f => f.Value.Count)}");
// 매핑 데이터를 Excel 시트에 기록
Debug.WriteLine("[DEBUG] 매핑 데이터를 Excel에 기록 중...");
WriteMappingDataToExcel();
Debug.WriteLine("[DEBUG] 매핑 데이터 Excel 기록 완료");
}
progress?.Report(100);
return true;
}
catch (OperationCanceledException)
{
Debug.WriteLine("❌ 작업이 취소되었습니다.");
progress?.Report(0);
return false;
}
catch (Teigha.Runtime.Exception ex)
{
progress?.Report(0);
Debug.WriteLine($"❌ DWG 파일 처리 중 Teigha 오류 발생:");
Debug.WriteLine($" 메시지: {ex.Message}");
Debug.WriteLine($" ErrorStatus: {ex.ErrorStatus}");
if (ex.InnerException != null)
{
Debug.WriteLine($" 내부 예외: {ex.InnerException.Message}");
}
Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
return false;
}
catch (System.Exception ex)
{
progress?.Report(0);
Debug.WriteLine($"❌ 일반 오류 발생:");
Debug.WriteLine($" 메시지: {ex.Message}");
Debug.WriteLine($" 예외 타입: {ex.GetType().Name}");
if (ex.InnerException != null)
{
Debug.WriteLine($" 내부 예외: {ex.InnerException.Message}");
}
Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
return false;
}
}
// 매핑 데이터를 Excel 시트에 기록
private void WriteMappingDataToExcel()
{
try
{
int currentRow = 2; // 헤더 다음 행부터 시작
Debug.WriteLine($"[DEBUG] Writing mapping data to Excel. Total files: {FileToMapkeyToLabelTagValuePdf.Count}");
Debug.WriteLine($"[DEBUG] 시작 행: {currentRow}");
foreach (var fileEntry in FileToMapkeyToLabelTagValuePdf)
{
string fileName = fileEntry.Key;
var mappingData = fileEntry.Value;
Debug.WriteLine($"[DEBUG] Processing file: {fileName}, entries: {mappingData.Count}");
foreach (var mapEntry in mappingData)
{
string mapKey = mapEntry.Key;
(string aiLabel, string dwgTag, string attValue, string pdfValue) = mapEntry.Value;
// null 값 방지
if (string.IsNullOrEmpty(fileName) || string.IsNullOrEmpty(mapKey))
{
Debug.WriteLine($"[DEBUG] Skipping entry with null/empty values: fileName={fileName}, mapKey={mapKey}");
continue;
}
Debug.WriteLine($"[DEBUG] Writing row {currentRow}: {fileName} | {mapKey} | {aiLabel} | {dwgTag} | PDF: {pdfValue}");
try
{
mappingSheet.Cells[currentRow, 1] = fileName; // FileName
mappingSheet.Cells[currentRow, 2] = mapKey; // MapKey
mappingSheet.Cells[currentRow, 3] = aiLabel ?? ""; // AILabel
mappingSheet.Cells[currentRow, 4] = dwgTag ?? ""; // DwgTag
mappingSheet.Cells[currentRow, 5] = attValue ?? ""; // DwgValue (Att_value)
mappingSheet.Cells[currentRow, 6] = pdfValue ?? ""; // PdfValue (Pdf_value)
Debug.WriteLine($"[DEBUG] Row {currentRow} written successfully");
}
catch (System.Exception ex)
{
Debug.WriteLine($"❌ Error writing row {currentRow}: {ex.Message}");
}
currentRow++;
}
}
Debug.WriteLine($"[DEBUG] Mapping data written to Excel. Total rows: {currentRow - 2}");
Debug.WriteLine($"[DEBUG] 최종 행 번호: {currentRow}");
}
catch (System.Exception ex)
{
Debug.WriteLine($"❌ 매핑 데이터 Excel 기록 중 오류:");
Debug.WriteLine($" 메시지: {ex.Message}");
Debug.WriteLine($" 예외 타입: {ex.GetType().Name}");
if (ex.InnerException != null)
{
Debug.WriteLine($" 내부 예외: {ex.InnerException.Message}");
}
Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
throw; // 상위 메서드로 예외 전파
}
}
/// <summary>
/// 기존 Excel 파일을 열어 JSON 파일의 PDF 분석 결과로 매핑 시트를 업데이트합니다.
/// </summary>
/// <param name="excelFilePath">기존 Excel 파일 경로</param>
/// <param name="jsonFilePath">PDF 분석 결과 JSON 파일 경로</param>
/// <returns>성공 시 true, 실패 시 false</returns>
public bool UpdateExistingExcelWithJson(string excelFilePath, string jsonFilePath)
{
try
{
Debug.WriteLine($"[DEBUG] 기존 Excel 파일 업데이트 시작: {excelFilePath}");
if (!File.Exists(excelFilePath))
{
Debug.WriteLine($"❌ Excel 파일이 존재하지 않습니다: {excelFilePath}");
return false;
}
if (!File.Exists(jsonFilePath))
{
Debug.WriteLine($"❌ JSON 파일이 존재하지 않습니다: {jsonFilePath}");
return false;
}
Debug.WriteLine($"[DEBUG] Excel 애플리케이션 초기화 중...");
// 기존 Excel 파일 열기
if (excelApplication == null)
{
excelApplication = new Excel.Application();
excelApplication.Visible = false;
Debug.WriteLine("[DEBUG] 새 Excel 애플리케이션 생성됨");
}
Debug.WriteLine($"[DEBUG] Excel 파일 열기 시도: {excelFilePath}");
mappingWorkbook = excelApplication.Workbooks.Open(excelFilePath);
Debug.WriteLine("[DEBUG] Excel 파일 열기 성공");
Debug.WriteLine("[DEBUG] 'Mapping Data' 시트 찾는 중...");
mappingSheet = (Excel.Worksheet)mappingWorkbook.Sheets["Mapping Data"];
if (mappingSheet == null)
{
Debug.WriteLine("❌ 'Mapping Data' 시트를 찾을 수 없습니다.");
// 사용 가능한 시트 목록 출력
Debug.WriteLine("사용 가능한 시트:");
foreach (Excel.Worksheet sheet in mappingWorkbook.Sheets)
{
Debug.WriteLine($" - {sheet.Name}");
}
return false;
}
Debug.WriteLine("✅ 기존 Excel 파일 열기 성공");
// JSON에서 PDF 값 업데이트
Debug.WriteLine("[DEBUG] JSON 파싱 및 업데이트 시작");
bool result = UpdateMappingSheetFromJson(jsonFilePath);
Debug.WriteLine($"[DEBUG] 업데이트 결과: {result}");
return result;
}
catch (System.Exception ex)
{
Debug.WriteLine($"❌ 기존 Excel 파일 업데이트 중 오류:");
Debug.WriteLine($" 메시지: {ex.Message}");
Debug.WriteLine($" 예외 타입: {ex.GetType().Name}");
if (ex.InnerException != null)
{
Debug.WriteLine($" 내부 예외: {ex.InnerException.Message}");
}
Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
return false;
}
}
/// <summary>
/// JSON 파일에서 PDF 분석 결과를 읽어 Excel 매핑 시트의 Pdf_value 컬럼을 업데이트합니다.
/// </summary>
/// <param name="jsonFilePath">PDF 분석 결과 JSON 파일 경로</param>
/// <returns>성공 시 true, 실패 시 false</returns>
public bool UpdateMappingSheetFromJson(string jsonFilePath)
{
try
{
Debug.WriteLine($"[DEBUG] JSON 파일에서 PDF 값 업데이트 시작: {jsonFilePath}");
if (!File.Exists(jsonFilePath))
{
Debug.WriteLine($"❌ JSON 파일이 존재하지 않습니다: {jsonFilePath}");
return false;
}
if (mappingSheet == null)
{
Debug.WriteLine("❌ 매핑 시트가 초기화되지 않았습니다.");
return false;
}
// 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;
}
int updatedCount = 0;
int totalEntries = 0;
foreach (JObject result in results)
{
var fileInfo = result["file_info"];
var pdfAnalysis = result["pdf_analysis"];
if (fileInfo == null || pdfAnalysis == null) continue;
string fileName = fileInfo["name"]?.ToString();
if (string.IsNullOrEmpty(fileName)) continue;
// 파일 확장자 제거
string fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileName);
Debug.WriteLine($"[DEBUG] Processing PDF file: {fileNameWithoutExt}");
// PDF 분석 결과의 각 필드 처리
foreach (var property in pdfAnalysis.Cast<JProperty>())
{
string aiLabel = property.Name; // 예: "설계공구_Station_col1"
var valueObj = property.Value as JObject;
if (valueObj == null) continue;
string pdfValue = valueObj["value"]?.ToString();
if (string.IsNullOrEmpty(pdfValue)) continue;
totalEntries++;
Debug.WriteLine($"[DEBUG] Searching for match: FileName={fileNameWithoutExt}, AILabel={aiLabel}, Value={pdfValue}");
// Excel 시트에서 매칭되는 행 찾기 및 업데이트
if (UpdateExcelRow(fileNameWithoutExt, aiLabel, pdfValue))
{
updatedCount++;
Debug.WriteLine($"✅ Updated: {fileNameWithoutExt} -> {aiLabel} = {pdfValue}");
}
else
{
Debug.WriteLine($"⚠️ No match found: {fileNameWithoutExt} -> {aiLabel}");
}
}
}
Debug.WriteLine($"[DEBUG] PDF 값 업데이트 완료: {updatedCount}/{totalEntries} 업데이트됨");
return true;
}
catch (System.Exception ex)
{
Debug.WriteLine($"❌ JSON에서 PDF 값 업데이트 중 오류:");
Debug.WriteLine($" 메시지: {ex.Message}");
Debug.WriteLine($" 예외 타입: {ex.GetType().Name}");
if (ex.InnerException != null)
{
Debug.WriteLine($" 내부 예외: {ex.InnerException.Message}");
}
Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
return false;
}
}
/// <summary>
/// Excel 매핑 시트에서 FileName과 AILabel이 매칭되는 행을 찾아 Pdf_value를 업데이트합니다.
/// </summary>
/// <param name="fileName">파일명 (확장자 제외)</param>
/// <param name="aiLabel">AI 라벨 (예: "설계공구_Station_col1")</param>
/// <param name="pdfValue">PDF에서 추출된 값</param>
/// <returns>업데이트 성공 시 true</returns>
private bool UpdateExcelRow(string fileName, string aiLabel, string pdfValue)
{
try
{
// Excel 시트의 마지막 사용된 행 찾기
Excel.Range usedRange = mappingSheet.UsedRange;
if (usedRange == null) return false;
int lastRow = usedRange.Rows.Count;
// 2행부터 검색 (1행은 헤더)
for (int row = 2; row <= lastRow; row++)
{
// Column 1: FileName, Column 3: AILabel 확인
var cellFileName = mappingSheet.Cells[row, 1]?.Value?.ToString() ?? "";
var cellAiLabel = mappingSheet.Cells[row, 3]?.Value?.ToString() ?? "";
Debug.WriteLine($"[DEBUG] Row {row}: FileName='{cellFileName}', AILabel='{cellAiLabel}'");
// 매칭 확인 (대소문자 구분 없이)
if (string.Equals(cellFileName.Trim(), fileName.Trim(), StringComparison.OrdinalIgnoreCase) &&
string.Equals(cellAiLabel.Trim(), aiLabel.Trim(), StringComparison.OrdinalIgnoreCase))
{
// Column 6: Pdf_value에 값 기록
mappingSheet.Cells[row, 6] = pdfValue;
Debug.WriteLine($"[DEBUG] Updated row {row}, column 6 with value: {pdfValue}");
return true;
}
}
return false; // 매칭되는 행을 찾지 못함
}
catch (System.Exception ex)
{
Debug.WriteLine($"❌ Excel 행 업데이트 중 오류: {ex.Message}");
return false;
}
}
// Paste the helper function from above here
public string GetPromptFromAttributeReference(Transaction tr, BlockReference blockref, string tag)
{
string prompt = null;
BlockTableRecord blockDef = tr.GetObject(blockref.BlockTableRecord, OpenMode.ForRead) as BlockTableRecord;
if (blockDef == null) return null;
foreach (ObjectId objId in blockDef)
{
AttributeDefinition attDef = tr.GetObject(objId, OpenMode.ForRead) as AttributeDefinition;
if (attDef != null)
{
if (attDef.Tag.Equals(tag, System.StringComparison.OrdinalIgnoreCase))
{
prompt = attDef.Prompt;
break;
}
}
}
return prompt;
}
/// <summary>
/// 현재 열려있는 Excel 워크북을 저장합니다.
/// </summary>
/// <returns>성공 시 true, 실패 시 false</returns>
public bool SaveExcel()
{
try
{
if (mappingWorkbook != null)
{
mappingWorkbook.Save();
Debug.WriteLine("✅ Excel 파일 저장 완료");
return true;
}
if (workbook1 != null)
{
workbook1.Save();
Debug.WriteLine("✅ Excel 파일 저장 완료");
return true;
}
Debug.WriteLine("❌ 저장할 워크북이 없습니다.");
return false;
}
catch (System.Exception ex)
{
Debug.WriteLine($"❌ Excel 파일 저장 중 오류: {ex.Message}");
return false;
}
}
/// <summary>
/// 매핑 워크북만 저장하고 닫습니다 (완전한 매핑용).
/// </summary>
/// <param name="savePath">저장할 파일 경로</param>
public void SaveMappingWorkbookOnly(string savePath)
{
try
{
Debug.WriteLine($"[DEBUG] 매핑 워크북 저장 시작: {savePath}");
if (mappingWorkbook == null)
{
Debug.WriteLine("❌ 매핑 워크북이 초기화되지 않았습니다.");
return;
}
string directory = Path.GetDirectoryName(savePath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
// 매핑 워크북만 저장 (Excel 2007+ 형식으로)
mappingWorkbook.SaveAs(savePath,
FileFormat: Excel.XlFileFormat.xlOpenXMLWorkbook,
AccessMode: Excel.XlSaveAsAccessMode.xlNoChange);
Debug.WriteLine($"✅ 매핑 워크북 저장 완료: {Path.GetFileName(savePath)}");
}
catch (System.Exception ex)
{
Debug.WriteLine($"❌ 매핑 워크북 저장 중 오류: {ex.Message}");
Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
throw;
}
}
/// <summary>
/// 현재 Excel 워크북을 지정된 경로에 저장하고 Excel 애플리케이션을 종료합니다.
/// </summary>
/// <param name="savePath">Excel 파일을 저장할 전체 경로</param>
public void SaveAndCloseExcel(string savePath)
{
if (workbook1 == null) return;
try
{
string directory = Path.GetDirectoryName(savePath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
workbook1.SaveAs(savePath, AccessMode: Excel.XlSaveAsAccessMode.xlNoChange);
// 매핑 데이터 워크북도 저장
if (mappingWorkbook != null)
{
string mappingPath = Path.Combine(directory, Path.GetFileNameWithoutExtension(savePath) + "_Mapping.xlsx");
mappingWorkbook.SaveAs(mappingPath, AccessMode: Excel.XlSaveAsAccessMode.xlNoChange);
}
}
catch (System.Exception ex)
{
Debug.WriteLine($"Excel 파일 저장 중 오류 발생: {ex.Message}");
}
finally
{
CloseExcelObjects();
}
}
/// <summary>
/// Excel 객체들을 닫습니다 (저장하지 않음).
/// </summary>
public void CloseExcelObjectsWithoutSaving()
{
try
{
Debug.WriteLine("[DEBUG] Excel 객체 정리 시작");
if (workbook1 != null)
{
try { workbook1.Close(false); }
catch { }
}
if (mappingWorkbook != null)
{
try { mappingWorkbook.Close(false); }
catch { }
}
if (excelApplication != null)
{
try { excelApplication.Quit(); }
catch { }
}
ReleaseExcelObjects();
Debug.WriteLine("✅ Excel 객체 정리 완료");
}
catch (System.Exception ex)
{
Debug.WriteLine($"❌ Excel 객체 정리 중 오류: {ex.Message}");
}
}
private void CloseExcelObjects()
{
if (workbook1 != null)
{
try { workbook1.Close(false); }
catch { }
}
if (mappingWorkbook != null)
{
try { mappingWorkbook.Close(false); }
catch { }
}
if (excelApplication != null)
{
try { excelApplication.Quit(); }
catch { }
}
ReleaseExcelObjects();
}
private void ReleaseExcelObjects()
{
ReleaseComObject(titleBlockSheet);
ReleaseComObject(textEntitiesSheet);
ReleaseComObject(mappingSheet);
ReleaseComObject(workbook1);
ReleaseComObject(mappingWorkbook);
ReleaseComObject(excelApplication);
titleBlockSheet = null;
textEntitiesSheet = null;
mappingSheet = null;
workbook1 = null;
mappingWorkbook = null;
excelApplication = null;
}
private void ReleaseComObject(object obj)
{
try
{
if (obj != null && Marshal.IsComObject(obj))
{
Marshal.ReleaseComObject(obj);
}
}
catch (System.Exception)
{
// 해제 중 오류 발생 시 무시
}
finally
{
obj = null;
}
}
/// <summary>
/// Layer ID로부터 Layer 이름을 가져옵니다.
/// </summary>
/// <param name="layerId">Layer ObjectId</param>
/// <param name="transaction">현재 트랜잭션</param>
/// <param name="database">데이터베이스 객체</param>
/// <returns>Layer 이름 또는 빈 문자열</returns>
private string GetLayerName(ObjectId layerId, Transaction transaction, Database database)
{
try
{
using (var layerTableRecord = transaction.GetObject(layerId, OpenMode.ForRead) as LayerTableRecord)
{
return layerTableRecord?.Name ?? "";
}
}
catch (System.Exception ex)
{
Debug.WriteLine($"Layer 이름 가져오기 오류: {ex.Message}");
return "";
}
}
/// <summary>
/// 매핑 딕셔너리를 JSON 파일로 저장합니다.
/// </summary>
/// <param name="filePath">저장할 JSON 파일 경로</param>
public void SaveMappingDictionary(string filePath)
{
try
{
Debug.WriteLine($"[DEBUG] 매핑 딕셔너리 저장 시작: {filePath}");
// 딕셔너리를 직렬화 가능한 형태로 변환
var serializableData = new Dictionary<string, Dictionary<string, object>>();
foreach (var fileEntry in FileToMapkeyToLabelTagValuePdf)
{
var fileData = new Dictionary<string, object>();
foreach (var mapEntry in fileEntry.Value)
{
fileData[mapEntry.Key] = new
{
AILabel = mapEntry.Value.Item1,
DwgTag = mapEntry.Value.Item2,
DwgValue = mapEntry.Value.Item3,
PdfValue = mapEntry.Value.Item4
};
}
serializableData[fileEntry.Key] = fileData;
}
// JSON으로 직렬화
string jsonContent = JsonConvert.SerializeObject(serializableData, Formatting.Indented);
File.WriteAllText(filePath, jsonContent, System.Text.Encoding.UTF8);
Debug.WriteLine($"✅ 매핑 딕셔너리 저장 완료: {Path.GetFileName(filePath)}");
Debug.WriteLine($"📊 저장된 파일 수: {FileToMapkeyToLabelTagValuePdf.Count}");
}
catch (System.Exception ex)
{
Debug.WriteLine($"❌ 매핑 딕셔너리 저장 중 오류:");
Debug.WriteLine($" 메시지: {ex.Message}");
Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
throw;
}
}
/// <summary>
/// JSON 파일에서 매핑 딕셔너리를 로드합니다.
/// </summary>
/// <param name="filePath">로드할 JSON 파일 경로</param>
public void LoadMappingDictionary(string filePath)
{
try
{
Debug.WriteLine($"[DEBUG] 매핑 딕셔너리 로드 시작: {filePath}");
if (!File.Exists(filePath))
{
Debug.WriteLine($"⚠️ 매핑 파일이 존재하지 않습니다: {filePath}");
return;
}
string jsonContent = File.ReadAllText(filePath, System.Text.Encoding.UTF8);
Debug.WriteLine($"[DEBUG] JSON 내용 길이: {jsonContent.Length}");
// JSON 내용 정리 (주석 제거 등)
jsonContent = CleanJsonContent(jsonContent);
Dictionary<string, Dictionary<string, JObject>> deserializedData;
try
{
deserializedData = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, JObject>>>(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();
Debug.WriteLine("[DEBUG] 기존 딕셔너리 초기화됨");
if (deserializedData != null)
{
Debug.WriteLine($"[DEBUG] 역직렬화된 파일 수: {deserializedData.Count}");
foreach (var fileEntry in deserializedData)
{
Debug.WriteLine($"[DEBUG] 파일 처리 중: {fileEntry.Key}");
var fileData = new Dictionary<string, (string, string, string, string)>();
foreach (var mapEntry in fileEntry.Value)
{
var valueObj = mapEntry.Value;
string aiLabel = valueObj["AILabel"]?.ToString() ?? "";
string dwgTag = valueObj["DwgTag"]?.ToString() ?? "";
string dwgValue = valueObj["DwgValue"]?.ToString() ?? "";
string pdfValue = valueObj["PdfValue"]?.ToString() ?? "";
fileData[mapEntry.Key] = (aiLabel, dwgTag, dwgValue, pdfValue);
Debug.WriteLine($"[DEBUG] 항목 로드: {mapEntry.Key} -> AI:{aiLabel}, DWG:{dwgTag}, PDF:{pdfValue}");
}
FileToMapkeyToLabelTagValuePdf[fileEntry.Key] = fileData;
Debug.WriteLine($"[DEBUG] 파일 {fileEntry.Key}: {fileData.Count}개 항목 로드됨");
}
}
Debug.WriteLine($"✅ 매핑 딕셔너리 로드 완료");
Debug.WriteLine($"📊 로드된 파일 수: {FileToMapkeyToLabelTagValuePdf.Count}");
Debug.WriteLine($"📊 총 항목 수: {FileToMapkeyToLabelTagValuePdf.Sum(f => f.Value.Count)}");
}
catch (System.Exception ex)
{
Debug.WriteLine($"❌ 매핑 딕셔너리 로드 중 오류:");
Debug.WriteLine($" 메시지: {ex.Message}");
Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
throw;
}
}
/// <summary>
/// 매핑 딕셔너리 데이터를 Excel 시트에 기록합니다 (완전한 매핑용).
/// </summary>
public void WriteCompleteMapping()
{
try
{
Debug.WriteLine("[DEBUG] WriteCompleteMapping 시작");
Debug.WriteLine($"[DEBUG] 매핑 딕셔너리 항목 수: {FileToMapkeyToLabelTagValuePdf.Count}");
if (FileToMapkeyToLabelTagValuePdf.Count == 0)
{
Debug.WriteLine("⚠️ 매핑 딕셔너리가 비어있습니다.");
return;
}
if (mappingSheet == null)
{
Debug.WriteLine("❌ 매핑 시트가 초기화되지 않았습니다.");
return;
}
Debug.WriteLine($"[DEBUG] 매핑 시트 이름: {mappingSheet.Name}");
Debug.WriteLine($"[DEBUG] 전체 데이터 항목 수: {FileToMapkeyToLabelTagValuePdf.Sum(f => f.Value.Count)}");
// 샘플 데이터 출력 (처음 3개)
int sampleCount = 0;
foreach (var fileEntry in FileToMapkeyToLabelTagValuePdf.Take(2))
{
Debug.WriteLine($"[DEBUG] 파일: {fileEntry.Key}");
foreach (var mapEntry in fileEntry.Value.Take(3))
{
var (aiLabel, dwgTag, dwgValue, pdfValue) = mapEntry.Value;
Debug.WriteLine($"[DEBUG] {mapEntry.Key} -> AI:{aiLabel}, DWG:{dwgTag}, DWGVAL:{dwgValue}, PDF:{pdfValue}");
sampleCount++;
}
}
// 기존 WriteMappingDataToExcel 메서드 호출
WriteMappingDataToExcel();
Debug.WriteLine("✅ WriteCompleteMapping 완료");
}
catch (System.Exception ex)
{
Debug.WriteLine($"❌ WriteCompleteMapping 중 오류:");
Debug.WriteLine($" 메시지: {ex.Message}");
Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
throw;
}
}
/// <summary>
/// PDF 분석 결과 JSON으로 매핑 딕셔너리의 PdfValue를 업데이트합니다.
/// </summary>
/// <param name="jsonFilePath">PDF 분석 결과 JSON 파일 경로</param>
public void UpdateWithPdfData(string jsonFilePath)
{
try
{
Debug.WriteLine($"[DEBUG] PDF 데이터로 매핑 딕셔너리 업데이트 시작: {jsonFilePath}");
if (!File.Exists(jsonFilePath))
{
Debug.WriteLine($"❌ JSON 파일이 존재하지 않습니다: {jsonFilePath}");
return;
}
// 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;
}
int updatedCount = 0;
int totalEntries = 0;
foreach (JObject result in results)
{
var fileInfo = result["file_info"];
var pdfAnalysis = result["pdf_analysis"];
if (fileInfo == null || pdfAnalysis == null) continue;
string fileName = fileInfo["name"]?.ToString();
if (string.IsNullOrEmpty(fileName)) continue;
// 파일 확장자 제거
string fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileName);
Debug.WriteLine($"[DEBUG] Processing PDF file: {fileNameWithoutExt}");
// 해당 파일의 매핑 데이터 확인
if (!FileToMapkeyToLabelTagValuePdf.ContainsKey(fileNameWithoutExt))
{
Debug.WriteLine($"⚠️ 매핑 데이터에 파일이 없습니다: {fileNameWithoutExt}");
continue;
}
// PDF 분석 결과의 각 필드 처리
foreach (var property in pdfAnalysis.Cast<JProperty>())
{
string aiLabel = property.Name; // 예: "설계공구_Station_col1"
var valueObj = property.Value as JObject;
if (valueObj == null) continue;
string pdfValue = valueObj["value"]?.ToString();
if (string.IsNullOrEmpty(pdfValue)) continue;
totalEntries++;
// 매핑 딕셔너리에서 해당 항목 찾기
var fileData = FileToMapkeyToLabelTagValuePdf[fileNameWithoutExt];
// AILabel로 매칭 찾기
var matchingEntry = fileData.FirstOrDefault(kvp =>
string.Equals(kvp.Value.Item1.Trim(), aiLabel.Trim(), StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrEmpty(matchingEntry.Key))
{
// 기존 값 유지하면서 PdfValue만 업데이트
var existingValue = matchingEntry.Value;
fileData[matchingEntry.Key] = (existingValue.Item1, existingValue.Item2, existingValue.Item3, pdfValue);
updatedCount++;
Debug.WriteLine($"✅ Updated: {fileNameWithoutExt} -> {aiLabel} = {pdfValue}");
}
else
{
Debug.WriteLine($"⚠️ No match found: {fileNameWithoutExt} -> {aiLabel}");
}
}
}
Debug.WriteLine($"[DEBUG] PDF 데이터 업데이트 완료: {updatedCount}/{totalEntries} 업데이트됨");
}
catch (System.Exception ex)
{
Debug.WriteLine($"❌ PDF 데이터 업데이트 중 오류:");
Debug.WriteLine($" 메시지: {ex.Message}");
Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
throw;
}
}
/// <summary>
/// JSON 내용을 정리하여 파싱 가능한 상태로 만듭니다.
/// 주석 제거 및 기타 무효한 문자 처리
/// </summary>
/// <param name="jsonContent">원본 JSON 내용</param>
/// <returns>정리된 JSON 내용</returns>
private string CleanJsonContent(string jsonContent)
{
if (string.IsNullOrEmpty(jsonContent))
return jsonContent;
try
{
// 줄별로 처리하여 주석 제거
var lines = jsonContent.Split('\n');
var cleanedLines = new List<string>();
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()
{
try
{
Debug.WriteLine("[DEBUG] ExportExcel Dispose 시작");
if (excelApplication != null)
{
Debug.WriteLine("[DEBUG] Excel 객체 정리 중...");
CloseExcelObjects();
}
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)
{
Debug.WriteLine($"[DEBUG] ExportExcel Dispose 중 전역 오류: {ex.Message}");
// Disposal 오류는 로그만 남기고 계속 진행
}
}
}
}