4. WaferLine 예광탄 – 4.3 WaferLine 동작 구현 [Wafer 코팅 시뮬레이션]

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

지난 강의에서는 Wafer를 코팅하는 설비를 WaferLine 클래스로 정의했어요.

이번 강의에는 최종 사용자와 WaferLine과 상호 작용하는 부분을 구현하는 실습입니다.

1. 이벤트 핸들러 추가

Wafer를 추가하는 TrackBar와 코팅액을 추가하는 TrackBar의 Scroll 이벤트 핸들러를 등록합니다. 이벤트 핸들러는 코드만 따라 친다고 동작하지 않습니다. 속성 창에서 이벤트를 등록하세요.

        private void tbar_wafer_Scroll(object sender, EventArgs e)
        {
        }

        private void tbar_pr_Scroll(object sender, EventArgs e)
        {
        }

Wafer 투입 버튼과 코팅 액 투입 버튼의 Click 이벤트 핸들러를 등록합니다.

        private void btn_wafer_Click(object sender, EventArgs e)
        {
 
        }

        private void btn_pr_Click(object sender, EventArgs e)
        {
 
        }

회적 속도와 낙하 속도의 TrackBar의 Scroll 이벤트 핸들러를 등록하세요.

        private void tbar_spin_Scroll(object sender, EventArgs e)
        {

        }

        private void tbar_drop_Scroll(object sender, EventArgs e)
        {

        }

시작 버튼의 Click 이벤트 핸들러, 코팅 완료한 Wafer 콤보 박스의 선택 변경 이벤트 핸들러를 등록합니다.

        private void btn_start_Click(object sender, EventArgs e)
        {


        }

        private void cb_awafer_SelectedIndexChanged(object sender, EventArgs e)
        {

        }

코팅 타이머의 Tick 이벤트 핸들러도 등록합니다.

코팅 전 Wafer를 시각화하는 패널과 코팅 액을 시각화하는 패널, 현재 낙하하는 코팅 액 패널의 Paint 이벤트 핸들러를 등록합니다.

        private void tm_coating_Tick(object sender, EventArgs e)
        {

        }

        private void pn_wafer_Paint(object sender, PaintEventArgs e)
        {
        }

        private void pn_pr_Paint(object sender, PaintEventArgs e)
        {

        }

2. 이벤트 핸들러 구현

예광탄에서 WaferLine의 동작을 표현할 것이므로 하나의 WaferLine을 생성합니다.

    public partial class Form1 : Form
    {        
        WaferLine wl = null;
        public Form1()
        {
            InitializeComponent();            
            wl = new WaferLine(1);
        }
    }

tbar_wafer의 Scroll 이벤트 핸들러에서는 tbar_wafer의 Value로 lb_wafer의 Text 속성을 설정합니다.

        private void tbar_wafer_Scroll(object sender, EventArgs e)
        {
            if (wl == null) { return; }
            lb_wafer.Text = tbar_wafer.Value.ToString();
        }

tbar_pr의 Scroll 이벤트 핸들러에서도 lb_pr의 Text 속성을 tbar_pr의 Value로 설정합니다.

        private void tbar_pr_Scroll(object sender, EventArgs e)
        {
            if (wl == null) { return; }
            lb_pr.Text = tbar_pr.Value.ToString();
        }

btn_wafer의 Click 이벤트 핸들러를 구현합시다.

코팅 전 Wafer 개수를 얻어와서 현재 최대 보관할 수 있는 개수와 현재 추가하려고 하는 개수를 비교하여 조절합니다.

조절한 값으로 Wafer를 추가합니다.

그리고 변경한 내용에 맞게 컨트롤의 속성들을 설정합니다.

        private void btn_wafer_Click(object sender, EventArgs e)
        {
            if (wl == null) { return; }
            int awcnt = tbar_wafer.Value;
            if (awcnt > (tbar_wafer.Maximum - wl.BWCnt))
            {
                awcnt = tbar_wafer.Maximum - wl.BWCnt;
            }
            wl.InWafer(awcnt);
            tbar_wafer.Value = 0;
            lb_wafer.Text = "0";
            pn_wafer.Invalidate();
            ts_lb.Text = string.Format("Wafer {0}개 투입, 현재:{1}개", awcnt, wl.BWCnt);
            lb_wcnt.Text = wl.BWCnt.ToString();
        }

btn_pr의 Click 이벤트 핸들러를 구현합시다.

현재 코팅 액이 몇 병인지 얻어온 후에 현재 추가할 수 있는 최대 병 수와 추가하려고 하는 병 수를 비교하여 조절합니다.

조절한 값으로 투입합니다.

투입한 값에 맞게 컨트롤의 속성들을 설정합니다.

        private void btn_pr_Click(object sender, EventArgs e)
        {
            if (wl == null) { return; }
            int pcnt = tbar_pr.Value;
            if (pcnt > (tbar_pr.Maximum - wl.PCnt))
            {
                pcnt = tbar_pr.Maximum - wl.PCnt;
            }
            wl.InPr(pcnt);
            tbar_pr.Value = 0;
            lb_pr.Text = "0";
            pn_pr.Invalidate();
            ts_lb.Text = string.Format("코팅액 {0}병 투입, 현재:{1}병", pcnt, wl.PCnt);
            lb_pcnt.Text = wl.PCnt.ToString();
        }

tbar_spin의 Scroll 이벤트 핸들러와 tbar_drop의 Scroll 이벤트 핸들러를 구현합시다.

여기에서는 단순히 TrackBar의 Value로 속도를 설정합니다.

그리고 설정한 것에 의해 코팅 속도를 변경합니다. 코팅 속도 변경하는 부분은 ChangeInterval 이름의 메서드로 구현할게요.

        private void tbar_spin_Scroll(object sender, EventArgs e)
        {
            if (wl == null) { return; }
            lb_spin.Text = tbar_spin.Value.ToString();
            wl.SetSpin(tbar_spin.Value);
            ChangeInterval();
        }

        private void tbar_drop_Scroll(object sender, EventArgs e)
        {
            if (wl == null) { return; }
            lb_drop.Text = tbar_drop.Value.ToString();
            wl.SetDrop(tbar_drop.Value);
            ChangeInterval();
        }

        private void ChangeInterval()
        {
            if (wl == null) { return; }
            tm_coating.Interval = 6000000 / (wl.Spin * wl.Drop);
        }

btn_start의 Click 이벤트 핸들러를 구현합시다.

먼저 타이머가 가동 중인지 여부에 따라 타이머를 가동 혹은 멈춤으로 설정을 바꿔줍니다.

그리고 버튼의 Text 속성도 가동 여부에 따라 “시작” 혹은 “멈춤”으로 바꿔줍니다.

        private void btn_start_Click(object sender, EventArgs e)
        {
            if (wl == null) { return; }
            if (tm_coating.Enabled)
            {
                tm_coating.Enabled = false;
                btn_start.Text = "시작";
            }
            else
            {
                tm_coating.Enabled = true;
                btn_start.Text = "멈춤";
            }
        }

cb_awafer의 선택 변경 이벤트 핸들러를 구현합시다.

현재 선택한 Wafer로 pn_awafer의 Wafer 속성을 설정합니다.

그리고 pn_awafer의 Invalidate 메서드를 호출하여 다시 그려주게 합니다.

(*동영상 강의에서는 가장 최근에 코팅한 Wafer로 그려주는 것으로 잘못 작성했네요.*)

        private void cb_awafer_SelectedIndexChanged(object sender, EventArgs e)
        {
            if(cb_awafer.SelectedIndex == -1)
            {
                return;
            }
            Wafer wafer = cb_awafer.SelectedItem as Wafer;
            pn_awafer.Wafer = wafer;
            pn_awafer.Invalidate();
        }

tm_coating의 Tick 이벤트 핸들러를 구현합시다.

        private void tm_coating_Tick(object sender, EventArgs e)

WaferLine의 Coating 메서드를 호출합니다.

만약 메서드 호출 결과가 거짓이면 더 이상 코팅할 것이 없는 것이므로 타이머를 비활성화합니다.

            if (wl == null) { return; }
            if (wl.Coating() == false)
            {
                tm_coating.Enabled = false;
                btn_start.Text = "시작";
            }

현재 코팅하는 Wafer를 참조합니다.

그리고 pn_nwafer의 Wafer 속성을 설정합니다.

            Wafer wafer = wl.Now;
            pn_nwafer.Wafer = wafer;

wafer가 null이 아닐 때만 수행합니다.

wafer에서 현재 코팅한 쉘 정보를 얻어옵니다.

만약 현재 코팅한 쉘이 1이면 이전 코팅한 Wafer가 코팅 완료한 것이므로 이에 관한 처리를 해 줍니다.

            if (wafer != null)
            {
                int ccount = wafer.Now;
                if (ccount == 1)
                {
                    Wafer lwafer = wl.LastWafer;
                    if (lwafer != null)
                    {
                        cb_awafer.Items.Add(lwafer);
                        lb_awcnt.Text = wl.AWCnt.ToString();
                        ts_lb.Text = string.Format("코팅 완료:{0}", lwafer);
                    }
                    lb_wcnt.Text = wl.BWCnt.ToString();
                }
            }

그리고 현재 코팅 라인의 남은 코팅 양(NPcnt)가 999라면 새로운 코팅 액을 꺼낸 것이므로 이를 처리합니다.

            if (wl.NPcnt == 999)
            {
                lb_pcnt.Text = wl.PCnt.ToString();
                ts_lb.Text = string.Format("코팅액 교체: 남은 코팅액:{0}병", wl.PCnt);
            }
            Invalidate(true);

다음은 tm_coating의 Tick 이벤트 핸들러의 전체 코드입니다.

        private void tm_coating_Tick(object sender, EventArgs e)
        {
            if (wl == null) { return; }
            if (wl.Coating() == false)
            {
                tm_coating.Enabled = false;
                btn_start.Text = "시작";
            }
            Wafer wafer = wl.Now;
            pn_nwafer.Wafer = wafer;
            if (wafer != null)
            {
                int ccount = wafer.Now;
                if (ccount == 1)
                {
                    Wafer lwafer = wl.LastWafer;
                    if (lwafer != null)
                    {
                        cb_awafer.Items.Add(lwafer);
                        lb_awcnt.Text = wl.AWCnt.ToString();
                        ts_lb.Text = string.Format("코팅 완료:{0}", lwafer);
                    }
                    lb_wcnt.Text = wl.BWCnt.ToString();
                }
            }
            if (wl.NPcnt == 999)
            {
                lb_pcnt.Text = wl.PCnt.ToString();
                ts_lb.Text = string.Format("코팅액 교체: 남은 코팅액:{0}병", wl.PCnt);
            }
            Invalidate(true);
        }

pn_wafer의 Paint 이벤트 핸들러를 구현합시다.

        private void pn_wafer_Paint(object sender, PaintEventArgs e)

코팅 전 Wafer는 최대 200개를 보관할 수 있게 하드 코팅할 거예요.

한 줄에 10개를 표현하여 20줄로 표현할게요.

먼저 10X20의 모눈을 그려줍니다.

(*패널의 너비와 높이에 따라 마지막 칸과 줄에 여백이 생길 수 있습니다.*)

            if (wl == null) { return; }
            Graphics graphics = e.Graphics;
            Pen pen = new Pen(Color.DarkGray, 1);
            pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
            int xu = pn_wafer.Width / 10;
            int yu = pn_wafer.Height / 20;
            int wcnt = wl.BWCnt;
            for (int x = 1; x < 10; x++)
            {
                graphics.DrawLine(pen, new Point(x * xu, 0), new Point(x * xu, pn_wafer.Height));
            }
            for (int y = 1; y < 20; y++)
            {
                graphics.DrawLine(pen, new Point(0, y * yu), new Point(pn_wafer.Width, y * yu));
            }

Wafer가 있는 쉘과 그렇지 않은 쉘을 다른 브뤄시를 이용하여 채워줍니다.

            for (int i = 0, ri = 200; i < 200; i++, ri--)
            {
                Brush brush;
                if (ri <= wcnt)
                {
                    brush = new HatchBrush(HatchStyle.DiagonalCross, Color.Goldenrod);
                }
                else
                {
                    brush = new SolidBrush(pn_wafer.BackColor);
                }
                int x = i % 10;
                int y = i / 10;
                graphics.FillRectangle(brush, new Rectangle(x * xu + 1, y * yu + 1, xu - 1, yu - 1));
            }

다음은 pn_wafer의 Paint 이벤트 핸들러입니다.

        private void pn_wafer_Paint(object sender, PaintEventArgs e)
        {
            if (wl == null) { return; }
            Graphics graphics = e.Graphics;
            Pen pen = new Pen(Color.DarkGray, 1);
            pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
            int xu = pn_wafer.Width / 10;
            int yu = pn_wafer.Height / 20;
            int wcnt = wl.BWCnt;
            for (int x = 1; x < 10; x++)
            {
                graphics.DrawLine(pen, new Point(x * xu, 0), new Point(x * xu, pn_wafer.Height));
            }
            for (int y = 1; y < 20; y++)
            {
                graphics.DrawLine(pen, new Point(0, y * yu), new Point(pn_wafer.Width, y * yu));
            }
            for (int i = 0, ri = 200; i < 200; i++, ri--)
            {
                Brush brush;
                if (ri <= wcnt)
                {
                    brush = new HatchBrush(HatchStyle.DiagonalCross, Color.Goldenrod);
                }
                else
                {
                    brush = new SolidBrush(pn_wafer.BackColor);
                }
                int x = i % 10;
                int y = i / 10;
                graphics.FillRectangle(brush, new Rectangle(x * xu + 1, y * yu + 1, xu - 1, yu - 1));
            }
        }

pn_pr의 Paint 이벤트 핸들러도 같은 원리로 구현합니다. 차이가 있다면 1줄이 1병입니다.

최대 20병을 보관할 수 있는 것으로 하드 코딩할게요.

        private void pn_pr_Paint(object sender, PaintEventArgs e)
        {
            if (wl == null) { return; }
            Graphics graphics = e.Graphics;
            Pen pen = new Pen(Color.DarkGray, 1);
            pen.DashStyle = DashStyle.Dot;
            int yu = pn_pr.Height / 20;
            for (int y = 1; y < 20; y++)
            {
                graphics.DrawLine(pen, new Point(0, y * yu), new Point(pn_pr.Width, y * yu));
            }
            int pcnt = wl.PCnt;
            for (int i = 0, ri = 20; i < 20; i++, ri--)
            {
                Color color = pn_pr.BackColor;
                if (ri <= pcnt)
                {
                    color = Color.DarkCyan;
                }
                Brush brush = new SolidBrush(color);
                graphics.FillRectangle(brush, new Rectangle(0, i * yu + 1, pn_pr.Width, yu - 1));
            }
        }

pn_npr의 Paint 이벤트 핸들러도 구현합시다.

        private void pn_npr_Paint(object sender, PaintEventArgs e)
        {
            if (wl == null) { return; }
            Graphics graphics = e.Graphics;
            int npcnt = wl.NPcnt;
            for (int x = 0; x < 50; x++)
            {
                for (int y = 0; y < 20; y++)
                {
                    if (npcnt < (1000 - y * 50 + x))
                    {
                        graphics.DrawLine(Pens.White, new Point(x, y), new Point(x + 1, y));
                    }
                    else
                    {
                        graphics.DrawLine(Pens.DarkCyan, new Point(x, y), new Point(x + 1, y));
                    }
                }
            }
        }

3. Form1.cs 소스 코드

다음은 현재까지 작성한 Form1.cs 소스 코드입니다.

(* 다음 강의에서는 현재 작성한 것을 WaferLineControl로 바꾸는 작업을 할 거예요. *)

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

namespace WaferLine_예광탄
{
    public partial class Form1 : Form
    {        
        WaferLine wl = null;
        public Form1()
        {
            InitializeComponent();            
            wl = new WaferLine(1);
        }
        private void tbar_wafer_Scroll(object sender, EventArgs e)
        {
            if (wl == null) { return; }
            lb_wafer.Text = tbar_wafer.Value.ToString();
        }

        private void tbar_pr_Scroll(object sender, EventArgs e)
        {
            if (wl == null) { return; }
            lb_pr.Text = tbar_pr.Value.ToString();

        }

        private void btn_wafer_Click(object sender, EventArgs e)
        {
            if (wl == null) { return; }
            int bwcnt = tbar_wafer.Value;
            if (bwcnt > (tbar_wafer.Maximum - wl.BWCnt))
            {
                bwcnt = tbar_wafer.Maximum - wl.BWCnt;
            }
            wl.InWafer(bwcnt);
            tbar_wafer.Value = 0;
            lb_wafer.Text = "0";
            pn_wafer.Invalidate();
            ts_lb.Text = string.Format("Wafer {0}개 투입, 현재:{1}개", bwcnt, wl.BWCnt);
            lb_wcnt.Text = wl.BWCnt.ToString();
        }

        private void btn_pr_Click(object sender, EventArgs e)
        {
            if (wl == null) { return; }
            int pcnt = tbar_pr.Value;
            if (pcnt > (tbar_pr.Maximum - wl.PCnt))
            {
                pcnt = tbar_pr.Maximum - wl.PCnt;
            }
            wl.InPr(pcnt);
            tbar_pr.Value = 0;
            lb_pr.Text = "0";
            pn_pr.Invalidate();
            ts_lb.Text = string.Format("코팅액 {0}병 투입, 현재:{1}병", pcnt, wl.PCnt);
            lb_pcnt.Text = wl.PCnt.ToString();
        }

        private void tbar_spin_Scroll(object sender, EventArgs e)
        {
            if (wl == null) { return; }
            lb_spin.Text = tbar_spin.Value.ToString();
            wl.SetSpin(tbar_spin.Value);
            ChangeInterval();
        }

        private void ChangeInterval()
        {
            if (wl == null) { return; }
            tm_coating.Interval = 6000000 / (wl.Spin * wl.Drop);
        }

        private void tbar_drop_Scroll(object sender, EventArgs e)
        {
            if (wl == null) { return; }
            lb_drop.Text = tbar_drop.Value.ToString();
            wl.SetDrop(tbar_drop.Value);
            ChangeInterval();
        }

        private void btn_start_Click(object sender, EventArgs e)
        {
            if (wl == null) { return; }
            if (tm_coating.Enabled)
            {
                tm_coating.Enabled = false;
                btn_start.Text = "시작";
            }
            else
            {
                tm_coating.Enabled = true;
                btn_start.Text = "멈춤";
            }
        }

        private void cb_awafer_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (wl == null) { return; }
            pn_awafer.Wafer = wl.LastWafer;
            pn_awafer.Invalidate();
        }

        private void tm_coating_Tick(object sender, EventArgs e)
        {
            if (wl == null) { return; }
            if (wl.Coating() == false)
            {
                tm_coating.Enabled = false;
                btn_start.Text = "시작";
            }
            Wafer wafer = wl.Now;
            pn_nwafer.Wafer = wafer;
            if (wafer != null)
            {
                int ccount = wafer.Now;
                if (ccount == 1)
                {
                    Wafer lwafer = wl.LastWafer;
                    if (lwafer != null)
                    {
                        cb_awafer.Items.Add(lwafer);
                        lb_awcnt.Text = wl.AWCnt.ToString();
                        ts_lb.Text = string.Format("코팅 완료:{0}", lwafer);
                    }
                    lb_wcnt.Text = wl.BWCnt.ToString();
                }
            }
            if (wl.NPcnt == 999)
            {
                lb_pcnt.Text = wl.PCnt.ToString();
                ts_lb.Text = string.Format("코팅액 교체: 남은 코팅액:{0}병", wl.PCnt);
            }
            Invalidate(true);
        }

        private void pn_wafer_Paint(object sender, PaintEventArgs e)
        {
            if (wl == null) { return; }
            Graphics graphics = e.Graphics;
            Pen pen = new Pen(Color.DarkGray, 1);
            pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
            int xu = pn_wafer.Width / 10;
            int yu = pn_wafer.Height / 20;
            int wcnt = wl.BWCnt;
            for (int x = 1; x < 10; x++)
            {
                graphics.DrawLine(pen, new Point(x * xu, 0), new Point(x * xu, pn_wafer.Height));
            }
            for (int y = 1; y < 20; y++)
            {
                graphics.DrawLine(pen, new Point(0, y * yu), new Point(pn_wafer.Width, y * yu));
            }
            for (int i = 0, ri = 200; i < 200; i++, ri--)
            {
                Brush brush;
                if (ri <= wcnt)
                {
                    brush = new HatchBrush(HatchStyle.DiagonalCross, Color.Goldenrod);
                }
                else
                {
                    brush = new SolidBrush(pn_wafer.BackColor);
                }
                int x = i % 10;
                int y = i / 10;
                graphics.FillRectangle(brush, new Rectangle(x * xu + 1, y * yu + 1, xu - 1, yu - 1));
            }
        }

        private void pn_pr_Paint(object sender, PaintEventArgs e)
        {
            if (wl == null) { return; }
            Graphics graphics = e.Graphics;
            Pen pen = new Pen(Color.DarkGray, 1);
            pen.DashStyle = DashStyle.Dot;
            int yu = pn_pr.Height / 20;
            for (int y = 1; y < 20; y++)
            {
                graphics.DrawLine(pen, new Point(0, y * yu), new Point(pn_pr.Width, y * yu));
            }
            int pcnt = wl.PCnt;
            for (int i = 0, ri = 20; i < 20; i++, ri--)
            {
                Color color = pn_pr.BackColor;
                if (ri <= pcnt)
                {
                    color = Color.DarkCyan;
                }
                Brush brush = new SolidBrush(color);
                graphics.FillRectangle(brush, new Rectangle(0, i * yu + 1, pn_pr.Width, yu - 1));
            }
        }

        private void pn_npr_Paint(object sender, PaintEventArgs e)
        {
            if (wl == null) { return; }
            Graphics graphics = e.Graphics;
            int npcnt = wl.NPcnt;
            for (int x = 0; x < 50; x++)
            {
                for (int y = 0; y < 20; y++)
                {
                    if (npcnt < (1000 - y * 50 + x))
                    {
                        graphics.DrawLine(Pens.White, new Point(x, y), new Point(x + 1, y));
                    }
                    else
                    {
                        graphics.DrawLine(Pens.DarkCyan, new Point(x, y), new Point(x + 1, y));
                    }
                }
            }

    }
}