5. WaferLine 공장 – 5.3 이벤트로 상호 작용 구현 [Wafer 코팅 시뮬레이션]

안녕하세요. 언제나휴일입니다.

1. 해야 할 일

이번 강의에서는 이벤트로 상호작용을 구현하는 실습입니다.

대리자와 이벤트에 관한 문법적인 사항을 모르신다면 먼저 학습하시면 이해하기 쉬울거예요.

공장 시뮬레이션의 WaferLineForm에 의해 특정 WaferLine에 Wafer 투입, 코팅액 투입, 회전 속도 및 코팅액 낙하 속도 조절 및 WaferLine 코팅 가동 및 멈춤을 수행할 수 있습니다.

그리고 MainForm에서는 WaferLine의 이러한 변화를 인지하여 화면에 표시해야 할 부분이 있습니다.

이를 위해 이벤트를 사용하여 구현하는 실습을 진행할 거예요.

2. 대리자와 이벤트 인자 형식 정의하기

이벤트 멤버는 대리자 형식을 event 키워드와 함께 캡슐화한 멤버입니다.

그리고 event를 목적으로 정의하는 대리자는 반환 형식은 void 입력 인자는 이벤트 전송자와 이벤트 처리 인자로 정의하는  것이 일반적입니다.

여기에서는 Wafer 추가 이벤트, 코팅액 추가 이벤트, Wafer 코팅 완료 이벤트, 코팅액 소진 이벤트, 회전 속도 설정 이벤트, 낙하 속도 설정 이벤트를 정의할 거예요.

이들은 WaferLine에 이벤트로 캡슐화할 것이므로 WaferLineLib에 정의합니다.

이벤트 처리 인자는 이벤트 핸들러를 구현하면서 필요할 것으로 예측할 수 있는 사건 정보를 멤버 속성으로 정의하는 클래스입니다.

설계 과정에서 상호 작용에 관한 어떠한 이벤트가 필요한 지를 판단하는 것이 어렵지 구현은 어렵지 않습니다.

다음은 Wafer 투입 이벤트에 관한 대리자와 이벤트 인자 형식입니다.

이벤트 인자에는 생산 라인 번호와 코팅 전 웨이퍼 개수를 속성으로 제공합니다.

using System;

namespace WaferLineLib
{
    public delegate void AddWaferEventHandler(object sender, AddWaferEventArgs e);
    public class AddWaferEventArgs:EventArgs
    {
        public int No { get; }
        public int BWCnt
        { get; }
        public AddWaferEventArgs(int no, int bwcnt)
        {
            No = no;
            BWCnt = bwcnt;
        }
    }
}

다음은 코팅액 투입 이벤트에 관한 대리자와 이벤트 인자 형식입니다.

이벤트 인자 형식에는 생산 라인 번호와 코팅 액 병수를 속성으로 제공합니다.

using System;

namespace WaferLineLib
{
    public delegate void AddPREventHandler(object sender, AddPREventArgs e);
    public class AddPREventArgs:EventArgs
    {
        public int No { get; }
        public int PCnt { get; }
        public AddPREventArgs(int no,int pcnt)
        {
            No = no;
            PCnt = pcnt;
        }
    }
}

다음은 Wafer 코팅 완료 이벤트에 관한 대리자와 이벤트 인자 형식입니다.

이벤트 인자 형식에는 생산 라인 번호와 코팅 전 Wafer 개수와 코팅 후 Wafer 개수를 속성으로 제공합니다.

using System;

namespace WaferLineLib
{
    public delegate void EndCoatingEventHandler(object sender, EndCoatingEventArgs e);
    public class EndCoatingEventArgs:EventArgs
    {
        public int No { get; }
        public int BWCnt { get; }
        public int AWCnt { get; }
        public EndCoatingEventArgs(int no, int bwcnt,int awcnt)
        {
            No = no;
            BWCnt = bwcnt;
            AWCnt = awcnt;
        }
    }
}

다음은 코팅액 소진 이벤트에 관한 대리자와 이벤트 인자 형식입니다.

이벤트 인자 형식에서는 생산 라인 번호를 제공합니다.

using System;

namespace WaferLineLib
{
    public delegate void EndPREventHandler(object sender, EndPREventArgs e);
    public class EndPREventArgs:EventArgs
    {
        public int No { get; }
        public EndPREventArgs(int no)
        {
            No = no;
        }
    }
}

다음은 회전 속도 설정 이벤트에 관한 대리자와 이벤트 인자 형식입니다.

이벤트 인자 형식에서는 생산 라인 번호와 회전 속도를 속성으로 제공합니다.

using System;

namespace WaferLineLib
{
    public delegate void SetSpeedEventHandler(object sender, SetDropEventArgs e);
    public class SetSpeedEventArgs:EventArgs
    {
        public int No { get; }
        public int Speed { get; }
        public SetSpeedEventArgs(int no,int speed)
        {
            No = no;
            Speed = speed;
        }
    }
}

다음은 낙하 속도 설정 이벤트에 관한 대리자와 이벤트 인자 형식입니다.

이벤트 인자 형식에서는 생산 라인 번호와 낙하 속도를 속성으로 제공합니다.

using System;

namespace WaferLineLib
{
    public delegate void SetDropEventHandler(object sender, SetDropEventArgs e);
    public class SetDropEventArgs:EventArgs
    {
        public int No { get; }
        public int Drop { get; }
        public SetDropEventArgs(int no, int drop)
        {
            No = no;
            Drop = drop;
        }
    }
}

3. WaferLine에 이벤트 멤버 캡슐화 및 처리

이제 WaferLine에 이벤트 멤버를 캡슐화합니다.

    public class WaferLine : IEnumerable<Wafer>
    {
        public event AddWaferEventHandler AddedWafer;
        public event AddPREventHandler AddedPR;
        public event SetSpeedEventHandler SettedSpeed;
        public event SetDropEventHandler SettedDrop;
        public event EndPREventHandler EndedPR;
        public event EndCoatingEventHandler EndedCoating;
        ... 생략...

Wafer를 추가하는 InWafer 메서드에서 이벤트를 통보하는 코드를 추가합니다.

        public int InWafer(int wcnt)
        {
            int avail = 200 - BWCnt;
            if (wcnt > avail)
            {
                wcnt = avail;
            }
            for (int i = 0; i < wcnt; i++)
            {
                bwafers.Add(new Wafer());
            }
            if(AddedWafer != null)
            {
                AddedWafer(this, new AddWaferEventArgs(No, BWCnt));
            }
            return bwafers.Count;
        }

코팅액을 투입하는 InPr 메서드에서 이벤트를 통보하는 코드도 추가합니다.

        public int InPr(int pcnt)
        {
            int avail = 20 - PCnt;
            if (pcnt > avail)
            {
                pcnt = avail;
            }
            PCnt += pcnt;
            if(AddedPR != null)
            {
                AddedPR(this, new AddPREventArgs(No, PCnt));
            }
            return PCnt;
        }

회전 속도를 설정하는 SetSpin 메서드에도 이벤트를 통보하는 코드를 추가합니다.

        public void SetSpin(int spin)
        {
            Spin = spin;
            if(SettedSpeed != null)
            {
                SettedSpeed(this, new SetDropEventArgs(No, spin));
            }
        }

낙하 속도를 설정하는 SetDrop 메서드에도 이벤트를 통보하는 코드를 추가합니다.

        public void SetDrop(int drop)
        {
            Drop = drop;
            if(SettedDrop != null)
            {
                SettedDrop(this, new SetDropEventArgs(No, drop));
            }
        }

Coating 메서드에서 코팅액이 소진하는 부분과 하나의 Wafer를 코팅 완료하는 부분에서 이벤트를 통보하는 코드를 추가합니다.

        public bool Coating()
        {
            if (nowp == 0)
            {
                if(EndedPR != null)
                {
                    EndedPR(this, new EndPREventArgs(No));
                }
                if (PCnt == 0)
                {
                    return false;
                }
                nowp = 1000;
                PCnt--;
            }
            if (nwafer == null)
            {
                if (bwafers.Count == 0)
                {
                    return false;
                }
                nwafer = bwafers[0];
                bwafers.RemoveAt(0);
            }
            nwafer.Coating(rand.Next(70, 100));
            nowp--;
            if (nwafer.Increment() == false)
            {
                awafers.Add(nwafer);
                nwafer = null;
                if(EndedCoating != null)
                {
                    EndedCoating(this, new EndCoatingEventArgs(No, BWCnt, AWCnt));
                }
            }
            return true;
        }

현재까지 작성한 WaferLine 클래스의 소스 코드는 다음과 같습니다.

using System;
using System.Collections;
using System.Collections.Generic;
using WaferLineLib;

namespace WaferLineLib
{
    public class WaferLine : IEnumerable<Wafer>
    {
        public event AddWaferEventHandler AddedWafer;
        public event AddPREventHandler AddedPR;
        public event SetSpeedEventHandler SettedSpeed;
        public event SetDropEventHandler SettedDrop;
        public event EndPREventHandler EndedPR;
        public event EndCoatingEventHandler EndedCoating;
        public int No { get; }
        public int Spin
        {
            get;
            set;
        }
        public int Drop
        {
            get;
            set;
        }
        public WaferLine(int no)
        {
            No = no;
            Spin = 1000;
            Drop = 20;
        }
        List<Wafer> bwafers = new List<Wafer>();
        List<Wafer> awafers = new List<Wafer>();
        public Wafer LastWafer
        {
            get
            {
                if (awafers.Count == 0)
                {
                    return null;
                }
                return awafers[awafers.Count - 1];
            }
        }
        public void EndCoating(int bwcnt, int awcnt)
        {
            while (bwafers.Count > bwcnt)
            {
                bwafers.RemoveAt(0);
            }
            while (awafers.Count < awcnt)
            {
                awafers.Add(null);
            }
        }
        Wafer nwafer;
        int nowp;//1병은 1000쉘을 코팅할 수 있다.
        public Wafer Now { get { return nwafer; } }
        public int BWCnt
        {
            get { return bwafers.Count; }
        }
        public int InWafer(int wcnt)
        {
            int avail = 200 - BWCnt;
            if (wcnt > avail)
            {
                wcnt = avail;
            }
            for (int i = 0; i < wcnt; i++)
            {
                bwafers.Add(new Wafer());
            }
            if(AddedWafer != null)
            {
                AddedWafer(this, new AddWaferEventArgs(No, BWCnt));
            }
            return bwafers.Count;
        }
        public int AWCnt
        {
            get { return awafers.Count; }
        }
        public int PCnt { get; set; }
        public int NPcnt { get { return nowp; } }
        public int InPr(int pcnt)
        {
            int avail = 20 - PCnt;
            if (pcnt > avail)
            {
                pcnt = avail;
            }
            PCnt += pcnt;
            if(AddedPR != null)
            {
                AddedPR(this, new AddPREventArgs(No, PCnt));
            }
            return PCnt;
        }
        public void SetSpin(int spin)
        {
            Spin = spin;
            if(SettedSpeed != null)
            {
                SettedSpeed(this, new SetDropEventArgs(No, spin));
            }
        }
        public void SetDrop(int drop)
        {
            Drop = drop;
            if(SettedDrop != null)
            {
                SettedDrop(this, new SetDropEventArgs(No, drop));
            }
        }
        Random rand = new Random();
        public bool Coating()
        {
            if (nowp == 0)
            {
                if(EndedPR != null)
                {
                    EndedPR(this, new EndPREventArgs(No));
                }
                if (PCnt == 0)
                {
                    return false;
                }
                nowp = 1000;
                PCnt--;
            }
            if (nwafer == null)
            {
                if (bwafers.Count == 0)
                {
                    return false;
                }
                nwafer = bwafers[0];
                bwafers.RemoveAt(0);
            }
            nwafer.Coating(rand.Next(70, 100));
            nowp--;
            if (nwafer.Increment() == false)
            {
                awafers.Add(nwafer);
                nwafer = null;
                if(EndedCoating != null)
                {
                    EndedCoating(this, new EndCoatingEventArgs(No, BWCnt, AWCnt));
                }
            }
            return true;
        }
        public override string ToString()
        {
            return string.Format("WaferLine No:{0}", No);
        }

        public IEnumerator<Wafer> GetEnumerator()
        {
            return awafers.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return awafers.GetEnumerator();
        }
    }
}

4. WaferLineForm에 이벤트 관련 코드 구현

WaferLineForm에 WaferLine관련 이벤트를 멤버로 캡슐화합니다.

    public partial class WaferLineForm : Form
    {
        public event AddWaferEventHandler AddedWafer;
        public event AddPREventHandler AddedPR;
        public event SetSpeedEventHandler SettedSpeed;
        public event SetDropEventHandler SettedDrop;
        public event EndPREventHandler EndedPR;
        public event EndCoatingEventHandler EndedCoating;
        ... 중략...

생성자에서 WaferLine에 이벤트 핸들러를 등록합시다.

        public WaferLineForm(WaferLine wl)
        {
            InitializeComponent();
            WLine = wl;
            WLine.AddedWafer += WLine_AddedWafer;
            WLine.AddedPR += WLine_AddedPR;
            WLine.EndedCoating += WLine_EndedCoating;
            WLine.EndedPR += WLine_EndedPR;
            WLine.SettedSpeed += WLine_SettedSpeed;
            WLine.SettedDrop += WLine_SettedDrop;
        }

각 이벤트 핸들러에서는 이벤트를 통보합니다.

여기에서 이벤트 핸들러는 WaferLine에 의해 이벤트를 통보 받은 것을 그대로 ByPass해 줍니다.

        private void WLine_SettedDrop(object sender, SetDropEventArgs e)
        {
            if(SettedDrop != null)
            {
                SettedDrop(this, e);
            }
        }

        private void WLine_SettedSpeed(object sender, SetDropEventArgs e)
        {
            if(SettedSpeed != null)
            {
                SettedSpeed(this, e);
            }
        }

        private void WLine_EndedPR(object sender, EndPREventArgs e)
        {
            if(EndedPR != null)
            {
                EndedPR(this, e);
            }
        }

        private void WLine_EndedCoating(object sender, EndCoatingEventArgs e)
        {
            if(EndedCoating != null)
            {
                EndedCoating(this, e);
            }
        }

        private void WLine_AddedPR(object sender, AddPREventArgs e)
        {
            if(AddedPR != null)
            {
                AddedPR(this, e);
            }
        }

        private void WLine_AddedWafer(object sender, AddWaferEventArgs e)
        {
            if(AddedWafer != null)
            {
                AddedWafer(this, e);
            }
        }

현재까지 WaferLineForm.cs 코드는 다음과 같습니다.

using System;
using System.Windows.Forms;
using WaferLineLib;

namespace WaferLine_공장_시뮬레이션
{
    public partial class WaferLineForm : Form
    {
        public event AddWaferEventHandler AddedWafer;
        public event AddPREventHandler AddedPR;
        public event SetSpeedEventHandler SettedSpeed;
        public event SetDropEventHandler SettedDrop;
        public event EndPREventHandler EndedPR;
        public event EndCoatingEventHandler EndedCoating;

        public WaferLine WLine { get; }
        public int No { get { return WLine.No; } }
        public WaferLineForm(WaferLine wl)
        {
            InitializeComponent();
            WLine = wl;
            WLine.AddedWafer += WLine_AddedWafer;
            WLine.AddedPR += WLine_AddedPR;
            WLine.EndedCoating += WLine_EndedCoating;
            WLine.EndedPR += WLine_EndedPR;
            WLine.SettedSpeed += WLine_SettedSpeed;
            WLine.SettedDrop += WLine_SettedDrop;
        }

        private void WLine_SettedDrop(object sender, SetDropEventArgs e)
        {
            if(SettedDrop != null)
            {
                SettedDrop(this, e);
            }
        }

        private void WLine_SettedSpeed(object sender, SetDropEventArgs e)
        {
            if(SettedSpeed != null)
            {
                SettedSpeed(this, e);
            }
        }

        private void WLine_EndedPR(object sender, EndPREventArgs e)
        {
            if(EndedPR != null)
            {
                EndedPR(this, e);
            }
        }

        private void WLine_EndedCoating(object sender, EndCoatingEventArgs e)
        {
            if(EndedCoating != null)
            {
                EndedCoating(this, e);
            }
        }

        private void WLine_AddedPR(object sender, AddPREventArgs e)
        {
            if(AddedPR != null)
            {
                AddedPR(this, e);
            }
        }

        private void WLine_AddedWafer(object sender, AddWaferEventArgs e)
        {
            if(AddedWafer != null)
            {
                AddedWafer(this, e);
            }
        }

        private void WaferLineForm_Load(object sender, EventArgs e)
        {
            lb_no.Text = No.ToString();
            wlc.Line = WLine;
        }

        private void WaferLineForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.Hide();
            e.Cancel = true;
        }
    }
}

5. MainForm에 이벤트 핸들러 등록 및 구현하기

MainForm에 WaferLine을 추가는 btn_add_Click이벤트 핸들러에서 수행합니다.

WaferLine을 생성할 때 이벤트 핸들러를 등록하는 코드를 추가합니다.

        private void btn_add_Click(object sender, EventArgs e)
        {
            int no = next_lineno;
            next_lineno++;
            WaferLine wl = new WaferLine(no);
            string[] sitems = new string[] { no.ToString(), "0", "0" };
            ListViewItem lvi = new ListViewItem(sitems);
            lvi.Tag = wl;
            lv_line.Items.Add(lvi);
            cb_line.Items.Add(wl);
            lb_next_no.Text = next_lineno.ToString();
            WaferLineForm wlf = new WaferLineForm(wl);
            wlf.EndedCoating += Wlf_EndedCoating;
            wlf.AddedWafer += Wlf_AddedWafer;
            wlf.AddedPR += Wlf_AddedPR;
            wlf.SettedSpeed += Wlf_SettedSpeed;
            wlf.SettedDrop += Wlf_SettedDrop;
            wlf.EndedPR += Wlf_EndedPR;
            wdic.Add(no, wlf);
        }

코팅액 완료 인벤트 핸들러(Wlf_EndedPR)에서는 해당 생산 라인이 lv_line의 선택 항목일 때 코팅액을 표시하는 lb_pr의 Text 속성을 설정합니다.

        private void Wlf_EndedPR(object sender, EndPREventArgs e)
        {
            ListViewItem lvi = lv_line.Items[e.No - 1];
            if(lvi.Selected)
            {
                WaferLine wl = lvi.Tag as WaferLine;
                lb_pr.Text = wl.PCnt.ToString();
            }
        }

회전 속도와 낙하 속도 설정 이벤트 핸들러에서도 해당 생산 라인을 선택한 것인지 확인하여 관련 속성을 표시하는 컨트롤 정보를 설정합니다.

그리고 코팅액 투입도 마찬가지입니다.

세가지 이벤트 핸들러에서는 같은 메서드(ChangeStatusSelectedWaferLine)를 호출하게 구현할게요.

        private void Wlf_SettedDrop(object sender, SetDropEventArgs e)
        {
            ChangeStatusSelectedWaferLine(e.No);
        }

        private void ChangeStatusSelectedWaferLine(int no)
        {
            ListViewItem lvi = lv_line.Items[no - 1];
            if(lvi.Selected)
            {
                WaferLine wl = lvi.Tag as WaferLine;
                lb_spin.Text = wl.Spin.ToString();
                lb_drop.Text = wl.Drop.ToString();
                lb_pr.Text = wl.PCnt.ToString();
            }
        }

        private void Wlf_SettedSpeed(object sender, SetDropEventArgs e)
        {
            ChangeStatusSelectedWaferLine(e.No);
        }
        private void Wlf_AddedPR(object sender, AddPREventArgs e)
        {
            ChangeStatusSelectedWaferLine(e.No);
        }

Wafer 추가 이벤트 핸들러에서는 코팅 전 Wafer개수와 코팅 후 Wafer 개수를 확인하여 lv_line에서 해당 항목의 내용을 다시 설정해 주어야 합니다.

Wafer 코팅 완료 이벤트 핸들러에서 같은 작업을 수행합니다.

대신 여기에서는 해당 WaferLine이 선택 상태일 때는 가장 최근에 코팅한 Wafer를 cb_awafer에 항목 추가하는 것이 필요합니다.

공통 작업은 ChangeStatusWaferLine 메서드로 구현할게요.

        private void Wlf_AddedWafer(object sender, AddWaferEventArgs e)
        {
            ChangeStatusWaferLine(e.No);
        }
        void ChangeStatusWaferLine(int no)
        {
            ListViewItem lvi = lv_line.Items[no - 1];
            WaferLine wl = lvi.Tag as WaferLine;
            lvi.SubItems[1].Text = wl.BWCnt.ToString();
            lvi.SubItems[2].Text = wl.AWCnt.ToString();
            cb_line.Items[no - 1] = wl;
        }

        private void Wlf_EndedCoating(object sender, EndCoatingEventArgs e)
        {
            ChangeStatusWaferLine(e.No);
            ListViewItem lvi = lv_line.Items[e.No - 1];
            if(lvi.Selected)
            {
                WaferLine wl = lvi.Tag as WaferLine;
                cb_awafer.Items.Add(wl.LastWafer);
                lb_pr.Text = wl.PCnt.ToString();
            }
        }

현재까지 작성한 MainForm.cs 소스 코드는 다음과 같습니다.

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using WaferLineLib;

namespace WaferLine_공장_시뮬레이션
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        int next_lineno = 1;
        Dictionary<int, WaferLineForm> wdic = new Dictionary<int, WaferLineForm>();

        private void btn_add_Click(object sender, EventArgs e)
        {
            int no = next_lineno;
            next_lineno++;
            WaferLine wl = new WaferLine(no);
            string[] sitems = new string[] { no.ToString(), "0", "0" };
            ListViewItem lvi = new ListViewItem(sitems);
            lvi.Tag = wl;
            lv_line.Items.Add(lvi);
            cb_line.Items.Add(wl);
            lb_next_no.Text = next_lineno.ToString();
            WaferLineForm wlf = new WaferLineForm(wl);
            wlf.EndedCoating += Wlf_EndedCoating;
            wlf.AddedWafer += Wlf_AddedWafer;
            wlf.AddedPR += Wlf_AddedPR;
            wlf.SettedSpeed += Wlf_SettedSpeed;
            wlf.SettedDrop += Wlf_SettedDrop;
            wlf.EndedPR += Wlf_EndedPR;
            wdic.Add(no, wlf);
        }

        private void Wlf_EndedPR(object sender, EndPREventArgs e)
        {
            ListViewItem lvi = lv_line.Items[e.No - 1];
            if(lvi.Selected)
            {
                WaferLine wl = lvi.Tag as WaferLine;
                lb_pr.Text = wl.PCnt.ToString();
            }
        }

        private void Wlf_SettedDrop(object sender, SetDropEventArgs e)
        {
            ChangeStatusSelectedWaferLine(e.No);
        }

        private void ChangeStatusSelectedWaferLine(int no)
        {
            ListViewItem lvi = lv_line.Items[no - 1];
            if(lvi.Selected)
            {
                WaferLine wl = lvi.Tag as WaferLine;
                lb_spin.Text = wl.Spin.ToString();
                lb_drop.Text = wl.Drop.ToString();
                lb_pr.Text = wl.PCnt.ToString();
            }
        }

        private void Wlf_SettedSpeed(object sender, SetDropEventArgs e)
        {
            ChangeStatusSelectedWaferLine(e.No);
        }

        private void Wlf_AddedPR(object sender, AddPREventArgs e)
        {
            ChangeStatusSelectedWaferLine(e.No);
        }

        private void Wlf_AddedWafer(object sender, AddWaferEventArgs e)
        {
            ChangeStatusWaferLine(e.No);
        }
        void ChangeStatusWaferLine(int no)
        {
            ListViewItem lvi = lv_line.Items[no - 1];
            WaferLine wl = lvi.Tag as WaferLine;
            lvi.SubItems[1].Text = wl.BWCnt.ToString();
            lvi.SubItems[2].Text = wl.AWCnt.ToString();
            cb_line.Items[no - 1] = wl;
        }

        private void Wlf_EndedCoating(object sender, EndCoatingEventArgs e)
        {
            ChangeStatusWaferLine(e.No);
            ListViewItem lvi = lv_line.Items[e.No - 1];
            if(lvi.Selected)
            {
                WaferLine wl = lvi.Tag as WaferLine;
                cb_awafer.Items.Add(wl.LastWafer);
                lb_pr.Text = wl.PCnt.ToString();
            }
        }

        private void btn_manage_Click(object sender, EventArgs e)
        {
            if(cb_line.SelectedIndex == -1)
            {
                return;
            }
            WaferLine wl = cb_line.SelectedItem as WaferLine;
            if(wdic[wl.No].Visible == false)
            {
                wdic[wl.No].Show();
            }
        }

        private void cb_awafer_SelectedIndexChanged(object sender, EventArgs e)
        {
            pn_awafer.Wafer = cb_awafer.SelectedItem as Wafer;
            pn_awafer.Invalidate();
        }

    }
}