반응형

   

static class Ieee754Converter
{
    public static byte[] GetBytes(float value)
    {
        byte[] bytes = BitConverter.GetBytes(value);
        return bytes;
    }
    public static float ToSingle(byte[] bytes)
    {
        float value = BitConverter.ToSingle(bytes, 0);
        return value;
    }
}

 

예 : +7.282600E+01  <-> {0xE9} {0xA6} {0x91} {0x42}

 

byte[ ] temp = Ieee754Converter.GetBytes(3.141592f);
float value = Ieee754Converter.ToSingle(temp);
       

반응형
반응형

 

	public double ThresholdCurrent(in double[] Current, in double[] Power, double PowerLow, double PowerHigh)
	{
		double thresholdCurrent = 0;

		try
		{
			int startIndex = Array.FindIndex(Power, x => x >= PowerLow);
			int endIndex = Array.FindIndex(Power, x => x >= PowerHigh);

			var subCurrent = Current.Skip(startIndex).Take(endIndex - startIndex + 1).ToArray();
			var subPower = Power.Skip(startIndex).Take(endIndex - startIndex + 1).ToArray();

			var (slope, intercept) = LeastSquaresFit(subCurrent, subPower);

			if(slope != 0) thresholdCurrent = -intercept / slope;            
		}
		catch
		{
			thresholdCurrent = 0 ;
		}		

		return thresholdCurrent;
	}	
	//-----------------------------------------------------------------------------------------------------------------
	private (double Slope, double Intercept) LeastSquaresFit(double[] x, double[] y)
	{
		int n = x.Length;        
        
        double sumX = x.Sum();
        double sumY = y.Sum();
        double sumXY = x.Zip(y, (xi, yi) => xi * yi).Sum();
        double sumXX = x.Select(xi => xi * xi).Sum();
        
        var Slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
        var yIntercept = (sumY - slope * sumX) / n;
		
		return (slope, yIntercept);
	}
	//-----------------------------------------------------------------------------------------------------------------
반응형
반응형

비주얼 스튜디오로 만든 Winform 프로젝트를 수정할 일이 생겨서 프로젝트를 열었는데 폼 디자이너가 전혀 열리지 않는 현상이 발생했다. 인터넷을 뒤져서 이것저것 찾아 보았지만 해결을 할 수가 없었다. 

 

그러다 우연히 프로젝트가 들어있는 폴더를 밖으로 빼고 다시 불러와 보니 정상적으로 폼 디자이너가 보이기 시작했다. 

원인은 이번에 비주얼 스튜디오가 업그레이드 되면서 프로젝트 파일들이 들어있는 폴더에 ( ) 를 사용했을 경우 문제가 발생한다는 것을 알아냈다. 

 

난 프로그램 버젼을 폴더에 () 안에 표시하는데 그걸 빼고 했더니 정상 동작이 되었다. 이게 오류인지 일부러 그렇게 만든건지 모르겠다. 

하여간 아래 처럼 ( ) 제외해야 폼 디자이너가 정상적으로 나타난다. 

 

기존 폴더 : D:\TestProject(Ver 1.0.0)\   오류

수정 폴더 : D:\TestProject\                    정상

 

 

반응형
반응형

계측기와 데이타를 주고 받는 경우 계측기에서 측정된 데이타가 ASCii 형태로 들어 오는 경우가 있다. 이럴 경우 데이타의 갯수가 짧은 경우에는 상관이 없지만 데이타 갯수가 수십 ~ 수백개가 될 경우 통신 속도문제로 데이타를 주고 받는데 시간이 많이 걸리게 된다. 

 

더군다나 ASCii 를 사용하게 되면 데이타는 더 길어질 수 밖에 없어서 시간이 더 많이 소요가 된다. 

이럴때 데이타의 길이를 줄이기 위해 계측기가 IEEE754형태의 단정밀로 변환해서 데이타를 주고 받을 수 있는 경우가 있는데 이럴경우 데이타의 길이를 1/3로 줄일 수 있다. (13byte 아스키값을 4byte 로 변형)

 

예 : +7.282600E+01  <-> {0xE9} {0xA6} {0x91} {0x42}

 

변환 방법은 간단하게 Union 사용하면 된다. 

 

union IEEE754_Float
{
    unsigned char uData[4] ;
    float fValue ;
} ;

 

 

IEEE754_Float data ;

data.fValue = 72.826 ;     // float 변수에 값을 넣으면 uData에 각각의 바이트로 자동으로 변환되어 저장

 

IEEE754_Float data ;

data. uData[0] = 0xE9 ;  // 각 바이트에 값을 넣으면 fValue 에 자동으로 변환된 float 값이 저장된다. 

data. uData[1] = 0xA6 ;

data. uData[2] = 0x91 ;

data. uData[3] = 0x42 ;

 

 

 

 

반응형
반응형

이전에 JSON을 이용하여 제어프로그램의 Recipe 파일의 저장과 불러오기 기능에 대해 포스팅 했었다. 

그런데 조금 사용하기 불편하고 코딩량이 많아서 다른 수단을 찾다가 YAML 이 최근에 설정파일 저장용으로 많이 사용된다는 것을 보고 사용해 보았다. 코딩량도 상당히 줄었고 무엇보다 항목을 추가하거나 제거할 때 코딩에서 수정해야 하는 부분이 많이 줄어서 현재 모든 프로그램에서 사용하고 있다. 

 

1. YAML 사용하기 위해 Nuget에서 YamlDotNet 을 먼저 설치한다. 

 

2. Yaml 데이타를 저장하거나 불러오기 위한 클래스 생성 : 다양한 종류의 설정항목에 대응하기 위해 generic class 형식으로 만듦

public class YamlControl<T>
{ 
    public bool SaveYaml(string FileName, T data)
    {
        bool result = true;

        try
        {
            var serializer = new SerializerBuilder().Build().Serialize(data);

            using StreamWriter sw = new StreamWriter(FileName);
            sw.Write(serializer);
        }
        catch
        {
            result = false;    
        }

        return result;
    }

    public bool LoadYaml(string FileName, ref T objectData)
    {
        bool result = true;

        try
        {
            var yaml = File.ReadAllText(FileName);
            objectData = new DeserializerBuilder().Build().Deserialize<T>(yaml);
        }
        catch
        {
            result = false;
        }            

        return result;
    }
}

 

3. 설정항목에 대한 클래스 생성 : 아래 클래스는 프로그램에 따라 변경해야 하며 아래 내용은 예시적으로 만든 임의의 클래스 

public class RecipeInform
{
    public BurnInParam burnInParam = new BurnInParam();
    public SweepMeasParam sweepMeasParam = new SweepMeasParam();

    public class BurnInParam
    {
        public bool Measurement { get; set; }
        public double Bias { get; set; }
        public double Step { get; set; }
    }
    
    public class SweepMeasParam
    {
        public double StartCurrent { get; set; }
        public double StopCurrent { get; set; }
        public double StepCurrent { get; set; }
    }
}

 

4. 사용방법 

항목 불러오기 

YamlControl<RecipeInform> yamlControl = new YamlControl<RecipeInform>();
string filename = string.Format("{0}{1}.yaml", Global.conditionFolder, comboCondition.Text);
RecipeInform recipe = new RecipeInform();
yamlControl.LoadYaml(filename, ref recipe);


항목 저장하기 

RecipeInform recipe = new RecipeInform();
recipe.burnInParam.Measurement = checkBurnInMeas.Checked;
recipe.burnInParam.Bias = double.Parse(textBiasCurrent.Text);
recipe.burnInParam.Step = double.Parse(textBurnInStepCurrent.Text);
recipe.sweepMeasParam.StartCurrent = double.Parse(textStartCurrent.Text);
recipe.sweepMeasParam.StopCurrent = double.Parse(textStopCurrent.Text);
recipe.sweepMeasParam.StepCurrent = double.Parse(textStepCurrent.Text);


string filename = string.Format("{0}{1}.yaml", Global.conditionFolder, comboCondition.Text);
YamlControl<RecipeInform> yamlControl = new YamlControl<RecipeInform>();
yamlControl.SaveYaml(filename, recipe) ;
반응형
반응형

 

    public class FML300 
    {
        private readonly SerialPort commPort ;
        private readonly ModbusSerialMaster modbusSerialMaster ;

        public FML300(ModbusSerialMaster modbusSerial)
        {
            modbusSerialMaster = modbusSerial;
        }
        //---------------------------------------------------------------------------
        public FML300(SerialPort serialPort)
        {
            commPort = serialPort ;
            modbusSerialMaster = ModbusSerialMaster.CreateRtu(commPort) ;
        }
        //---------------------------------------------------------------------------
        public FML300(string portNumber)
        {
            try
            {
                commPort = new SerialPort(portNumber, 38400) ;
                commPort.Parity = Parity.None;
                commPort.StopBits = StopBits.One;
                commPort.ReadTimeout = 100;
                commPort.WriteTimeout = 100;
                commPort.Open();

                modbusSerialMaster = ModbusSerialMaster.CreateRtu(commPort) ;
            }
            catch
            {
            }
        }
        //---------------------------------------------------------------------------
        public bool InstantaneousFlowRate(byte DeviceAddress, ref double FlowRate)
        {
            bool result = true ;

            try
            {
                var buffer = modbusSerialMaster.ReadInputRegisters(DeviceAddress, 3, 1);
                FlowRate = buffer[0] ;
            }
            catch
            {
                result = false ;
            }

            return result ;
        }
        //---------------------------------------------------------------------------
        public bool FlowTemperature(byte DeviceAddress, ref double Temperature)
        {
            bool result = true ;    

            try
            { 
                var buffer = modbusSerialMaster.ReadInputRegisters(DeviceAddress, 4, 1) ;
                Temperature = buffer[0] ;
                if(Temperature > 6000) Temperature = 0;     // 물이 흐르지 않으면 온도가 큰값이 읽혀진다. 
            }
            catch
            {
                result = false ;
            }

            return result ;
        }
        //---------------------------------------------------------------------------
        public int FlowRateAndTemperature(byte DeviceAddress, ref double FlowRate, ref double Temperature)
        {
            int result = 0 ;    

            try
            { 
                var buffer = modbusSerialMaster.ReadInputRegisters(DeviceAddress, 1, 4) ;

                FlowRate = (double)buffer[1] / 100.0 ;
                Temperature = (double)buffer[2] / 10.0 ;

                if(Temperature > 6000) Temperature = 0;     // 물이 흐르지 않으면 온도가 큰값이 읽혀진다. 
            }
            catch
            {
                result = -1 ;
            }

            return result ;
        }
        //---------------------------------------------------------------------------
    }
반응형
반응형

 

    public class RodemSeries
    {
        private SerialPort commPort;
        public double InputVoltage { get; }
        public double OutputVoltage { get; }
        public double Temperature { get; }
        public int BlackOut { get; }        // 0:No, 1:Yes
	    public int LowBattery { get; }      // 0:No, 1:Yes

        public RodemSeries(SerialPort CommPort)
        {
            commPort = CommPort;
        }
        //---------------------------------------------------------------------------
        public bool Initialize(string ModelName)
        {
            bool result = false ;
	        byte[] sendBuffer = new byte[] {(byte)'I', 0x0d} ;

            try
            {
                commPort.Write(sendBuffer, 0, sendBuffer.Length);
                Thread.Sleep(500);

                if(commPort.BytesToRead > 0)
                {
                    byte[] readBuffer = new byte[commPort.BytesToRead + 5];
                    commPort.Read(readBuffer, 0, commPort.BytesToRead);
                    string str = Encoding.Default.GetString(readBuffer);
                    result = str.Contains(ModelName);
                }
            }
            catch 
            { 
                result = false; 
            }

            return result ;
        }
        //---------------------------------------------------------------------------
        public bool ReadStatus()
        {
            bool result = true;
	        byte[] sendBuffer = new byte[] {(byte)'Q', (byte)'1', 0x0d} ;

            try
            {
                commPort.Write(sendBuffer, 0, sendBuffer.Length);
                Thread.Sleep(500);

                if(commPort.BytesToRead > 0)
                {
                    byte[] readBuffer = new byte[commPort.BytesToRead + 5];
                    commPort.Read(readBuffer, 0, commPort.BytesToRead);

                    if(readBuffer[0] != (byte)'(') result = false;
                    else
                    {
                        result = StatusInformation(readBuffer);    
                    }                    
                }
                else
                {
                    result = false;
                }
            }
            catch 
            { 
                result = false; 
            }

            return result;
        }
        //---------------------------------------------------------------------------
        private bool StatusInformation(byte[] Buffer)
        {
            bool result = true;

            try
            {
                string str = Encoding.Default.GetString(Buffer);
                string[] data = str.Split(' ');

                OutputVoltage = double.Parse(data[2]) ;
                Temperature = double.Parse(data[6]) ;

                BlackOut = Buffer[38] - 0x30;
                LowBattery = Buffer[39] - 0x30;

                if (BlackOut > 1 || BlackOut < 0) BlackOut = 0;
                if (LowBattery > 1 || LowBattery < 0) LowBattery = 0;
            }
            catch
            {
                result = false;
            }

            return result;
        }
        //---------------------------------------------------------------------------
    }
반응형
반응형

최근 레이저의 광출력을 측정하는 계측기를 사용하는 과정에서 해당 계측기 업체가 제공하는 소스를 그대로 복사해서 함수를 하나 만들어서 사용을 했습니다. 

 

그런데 이 장비가 번인 장비여서 기본적으로 1000시간 이상 구동을 해야 하는데 200~300 시간 정도 지나면 프로그램이 다운이 되어 버리는 증상이 나타났습니다. 아무리 소스 코드를 봐도 오류를 찾을 수 없었는데 혹시나 해서 계측기 업체가 제공한 소스 코드를 Chat-GPT 에 돌려서 보니 VARIANT 형을 사용하는 과정에서 변수를 Clear 하지 않아서 오버플로우가 발생하는 현상이 있는 것을 찾아 냈습니다. 

아래 함수가 이번에 수정한 부분인데  VARIANT value, param1, param2;  이 세개의 변수가 VARIANT 형으로 선언이 되어 있고 그 중에서 value 변수는 배열 형식으로 데이타를 받아 오도록 되어 있습니다. 

 

VARIANT 변수가 단일 변수형식일 경우에는 상관 없다고 하는데 배열이나 문자열 같이 데이타가 큰 형식으로 변경이 될 경우에는 반드시 변수를 Clear 해주어야만 메모리 오버플로우가 발생하지 않습니다. 

 

아래 함수를 try finally 구문을 이용해서 함수가 마무리되면  VariantClear() 를 이용해서 메모리 해제를 시켜 주었고 이렇게 코드를 수정한 이후에는 프로그램이 다운되지 않고 정상적으로 잘 동작합니다. 

 

C++ builder 에서는 Variant 라는 형으로 VARIANT 형을 사용하는데 빌더에서 만든 Variant 형은 자동으로 메모리 해제가 되도록 프로그램 되어 있어서 사용자가 메모리 해제를 신경쓰지 않아도 됩니다. 

 

bool __fastcall TAging::readPowerData(const int Channel)
{
	VARIANT value, param1, param2;
	int check = -1 ;
	double Power ;
	bool result = true ;

	try
	{
		try
		{
			check = g_PowerMeter->GetData(ChannelInformation[Channel].Handle, 0, &value, &param1, &param2) ;

			if(check != 0) result = false ;
			else
			{
				std::vector<double> PowerData = getDoubles(value) ;

				if(PowerData.size() > 0)
				{
					Power = PowerData[0] ;
					Power = (Power * MachineData.PowerCalibration[Channel][0]) + MachineData.PowerCalibration[Channel][1] ;

					ChannelInformation[Channel].MeasData.Power = Power ;
				}
				else
				{
					result = false ;
				}
			}
		}
		catch(...)
		{
			check = -1 ;
			result = false ;
		}
	}
	__finally
	{
		VariantClear(&value);
		VariantClear(&param1);
		VariantClear(&param2);
	}

	return result ;
}

 

반응형

+ Recent posts