2026-01-26 18:56:53 +09:00

884 lines
45 KiB
C#

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);
addrOffset = addrNo - startAddress;
DWordInputs[addrOffset].description.Comment_EN = descEng;
DWordInputs[addrOffset].description.Comment_KR = descKor;
break;
case 7: // D Device Word Output
addrNo = int.Parse(address.Substring(1), System.Globalization.NumberStyles.Number);
addrOffset = addrNo - startAddress;
DWordOutputs[addrOffset].description.Comment_EN = descEng;
DWordOutputs[addrOffset].description.Comment_KR = 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
}
}