using System; using System.Configuration; using System.Data; using System.Data.OleDb; using System.Diagnostics; using System.IO; using System.Text; namespace AuroraPLC { public abstract partial class PLCBase { #region PLC 엑셀파일 경로 //const string _alarmSettingPath = "External\\PLC\\PLC_DeviceMap_SettingAlarm.xlsx"; string _bitInputPath = "External\\PLC\\PLC_DeviceMap_Bit_Input_B_Device"; string _bitOutputPath = "External\\PLC\\PLC_DeviceMap_Bit_Output_B_Device"; string _mBitInputPath = "External\\PLC\\PLC_DeviceMap_Bit_Input_M_Device"; string _mBitOutputPath = "External\\PLC\\PLC_DeviceMap_Bit_Output_M_Device"; string _wordInputPath = "External\\PLC\\PLC_DeviceMap_Word_Input_W_Device"; string _wordOutputPath = "External\\PLC\\PLC_DeviceMap_Word_Output_W_Device"; string _dWordInputPath = "External\\PLC\\PLC_DeviceMap_Word_Input_D_Device"; string _dWordOutputPath = "External\\PLC\\PLC_DeviceMap_Word_Output_D_Device"; /* public string AlarmSettingPath { get { var recipePath = ConfigurationManager.AppSettings["DATA_PATH"]; return Path.Combine(recipePath, _alarmSettingPath); } } */ public string BitInputPath { get { var recipePath = ConfigurationManager.AppSettings["DATA_PATH"]; if (mmapFileType == PLCMMapFileType.CSV) { return Path.Combine(recipePath, _bitInputPath + ".csv"); } else { return Path.Combine(recipePath, _bitInputPath + ".xlsx"); } } } public string BitOutputPath { get { var recipePath = ConfigurationManager.AppSettings["DATA_PATH"]; var m_connConfig = _connConfig as PLCMXComponentConnConfig; if (mmapFileType == PLCMMapFileType.CSV) { return Path.Combine(recipePath, _bitOutputPath + ".csv"); } else { return Path.Combine(recipePath, _bitOutputPath + ".xlsx"); } } } public string MBitInputPath { get { var recipePath = ConfigurationManager.AppSettings["DATA_PATH"]; if (mmapFileType == PLCMMapFileType.CSV) { return Path.Combine(recipePath, _mBitInputPath + ".csv"); } else { return Path.Combine(recipePath, _mBitInputPath + ".xlsx"); } } } public string MBitOutputPath { get { var recipePath = ConfigurationManager.AppSettings["DATA_PATH"]; if (mmapFileType == PLCMMapFileType.CSV) { return Path.Combine(recipePath, _mBitOutputPath + ".csv"); } else { return Path.Combine(recipePath, _mBitOutputPath + ".xlsx"); } } } public string WordInputPath { get { var recipePath = ConfigurationManager.AppSettings["DATA_PATH"]; if (mmapFileType == PLCMMapFileType.CSV) { return Path.Combine(recipePath, _wordInputPath + ".csv"); } else { return Path.Combine(recipePath, _wordInputPath + ".xlsx"); } } } public string WordOutputPath { get { var recipePath = ConfigurationManager.AppSettings["DATA_PATH"]; if (mmapFileType == PLCMMapFileType.CSV) { return Path.Combine(recipePath, _wordOutputPath + ".csv"); } else { return Path.Combine(recipePath, _wordOutputPath + ".xlsx"); } } } public string DWordInputPath { get { var recipePath = ConfigurationManager.AppSettings["DATA_PATH"]; if (mmapFileType == PLCMMapFileType.CSV) { return Path.Combine(recipePath, _dWordInputPath + ".csv"); } else { return Path.Combine(recipePath, _dWordInputPath + ".xlsx"); } } } public string DWordOutputPath { get { var recipePath = ConfigurationManager.AppSettings["DATA_PATH"]; if (mmapFileType == PLCMMapFileType.CSV) { return Path.Combine(recipePath, _dWordOutputPath + ".csv"); } else { return Path.Combine(recipePath, _dWordOutputPath + ".xlsx"); } } } #endregion protected PLCMMapFileType mmapFileType; #region CSV 파일 방식 파서 /* // BOM 이 있어야 쓸 수 있음.. protected static Encoding GetEncoding(string fileName) { // Read the BOM (Byte Order Mark) var bom = new byte[4]; using (var file = new FileStream(fileName, FileMode.Open, FileAccess.Read)) { file.Read(bom, 0, 4); } // Analyze the BOM if (bom[0] == 0x2B && bom[1] == 0x2F && bom[2] == 0x76) { return Encoding.UTF7; } if (bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF) { return Encoding.UTF8; } if (bom[0] == 0xFF && bom[1] == 0xFE) { return Encoding.Unicode; // UTF-16LE } if (bom[0] == 0xFE && bom[1] == 0xFF) { return Encoding.BigEndianUnicode; // UTF-16BE } if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xFE && bom[3] == 0xFF) { return Encoding.UTF32; } return Encoding.Default; } */ private void AllocateMemBuffer(int index, ref string[] memLines, out int startAddress, out int endAddress, out ePLCErrCode retCode) { retCode = ePLCErrCode.OK; startAddress = 0; endAddress = 0; // 시작 주소와 끝 주소를 찾아서 전체 주소 범위를 계산하여 버퍼 범위를 할당 // 필수조건 // - 시작 주소 블럭(16개)은 무조건 2번째 라인에 위치 // - 마지막 주소 블럭(16개)은 끝에 위치 (최소한 끝에서 5개 라인 내에 위치해야 함) // 1.1 시작 주소 찾기 if (memLines.Length == 1) { return; } var startAddrLine = memLines[1]; // 예외 검사 if (string.IsNullOrEmpty(startAddrLine)) { Log.E($"[{DeviceId}] Invalid start address: index: {index}"); // 리턴으로 에러 알람을 발생시켜야 겠는데 !! retCode = ePLCErrCode.MemMapPaser_InValid_StartAddress; return; } var strTokens = startAddrLine.Split(','); // 토큰 분리 if (strTokens.Length < 3) { // 주소, 영문, 국문이 없으면 무효한 라인이므로, 스킵 Log.E($"[{DeviceId}] Invalid start address line: index: {index}"); retCode = ePLCErrCode.MemMapPaser_InValid_StartAddress; return; } // 앞뒤로 불필요한 공백 제거, B/W 문자 제거하여 정수로 변환 if (index == 0 || index == 1 || index == 4 || index == 5) { startAddress = int.Parse(strTokens[0].ToUpper().Trim().Substring(1), System.Globalization.NumberStyles.HexNumber); } else if (index == 2 || index == 3 || index == 6 || index == 7) { startAddress = int.Parse(strTokens[0].ToUpper().Trim().Substring(1), System.Globalization.NumberStyles.Number); } // 1.2 마지막 주소 찾기 for (int k = memLines.Length; k > memLines.Length - 5; --k) { var endAddrLine = memLines[k - 1]; // 빈 라인이면 스킵 if (string.IsNullOrEmpty(endAddrLine)) { continue; } var strTokensEnd = endAddrLine.Split(','); // 토큰 분리 if (strTokensEnd.Length < 3) { // 주소, 영문, 국문이 없으면 무효한 라인이므로, 스킵 Log.E($"[{DeviceId}] Invalid end address line: index: {index}"); retCode = ePLCErrCode.MemMapPaser_InValid_EndAddress; return; } if (index == 0 || index == 1 || index == 4 || index == 5) { endAddress = int.Parse(strTokensEnd[0].ToUpper().Trim().Substring(1), System.Globalization.NumberStyles.HexNumber); } else if (index == 2 || index == 3 || index == 6 || index == 7) { endAddress = int.Parse(strTokensEnd[0].ToUpper().Trim().Substring(1), System.Globalization.NumberStyles.Number); } // 성공 break; } // 버퍼에 할당할 메모리 크기 계산 int memBufferSize = endAddress - startAddress + 1; if (memBufferSize <= 0) { Log.E($"[{DeviceId}] Invalid memory size (Start/End addresses are wrong): index: {index}"); retCode = ePLCErrCode.MemMapPaser_InValid_MemorySize; return; } // 각 case 에 따라 메모리 크기 할당 switch (index) { case 0: // B Device Bit Input for (int idx = 0; idx < memBufferSize; ++idx) { BitInputs.Add( (startAddress + idx, new AddrCommentList(string.Empty, string.Empty)) ); } break; case 1: // B Device Bit Output for (int idx = 0; idx < memBufferSize; ++idx) { BitOutputs.Add( (startAddress + idx, new AddrCommentList(string.Empty, string.Empty)) ); } break; case 2: // M Device Bit Input for (int idx = 0; idx < memBufferSize; ++idx) { MBitInputs.Add( (startAddress + idx, new AddrCommentList(string.Empty, string.Empty)) ); } break; case 3: // M Device Bit Output for (int idx = 0; idx < memBufferSize; ++idx) { MBitOutputs.Add( (startAddress + idx, new AddrCommentList(string.Empty, string.Empty)) ); } break; case 4: // W Device Word Input for (int idx = 0; idx < memBufferSize; ++idx) { WordInputs.Add( (startAddress + idx, new AddrCommentList(string.Empty, string.Empty)) ); } break; case 5: // W Device Word Output for (int idx = 0; idx < memBufferSize; ++idx) { WordOutputs.Add( (startAddress + idx, new AddrCommentList(string.Empty, string.Empty)) ); } break; case 6: // D Device Word Input //for (int idx = 0; idx < memBufferSize; ++idx) { // DWordInputs.Add( // (startAddress + idx, // new AddrCommentList(string.Empty, string.Empty)) // ); //} break; case 7: // D Device Word Output //for (int idx = 0; idx < memBufferSize; ++idx) { // DWordOutputs.Add( // (startAddress + idx, // new AddrCommentList(string.Empty, string.Empty)) // ); //} break; } } protected bool ParsePLCBitWordAddressFromCSV(string bitInputPath, string bitOutputPath, string wordInputPath, string wordOutputPath, out ePLCErrCode retCode) { retCode = ePLCErrCode.OK; string[] strPath = new string[8]; //이전과의 호환성 고려, 만약 경로를 설정한 경우(해당 경로로 읽기) //var m_connConfig = _connConfig as PLCMXComponentConnConfig; //if (m_connConfig.PLCInterfaceMapDO != null) { // BitInputPath = m_connConfig.PLCInterfaceMapDO.ToString(); //} int index = 0; try { strPath[0] = BitInputPath; strPath[1] = BitOutputPath; strPath[2] = MBitInputPath; strPath[3] = MBitOutputPath; strPath[4] = WordInputPath; strPath[5] = WordOutputPath; strPath[6] = DWordInputPath; strPath[7] = DWordOutputPath; // 각 CSV 파일 파싱 for (int i = 0; i < strPath.Length; ++i) { index = i; if (File.Exists(strPath[i])) { //var allLines = File.ReadAllLines(strPath[i], Encoding.Default); var allLines = File.ReadAllLines(strPath[i], Encoding.UTF8); //var allLines = File.ReadAllLines(strPath[i], GetEncoding(strPath[i])); AllocateMemBuffer(i, ref allLines, out int startAddress, out int endAddress, out ePLCErrCode retCodeAllocateBuffer); if (retCodeAllocateBuffer != ePLCErrCode.OK) { // 에러 retCode = retCodeAllocateBuffer; // 에러가 발생하면, 중단하고 빠져나가자...(다음 메모리맵 파일을 파싱하는게 더 나을까???) return false; } //========================================================== int j = 0; // 더미...첫번째 라인(헤더 정보) 스킵용 foreach (var line in allLines) { // 맨 첫번째 라인은 텍스트 헤더 정보이므로, 스킵 if (j == 0) { ++j; continue; } if (string.IsNullOrEmpty(line)) { continue; } var splits = line.Split(','); if (splits.Length < 3) { // 주소, 영문, 국문이 없으면 무효한 라인이므로, 스킵 continue; } // 앞뒤로 불필요한 공백 제거 var address = splits[0].ToUpper().Trim(); var descEng = splits[1].Trim(); var descKor = splits[2].Trim(); // 데이터 파일 가독성을 위한 띄어쓰기 처리 추가 if (string.IsNullOrEmpty(address)) continue; // Comment 정보를 해당 배열에 저장 int addrNo, addrOffset; switch (i) { case 0: // B Device Bit Input addrNo = int.Parse(address.Substring(1), System.Globalization.NumberStyles.HexNumber); addrOffset = addrNo - startAddress; BitInputs[addrOffset].description.Comment_EN = descEng; BitInputs[addrOffset].description.Comment_KR = descKor; break; case 1: // B Device Bit Output addrNo = int.Parse(address.Substring(1), System.Globalization.NumberStyles.HexNumber); addrOffset = addrNo - startAddress; BitOutputs[addrOffset].description.Comment_EN = descEng; BitOutputs[addrOffset].description.Comment_KR = descKor; break; case 2: // M Device Bit Input addrNo = int.Parse(address.Substring(1), System.Globalization.NumberStyles.Number); addrOffset = addrNo - startAddress; MBitInputs[addrOffset].description.Comment_EN = descEng; MBitInputs[addrOffset].description.Comment_KR = descKor; break; case 3: // M Device Bit Output addrNo = int.Parse(address.Substring(1), System.Globalization.NumberStyles.Number); addrOffset = addrNo - startAddress; MBitOutputs[addrOffset].description.Comment_EN = descEng; MBitOutputs[addrOffset].description.Comment_KR = descKor; break; case 4: // W Device Word Input addrNo = int.Parse(address.Substring(1), System.Globalization.NumberStyles.HexNumber); addrOffset = addrNo - startAddress; WordInputs[addrOffset].description.Comment_EN = descEng; WordInputs[addrOffset].description.Comment_KR = descKor; break; case 5: // W Device Word Output addrNo = int.Parse(address.Substring(1), System.Globalization.NumberStyles.HexNumber); addrOffset = addrNo - startAddress; WordOutputs[addrOffset].description.Comment_EN = descEng; WordOutputs[addrOffset].description.Comment_KR = descKor; break; case 6: // D Device Word Input addrNo = int.Parse(address.Substring(1), System.Globalization.NumberStyles.Number); // D는 미리 버퍼를 안 만들었으므로, 줄마다 바로 Add DWordInputs.Add( (addrNo, new AddrCommentList(descEng, descKor)) ); break; case 7: // D Device Word Output addrNo = int.Parse(address.Substring(1), System.Globalization.NumberStyles.Number); DWordOutputs.Add( (addrNo, new AddrCommentList(descEng, descKor)) ); break; } } } else { Log.W($"[{DeviceId}] Not exist PLC address file ({strPath[i]})"); switch (i) { case 0: case 2: { retCode = ePLCErrCode.MemMapPaser_BitInput_LoadFail; break; } case 1: case 3: { retCode = ePLCErrCode.MemMapPaser_BitOutput_LoadFail; break; } case 4: case 6: { retCode = ePLCErrCode.MemMapPaser_WordInput_LoadFail; break; } case 5: case 7: { retCode = ePLCErrCode.MemMapPaser_WordOutput_LoadFail; break; } } // 경로에 8개의 파일이 없어도 파싱이 될 수 있도록 수정 (대신 로그 기록 보내기) //return false; Log.W("Some PLC CSV files cannot be read."); } } Log.I($"[{DeviceId}] PLC address read OK"); return true; } catch (Exception ex) { Log.E($"[{DeviceId}] PLC address read error: {ex.Message}"); switch (index) { case 0: case 2: { retCode = ePLCErrCode.MemMapPaser_BitInput_LoadFail; break; } case 1: case 3: { retCode = ePLCErrCode.MemMapPaser_BitOutput_LoadFail; break; } case 4: case 6: { retCode = ePLCErrCode.MemMapPaser_WordInput_LoadFail; break; } case 5: case 7: { retCode = ePLCErrCode.MemMapPaser_WordOutput_LoadFail; break; } } return false; } finally { } } #endregion #region 엑셀파일 방식 파서 protected bool ParsePLCBitWordAddressFromExcel(string bitInputPath, string bitOutputPath, string wordInputPath, string wordOutputPath, out ePLCErrCode retCode) { retCode = ePLCErrCode.OK; string[] strPath = new string[4]; string[] strQuery = new string[4]; OleDbConnection oleCon = new OleDbConnection(); System.Data.DataTable dt = new System.Data.DataTable(); System.Data.DataTable dt1 = new System.Data.DataTable(); int index = 0; try { strPath[0] = BitInputPath; strPath[1] = BitOutputPath; strPath[2] = MBitInputPath; strPath[3] = MBitOutputPath; strPath[4] = WordInputPath; strPath[5] = WordOutputPath; strPath[6] = DWordInputPath; strPath[7] = DWordOutputPath; // 각 엑셀파일 파싱 for (int i = 0; i < strPath.Length; i++) { index = i; if (File.Exists(strPath[i])) { string[] strTemp = strPath[i].Split('.'); if (strTemp[strTemp.Length - 1] == "xlsx") { strQuery[i] = string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 12.0;HDR=YES;\"", strPath[i]); } else { strQuery[i] = string.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties=Excel 8.0;", strPath[i]); } oleCon.ConnectionString = strQuery[i]; oleCon.Open(); dt = oleCon.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null); int j = 0; // 메모리 주소, CommentEng, CommentKor, Data 인덱스 매칭용 int indexAddr = 0, indexCommentEng = 0, indexCommentKor = 0, indexData = 0; foreach (DataRow row in dt.Rows) { string strSheets = row["TABLE_NAME"].ToString().Trim(new[] { '$' }); if (strSheets != "UNION") { /* // 중요하게 쓰이는 곳이 없어서 주석처리 !!! // 각 탭의 메모리 주소를 문자열 배열에 저장 switch (i) { case 0: AddrSheet_BitInput[j] = strSheets; break; case 1: AddrSheet_BitOutput[j] = strSheets; break; case 2: AddrSheet_WordInput[j] = strSheets; break; case 3: AddrSheet_WordOutput[j] = strSheets; break; } j++; */ OleDbDataAdapter da = new OleDbDataAdapter(string.Format("SELECT * FROM [{0}$]", strSheets), oleCon); da.Fill(dt1); // 각 탭의 행 정보 (2행부터 마지막행까지)를 dt1 에 채움 (append 방식) // DBNull 이 중간에 들어가 있는 경우가 있다 // 따라서, 데이터가 들어가 있는 인덱스를 찾아놔야 함...한번만 찾으면 됨.. if (j == 0) { for (int k = 0; k < dt1.Columns.Count; k++) { if (dt1.Columns[k].Caption == "Device No") indexAddr = k; if (dt1.Columns[k].Caption == "Comment ENG") indexCommentEng = k; if (dt1.Columns[k].Caption == "Comment KOR") indexCommentKor = k; if (dt1.Columns[k].Caption == "Data") indexData = k; } } } } j = 0; foreach (DataRow row1 in dt1.Rows) { // 각 장치(B/W, etc)의 모든 행 정보가 dt1.Rows 에 저장되어 있음 // Comment 정보를 해당 배열에 저장 switch (i) { case 0: // B Device Bit Input BitInputs.Add( (int.Parse(row1.ItemArray[indexAddr].ToString().Substring(1), System.Globalization.NumberStyles.HexNumber), new AddrCommentList(row1.ItemArray[indexCommentEng].ToString(), row1.ItemArray[indexCommentKor].ToString()))); break; case 1: // B Device Bit Output BitOutputs.Add( (int.Parse(row1.ItemArray[indexAddr].ToString().Substring(1), System.Globalization.NumberStyles.HexNumber), new AddrCommentList(row1.ItemArray[indexCommentEng].ToString(), row1.ItemArray[indexCommentKor].ToString()))); break; case 2: // M Device Bit Input MBitInputs.Add( (int.Parse(row1.ItemArray[indexAddr].ToString().Substring(1), System.Globalization.NumberStyles.Number), new AddrCommentList(row1.ItemArray[indexCommentEng].ToString(), row1.ItemArray[indexCommentKor].ToString()))); break; case 3: // M DeviceBit Output MBitOutputs.Add( (int.Parse(row1.ItemArray[indexAddr].ToString().Substring(1), System.Globalization.NumberStyles.Number), new AddrCommentList(row1.ItemArray[indexCommentEng].ToString(), row1.ItemArray[indexCommentKor].ToString()))); break; case 4: // W Device Word Input WordInputs.Add( (int.Parse(row1.ItemArray[indexAddr].ToString().Substring(1), System.Globalization.NumberStyles.HexNumber), new AddrCommentList(row1.ItemArray[indexCommentEng].ToString(), row1.ItemArray[indexCommentKor].ToString()))); break; case 5: // W Device Word Output WordOutputs.Add( (int.Parse(row1.ItemArray[indexAddr].ToString().Substring(1), System.Globalization.NumberStyles.HexNumber), new AddrCommentList(row1.ItemArray[indexCommentEng].ToString(), row1.ItemArray[indexCommentKor].ToString()))); break; case 6: // D Device Word Input DWordInputs.Add( (int.Parse(row1.ItemArray[indexAddr].ToString().Substring(1), System.Globalization.NumberStyles.Number), new AddrCommentList(row1.ItemArray[indexCommentEng].ToString(), row1.ItemArray[indexCommentKor].ToString()))); break; case 7: // D Device Word Output DWordOutputs.Add( (int.Parse(row1.ItemArray[indexAddr].ToString().Substring(1), System.Globalization.NumberStyles.Number), new AddrCommentList(row1.ItemArray[indexCommentEng].ToString(), row1.ItemArray[indexCommentKor].ToString()))); break; } ++j; } dt1.Clear(); if (oleCon.State == ConnectionState.Open) oleCon.Close(); } else { Log.W($"[{DeviceId}] Not exist PLC address file ({strPath[i]})"); switch (i) { case 0: case 2: { retCode = ePLCErrCode.MemMapPaser_BitInput_LoadFail; break; } case 1: case 3: { retCode = ePLCErrCode.MemMapPaser_BitOutput_LoadFail; break; } case 4: case 6: { retCode = ePLCErrCode.MemMapPaser_WordInput_LoadFail; break; } case 5: case 7: { retCode = ePLCErrCode.MemMapPaser_WordOutput_LoadFail; break; } } return false; } } Log.I($"[{DeviceId}] PLC address read OK"); return true; } catch (Exception ex) { Log.E($"[{DeviceId}] PLC address read error: {ex.Message}"); switch (index) { case 0: case 2: { retCode = ePLCErrCode.MemMapPaser_BitInput_LoadFail; break; } case 1: case 3: { retCode = ePLCErrCode.MemMapPaser_BitOutput_LoadFail; break; } case 4: case 6: { retCode = ePLCErrCode.MemMapPaser_WordInput_LoadFail; break; } case 5: case 7: { retCode = ePLCErrCode.MemMapPaser_WordOutput_LoadFail; break; } } return false; } finally { // Coverity CID 10025 //if (oleCon != null && oleCon.State != ConnectionState.Closed) oleCon.Close(); if (oleCon != null) { oleCon.Close(); oleCon.Dispose(); } } } #region 세팅/알람 (M, ZR 장치) protected struct stAlarmList { public string DevAddress; public string Msg_KR; public string Msg_EN; public string Msg_CN; //public string Station; public bool AlarmOn; public Stopwatch OnTime; public string StartTime; public bool Popup; public string Solution_KR; public string Solution_EN; public string Solution_CN; public string Cause_KR; public string Cause_EN; public string Cause_CN; public string ImagePath; } protected struct stSettingsList { public string DevAddress; public string Msg_KR; public string Msg_EN; public string Msg_CN; public int nWord; public int nDecimals; public double Divide; public string Unit; public double Value; } protected bool ParsePLCAlarmSettingAddress(string alarmSettingPath, ref stAlarmList[] alarmList, ref stSettingsList[] settingList, out ePLCErrCode retCode) { retCode = ePLCErrCode.OK; #region Read Excel Data File OleDbConnection oleCon = new OleDbConnection(); System.Data.DataTable dt = new System.Data.DataTable(); System.Data.DataTable dt1 = new System.Data.DataTable(); string strQuery = ""; var strPath = alarmSettingPath; try { if (File.Exists(strPath)) { string[] strTemp = strPath.Split('.'); if (strTemp[strTemp.Length - 1] == "xlsx") { strQuery = string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 12.0;HDR=YES;\"", strPath); } else { strQuery = string.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties=Excel 8.0;", strPath); } oleCon.ConnectionString = strQuery; oleCon.Open(); dt = oleCon.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null); foreach (DataRow row in dt.Rows) { string strSheets = row["TABLE_NAME"].ToString().Trim(new[] { '$' }); if (strSheets == "ALARM") { // 알람 탭 OleDbDataAdapter da = new OleDbDataAdapter(string.Format("SELECT * FROM [{0}$]", strSheets), oleCon); da.Fill(dt1); // dt1 에 ALARM 탭의 정보를 채움 Array.Resize(ref alarmList, dt1.Rows.Count); // dt1 의 행의 개수만큼 _stAlarmList 의 크기 늘리기 // stEventsList 구조체 필드 관련 // 엑셀 Column 에 대한 index 를 의미함 int iCode = 0; int iName_Kr = 0; int iName_En = 0; int iName_Cn = 0; int iPopup = 0; int iSolution_KR = 0; int iSolution_EN = 0; int iSolution_CN = 0; int iCause_KR = 0; int iCause_EN = 0; int iCause_CN = 0; int iImagePath = 0; // ALARM 탭의 첫 행의 열 정보가 Columns 에 배열로 저장되어 있음 for (int i = 0; i < dt1.Columns.Count; i++) { // Column 인덱스 매핑 ?? if (dt1.Columns[i].Caption == "Address") iCode = i; if (dt1.Columns[i].Caption == "DESCRIPTION 한글") iName_Kr = i; if (dt1.Columns[i].Caption == "DESCRIPTION 영문") iName_En = i; if (dt1.Columns[i].Caption == "DESCRIPTION 중문") iName_Cn = i; if (dt1.Columns[i].Caption == "POPUP") iPopup = i; if (dt1.Columns[i].Caption == "발생원인 한글") iCause_KR = i; if (dt1.Columns[i].Caption == "발생원인 영문") iCause_EN = i; if (dt1.Columns[i].Caption == "발생원인 중문") iCause_CN = i; if (dt1.Columns[i].Caption == "조치방법 한글") iSolution_KR = i; if (dt1.Columns[i].Caption == "조치방법 영문") iSolution_EN = i; if (dt1.Columns[i].Caption == "조치방법 중문") iSolution_CN = i; if (dt1.Columns[i].Caption == "IMAGE") iImagePath = i; } int index = 0; foreach (DataRow row1 in dt1.Rows) { // dt1.Rows 는 ALARM 탭의 2번째 행부터 데이터가 저장되어 있음 if (row1.ItemArray[0].ToString().Length > 0) { // 각 행의 첫번째 열의 정보 alarmList[index].AlarmOn = false; alarmList[index].DevAddress = row1.ItemArray[iCode].ToString(); // Address 정보 alarmList[index].Msg_KR = row1.ItemArray[iName_Kr].ToString(); // DESCRIPTION 한글 정보 alarmList[index].Msg_EN = row1.ItemArray[iName_En].ToString(); // DESCRIPTION 영문 정보 alarmList[index].Msg_CN = row1.ItemArray[iName_Cn].ToString(); // DESCRIPTION 중문 정보 alarmList[index].OnTime = new Stopwatch(); alarmList[index].StartTime = ""; alarmList[index].Popup = (row1.ItemArray[iPopup].ToString() == "1"); alarmList[index].Solution_KR = row1.ItemArray[iSolution_KR].ToString(); alarmList[index].Solution_EN = row1.ItemArray[iSolution_EN].ToString(); alarmList[index].Solution_CN = row1.ItemArray[iSolution_CN].ToString(); alarmList[index].Cause_KR = row1.ItemArray[iCause_KR].ToString(); alarmList[index].Cause_EN = row1.ItemArray[iCause_EN].ToString(); alarmList[index].Cause_CN = row1.ItemArray[iCause_CN].ToString(); alarmList[index].ImagePath = row1.ItemArray[iImagePath].ToString(); index++; } else { // 첫번째 열정보가 비어있는 경우, 예외처리인가 ?? alarmList[index].AlarmOn = false; alarmList[index].DevAddress = ""; alarmList[index].Msg_KR = ""; alarmList[index].Msg_EN = ""; alarmList[index].Msg_CN = ""; alarmList[index].OnTime = new Stopwatch(); alarmList[index].StartTime = ""; alarmList[index].Popup = false; alarmList[index].Solution_KR = ""; alarmList[index].Solution_EN = ""; alarmList[index].Solution_CN = ""; alarmList[index].Cause_KR = ""; alarmList[index].Cause_EN = ""; alarmList[index].Cause_CN = ""; alarmList[index].ImagePath = ""; index++; } } dt1.Clear(); } if (strSheets == "SETTING") { // 세팅 탭 OleDbDataAdapter da = new OleDbDataAdapter(string.Format("SELECT * FROM [{0}$]", strSheets), oleCon); da.Fill(dt1); Array.Resize(ref settingList, dt1.Rows.Count); // 엑셀 Column 에 대한 index 를 의미함 int iCode = 0; int iName_Kr = 0; int iName_En = 0; int iName_Cn = 0; int iWord = 0; int iDecimal = 0; int iUnit = 0; int iDivide = 0; for (int i = 0; i < dt1.Columns.Count; i++) { if (dt1.Columns[i].Caption == "Address") iCode = i; if (dt1.Columns[i].Caption == "DESCRIPTION 한글") iName_Kr = i; if (dt1.Columns[i].Caption == "DESCRIPTION 영문") iName_En = i; if (dt1.Columns[i].Caption == "DESCRIPTION 중문") iName_Cn = i; if (dt1.Columns[i].Caption == "Word Size") iWord = i; if (dt1.Columns[i].Caption == "Decimal Place(소수점)") iDecimal = i; if (dt1.Columns[i].Caption == "기준 단위") iUnit = i; if (dt1.Columns[i].Caption == "Divide") iDivide = i; } int index = 0; foreach (DataRow row1 in dt1.Rows) { if (row1.ItemArray[0].ToString().Length > 0) { // 각 행의 첫번째 열의 정보 settingList[index].DevAddress = row1.ItemArray[iCode].ToString(); settingList[index].Msg_KR = row1.ItemArray[iName_Kr].ToString(); settingList[index].Msg_EN = row1.ItemArray[iName_En].ToString(); settingList[index].Msg_CN = row1.ItemArray[iName_Cn].ToString(); settingList[index].Unit = row1.ItemArray[iUnit].ToString(); int.TryParse(row1.ItemArray[iWord].ToString(), out settingList[index].nWord); int.TryParse(row1.ItemArray[iDecimal].ToString(), out settingList[index].nDecimals); double.TryParse(row1.ItemArray[iDivide].ToString(), out settingList[index].Divide); if (settingList[index].Divide == 0) settingList[index].Divide = 1; index++; } else { settingList[index].DevAddress = ""; settingList[index].Msg_KR = ""; settingList[index].Msg_EN = ""; settingList[index].Msg_CN = ""; settingList[index].Unit = ""; settingList[index].nWord = 1; settingList[index].Divide = 1; settingList[index].nDecimals = 0; index++; } } dt1.Clear(); } } //if (oleCon.State == ConnectionState.Open) oleCon.Close(); return true; } else { // Not Exist Log.E($"Not exist: {strPath}"); retCode = ePLCErrCode.MemMapPaser_SettingAlarm_LoadFail; } return false; } catch (Exception ex) { Log.E($"Perhaps Microsoft Access database engine 64bit problem: {ex.Message}"); retCode = ePLCErrCode.MemMapPaser_SettingAlarm_LoadFail; return false; } finally { // Coverity CID 10024 //if (oleCon != null && oleCon.State != ConnectionState.Closed) oleCon.Close(); if (oleCon != null) { oleCon.Close(); oleCon.Dispose(); } } #endregion } #endregion #endregion } }