.net MAUI 에서 앱 아이콘 변경 방법

Software/C#|2025. 12. 2. 14:04
반응형

안드로이드 앱을 만들기 위해 MAUI를 이용해서 만들었습니다. 

앱을 만들고 나서 아이콘을 변경하기 위해 인터넷에 돌아 다니는 방법을 사용해서 변경했는데 왠지 기본 아이콘에서 전혀 변경이 안돼어서 몇일을 찾고 찾아서 드디어 해결을 했습니다. 

 

솔루션 탐색기에서 Platforms→Android→Resources→AndroidManifest.xml 파일을 클릭하면

애플리케이션 세부정보가 나타납니다.

애플리케이션 아이콘 항목을 아래와 같이 변경을 해 주어야만 아이콘이 변경이 됩니다.

Resources→AppIcon 위치에 원하는 이미지를 넣습니다. (예 : Icon.png)

그런후 프로젝트 파일을 엽니다. (확장자 : csproj)

아래 내용을 추가 합니다.

<!-- App Icon -->
	<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\icon.png" Color="#FFFFFF" />

그리고 다시 컴파일후에 실행하면 아이콘이 변경이 됩니다. 
반응형

댓글()

IEEE754 단정밀(32Bit) 데이타와 float 형 변수의 상호 변환 - C#

Software/C#|2025. 3. 31. 13:22
반응형

   

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

반응형

댓글()

IEEE754 단정밀(32Bit) 데이타와 float 형 변수의 상호 변환 - C++

Software/C++ Builder|2025. 3. 17. 16:05
반응형

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

 

 

 

 

반응형

댓글()

YAML 로 프로그램 설정 데이타 저장 및 불러오기 - C#

Software/C#|2025. 3. 14. 10:07
반응형

이전에 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) ;
반응형

댓글()

Flow Meter(FML300-D SERIES) 제어 클래스 - C#

Software/C#|2025. 3. 11. 15:35
반응형

 

    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 ;
        }
        //---------------------------------------------------------------------------
    }
반응형

댓글()

Chroma IT 의 UPS(Rodem Series) 제어 클래스 - C#

Software/C#|2025. 3. 11. 15:32
반응형

 

    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;
        }
        //---------------------------------------------------------------------------
    }
반응형

댓글()

VARIANT 형의 변수를 사용할 때 주의할 점 (C++)

Software/C++ Builder|2025. 3. 11. 15:18
반응형

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

 

그런데 이 장비가 번인 장비여서 기본적으로 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 ;
}

 

반응형

댓글()

Nice Cool 의 칠러 제어 (CM Series)

Software/C#|2024. 10. 29. 15:19
반응형

국내 나이스 쿨의 칠러 제어를 위한 코드 입니다. 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;
        }
    }

반응형

댓글()