[제품종류] IEC667Lite
[개발환경] Visual Studio 2008 C#
=============== 질 문 ===============설정 정보 저장을 위해 구조체 선언을 하고, SmartFile을 이용해 구조체 저장방법으로 진행하려고 합니다.
구조체 선언은 아래와 같이 진행하였습니다.
구조체 내 {("3종류 데이터 * 10개씩"을 갖는 구조체)를 다시 "10개"}를 갖는 배열형 멤버가 있습니다.
[StructLayout(LayoutKind.Sequential)]
public struct FINSTALL
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public double[] Set1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public byte[] Set2;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public byte[] Set3;
}
public struct FLIMIT
{
public double Min;
public double Max;
}
public struct FSETUP
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public FINSTALL[] Install;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public FLIMIT[] Limit;
public double Rbase;
public double Cbase;
public double Hbase;
public FSETUP(double ARbase, double ACbase, double AHbase)
{
Install = new FINSTALL[10];
for (int p = 0; p < 10; p++)
{
Install[p].Set1 = new double[10];
Install[p].Set2 = new byte[10];
Install[p].Set3 = new byte[10];
for (int s = 0; s < 10; s+)
{
Install[p].Set1[s] = 0.5;
Install[p].Set2[s] = 1;
Install[p].Set3[s] = 1;
}
}
Limit = new FLIMIT[3];
for (int m = 0; m < 3; m++)
{
Limit[m].Min = 0.0;
Limit[m].Max = 0.0;
}
Rbase = ARbase;
Cbase = ACbase;
Hbase = AHbase;
}
}
아래와 같이 파일을 불러올 때,
Setup = (FSETUP)SetupFile.StructType.Read(); 구문에서
NullReferenceException이 발생하는데 이유를 못 찾겠습니다.
SetupFile = new SmartX.SmartFile();
SetupFile.FilePathName = SETUP_FILE_PATH;
SetupFileOpenStatus = false;
Setup = new FSETUP(0.0, 0.0, 0.0);
if (SetupFile.Open())
{
SetupFile.StructType.SetStructType(typeof(FSETUP));
/*exception 발생 위치*/ Setup = (FSETUP)SetupFile.StructType.Read();
SetupFileOpenStatus = true;
}
else
{
SmartX.SmartMessageBox.Show("Failed to open settings file");
}
그리고, 아래와 같이 파일을 저장하고 있으며, 파일생성은 이루어지지만 정상적인 데이터를 볼 수 없습니다.
SetupFile.StructType.SetStructType(typeof(FSETUP));
SetupFileOpenStatus = true;
for (int p = 0; p < 10; p++)
{
for (int s = 0; s < 10; s+)
{
Setup.Install[p].Set1[s] = 0.0;
Setup.Install[p].Set2[s] = 0;
Setup.Install[p].Set3[s] = 0;
}
}
for (int m = 0; m < 3; m++)
{
Setup.Limit[m].Min = 0.4;
Setup.Limit[m].Max = 2.1;
}
Setup.Rbase = 0.0;
Setup.Cbase = 1.0;
Setup.Hbase = 0.0;
SetupFile.StructType.Write(Setup);
배열을 갖는 구조체를 smartfile을 통해 저장하고 불러오는 방식을 알고 싶습니다.
감사합니다.
=============== 답 변 ===============
안녕하세요.
문의주신 내용을 확인해본 결과 현재 구조체 안에 구조체 배열을 생성하여 사용하고 계신 것을 확인하였습니다.
이 때 문제가 되는 부분은 구조체 안에 [구조체 배열]을 사용하는 것이 문제가 됩니다. 구조체 배열의 경우 마샬 정보에서 크기를
정의할 수 없기 때문에 사용이 불가능합니다.
[구조체] : 사용 가능
[구조체 내부에 구조체] : 사용 가능
[구조체 내부에 구조체 배열] : 사용 불가능
[구조체 배열] : 사용 불가능
또한 하나의 구조체에서 2차원 배열을 사용하여 사용하는 방법으로 테스트를 진행해봤지만 마샬 정보를 입력할 때 2차원 배열의
[X, Y]의 X크기, Y크기를 개별로 정의할 수 없기 때문에 File Write는 정상적으로 써지지만 File Read 시 X, Y의 크기가 명확하지 않아
배열이 Index가 깨지게 되어 불가능합니다.
그렇기 때문에 해결 방법으로는 하나의 구조체에서 1차원 배열을 사용하여 정의하되 Getter, Setter 방식을 사용하여 2차원 적으로
데이터에 접근하는 방법이 있습니다. 자세한 내용은 아래의 예시 코드를 참고하시기 바랍니다.
[예시 코드]
public struct FSETUP
{
// 배열의 크기를 10X10 즉, 100으로 설정합니다.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
public double[] Set1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
public byte[] Set2;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
public byte[] Set3;
public double Rbase;
public double Cbase;
public double Hbase;
public FSETUP(double ARbase, double ACbase, double AHbase)
{
Set1 = new double[10 * 10];
Set2 = new byte[10 * 10];
Set3 = new byte[10 * 10];
Rbase = ARbase;
Cbase = ACbase;
Hbase = AHbase;
}
// 배열의 데이터를 iIndex와 jIndex로 2차원 배열과 같이 접근하여 Value를 설정합니다.
public void SSet1(int iIndex, int jIndex, double dValue)
{
Set1[(iIndex * 10) + jIndex] = dValue;
}
// 파일에서 읽어온 데이터를 iIndex와 jIndex로 2차원 배열과 같이 접근하여 가져옵니다.
public double GSet1(int iIndex, int jIndex)
{
return Set1[(iIndex * 10) + jIndex];
}
public void SSet2(int iIndex, int jIndex, byte bValue)
{
Set2[(iIndex * 10) + jIndex] = bValue;
}
public byte GSet2(int iIndex, int jIndex)
{
return Set2[(iIndex * 10) + jIndex];
}
public void SSet3(int iIndex, int jIndex, byte bValue)
{
Set3[(iIndex * 10) + jIndex] = bValue;
}
public byte GSet3(int iIndex, int jIndex)
{
return Set3[(iIndex * 10) + jIndex];
}
}
private void FileWrite()
{
FSETUP exStruct = new FSETUP(0.0, 0.0, 0.0);
for (int p = 0; p < 10; p++)
{
for (int s = 0; s < 10; s++)
{
// [p, s] 위치에 Value를 설정합니다.
exStruct.SSet1(p, s, (double)(s + 100));
exStruct.SSet2(p, s, (byte)s);
exStruct.SSet3(p, s, (byte)(s + 10));
}
}
// 설정이 완료된 구조체를 File에 Write 합니다.
smartFile1.StructType.Write(exStruct);
smartFile1.Close();
}
private void FileRead()
{
FSETUP exStruct;
exStruct = (FSETUP)smartFile1.StructType.Read();
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
// 구조체 파일에서 읽어온 데이터에서 [i, j] 위치의 Value를 ListBox에 출력합니다.
smartListBox1.AddItem(exStruct.GSet1(i, j).ToString());
}
}
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
smartListBox1.AddItem(exStruct.GSet2(i, j).ToString());
}
}
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
smartListBox1.AddItem(exStruct.GSet3(i, j).ToString());
}
}
}
감사합니다.
=============== 답 변 ===============
먼저, 지원에 감사드립니다.
알려주신 방법으로 코드를 수정 작성하여 디버깅 하여, 파일로의 저장 및 불러오기는 확인이 되었습니다.
하지만, 첨부된 이미지와 같이 저장시점의 내용과 불러오기시점의 내용이 다르게 나옵니다.
public struct FSETUP
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100/*10proc's * 10steps*/)]
public double[] Install_Power;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
public byte[] Install_Shots;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
public byte[] Install_Freqc;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3/*Modes == 3*/)]
public double[] Limit_Min;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public double[] Limit_Max;
public byte Pnt;
public byte Fsd;
public byte AutoDeleteMonth;
public double Rbase;
public double Cbase;
public double Hbase;
public FSETUP(byte APnt, byte AFsd, byte AAutoDeleteMonth, double ARbase, double ACbase, double AHbase)
{
Install_Power = new double[10/*10proc's*/ * 10/*10steps*/];
Install_Shots = new byte[10 * 10];
Install_Freqc = new byte[10 * 10];
Limit_Min = new double[3];
Limit_Max = new double[3];
Pnt = APnt;
Fsd = AFsd;
AutoDeleteMonth = AAutoDeleteMonth;
Rbase = ARbase;
Cbase = ACbase;
Hbase = AHbase;
}
}
public static FSETUP Setup;
private void btnTempFsave_Click(object sender, EventArgs e)
{
if (vars.SetupFile.Open())
{
Random rand = new Random();
for (int p/*roc*/ = 0; p < 10; p++)
for (int s/*teps*/ = 0; s < 10; s++)
{
vars.Setup.Install_Power[(p * 10) + s] = rand.NextDouble();
vars.Setup.Install_Shots[(p * 10) + s] = (byte)rand.Next(1, 10);
vars.Setup.Install_Freqc[(p * 10) + s] = (byte)rand.Next(1, 20);
}
for (int m/*ode*/ = 0; m < 3; m++)
{
vars.Setup.Limit_Min[m] = rand.Next(0, 2) + rand.NextDouble();
vars.Setup.Limit_Max[m] = rand.Next(0, 2) + rand.NextDouble();
}
vars.Setup.Pnt = 25;
vars.Setup.Fsd = 100;
vars.Setup.AutoDeleteMonth = 3;
vars.Setup.Rbase = 0.0;
vars.Setup.Cbase = 1.0;
vars.Setup.Hbase = 0.0;
vars.SetupFile.StructType.SetStructType(typeof(vars.FSETUP));
vars.SetupFile.StructType.Write(vars.Setup);
vars.SetupFile.Close();
SmartX.SmartMessageBox.Show("Settings file write successfully");
}
else
{
SmartX.SmartMessageBox.Show("Failed to open settings file");
}
}
private void btnTempFread_Click(object sender, EventArgs e)
{
if (vars.SetupFile.Open())
{
vars.SetupFile.StructType.SetStructType(typeof(vars.FSETUP));
vars.Setup = (vars.FSETUP)vars.SetupFile.StructType.Read();
vars.SetupFile.Close();
}
else
{
SmartX.SmartMessageBox.Show("Failed to open settings file");
}
}
첨부 이미지는 파일을 write, read 하기 전 vars.Setup의 파일 내용을 확인해본 결과입니다.
검토 부탁드립니다.
감사합니다.
=============== 답 변 ===============
안녕하세요.
올려주신 예시 코드로 테스트를 진행해본 결과 파일에 구조체 데이터를 Index없이 쓰기 할 경우 첫번째 구조체 데이터 이후에
다음줄에 두번째 구조체 데이터, 그 다음줄에 세번째 구조체 데이터가 써지게 됩니다. 즉, 한 개의 파일에 여러 번 구조체 데이터를
쓰기 했을 때 순차적으로 데이터가 써지는데 현재 읽기(Read)를 진행하셨을 때 Index를 지정하지 않을 경우 첫번째로 쓴 구조체
데이터만 읽어오기 때문에 다른 데이터가 읽어와 지는 것으로 보여지고 있는 것으로 판단됩니다.
Read할 때 Index의 값으로 0번 ~ N번을 설정해주시면 정상적으로 해당 줄의 구조체 데이터를 읽어오는 것을 확인하였으니
Read 시 원하는 N번째의 구조체 데이터를 Index를 설정하여 읽어와 주시기 바랍니다.
감사합니다.
=============== 답 변 ===============
별도의 인덱스 지정없이 사용할 경우 하나의 데이터 영역만 사용할 것이라 생각했습니다.
0번 인덱스 지정 후, overwrite/read하니 정상적으로 진행이됩니다.
감사합니다.