using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace Test_ProPLC { public partial class Form1 : Form { arDev.FakePLC dev2; public Form1() { InitializeComponent(); this.serialPort1.DataReceived += SerialPort1_DataReceived; dev2 = new arDev.FakePLC(); this.dev2.ValueChanged += Dev2_ValueChanged; this.dev2.Message += Dev2_Message; this.FormClosed += Form1_FormClosed; } private void Form1_FormClosed(object sender, FormClosedEventArgs e) { timer1.Stop(); } private void Dev2_ValueChanged(object sender, arDev.Arduino.DIO.IOValueEventArgs e) { addMsg2($"val({e.ArrIDX}) -> {e.NewValue}"); } byte[] buffer1; List Tempbuffer1 = new List(); byte[] buffer2; List Tempbuffer2 = new List(); Boolean STX11 = false; Boolean STX21 = false; Boolean ETX11 = false; Boolean STX12 = false; Boolean STX22 = false; Boolean ETX12 = false; //MAIN PLC byte[] DIpinList1 = new byte[] { 22, 23, 24, 25, 26, 27, 28, 29, 41 }; string[] DIpinName1 = new string[] { "EMERGENCTY", "OVR_L", "LIDAR_STOP", "LIDAR_SLOW", "OVR_R", "MARK", "GATEOUT_B", "GATEOUT_F", "ALIGN" }; byte[] DOpinList1 = new byte[] { 33, 34, 35, 36, 37, 43, 44, 39, 40 }; string[] DopinName1 = new string[] { "MOT_POWER_GUIDE", "MOT_POWER_L", "MOT_POWER_R", "LIDAR_DIR", "CHARGE_ON", "MOT_DIR_L", "MOT_RUN_L", "MOT_DIR_R", "MOT_RUN_R" }; // SUB PLC byte[] DIpinList2 = new byte[] { 22, 23, 24, 25, 41, 42, 43, 45, 38 }; string[] DIpinName2 = new string[] { "BTN_GUIDE_UP", "BTN_GUIDE_DN", "BTN_QA1", "BTN_QC", "LIM_HIGH_R", "LIM_LOW_R", "LIM_HIGH_L", "BTN_PACK", "LIM_LOW_L" }; byte[] DOpinList2 = new byte[] { 30, 31, 32, 33 }; string[] DopinName2 = new string[] { "GMOT_INT", "GMOT_DIR", "GMOT_RUN", "GMOT_STA" }; byte LEN1 = 0; byte CHK1 = 0; int bufferCount1 = 0; byte LEN2 = 0; byte CHK2 = 0; int bufferCount2 = 0; private void SerialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { bufferCount1 = this.serialPort1.BytesToRead; byte[] rxBuffer = new byte[bufferCount1]; serialPort1.Read(rxBuffer, 0, bufferCount1); foreach (byte incomingByte in rxBuffer) { if (STX11 == false) { if (incomingByte != '@') { STX21 = false; ETX11 = false; continue; } else { STX11 = true; Tempbuffer1.Add(incomingByte); } } else if (STX21 == false) { if (Tempbuffer1.Count != 1 || Tempbuffer1.Count < 1 || Tempbuffer1[0] != '@' || incomingByte != '@') { STX11 = false; ETX11 = false; continue; } else { STX21 = true; Tempbuffer1.Add(incomingByte); } } else { Tempbuffer1.Add(incomingByte); //여기서부터는무조건 누적한다. if (Tempbuffer1.Count == 3) { if (Tempbuffer1[0] != 0x40 || Tempbuffer1[1] != '@') { STX11 = false; STX21 = false; ETX11 = false; LEN1 = 0; Tempbuffer1.Clear(); } else LEN1 = incomingByte; //데이터 길이가온다 } else if (Tempbuffer1.Count == LEN1 + 2 + 1 + 1) //체크섬이 왔다 { CHK1 = incomingByte; } else if (Tempbuffer1.Count == LEN1 + 2 + 1 + 1 + 1) //ETX1 { if (incomingByte != 0x0D) { //ETX가 와야하는데 다른데이터가 왔다 STX11 = false; STX21 = false; ETX11 = false; Console.WriteLine("에러 모두 파기"); Tempbuffer1.Clear(); } } else if (Tempbuffer1.Count == LEN1 + 2 + 1 + 1 + 1 + 1) { //전체길이를 만족햇다. if (incomingByte != 0x0A) { //ETX가 와야하는데 다른데이터가 왔다 STX11 = false; STX21 = false; ETX11 = false; Console.WriteLine("에러 모두 파기"); Tempbuffer1.Clear(); } else { STX11 = false; STX21 = false; ETX11 = false; if (buffer1 == null) buffer1 = new byte[Tempbuffer1.Count]; else if (buffer1.Length != Tempbuffer1.Count) Array.Resize(ref buffer1, Tempbuffer1.Count); Tempbuffer1.CopyTo(buffer1); Tempbuffer1.Clear(); parser(buffer1, tbMsg1, 0); } } } } } private void Dev2_Message(object sender, arDev.arRS232.MessageEventArgs e) { addMsg2($"{e.Message}"); } delegate void DisplayMessage(string message); void parser(byte[] buffer, Action func, int type) { // DisplayMessage messageTarget; var hexString = buffer.GetHexString(); var len = buffer[2]; if (len + 6 != buffer.Length) { func(string.Format("길이오류 예상:{0},수신:{1},{2}", len + 5, buffer.Length, hexString)); } else if (buffer[0] != 0x40 || buffer[1] != 0x40 || buffer[buffer.Length - 2] != 0x0D || buffer[buffer.Length - 1] != 0x0A) { func(string.Format("헤더 오류 : {0}", hexString)); } else { var cmd = buffer[3]; if (cmd == 'I') //IOData { if (len != 15) { func("IO수신길이오류:" + DateTime.Now.ToString("mm:ss.fff") + " 수신 : " + hexString); } else { var IOData = BitConverter.ToUInt32(buffer, 4); var An1 = BitConverter.ToUInt16(buffer, 8); var An2 = BitConverter.ToUInt16(buffer, 10); var Ao11 = buffer[12]; var Ao12 = buffer[13]; var FGData = BitConverter.ToInt32(buffer, 14); byte[] buf_iodata = new byte[4]; Array.Copy(buffer, 4, buf_iodata, 0, 4); var ba = new System.Collections.BitArray(buf_iodata); for (int i = 0; i < 32; i++) { Boolean val = ba[i]; if (val) { } if (type == 0) this.gv1.setValue(i, val); else this.gv2.setValue(i, val); } var binstr = Convert.ToString(IOData, 2).PadLeft(32, '0'); string msg = string.Format(DateTime.Now.ToString("mm:ss.fff") + " IO:{0},AN={1:0000},{2:0000},FG={3},AO={4},{5}", binstr, An1, An2, FGData, Ao11, Ao12); func(msg); if (type == 0) gv1.Invalidate(); else gv2.Invalidate(); } } else if (cmd == 'T') { Boolean errorMessage = buffer[4] == 1; var message = System.Text.Encoding.Default.GetString(buffer, 5, len - 2);//데이터타입과 에러여부 제거 if (type == 0) addMsg1("Rx : " + hexString + ", 메세지: " + message); else addMsg2("Rx : " + hexString + ", 메세지: " + message); } else { if (type == 0) addMsg1(DateTime.Now.ToString("mm:ss.fff") + " 수신 : " + hexString + "\n" + "Data=" + System.Text.Encoding.Default.GetString(buffer)); else addMsg2(DateTime.Now.ToString("mm:ss.fff") + " 수신 : " + hexString + "\n" + "Data=" + System.Text.Encoding.Default.GetString(buffer)); //func(); } } //Console.WriteLine("Rx : " + hexString); } void tbMsg1(string m) { if (textBox3.InvokeRequired) { textBox3.BeginInvoke(new Action(() => { textBox3.Text = (m + "\n"); // textBox3.ScrollToCaret(); })); } else { textBox3.Text = (m + "\n"); //textBox3.ScrollToCaret(); } } void tbMsg2(string m) { if (textBox4.InvokeRequired) { textBox4.BeginInvoke(new Action(() => { textBox4.Text = (m + "\n"); // richTextBox2.ScrollToCaret(); })); } else { textBox4.Text = (m + "\n"); //richTextBox2.ScrollToCaret(); } } private void button1_Click(object sender, EventArgs e) { if (this.dev2.IsOpen) { this.serialPort1.Close(); this.button1.BackColor = SystemColors.Control; } else { if (textbox1.Text.isEmpty()) return; this.serialPort1.PortName = textbox1.Text; this.serialPort1.BaudRate = 250000; this.serialPort1.Open(); button1.BackColor = Color.Lime; } } private void timer1_Tick(object sender, EventArgs e) { this.Text = bufferCount1.ToString() + "/" + bufferCount2.ToString(); } private void button2_Click(object sender, EventArgs e) { if (textbox2.Text.isEmpty()) return; if (this.dev2.IsOpen) { this.dev2.Close(); this.button2.BackColor = SystemColors.Control; } else { this.dev2.PortName = textbox2.Text; this.dev2.BaudRate = int.Parse(comboBox1.Text); this.dev2.Open(); button2.BackColor = Color.Lime; // timer1.Start(); } addMsg2($"open:{this.dev2.IsOpen}"); } private void Form1_Load(object sender, EventArgs e) { textbox1.Items.Clear(); textbox2.Items.Clear(); foreach (var port in System.IO.Ports.SerialPort.GetPortNames()) { textbox1.Items.Add(port); textbox2.Items.Add(port); } // textbox1.Text = "COM15"; // textbox2.Text = "COM16"; List values = new List(); for (int i = 0; i < 32; i++) values.Add(false); this.gv1.setValue(values.ToArray()); this.gv2.setValue(values.ToArray()); //그리드뷰 값초기화 for (int i = 0; i < 32; i++) { string pinName = i.ToString(); if (i < 16 && i < DIpinName1.Length) pinName = string.Format("[{0}:{2}] {1}", i, DIpinName1[i], DIpinList1[i]); else if (i >= 16 && (i - 16) < DopinName1.Length) pinName = string.Format("[{0}:{2}] {1}", i, DopinName1[i - 16], DOpinList1[i - 16]); this.gv1.setTitle(i, pinName); this.gv1.setValue(i, 0); } for (int i = 0; i < 32; i++) { string pinName = i.ToString(); if (i < 16) { var pin = (arDev.FakePLC.DIName)i; pinName = $"[{i}] {pin}"; } else { var pin = (arDev.FakePLC.DOName)(i-16); pinName = $"[{i}] {pin}"; } this.gv2.setTitle(i, pinName); this.gv2.setValue(i, 0); } timer1.Start(); } private void gv1_ItemClick(object sender, arFrame.Control.GridView.ItemClickEventArgs e) { //sub item click if (e.idx < 16) { Util.MsgE("입력은 상태를 변경할 수 없습니다"); } else { //이 인덱스에 해당하는 포트번호를 가져왕야함 var dataIndex = e.idx - 16; if (dataIndex >= 0) { var pinNo = DOpinList1[e.idx - 16]; var curVal = gv1.getValue(e.idx); byte newval = (byte)(curVal == 0 ? 1 : 0); Sendcommand1(eCommand1.SET_DOUTPUT, pinNo, newval); } else Util.MsgE("해당 주소는 허용되지 않습니다."); } } byte makeChecksum(byte[] buffer) { //return 0; byte chk = 0; foreach (var b in buffer) chk = (byte)(chk ^ b); return chk; } enum eCommand1 : byte { LOAD = 0, //EEPROM 불러오기 SAVE, //EEPROM 저장 RESET, //초기화 SET_PINMODE, //PINMODE 설정 SET_DOUTPUT, //디지털출력설정(포트번호,값[1,0]) SET_AOUTPUT, //아날로그출력설정(포트GET_SETTING = 50, 포트번호,값(0~255) GET_SETTING = 50, //셋팅값 요청 MOT = 100, //p1(0=left,1=right), p2(0=stop,1=pos run,2=neg run) MOVE_GO, MOVE_BACK, MOVE_LEFT, MOVE_RIGHT, MOVE_STOP } enum eCommand2 : byte { LOAD = 0, //EEPROM 불러오기 SAVE, //EEPROM 저장 RESET, //초기화 SET_PINMODE, //PINMODE 설정 SET_DOUTPUT, //디지털출력설정(포트번호,값[1,0]) SET_AOUTPUT, //아날로그출력설정(포트번호,값[0~255]) GET_SETTING = 50, //셋팅값 요청 GUIDE_MOT, SET_GUIDE_SPD = 100,//가이드커버(양쪽) 속도 설정(0~255) } void addMsg1(string m) { if (logMain.InvokeRequired) { logMain.BeginInvoke(new Action(() => { if (m.IndexOf('\n') != -1) m = m.Replace("\r", "").Replace("\n", ""); logMain.AddMsg(m); })); } else { if (m.IndexOf('\n') != -1) m = m.Replace("\r", "").Replace("\n", ""); logMain.AddMsg(m); } } void addMsg2(string m) { if (logSub.InvokeRequired) { logSub.BeginInvoke(new Action(() => { if (m.IndexOf('\n') != -1) m = m.Replace("\r", "").Replace("\n", ""); logSub.AddMsg(m); })); } else { if (m.IndexOf('\n') != -1) m = m.Replace("\r", "").Replace("\n", ""); logSub.AddMsg(m); } } private void gv2_ItemClick(object sender, arFrame.Control.GridView.ItemClickEventArgs e) { //sub item click if (e.idx < 16) { Util.MsgE("입력은 상태를 변경할 수 없습니다"); } else { //이 인덱스에 해당하는 포트번호를 가져왕야함 var dataIndex = e.idx - 16; if (dataIndex >= 0) { var pinNo = DOpinList2[e.idx - 16]; var curVal = gv2.getValue(e.idx); byte newval = (byte)(curVal == 0 ? 1 : 0); // Sendcommand2(eCommand2.SET_DOUTPUT, pinNo, newval); } else Util.MsgE("해당 주소는 허용되지 않습니다."); } } void Sendcommand1(eCommand1 cmd, byte p1, byte p2) { //이번호의 상태를 on/off 해줘야함 List buffer = new List(); buffer.Add((byte)cmd); //command buffer.Add(p1); buffer.Add(p2); byte dataLen = (byte)buffer.Count; //데이터길이 byte checksum = makeChecksum(buffer.ToArray()); buffer.Insert(0, (byte)'@'); //stx buffer.Insert(1, (byte)'@'); //stx buffer.Insert(2, dataLen); buffer.Add(checksum); //길이를 제외한 실제 데이터 영역만 체크섬 buffer.Add(0x0D); //etx buffer.Add(0x0A); //etx addMsg1("Tx Buffer : " + buffer.ToArray().GetHexString()); if (this.serialPort1.IsOpen) serialPort1.Write(buffer.ToArray(), 0, buffer.Count); else addMsg1("포트(2)가 열리지 않았습니다"); } private void button3_Click(object sender, EventArgs e) { //-zup dev2.ZMot(arDev.FakePLC.ZMotDirection.Up); } private void button4_Click(object sender, EventArgs e) { //z-stop dev2.ZMot(arDev.FakePLC.ZMotDirection.Stop); } private void button5_Click(object sender, EventArgs e) { //z-down dev2.ZMot(arDev.FakePLC.ZMotDirection.Down); } private void button8_Click(object sender, EventArgs e) { Sendcommand1(eCommand1.MOT, 0, 1); } private void button6_Click(object sender, EventArgs e) { Sendcommand1(eCommand1.MOT, 0, 0); } private void button7_Click(object sender, EventArgs e) { Sendcommand1(eCommand1.MOT, 0, 2); } private void button10_Click(object sender, EventArgs e) { Sendcommand1(eCommand1.MOT, 1, 2); } private void button9_Click(object sender, EventArgs e) { Sendcommand1(eCommand1.MOT, 1, 0); } private void button11_Click(object sender, EventArgs e) { Sendcommand1(eCommand1.MOT, 1, 1); } private void button14_Click(object sender, EventArgs e) { Sendcommand1(eCommand1.MOVE_GO, 0, 0); } private void button12_Click(object sender, EventArgs e) { Sendcommand1(eCommand1.MOVE_STOP, 0, 0); } private void button13_Click(object sender, EventArgs e) { Sendcommand1(eCommand1.MOVE_BACK, 0, 0); } private void button15_Click(object sender, EventArgs e) { //지정된 %의 값을 전송한다 var but = sender as Button; var percValue = byte.Parse(but.Text.ToString()); var realValue = (percValue / 100.0) * 255.0; Sendcommand1(eCommand1.SET_AOUTPUT, 11, (byte)realValue); Sendcommand1(eCommand1.SET_AOUTPUT, 12, (byte)realValue); } private void button34_Click(object sender, EventArgs e) { //지정된 %의 값을 전송한다 var but = sender as Button; var percValue = byte.Parse(but.Text.ToString()); var realValue = (percValue / 100.0) * 255.0; // Sendcommand2(eCommand2.SET_AOUTPUT, 11, (byte)realValue); //Z축 모터는 동일하게 사용한다 } private void timer1_Tick_1(object sender, EventArgs e) { timer1.Stop(); if (dev2 != null && dev2.IsOpen) { for(byte i = 0; i < 32;i++) { bool v = false; if (i < 16) v = dev2.GetValueI(i); else v = dev2.GetValueO((byte)(i-16)); this.gv2.setValue(i, v); } this.gv2.Invalidate(); } timer1.Start(); } } }