using log4net; using System; using System.Collections.Generic; using System.IO.Ports; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace DIT.Framework.Module { /// /// Serial Module을 제어하기 위한 클래스 /// public class SerialModule { /// /// Exception Log /// private ILog ExceptionLog = LogManager.GetLogger("SerialException"); /// /// Exception Log 사용유무 확인 /// public bool UseExceptionLog { get; set; } /// /// Exception Log를 기록하기 위한 메서드 /// /// Error Message private void WriteExceptionLog(string msg) { if (UseExceptionLog) ExceptionLog.Debug(msg); } /// /// lock 제어를 하기 위함. /// private object thislock; /// /// lock 제어를 하기 위함. /// private bool lockcheck; /// /// SerialPort와 직접 통신. /// private SerialPort _serialPort; /// /// SerialPort에서 받은 Data를 Encoding형식에 맞게 Parsing하기 위함. /// public Encoding EncodeType { get { return encodeType; } private set { encodeType = value; } } private Encoding encodeType; /// /// Carriage Return /// public char CR { get { return '\r'; }} /// /// Line Feed /// public char LF { get { return '\n'; }} public char Spliiter { get { return splitter; } private set { splitter = value; } } private char splitter; /// /// SerialPort의 Open 여부 /// public bool isOpen { get { return _serialPort.IsOpen; } } /// /// Splitter로 Split하기 전 Data를 모아두는 저장소. /// private List RecvData; /// /// encodeType을 설정해주는 메서드 /// /// public void SetEncodeType(Encoding encodeType) { this.EncodeType = encodeType; } /// /// SerialPort를 생성하고 내부 Data를 설정하는 생성자.(Base Encoding은 ASCII) /// /// Port이름 ex)COM6, COM13 /// 전송속도 ex)4800, 9600, 19200 /// Data의 마지막을 확인할 문자 기본 '\r' /// 바이트 당 데이터 비트의 표준길이 ex)5, 6, 7, 8 /// 패리티 검사 프로토콜 ex)Ports.Parity.None /// 비트당 정지비트의 표준 개수 ex)Ports.StopBits.One /// 직렬 전송을 위한 핸드셰이킹 ex)Ports.Handshake.None public SerialModule(string PortName, int BaudRate, char Spliiter = '\r', int DataBits = 8, Parity Parity = Parity.None, StopBits StopBits = StopBits.One, Handshake Handshake = Handshake.None) { thislock = new object(); RecvData = new List(); _serialPort = new SerialPort(); _serialPort.PortName = PortName; _serialPort.BaudRate = BaudRate; _serialPort.DataBits = DataBits; _serialPort.Parity = Parity; _serialPort.StopBits = StopBits; _serialPort.Handshake = Handshake; _serialPort.ReadTimeout = 500; _serialPort.WriteTimeout = 500; this.Spliiter = Spliiter; EncodeType = Encoding.ASCII; } /// /// SerialPort를 생성하고 내부 Data를 설정하는 생성자.(Base Encoding은 ASCII) /// /// Port이름 ex)COM6, COM13 /// 전송속도 ex)4800, 9600, 19200 /// 바이트 당 데이터 비트의 표준길이 ex)5, 6, 7, 8 /// 패리티 검사 프로토콜 ex)Ports.Parity.None /// 비트당 정지비트의 표준 개수 ex)Ports.StopBits.One /// 직렬 전송을 위한 핸드셰이킹 ex)Ports.Handshake.None public SerialModule(string PortName, int BaudRate, int DataBits = 8, Parity Parity = Parity.None, StopBits StopBits = StopBits.One, Handshake Handshake = Handshake.None) { thislock = new object(); RecvData = new List(); _serialPort = new SerialPort(); _serialPort.PortName = PortName; _serialPort.BaudRate = BaudRate; _serialPort.DataBits = DataBits; _serialPort.Parity = Parity; _serialPort.StopBits = StopBits; _serialPort.Handshake = Handshake; _serialPort.ReadTimeout = 500; _serialPort.WriteTimeout = 500; EncodeType = Encoding.ASCII; } /// /// 사용가능한 Portname을 반환. /// /// Portname 값 public static string[] GetPorts() { return SerialPort.GetPortNames(); } /// /// 설정 Port Open /// /// 성공 실패 여부 public bool Open() { try { _serialPort.Open(); return true; } catch(Exception e) { WriteExceptionLog(e.Message); return false; } } /// /// 설정 Port Close /// /// 성공 실패 여부 public bool Close() { try { _serialPort.Close(); return true; } catch(Exception e) { WriteExceptionLog(e.Message); return false; } } /// /// SerialPort에 Data를 보내기 위한 메서드 /// /// string형 Data /// public bool SendData(string sCmd) { try { lock(thislock) { while(lockcheck) Monitor.Wait(thislock); lockcheck = true; _serialPort.Write(sCmd); lockcheck = false; Monitor.Pulse(thislock); } return true; } catch (Exception e) { WriteExceptionLog(e.Message); lockcheck = false; return false; } } /// /// SerialPort에 Data를 보내기 위한 메서드 /// /// byte형 배열 Data /// public bool SendData(byte[] bCmd) { try { lock (thislock) { while (lockcheck) Monitor.Wait(thislock); lockcheck = true; _serialPort.Write(bCmd, 0, bCmd.Length); lockcheck = false; Monitor.Pulse(thislock); } return true; } catch (Exception e) { WriteExceptionLog(e.Message); lockcheck = false; return false; } } /// /// SerialPort에 Data를 보내기 위한 메서드 /// /// char형 배열 Data /// public bool SendData(char[] cCmd) { try { lock (thislock) { while (lockcheck) Monitor.Wait(thislock); lockcheck = true; _serialPort.Write(cCmd, 0, cCmd.Length); lockcheck = false; Monitor.Pulse(thislock); } return true; } catch (Exception e) { WriteExceptionLog(e.Message); lockcheck = false; return false; } } /// /// SerialPort에 Data를 보낸후 Read 대기 /// /// string Data /// public byte[] SendWaitData(string sCmd) { try { byte[] recvbyte; lock (thislock) { while (lockcheck) Monitor.Wait(thislock); lockcheck = true; _serialPort.BaseStream.Flush(); _serialPort.Write(sCmd); RecvData.Clear(); byte[] bytes; DateTime check = DateTime.Now; while (true) { if (_serialPort.BytesToRead > 0) { bytes = new byte[_serialPort.BytesToRead]; _serialPort.Read(bytes, 0, bytes.Length); if (CheckData(bytes)) { break; } } if ((DateTime.Now - check).TotalMilliseconds > _serialPort.ReadTimeout) { bytes = null; break; } } recvbyte = RecvData.ToArray(); lockcheck = false; Monitor.Pulse(thislock); } return recvbyte; } catch (Exception e) { WriteExceptionLog(e.Message); lockcheck = false; return null; } } /// /// SerialPort에 Data를 보낸후 Read 대기 /// /// byte형 배열 Data /// public byte[] SendWaitData(byte[] bCmd) { try { byte[] recvbyte; lock (thislock) { while (lockcheck) Monitor.Wait(thislock); lockcheck = true; _serialPort.BaseStream.Flush(); _serialPort.Write(bCmd, 0, bCmd.Length); RecvData.Clear(); byte[] bytes; DateTime check = DateTime.Now; while (true) { if (_serialPort.BytesToRead > 0) { bytes = new byte[_serialPort.BytesToRead]; _serialPort.Read(bytes, 0, bytes.Length); if (CheckData(bytes)) { break; } } if ((DateTime.Now - check).TotalMilliseconds > _serialPort.ReadTimeout) { bytes = null; break; } } recvbyte = RecvData.ToArray(); lockcheck = false; Monitor.Pulse(thislock); } return recvbyte; } catch (Exception e) { WriteExceptionLog(e.Message); lockcheck = false; return null; } } /// /// SerialPort에 Data를 보낸후 Read 대기 /// /// char형 배열 Data /// public byte[] SendWaitData(char[] cCmd) { try { byte[] recvbyte; lock (thislock) { while (lockcheck) Monitor.Wait(thislock); lockcheck = true; _serialPort.BaseStream.Flush(); _serialPort.Write(cCmd,0,cCmd.Length); RecvData.Clear(); byte[] bytes; DateTime check = DateTime.Now; while (true) { if (_serialPort.BytesToRead > 0) { bytes = new byte[_serialPort.BytesToRead]; _serialPort.Read(bytes, 0, bytes.Length); if (CheckData(bytes)) { break; } } if ((DateTime.Now - check).TotalMilliseconds > _serialPort.ReadTimeout) { bytes = null; break; } } recvbyte = RecvData.ToArray(); lockcheck = false; Monitor.Pulse(thislock); } return recvbyte; } catch (Exception e) { WriteExceptionLog(e.Message); lockcheck = false; return null; } } /// /// Splitter Data의 유무를 판별하기 위한 메서드 /// /// SerialPort에서 받은 Data /// private bool CheckData(byte[] bytes) { try { char[] datas = Encoding.ASCII.GetChars(bytes); RecvData.AddRange(bytes); if (datas.Contains(Spliiter)) return true; else return false; } catch (Exception e) { WriteExceptionLog(e.Message); return false; } } } }