반응형

   

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);
       

반응형
반응형

계측기와 데이타를 주고 받는 경우 계측기에서 측정된 데이타가 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 ;
}

 

반응형
반응형

국내 나이스 쿨의 칠러 제어를 위한 코드 입니다. RS232통신을 통해 제어를 합니다. 

Modbus RTU 방식으로 처리하고 라이브러리는 NModbus4를 사용하였습니다. 

NModbus4 는 Nuget을 통해 설치하시면 됩니다. 

 

 

/****************************************************************************************************
     * Device : Chiller(CM SERIES)
     * Manufacture : NiceCool
     * Devicce Interface : RS232(Modbus RTU)
     * Version : 1.0.0
     * Program : Sihwan Kim
     *****************************************************************************************************/
    public class NiceCool_CMSeries 
    {
        public bool Active { get; set; } = false;
        public int ReadTimeout
        {
            set { modbusSerialMaster.Transport.ReadTimeout = value; }
            get { return modbusSerialMaster.Transport.ReadTimeout; }
        } 

        public int WriteTimeout
        {
            set { modbusSerialMaster.Transport.WriteTimeout = value; }
            get { return modbusSerialMaster.Transport.WriteTimeout; }
        } 

        private byte address;
        private SerialPort commPort ;
        private ModbusSerialMaster modbusSerialMaster ;

        public NiceCool_CMSeries(byte Address, ModbusSerialMaster modbusSerial)
        {
            address = Address;
            modbusSerialMaster = modbusSerial;
        }
        //---------------------------------------------------------------------------
        public NiceCool_CMSeries(byte Address, SerialPort serialPort)
        {
            address = Address;
            commPort = serialPort ;
            modbusSerialMaster = ModbusSerialMaster.CreateRtu(commPort) ;
        }
        //---------------------------------------------------------------------------
        public NiceCool_CMSeries(byte Address, string portNumber)
        {
            address = Address;

            try
            {
                commPort           = new SerialPort() ;
                commPort.PortName  = portNumber ;
                commPort.BaudRate  = 4800;
                commPort.Parity = Systehttp://m.IO.Ports.Parity.Even;
                commPort.Open();

                modbusSerialMaster = ModbusSerialMaster.CreateRtu(commPort) ;
            }
            catch
            {
            }
        }
        //---------------------------------------------------------------------------
        public bool Initialize(bool control)
        {
            bool result = true ;
            double temp = 0;

            ReadTimeout = 300; 
            WriteTimeout = 300;

            commPort.ReadTimeout = 300;
            commPort.WriteTimeout = 300;

            if (control) result = WaterTemperature(ref temp) ;

            return result;
        }
        //---------------------------------------------------------------------------
        public bool WaterTemperature(ref double Temperature)
        {
            bool result = true;

            try
            {
                var value = modbusSerialMaster.ReadHoldingRegisters(address, 259, 1) ;
                Temperature = value[0] / 10.0 ;
            }
            catch
            {
                result = false ;
            }

            return result;
        }
        //---------------------------------------------------------------------------
        public void SetWaterTemperature(double Temperature)
        {
            ushort setTemp = (ushort)(Temperature * 10) ;

            try
            { 
                modbusSerialMaster.WriteSingleRegister(address, 271, setTemp) ;
            }
            catch { }
        }
        //---------------------------------------------------------------------------
        public bool Run(bool Status)
        {
            bool result = true;
            
            try
            { 
                ushort action = 0 ;

                if(Status == true) action = 257 ;
                else action = 256 ;

                modbusSerialMaster.WriteSingleRegister(address, 270, action) ;
                Active = Status ;
            }
            catch
            {
                result= false ;
            }

            return result ;
        }
        //---------------------------------------------------------------------------
        public bool WaterFlow(ref double FlowValue)
        {
            bool result = true;

            try
            { 
                var value = modbusSerialMaster.ReadHoldingRegisters(address, 266, 1) ;
                FlowValue = value[0] / 10.0 ;
            }
            catch
            {
                result = false ;
            }

            return result;
        }
    }

반응형
반응형

 

소코반 게임을 WPF 버젼으로 수정해서 올립니다. 아직 WPF를 공부중이라 MVVM 은 사용을 하지 않고 제작을 하였습니다.

코드는 지속적으로 수정을 진행할 예정입니다. 

소스 코드는 아래 Git에서 다운 받으시면 됩니다. 

 

https://github.com/Sihwan-Kim/PushPush.git

 

아래 파일은 맵화일과 Config파일 입니다. 실행파일이 있는 곳에 넣으시면 됩니다. 

stage.zip
0.00MB

 

Winform 버전의 알고리즘은 그대로 사용하였고 기능적인 부분이 조금 수정되었습니다. 

1. 윈폼 버전에서는 맵의 크기가 10 x 10으로 고정하였지만 WPF 버전에서는 크기를 동적으로 수정할 수 있도록 하였습니다.

2. 윈도우의 크기가 변경되면 맵이 항상 중앙에 위치 하도록 하였습니다. 

 

 

 

반응형

+ Recent posts