안녕하세요. 언제나휴일입니다.
1. 해야 할 일
WaferLine 공장과 중앙 관제와의 통신 작업을 진행 중입니다.
현재 중앙 관제에서 공장에 연결 시점에 공장의 Wafer 라인 정보와 ,라인의 코팅할 Wafer 개수를 확인하는 것까지 구현하였습니다.
이번 강의에서는 공장의 WaferLine에 변화가 발생하였을 때 중앙 관제에서 모니터링하는 부분을 구현할게요.
모니터링 대상은 Line 추가, Wafer 추가, 코팅액 추가, Spin 속도 설정, Drop 속도 설정입니다.
그리고 현재 코팅에 사용하는 코팅액 소진 및 하나의 Wafer 코팅 완료도 모니터링합시다.
2. MsyType 열거형 메시지 종류 추가
열거형에 공장에서 중앙 관제에 코팅액 추가, Spin 속도 설정, Drop 속도 설정 관련 메시지를 추가합시다.
그리고 현재 코팅에 사용하는 코팅액을 소진하여 교체해야 한다는 메시지와 하나의 Wafer를 코팅 완료하였다는 메시지를 추가합시다.
public enum MsgType { MSG_CF_ADDSI, MSG_FC_ADDLN, MSG_FC_ADDWF, MSG_FC_ADDPR, MSG_FC_SETSP, MSG_FC_SETDR, MSG_FC_ENDPR, MSG_FC_ENDCO }
3. ControlClient 클래스 추가 구현
앞에서 구현한 SendAddWafer와 SendAddLine처럼 코팅액 추가, Spin 설정, Drop 설정, 코팅액 소진, Wafer 코팅 완료에 관한 메서드를 구현합니다.
using System.IO; using System.Net; using System.Net.Sockets; namespace WaferLineCommLib { public class ControlClient { IPAddress cip; int cport; public ControlClient(IPAddress cip,int cport) { this.cip = cip; this.cport = cport; } public bool SendAddWafer(int no,int bwcnt) { byte[] packet = new byte[128]; MemoryStream ms = new MemoryStream(packet); BinaryWriter bw = new BinaryWriter(ms); bw.Write((int)MsgType.MSG_FC_ADDWF); bw.Write(no); bw.Write(bwcnt); bw.Close(); ms.Close(); return SendPacket(packet); } public bool SendAddLine(int no) { byte[] packet = new byte[128]; MemoryStream ms = new MemoryStream(packet); BinaryWriter bw = new BinaryWriter(ms); bw.Write((int)MsgType.MSG_FC_ADDLN); bw.Write(no); bw.Close(); ms.Close(); return SendPacket(packet); } bool SendPacket(byte[] packet) { try { Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint ep = new IPEndPoint(cip, cport); sock.Connect(ep); sock.Send(packet); sock.Close(); return true; } catch { return false; } } public bool SendAddPR(int no,int pcnt) { byte[] packet = new byte[128]; MemoryStream ms = new MemoryStream(packet); BinaryWriter bw = new BinaryWriter(ms); bw.Write((int)MsgType.MSG_FC_ADDPR); bw.Write(no); bw.Write(pcnt); bw.Close(); ms.Close(); return SendPacket(packet); } public bool SendSetSpeed(int no, int speed) { byte[] packet = new byte[128]; MemoryStream ms = new MemoryStream(packet); BinaryWriter bw = new BinaryWriter(ms); bw.Write((int)MsgType.MSG_FC_SETSP); bw.Write(no); bw.Write(speed); bw.Close(); ms.Close(); return SendPacket(packet); } public bool SendSetDrop(int no, int drop) { byte[] packet = new byte[128]; MemoryStream ms = new MemoryStream(packet); BinaryWriter bw = new BinaryWriter(ms); bw.Write((int)MsgType.MSG_FC_SETDR); bw.Write(no); bw.Write(drop); bw.Close(); ms.Close(); return SendPacket(packet); } public bool SendEndPR(int no) { byte[] packet = new byte[128]; MemoryStream ms = new MemoryStream(packet); BinaryWriter bw = new BinaryWriter(ms); bw.Write((int)MsgType.MSG_FC_ENDPR); bw.Write(no); bw.Close(); ms.Close(); return SendPacket(packet); } public bool SendEndedCoating(int no,int bwcnt,int awcnt) { byte[] packet = new byte[128]; MemoryStream ms = new MemoryStream(packet); BinaryWriter bw = new BinaryWriter(ms); bw.Write((int)MsgType.MSG_FC_ENDCO); bw.Write(no); bw.Write(bwcnt); bw.Write(awcnt); bw.Close(); ms.Close(); return SendPacket(packet); } } }
4. ControlServer 추가 구현
메시지 수신에 관한 이벤트 멤버를 추가로 캡슐화합니다.
namespace WaferLineCommLib { public class ControlServer { public event AddLineEventHandler AddedLine; public event AddWaferEventHandler AddedWafer; public event AddPREventHandler AddedPR; public event SetSpeedEventHandler SettedSpeed; public event SetDropEventHandler SettedDrop; public event EndPREventHandler EndedPR; public event EndCoatingEventHandler EndedCoating;
DoIt 메서드에서 새로 추가한 메시지 타입에 따라 수행하는 메서드를 호출하는 코드를 추가합니다.
물론 수행할 메서드는 새로 만들어야 합니다.
private void DoIt(Socket dosock) { byte[] packet = new byte[128]; dosock.Receive(packet); MemoryStream ms = new MemoryStream(packet); BinaryReader br = new BinaryReader(ms); MsgType msgtype = (MsgType)br.ReadInt32(); switch(msgtype) { case MsgType.MSG_FC_ADDLN: AddLineProc(br); break; case MsgType.MSG_FC_ADDWF: AddWaferProc(br); break; case MsgType.MSG_FC_ADDPR: AddPrProc(br); break; case MsgType.MSG_FC_SETSP: SetSpeedProc(br); break; case MsgType.MSG_FC_SETDR: SetDropProc(br); break; case MsgType.MSG_FC_ENDPR: EndPrProc(br); break; case MsgType.MSG_FC_ENDCO: EndCoatingProc(br); break; } br.Close(); ms.Close(); dosock.Close(); }
새롭게 추가한 메서드에서는 AddLineProc, AddWaferProc처럼 수신한 정보를 읽어와서 이벤트를 전송하는 형태로 구현합니다.
private void EndCoatingProc(BinaryReader br) { int no = br.ReadInt32(); int bwcnt = br.ReadInt32(); int awcnt = br.ReadInt32(); if (EndedCoating != null) { EndedCoating(this, new EndCoatingEventArgs(no, bwcnt,awcnt)); } } private void EndPrProc(BinaryReader br) { int no = br.ReadInt32(); if (EndedPR != null) { EndedPR(this, new EndPREventArgs(no)); } } private void SetDropProc(BinaryReader br) { int no = br.ReadInt32(); int drop = br.ReadInt32(); if (SettedDrop != null) { SettedDrop(this, new SetDropEventArgs(no,drop)); } } private void SetSpeedProc(BinaryReader br) { int no = br.ReadInt32(); int speed = br.ReadInt32(); if (SettedSpeed != null) { SettedSpeed(this, new SetSpeedEventArgs(no, speed)); } } private void AddPrProc(BinaryReader br) { int no = br.ReadInt32(); int pcnt = br.ReadInt32(); if (AddedPR != null) { AddedPR(this, new AddPREventArgs(no, pcnt)); } }
다음은 ControlServer.cs 소스 코드입니다.
using System.IO; using System.Net; using System.Net.Sockets; using WaferLineLib; namespace WaferLineCommLib { public class ControlServer { public event AddLineEventHandler AddedLine; public event AddWaferEventHandler AddedWafer; public event AddPREventHandler AddedPR; public event SetSpeedEventHandler SettedSpeed; public event SetDropEventHandler SettedDrop; public event EndPREventHandler EndedPR; public event EndCoatingEventHandler EndedCoating; string ip; int port; public ControlServer(string ip,int port) { this.ip = ip; this.port = port; } delegate void StartDele(); public void AsyncStart() { StartDele dele = Start; dele.BeginInvoke(null, null); } public void Start() { //소켓 생성 Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //소켓을 네트워크 인터페이스와 결합 IPAddress addr = IPAddress.Parse(ip); IPEndPoint ep = new IPEndPoint(addr, port); sock.Bind(ep); //백로그 큐 크기 설정 sock.Listen(5); //클라이언트 연결 요청 대기 및 수락 Loop while(true) { Socket dosock = sock.Accept(); DoIt(dosock); } } private void DoIt(Socket dosock) { byte[] packet = new byte[128]; dosock.Receive(packet); MemoryStream ms = new MemoryStream(packet); BinaryReader br = new BinaryReader(ms); MsgType msgtype = (MsgType)br.ReadInt32(); switch(msgtype) { case MsgType.MSG_FC_ADDLN: AddLineProc(br); break; case MsgType.MSG_FC_ADDWF: AddWaferProc(br); break; case MsgType.MSG_FC_ADDPR: AddPrProc(br); break; case MsgType.MSG_FC_SETSP: SetSpeedProc(br); break; case MsgType.MSG_FC_SETDR: SetDropProc(br); break; case MsgType.MSG_FC_ENDPR: EndPrProc(br); break; case MsgType.MSG_FC_ENDCO: EndCoatingProc(br); break; } br.Close(); ms.Close(); dosock.Close(); } private void EndCoatingProc(BinaryReader br) { int no = br.ReadInt32(); int bwcnt = br.ReadInt32(); int awcnt = br.ReadInt32(); if (EndedCoating != null) { EndedCoating(this, new EndCoatingEventArgs(no, bwcnt,awcnt)); } } private void EndPrProc(BinaryReader br) { int no = br.ReadInt32(); if (EndedPR != null) { EndedPR(this, new EndPREventArgs(no)); } } private void SetDropProc(BinaryReader br) { int no = br.ReadInt32(); int drop = br.ReadInt32(); if (SettedDrop != null) { SettedDrop(this, new SetDropEventArgs(no,drop)); } } private void SetSpeedProc(BinaryReader br) { int no = br.ReadInt32(); int speed = br.ReadInt32(); if (SettedSpeed != null) { SettedSpeed(this, new SetSpeedEventArgs(no, speed)); } } private void AddPrProc(BinaryReader br) { int no = br.ReadInt32(); int pcnt = br.ReadInt32(); if (AddedPR != null) { AddedPR(this, new AddPREventArgs(no, pcnt)); } } private void AddWaferProc(BinaryReader br) { int no = br.ReadInt32(); int bwcnt = br.ReadInt32(); if(AddedWafer != null) { AddedWafer(this, new AddWaferEventArgs(no, bwcnt)); } } private void AddLineProc(BinaryReader br) { int no = br.ReadInt32(); if(AddedLine != null) { AddedLine(this, new AddLineEventArgs(no)); } } } }
5. 공장의 MainForm 수정하기
중앙 관제 정보를 수신하였을 때 코팅액 및 Spin 속도, Drop 속도를 전송하는 부분을 추가합니다.
private void Manager_RecvStsEndPoint(object sender, RecvStsEndPtEventArgs e) { if (this.InvokeRequired) { RecvStsEndPtEventHandler dele = Manager_RecvStsEndPoint; this.Invoke(dele, new object[] { sender, e }); } else { Manager manager = Manager.Singleton; foreach(ListViewItem lvi in lv_line.Items) { int no = int.Parse(lvi.SubItems[0].Text); manager.AddLine(no); manager.AddWafer(no, int.Parse(lvi.SubItems[1].Text)); WaferLine wl = lvi.Tag as WaferLine; manager.AddPR(no, wl.PCnt); manager.SetSpeed(no, wl.Spin); manager.SetDrop(no, wl.Drop); } ts_lb.Text = string.Format("{0}:{1} 연결", e.IPAddress, e.Port); } }
6. 공장의 WaferLineForm 코드 수정하기
WaferLine의 상태 변화할 때 Manager를 통해 상태 변화를 전송하는 코드를 각 이벤트 핸들러에 추가합니다.
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) { Manager manager = Manager.Singleton; manager.SetDrop(e.No, e.Drop); if(SettedDrop != null) { SettedDrop(this, e); } } private void WLine_SettedSpeed(object sender, SetSpeedEventArgs e) { Manager manager = Manager.Singleton; manager.SetSpeed(No, e.Speed); if (SettedSpeed != null) { SettedSpeed(this, e); } } private void WLine_EndedPR(object sender, EndPREventArgs e) { Manager manager = Manager.Singleton; manager.EndedPR(e.No); if (EndedPR != null) { EndedPR(this, e); } } private void WLine_EndedCoating(object sender, EndCoatingEventArgs e) { Manager manager = Manager.Singleton; manager.EndedCoating(No, e.BWCnt, e.AWCnt); if (EndedCoating != null) { EndedCoating(this, e); } } private void WLine_AddedPR(object sender, AddPREventArgs e) { Manager manager = Manager.Singleton; manager.AddPR(e.No, e.PCnt); if (AddedPR != null) { AddedPR(this, e); } } private void WLine_AddedWafer(object sender, AddWaferEventArgs e) { Manager manager = Manager.Singleton; manager.AddWafer(e.No, e.BWCnt); 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; } internal void SetStatus() { wlc.SetStaus(); } } }
7. 공장의 Manager 추가 구현
AddPR 메서드부터 새로 추가한 메서드를 구현합시다.
구현할 내용은 AddWafer와 AddLine처럼 ControlClient 개체를 참조한 cc에 해당 정보를 전송하는 메서드를 호출하는 것입니다.
using System.Net; using WaferLineCommLib; using WaferLineLib; namespace WaferLine_공장_시뮬레이션 { public class Manager { public event RecvStsEndPtEventHandler RecvStsEndPoint; private Manager() { } static Manager manager = new Manager(); static public Manager Singleton { get { return manager; } } public void StartServer(string ip,int port) { FactoryServer fs = new FactoryServer(ip, port); fs.RecvStsEndPoint += Fs_RecvStsEndPoint; fs.StartAsync(); } ControlClient cc; private void Fs_RecvStsEndPoint(object sender, RecvStsEndPtEventArgs e) { IPAddress cip = e.IPAddress; int cport = e.Port; cc = new ControlClient(cip, cport); if(RecvStsEndPoint != null) { RecvStsEndPoint(this, e); } } public void AddLine(int no) { if(cc == null) { return; } if(cc.SendAddLine(no) == false) { cc = null; } } public void AddWafer(int no, int bwcnt) { if (cc == null) { return; } if (cc.SendAddWafer(no,bwcnt) == false) { cc = null; } } public void AddPR(int no, int pcnt) { if (cc == null) { return; } if (cc.SendAddPR(no, pcnt) == false) { cc = null; } } public void EndedPR(int no) { if (cc == null) { return; } if (cc.SendEndPR(no) == false) { cc = null; } } public void SetSpeed(int no, int spin) { if (cc == null) { return; } if (cc.SendSetSpeed(no,spin) == false) { cc = null; } } public void SetDrop(int no, int drop) { if (cc == null) { return; } if (cc.SendSetDrop(no, drop) == false) { cc = null; } } public void EndedCoating(int no, int bwcnt, int awcnt) { if (cc == null) { return; } if (cc.SendEndedCoating(no, bwcnt, awcnt) == false) { cc = null; } } } }
8. 중앙 관제의 CentralForm 코드 수정
btn_set_me_Click 이벤트 핸들러에 코팅액 추가, Spin 설정, Drop 설정, 코팅액 소진, Wafer 코팅 완료 이벤트 핸들러를 등록하는 코드를 추가합니다.
private void btn_set_me_Click(object sender, EventArgs e) { string ip = tbox_me_ip.Text; int port = int.Parse(tbox_me_port.Text); ControlServer cs = new ControlServer(ip, port); cs.AddedLine += Cs_AddedLine; cs.AddedWafer += Cs_AddedWafer; cs.AddedPR += Cs_AddedPR; cs.SettedSpeed += Cs_SettedSpeed; cs.SettedDrop += Cs_SettedDrop; cs.EndedPR += Cs_EndedPR; cs.EndedCoating += Cs_EndedCoating; cs.AsyncStart(); fc.SendMyInfo(ip, port); }
추가한 이벤트 핸들러에서는 크로스 스레드를 고려합니다.
각 이벤트 핸들러에서 전달받은 이벤트 인자를 컨트롤에 적절히 표시하도록 코드를 작성합니다.
private void Cs_EndedCoating(object sender, EndCoatingEventArgs e) { if (this.InvokeRequired) { EndCoatingEventHandler dele = Cs_EndedCoating; this.Invoke(dele, new object[] { sender, e }); } else { ListViewItem lvi = lvi_dic[e.No]; WaferLine wl = lvi.Tag as WaferLine; wl.EndCoating(e.BWCnt, e.AWCnt); lvi.SubItems[1].Text = e.BWCnt.ToString(); if (lvi.Selected) { lb_awafer.Text = e.AWCnt.ToString(); } } } private void Cs_EndedPR(object sender, EndPREventArgs e) { if (this.InvokeRequired) { EndPREventHandler dele = Cs_EndedPR; this.Invoke(dele, new object[] { sender, e }); } else { ListViewItem lvi = lvi_dic[e.No]; WaferLine wl = lvi.Tag as WaferLine; wl.PCnt--; if (lvi.Selected) { lb_npr.Text = wl.PCnt.ToString(); } } } private void Cs_SettedDrop(object sender, SetDropEventArgs e) { if (this.InvokeRequired) { SetDropEventHandler dele = Cs_SettedDrop; this.Invoke(dele, new object[] { sender, e }); } else { ListViewItem lvi = lvi_dic[e.No]; WaferLine wl = lvi.Tag as WaferLine; wl.Drop = e.Drop; if (lvi.Selected) { lb_drop.Text = e.Drop.ToString(); tbar_drop.Value = e.Drop; } } } private void Cs_SettedSpeed(object sender, SetSpeedEventArgs e) { if (this.InvokeRequired) { SetSpeedEventHandler dele = Cs_SettedSpeed; this.Invoke(dele, new object[] { sender, e }); } else { ListViewItem lvi = lvi_dic[e.No]; WaferLine wl = lvi.Tag as WaferLine; wl.Spin = e.Speed; if (lvi.Selected) { lb_spin.Text = e.Speed.ToString(); tbar_spin.Value = e.Speed; } } } private void Cs_AddedPR(object sender, AddPREventArgs e) { if (this.InvokeRequired) { AddPREventHandler dele = Cs_AddedPR; this.Invoke(dele, new object[] { sender, e }); } else { ListViewItem lvi = lvi_dic[e.No]; WaferLine wl = lvi.Tag as WaferLine; int gap = e.PCnt - wl.PCnt; wl.InPr(gap); if (lv_line.SelectedItems.Count == 0) { return; } if (lvi == lv_line.SelectedItems[0]) { lb_npr.Text = wl.PCnt.ToString(); } } }
lv_line의 선택 항목 변경 이벤트 핸들러를 등록합니다.
private void lv_line_SelectedIndexChanged(object sender, EventArgs e)
선택 항목이 없을 때는 정보를 표시할 컨트롤을 비활성화하고 선택항목이 있을 때 활성화할 수 있게 SetLineControl 메서드를 만들어서 호출하기로 할게요.
선택 항목을 얻어온 후에 WaferLine에 맞게 상태 정보를 컨트롤에 표시합니다.
private void lv_line_SelectedIndexChanged(object sender, EventArgs e) { if(lv_line.SelectedItems.Count ==0) { SetLineControl(false); return; } SetLineControl(true); ListViewItem lvi = lv_line.SelectedItems[0]; WaferLine wl = lvi.Tag as WaferLine; lb_npr.Text = wl.PCnt.ToString(); tbar_spin.Value = wl.Spin; tbar_drop.Value = wl.Drop; lb_spin.Text = wl.Spin.ToString(); lb_drop.Text = wl.Drop.ToString(); } private void SetLineControl(bool flag) { tbar_wafer.Enabled = flag; tbar_pr.Enabled = flag; tbar_spin.Enabled = flag; tbar_drop.Enabled = flag; btn_wafer.Enabled = flag; btn_pr.Enabled = flag; }
다음 강의는 Wafer 코팅 시뮬레이션 마지막 강의입니다.