using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading; namespace arDev { public abstract partial class arRS232 : IDisposable { protected System.IO.Ports.SerialPort _device; protected ManualResetEvent _mre; protected byte[] LastReceiveBuffer = new byte[] { }; /// /// 최종 전송 메세지 /// public byte[] lastSendBuffer = new byte[] { }; //public int ValidCheckTimeMSec { get; set; } = 5000; protected List tempBuffer = new List(); protected Boolean findSTX = false; public string errorMessage { get; set; } public DateTime LastConnTime { get; set; } public DateTime LastConnTryTime { get; set; } public DateTime lastSendTime; /// /// 메세지 수신시 사용하는 내부버퍼 /// protected List _buffer = new List(); /// /// 데이터조회간격(초) /// public float ScanInterval { get; set; } // public byte[] LastRecvData; public string LastRecvString { get { if (LastReceiveBuffer == null) return String.Empty; else return System.Text.Encoding.Default.GetString(LastReceiveBuffer); } } /// /// 마지막으로 데이터를 받은 시간 /// public DateTime lastRecvTime; public int WriteError = 0; public string WriteErrorMessage = string.Empty; public int WaitTimeout { get; set; } = 1000; public int MinRecvLength { get; set; } = 1; /// /// 포트이름 /// [Description("시리얼 포트 이름")] [Category("설정"), DisplayName("Port Name")] public string PortName { get { if (_device == null) return string.Empty; else return _device.PortName; } set { if (this.IsOpen) { Message?.Invoke(this, new MessageEventArgs("포트가 열려있어 포트이름을 변경할 수 없습니다", true)); } else if (String.IsNullOrEmpty(value) == false) _device.PortName = value; else { Message?.Invoke(this, new MessageEventArgs("No PortName", true)); } } } public int BaudRate { get { if (_device == null) return 0; else return _device.BaudRate; } set { if (this.IsOpen) { Message?.Invoke(this, new MessageEventArgs("포트가 열려있어 BaudRate(를) 변경할 수 없습니다", true)); } else if (value != 0) _device.BaudRate = value; else Message?.Invoke(this, new MessageEventArgs("No baud rate", true)); } } public arRS232() { _device = new System.IO.Ports.SerialPort(); this.BaudRate = 9600; ScanInterval = 10; _device.DataReceived += barcode_DataReceived; _device.ErrorReceived += this.barcode_ErrorReceived; _device.WriteTimeout = 5000; _device.ReadTimeout = 5000; // _device.DtrEnable = false; _device.ReadBufferSize = 8192; _device.WriteBufferSize = 8192; errorMessage = string.Empty; lastRecvTime = DateTime.Parse("1982-11-23"); LastConnTime = DateTime.Parse("1982-11-23"); LastConnTryTime = DateTime.Parse("1982-11-23"); lastRecvTime = DateTime.Parse("1982-11-23"); this._mre = new ManualResetEvent(true); } ~arRS232() { Dispose(false); } // Flag: Has Dispose already been called? bool disposed = false; // Public implementation of Dispose pattern callable by consumers. public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } // Protected implementation of Dispose pattern. protected virtual void Dispose(bool disposing) { if (disposed) return; if (disposing) { // Free any other managed objects here. // } _device.DataReceived -= barcode_DataReceived; _device.ErrorReceived -= this.barcode_ErrorReceived; // Free any unmanaged objects here. // disposed = true; } public Boolean Open() { try { _device.Open(); return IsOpen; } catch (Exception ex) { errorMessage = ex.Message; Message.Invoke(this, new MessageEventArgs(ex.Message, true)); return false; } } public string GetHexString(Byte[] input) { System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (byte b in input) sb.Append(" " + b.ToString("X2")); return sb.ToString(); } /// /// 포트가 열려있는지 확인 /// [Description("현재 시리얼포트가 열려있는지 확인합니다")] [Category("정보"), DisplayName("Port Open")] public Boolean IsOpen { get { if (_device == null) return false; return _device.IsOpen; } } public virtual void Close(Boolean PortClose = true) { if (_device != null && _device.IsOpen) { _device.DiscardInBuffer(); _device.DiscardOutBuffer(); if (PortClose) _device.Close(); //dispose에서는 포트를 직접 클리어하지 않게 해뒀다. } } protected Boolean RaiseRecvData() { return RaiseRecvData(LastReceiveBuffer.ToArray(), false); } /// /// 수신받은 메세지를 발생 시킵니다 /// /// /// public virtual Boolean RaiseRecvData(byte[] Data, bool udpatelastbuffer) { //181206 - 최종수신 메세지 기록 lastRecvTime = DateTime.Now; if (udpatelastbuffer && Data != null) { if (LastReceiveBuffer == null || LastReceiveBuffer.Length != Data.Length) { LastReceiveBuffer = new byte[Data.Length]; Array.Copy(Data, LastReceiveBuffer, Data.Length); } } try { Message?.Invoke(this, new MessageEventArgs(Data, true)); //recvmessage if (ProcessRecvData(Data) == false) { //Message?.Invoke(this, new MessageEventArgs(Data, true)); //recvmessage Message?.Invoke(this, new MessageEventArgs(this.errorMessage, true)); //errormessage return false; } else { return true; } } catch (Exception ex) { this.errorMessage = ex.Message; this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true)); return false; } } /// /// 수신받은 자료를 처리한다 /// /// /// public abstract bool ProcessRecvData(byte[] data); #region "Internal Events" void barcode_ErrorReceived(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e) { Message?.Invoke(this, new MessageEventArgs(e.ToString(), true)); } byte[] buffer = new byte[] { }; void barcode_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { try { int ReadCount = _device.BytesToRead; buffer = new byte[ReadCount]; _device.Read(buffer, 0, buffer.Length); System.Text.StringBuilder LogMsg = new StringBuilder(); byte[] remainBuffer; Repeat: if (CustomParser(buffer, out remainBuffer)) { //분석완료이므로 받은 데이터를 버퍼에 기록한다 if (LastReceiveBuffer == null || (LastReceiveBuffer.Length != tempBuffer.Count)) Array.Resize(ref LastReceiveBuffer, tempBuffer.Count); Array.Copy(tempBuffer.ToArray(), LastReceiveBuffer, tempBuffer.Count); tempBuffer.Clear(); //수신메세지발생 RaiseRecvData(); if (remainBuffer != null && remainBuffer.Length > 0) { //버퍼를 변경해서 다시 전송을 해준다. Array.Resize(ref buffer, remainBuffer.Length); Array.Copy(remainBuffer, buffer, remainBuffer.Length); goto Repeat; //남은 버퍼가 있다면 진행을 해준다. } } } catch (Exception ex) { //if (IsOpen) //{ // //_device.DiscardInBuffer(); // //_device.DiscardOutBuffer(); //} errorMessage = ex.Message; this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true)); } } #endregion #region "External Events" /// /// 오류 및 기타 일반 메세지 /// public event EventHandler Message; #endregion #region "Event Args" /// /// 데이터를 수신할떄 사용함(RAW 포함) /// public class ReceiveDataEventArgs : EventArgs { private byte[] _buffer = null; /// /// 바이트배열의 버퍼값 /// public byte[] Value { get { return _buffer; } } /// /// 버퍼(바이트배열)의 데이터를 문자로 반환합니다. /// public string StrValue { get { //return string.Empty; if (_buffer == null || _buffer.Length < 1) return string.Empty; else return System.Text.Encoding.Default.GetString(_buffer); } } public ReceiveDataEventArgs(byte[] buffer) { _buffer = buffer; } } /// /// 메세지를 강제 발생 /// /// /// protected virtual void RaiseMessage(MessageType mt, string message) { this.Message?.Invoke(this, new MessageEventArgs(mt, message)); } public enum MessageType { Normal, Error, Send, Recv, } public class MessageEventArgs : EventArgs { public MessageType MsgType { get; set; } private string _message = string.Empty; /// /// Recv,Send,Normal,Error 모두 지원 /// public string Message { get { return _message; } } private byte[] _data = null; /// /// Recv,Send에서만 값이 존재 합니다 /// public byte[] Data { get { return _data; } } public MessageEventArgs(string Message, bool isError = false) { if (isError) MsgType = MessageType.Error; else MsgType = MessageType.Normal; _message = Message; } public MessageEventArgs(MessageType msgtype, string Message) { MsgType = msgtype; _message = Message; _data = System.Text.Encoding.Default.GetBytes(Message); } public MessageEventArgs(byte[] buffer, bool isRecv = true) { if (isRecv) MsgType = MessageType.Recv; else MsgType = MessageType.Send; _data = new byte[buffer.Length]; Array.Copy(buffer, _data, Data.Length); _message = System.Text.Encoding.Default.GetString(_data); } } #endregion protected abstract bool CustomParser(byte[] buf, out byte[] remainBuffer); /// /// 포트가 열려있거나 데이터 수신시간이 없는경우 false를 반환합니다 /// public Boolean IsValid { get { if (IsOpen == false) return false; if (lastRecvTime.Year == 1982) return false; var ts = DateTime.Now - lastRecvTime; if (ts.TotalSeconds > (this.ScanInterval * 2.5)) return false; return true; } } protected bool WriteData(string cmd) { return WriteData(System.Text.Encoding.Default.GetBytes(cmd)); } /// /// 포트에 쓰기(barcode_DataReceived 이벤트로 메세지수신) /// protected Boolean WriteData(byte[] data) { Boolean bRet = false; //171205 : 타임아웃시간추가 if (!_mre.WaitOne(WaitTimeout)) { errorMessage = $"WriteData:MRE:WaitOne:TimeOut {WaitTimeout}ms"; this.Message?.Invoke(this, new MessageEventArgs(errorMessage, true)); return false; } _mre.Reset(); //Array.Resize(ref data, data.Length + 2); try { lastSendTime = DateTime.Now; if (lastSendBuffer == null) lastSendBuffer = new byte[data.Length]; //171113 else Array.Resize(ref lastSendBuffer, data.Length); Array.Copy(data, lastSendBuffer, data.Length); for (int i = 0; i < data.Length; i++) _device.Write(data, i, 1); //_device.Write(data, 0, data.Length); //171113 this.Message?.Invoke(this, new MessageEventArgs(data, false)); bRet = true; WriteError = 0; WriteErrorMessage = string.Empty; } catch (Exception ex) { // this.isinit = false; this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true)); bRet = false; WriteError += 1; //연속쓰기오류횟수 WriteErrorMessage = ex.Message; } finally { _mre.Set(); } return bRet; } } }