반응형

ScottPlot 가 버젼 5로 업데이트 되고 사용하는 방법이나 함수가 많이 변경이 된듯 합니다. 

이번 포스팅에서는 버젼 5를 기준으로 멀티 축을 그리는 방법에 대한 코드를 공유합니다. 

 

MultiAxis.zip
0.13MB

        (소스 코드)

    public partial class Form1 : Form
    {
        private ScottPlot.AxisPanels.RightAxis? YRight2 = null;  // 오른쪽 Y축 추가 
        private ScottPlot.AxisPanels.LeftAxis? YLeft2 = null;    // 왼쪽 Y축 추가 

        public Form1()
        {
            InitializeComponent();
            graphInit();
        }
        private void graphInit()
        {
            YLeft2 = formsPlot1.Plot.Axes.AddLeftAxis();        // 폼 디자인에 올려진 그래프 컨트롤에 왼쪽축을 추가하고 맵핑시킨다. 
            YRight2 = formsPlot1.Plot.Axes.AddRightAxis();      // 폼 디자인에 올려진 그래프 컨트롤에 오른쪽축을 추가하고 맵핑시킨다. 

            formsPlot1.Plot.Axes.Left.Label.Text = "Power";   // 기존 왼쪽 축의 항목을 설정 
            formsPlot1.Plot.Axes.Left.Label.ForeColor = Colors.Red;
            formsPlot1.Plot.Axes.Right.Label.Text = "Voltage";    // 기존 오른쪽 축의 항목을 설정 
            formsPlot1.Plot.Axes.Right.Label.ForeColor = Colors.Green;

            YLeft2.Label.Text = "SE";                           // 추가된 왼쪽 축의 항목을 설정 
            YLeft2.Label.ForeColor = Colors.Blue;
            YRight2.Label.Text = "RD";                          // 추가된 오른쪽 축의 항목을 설정 
            YRight2.Label.ForeColor = Colors.Purple;

            formsPlot1.Plot.Axes.Bottom.Label.Text = "Current"; // Bottom 축의 항목을 설정 
        }

        private void button1_Click(object sender, EventArgs e)
        {
            double[] dataX = { 0, 1, 2, 3, 4, 5};
            double[] dataLeftY1 = { 0, 10, 100, 300, 500, 800};
            double[] dataLeftY2 = { 0.1, 0.24, 1, 2, 2.1, 2.1};
            double[] dataRightY1 = { 0, 2, 2.1, 2.2, 2.3, 2.4};
            double[] dataRightY2 = { 100, 10, 5, 5.1, 5.2, 5.1 };

            var sigLeft1 = formsPlot1.Plot.Add.SignalXY(dataX, dataLeftY1, Colors.Red);       // 시그널 생성
            var sigLeft2 = formsPlot1.Plot.Add.SignalXY(dataX, dataLeftY2, Colors.Blue);
            var sigRight1 = formsPlot1.Plot.Add.SignalXY(dataX, dataRightY1, Colors.Green);
            var sigRight2 = formsPlot1.Plot.Add.SignalXY(dataX, dataRightY2, Colors.Purple);

            sigLeft1.Axes.YAxis = formsPlot1.Plot.Axes.Left;            // 시그널과 축을 맵핑 
            sigLeft2.Axes.YAxis = YLeft2!;
            sigRight1.Axes.YAxis = formsPlot1.Plot.Axes.Right;
            sigRight2.Axes.YAxis = YRight2!;

            graphScale(6, 1000, 4, 5, 100);   // 그래프 스케일 조정 
        }

        private void graphScale(double X_Axis, double Y_Left1, double Y_Left2, double Y_Right1, double Y_Right2)
        {
            formsPlot1.Plot.Axes.Bottom.Min = 0;
            formsPlot1.Plot.Axes.Bottom.Max = X_Axis;

            formsPlot1.Plot.Axes.Left.Min = 0;
            formsPlot1.Plot.Axes.Left.Max = Y_Left1;

            formsPlot1.Plot.Axes.Right.Min = 0;
            formsPlot1.Plot.Axes.Right.Max = Y_Right1;

            YLeft2!.Min = 0;
            YLeft2!.Max = Y_Left2;

            YRight2!.Min = 0;
            YRight2!.Max = Y_Right2;

            formsPlot1.Refresh();       // 그래프 갱신
        }
    }

 

 

출력결과 표시

 

반응형
반응형

델리게이트와 lambda 에 대해 공부를 하면서 어느경우에 써먹을 수 있나 생각을 많이 하게 됩니다. 

이번 포스팅에서는 간단하게 사칙연산을 위한 lambda 와 Func 델리케이트의 활용에 대해 포스팅 해 보았습니다. 

 

 

위와 같이 폼을 하나 구성하고 콤보 박스에 각연산에 대한 항목을 Items 에 추가하고 이벤트 함수를 만들면 됩니다. 

 

        private double calculate(Func<double, double, double> func, double a, double b)
        {
            var result = func(a, b);
            return result;
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            var a = double.Parse(textBox1.Text);
            var b = double.Parse(textBox2.Text);

            switch(comboBox1.SelectedIndex) 
            {
                case 0: textBox3.Text = calculate((i, j) => { return (i + j); }, a, b).ToString(); break;
                case 1: textBox3.Text = calculate((i, j) => { return (i - j); }, a, b).ToString(); break;
                case 2: textBox3.Text = calculate((i, j) => { return (i * j); }, a, b).ToString(); break;
                case 3: textBox3.Text = calculate((i, j) => { return (i / j); }, a, b).ToString(); break;
            }
        }

 

* calculate 함수는 Func 델리케이트와 피 연산자 값을  파라메터로 받는 함수입니다. 

* 콤보막스의 SelectedIndexChanged 이벤트 함수에서 switch-case 문을 이용하여 각각의 연산에 맞는 lambda 함수를 만들어 calculate 함수의 파라메터로 넘겨주면 계산된 결과가 출력이 됩니다. 

반응형
반응형

배열이나 구조체 형식의 List 의 경우 각각의 요소에 대해 배열로 만들 경우가 발생한다. 

형식 자체를 배열로 만드는 것은 ToArray() 를 사용할 수 있지만 각각의 요소를 배열로 만들 경우는 아래와 같이 사용하면 된다. 

 

class Point

{

    public int X {  get; set; }

    public int Y {  get; set; }

}

 

List<Point> ListPoints = new List<Point>() ;

 

배열변환 #1  :

    var arrayX = ListPoints.ConvertAll(s => s.X).ToArray() ; 

    var arrayY = ListPoints.ConvertAll(s => s.Y).ToArray() ;

 

배열변환 #2  :

    var arrayX = ListPoints.Select(s => s.X).ToArray() ; 

    var arrayY = ListPoints.Select (s => s.Y).ToArray() ;

반응형
반응형

장비제어 프로그램 개발시에 시스템의 하드웨어 설정값등을 저장하고 불러 오는 용도로 ini 파일 저장과 yaml 파일 저장 방법을 사용할 수 있도록 테스트 프로그램을 작성해 보았습니다. 

 

Ini_Yaml.zip
0.01MB

 

ini 파일 저장은 인터넷에 있는 Class 를 다운받아 사용하였습니다. yaml 은 YamlDotNet 라이브러리를 사용하였습니다. 

 

코드 작성이나 향후 항목 변경이 되었을 경우 코드 변경에서는 yaml이 편하고 좋습니다. 하지만 프로그램 사용자가 yaml 데이타를 직접 수정할 경우에는 규칙을 지켜서 수정해야 하는 번거로움은 좀 있네요.. 

 

파일에 저장되는 형태는 아래와 같습니다. 

ini 저장 데이타 파일

[Temperature]
Offset-1=12.3
Offset-2=53.987
[System]
Name=test
Item-1=0

 

yaml 저장 데이타 파일

temperature:
  Offset1: 3
  Offset2: 4
system:
  Name: bbb
  Item1: 3
  Item2: 0
  ListItems:
  - 11
  - 22
  - 33

 

아래 코드는 yaml 사용에 대한 내용입니다.

    public class MachineData
    {
        public class Temperature
        {
            public double Offset1 { get; set; }
            public double Offset2 { get; set; }
        }

        public class System
        {
            public string Name { get; set; }
            public int Item1 { get; set; }
            public int Item2 { get; set; }
            public List<double> ListItems { get; set; }

            public System() 
            { 
                ListItems = new List<double>(); 
            }
        }

        public Temperature temperature = new Temperature();
        public System system = new System();
    }

    public class YamlControl<T>
    { 
        public void SaveYaml(string FileName, T data)
        {
            var serializer = SerializeToYaml(data);

            using StreamWriter sw = new StreamWriter(FileName);
            sw.Write(serializer);
        }

        public T LoadYaml(string FileName)
        {
            T machine = DeserializeFromYaml<T>(FileName);

            return machine;
        }

        private string SerializeToYaml<T>(T obj)
        {
            var serializer = new SerializerBuilder().Build();
            return serializer.Serialize(obj);
        }

        // Deserialize YAML to an object
        private T DeserializeFromYaml<T>(string yaml)
        {
            var deserializer = new DeserializerBuilder().Build();
            return deserializer.Deserialize<T>(File.ReadAllText(yaml));
        }
    }
반응형
반응형

RadioButton 사용방법 2번째 입니다. 지난번에 이어 이번에도 Tag 속성을 이용하여 RadioButton 을 간단하게 사용하는 방법을 구현합니다. 

위 그림과 같이 GroupBox 안에 3개의 RadioButton 을 넣습니다. 그리고 순서대로 Tag 속성을 0,1,2 로 설정합니다.

첫번째 메뉴인 등심에 Checked 를 하고 GroupBox의 Tag 속성을 0으로 설정합니다.

 

그리고 RadioButton 3개의 Clicked 이벤트를 아래 코드와 같이 동일한 이벤트 함수로 구현합니다.

        private void MenuClicked(object sender, EventArgs e)
        {
            RadioButton radio = sender as RadioButton;
            if (radio != null)
            {
                groupBox1.Tag = radio.Tag;   // 눌려진 버튼의 tag 값을 groupBox 의 Tag 값에 저장한다. 
            }
        }

 

그리고 주문 버튼에 대한 이벤트 함수를 아래와 같이 switch 문을 이용해서 구현하면 간단하게 RadioButton 에 대한 선택 구현을 할수 있습니다.

 

        private void btnOrder_Click(object sender, EventArgs e)
        {
            var menu = int.Parse(groupBox1.Tag.ToString());
            switch (menu)
            {
                case 0: textBox1.Text = "등심주문"; break;
                case 1: textBox1.Text = "안심주문"; break;
                case 2: textBox1.Text = "갈비주문"; break;
            }
        }

반응형
반응형

특정 함수를 구현함에 있어 함수에서의 작업이 언제 끝날지 알수 없는 작업을 구현할 경우 무한루프를 사용하게 됩니다., 

무한루프 안에서 특정 작업이 마무리 되면 break 로 빠져 나오겠지만 어떠한 문제로 인해 작업이 진행되지 않고 계속 무한루프를 돌게 되는 경우가 았습니다. 이런경우 Timeout  기능을 넣어서 일정시간이 지나도 작업이 끝나지 않을경우 무한루프를 빠져 나오도록 프로그램을 구현합니다. 

 

Timeout 기능은 여러 방법이 있지만 이번 포스팅에서는 Stopwatch 객체를 이용해서 구현해 보았습니다.

 

public int WorkFunction(int TimeOut)
{
    int result = 0 ;
    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();  // stopwatch 객체 생성
    
    sw.Start() ; // stopwatch 시작
    
    try
    {
    	while(true)
        {
        	// workflow 구현
            
            var time = (int)(sw.ElapsedMilliseconds / 1000);
            if (time >= TimeOut)           //  Timeout 시간 경과 
            {
                result = -1;
                break;  
            }
        }
    }
    finally
    {
    	sw.Stop() ;   // stopwatch 종료
    }
    
    return result ;
}
반응형
반응형

장비제어에서 카메라에 모터를 장착하여 Auto Fucusing 을 사용하는 경우가 많이 있습니다. 

전문적인 비젼 전문가는 아니라서 최적의 Auto Fucusing 알고리즘을 코딩하지는 못하지만 그나마 간단하게 실무에서 사용이 가능한 Auto Fucusing 알고리즘 코드를 올려 놓습니다. 

 

이전 카메라 제어 소스(OpenCVSharp 카메라 제어 #4)의 CameraThread 클래스에 아래 함수를 추가 하시면 됩니다. 

public double calculate_focus_score()
{
    double score = 0;
    Mat median = new Mat();
    Mat laplacian = new Mat();

    try 
    { 
        Cv2.MedianBlur(sourceFrame, median, 3) ;
        Cv2.Laplacian(median, laplacian, median.Type().Depth, 3, 1, 0);

        Cv2.MeanStdDev(laplacian, out var mean, out var stddev);

        score = stddev.Val0 * stddev.Val0; 
    }
    finally
    { 
        median.Dispose();
        laplacian.Dispose();
    }

    return score ;
}

사용방법 / 순서 

 

1. 카메라 모터를 초기위치(최상단위치)로 옮겨 놓는다. 

2. 모터를 일정높이 만큼 낮춘다. 

3. calculate_focus_score() 함수를 실행하여 리턴되는 값과 현재 모터 위치값을 매핑한다. 

4. 2~3을 일정 높이에 다다를 때까지 반복한다. 

5. calculate_focus_score() 함수 실행 결과값이 가장 큰값이 포커싱이 가장 잘된 위치이므로

    값이 가장큰 모터 위치로 카메라를 이동한다. 

 

반응형
반응형

C++ 빌더 툴을 사용하다가 C#으로 전환하면서 하나 불편한 부분이 바로 RadioButton 이었습니다. 

빌더 툴에서 RadioGroup 라는 컴포넌트가 있어서 RadioGroup 의 Index 속성을 이용하여 코딩을 줄일 수가 있었는데 C#에는 그와 같은 컴포넌트가 없어서 너무 불편했습니다. 

 

아래 그림처럼 RadioButton 을 만들었을 경우 

일반적으로는 아래의 코드처럼 각각의 메뉴에 이벤트를 만들어 사용하게 됩니다. 

public partial class Form1 : Form
{
   enum Meat{등심,안심,갈비}

   private Meat _selectedMeat;

   public Form1()
   {
      InitializeComponent();
   }
   private void radioButton1_Click(object sender, EventArgs e)
   {
      this._selectedMeat = Meat.등심;
      DisplayMenu();
   }
   private void radioButton2_Click(object sender, EventArgs e)
   {
      this._selectedMeat = Meat.안심;
      DisplayMenu();
   }
   private void radioButton3_Click(object sender, EventArgs e)
   {
      this._selectedMeat = Meat.갈비;
      DisplayMenu();
   }
   private void DisplayMenu()
   {
      lblMsg.Text = string.Format("{0} 메뉴를 선택하셨습니다.", this._selectedMeat.ToString());
   }
}

 

위의 코드를 Tag 속성을 이용해서 간단하게 줄여 보겠습니다. 

먼저 메뉴 항목의 각 RadioButton Tag 속성을  (등심:0, 안심:1, 갈비:2) 로 설정합니다. 

그리고 3개의 RadioButton Click이벤트를 각각 만들지 말고 하나의 동일한 이벤트 함수를 만들어 공유 합니다. 

코드는 아래와 같습니다. 

 

public partial class Form1 : Form
{
    string[] Meat = { "등심", "안심", "갈비" };

    public Form1()
    {
        InitializeComponent();
    }

    private void RadioButton_Click(object sender, EventArgs e)
    {
        var select = int.Parse( (sender as RadioButton).Tag.ToString() );
        lblMsg.Text = string.Format("{0} 메뉴를 선택하셨습니다.", Meat[select]);
    }
}

4개의 함수가 하나의 함수로 줄어들고 코드도 2줄로 확 줄었습니다. 

이렇게 Tag를 이용하면 차후에 Switch 구문을 사용하여 코딩하기도 굉장히 쉽습이다. 

 

또다른 방법으로 패턴매칭을 이용하여 처리하는 방법 입니다. 위의 코드보다 복잡하지만 유용하게 사용되는 형식읠 때도 있습니다.

public partial class Form1 : Form
{
    string[] Meat = { "등심", "안심", "갈비" };

    public Form1()
    {
        InitializeComponent();
    }

    private void RadioButton_Click(object sender, EventArgs e)
    {
        RadioButton radio = sender as RadioButton ;
        
        _ = radio?.Tag switch
        {
            "0" => lblMsg.Text = string.Format("{0} 메뉴를 선택하셨습니다.", Meat[0]) ,
            "1" => lblMsg.Text = string.Format("{0} 메뉴를 선택하셨습니다.", Meat[1]) ,
            "2" => lblMsg.Text = string.Format("{0} 메뉴를 선택하셨습니다.", Meat[2]) ,
            _ => lblMsg.Text = "주문이 없습니다." 
        }
    }
}
반응형

+ Recent posts