using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Threading;

namespace Project.Device
{
    public class Barcode : IDisposable
    {
        protected Boolean _isinit = false;

        #region "Event Args"

        /// <summary>
        /// 데이터를 수신할떄 사용함(RAW 포함)
        /// </summary>
        public class ReceiveDataEventArgs : EventArgs
        {
            private byte[] _buffer = null;

            /// <summary>
            /// 바이트배열의 버퍼값
            /// </summary>
            public byte[] Value { get { return _buffer; } }

            /// <summary>
            /// 버퍼(바이트배열)의 데이터를 문자로 반환합니다.
            /// </summary>
            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;
            }
        }

        public class MessageEventArgs : EventArgs
        {
            private Boolean _isError = false;
            public Boolean IsError { get { return _isError; } }
            private string _message = string.Empty;
            public string Message { get { return _message; } }
            public MessageEventArgs(Boolean isError, string Message)
            {
                _isError = isError;
                _message = Message;
            }
        }

        #endregion

        #region "Enum & Structure"

        /// <summary>
        /// 데이터수신시 해당 데이터의 끝을 결정하는 방식을 설정합니다.
        /// </summary>
        public enum eTerminal : byte
        {
            /// <summary>
            /// line feed
            /// </summary>
            LF = 0,

            /// <summary>
            /// carrige return
            /// </summary>
            CR,

            /// <summary>
            /// cr+lf
            /// </summary>
            CrLf,

            /// <summary>
            /// stx +  ETx 구성된 프로토콜을 감지합니다. stx,etx는 임의 지정이 가능하며 기본값으로는 stx = 0x02, etx = 0x03 을 가지고 있습니다.
            /// </summary>
            ETX,

            /// <summary>
            /// 데이터의 길이를 가지고 판단합니다.
            /// </summary>
            Length,

            /// <summary>
            /// 설정없음 .. 일정시간동안 대기한 후 버퍼의 내용을 모두 데이터로 인정합니다.
            /// </summary>
            None,

            /// <summary>
            /// 내부 Receive 이벤트를 사용하지 않고 Raw 이벤트를 사용합니다.
            /// 이 값을 설정할 경우 내부 Receivce 이벤트 내에서 메세지 수신 이벤트가 발생하지 않습니다.
            /// DataParSER을 상속하여 해당 파서에서 데이터를 분리하세요. True 이면 분리성공, false 이면 완료될때까지 대기합니다.
            /// </summary>
            CustomParser
        }

        #endregion

        #region "Public variable"

        /// <summary>
        /// 오류가 발생했다면 이 변수에 그 내용이 기록됩니다.
        /// </summary>
        public string errorMessage = string.Empty;

        ///// <summary>
        ///// WriteDataSync 명령 사용시 최대로 기다리는 시간
        ///// </summary>
        //public int syncTimeout = 5000;

        /// <summary>
        /// 이 값은 종단기호 형식이 Length 일때 사용됩니다. 버퍼의 갯수이 이 값과 일치하면 수신 이벤트를 발생합니다.
        /// </summary>
        public int MaxDataLength = 0x0d;

        /// <summary>
        /// 마지막으로 데이터는 전송한 시간
        /// </summary>
        public DateTime lastSendTime;

        /// <summary>
        /// 최종 전송 메세지
        /// </summary>
        public byte[] lastSendBuffer;

        /// <summary>
        /// 마지막으로 데이터를 받은 시간
        /// </summary>
        public DateTime lastRecvTime = DateTime.Parse("1982-11-23");

        /// <summary>
        /// 데이터 전송시 전송메세지를 발생할것인가? 171113
        /// </summary>
        public Boolean EnableTxMessage { get; set; }

        /// <summary>
        /// terminal 이 stx 일때에만 사용하며 기본값은 0x03
        /// </summary>
        [Description("종단기호형식이 ETX일때 사용하는 데이터의 종료문자값입니다. 바이트값이므로 0~255 사이로 입력하세요.")]
        [Category("설정"), DisplayName("Data End Byte")]
        public byte ETX { get; set; }

        #endregion

        #region "Protect & private Variable"

        /// <summary>
        /// 메세지 수신시 사용하는 내부버퍼
        /// </summary>
        protected List<byte> _buffer = new List<byte>();


        /// <summary>
        /// 데이터의 끝을 분석하는 종단기호의 설정
        /// </summary>
        private eTerminal _term = eTerminal.LF;

        /// <summary>
        /// WriteDataSync 명령사용시 활성화됨
        /// </summary>
        protected Boolean _isSync = false;

        /// <summary>
        /// sync timeOUt체크시 사용합니다.
        /// </summary>
        private System.Diagnostics.Stopwatch _wat = new System.Diagnostics.Stopwatch();

        /// <summary>
        /// Serialport Device
        /// </summary>
        protected System.IO.Ports.SerialPort _device;

        /// <summary>
        /// for autoreset events
        /// </summary>
        protected ManualResetEvent _mre;

        protected Boolean isDisposed = false;

        #endregion

        #region "Internal Events"

        void barcode_ErrorReceived(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e)
        {
            if (Message != null) Message(this, new MessageEventArgs(true, e.ToString()));
        }

        

        void barcode_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            if (_isSync) return; //싱크모드일경우에는 해당 루틴에서 직접 읽는다
            _isSync = false;
            //none일경우에는 100ms 정도 기다려준다.
            if (_term == eTerminal.None)
            {
                //none 일경우에는 무조건 데이터로 취급한다.
                System.Threading.Thread.Sleep(200);
                _buffer.Clear();
            }

            try
            {
                int ReadCount = _device.BytesToRead;

                byte[] buffer = new byte[ReadCount];
                _device.Read(buffer, 0, buffer.Length);

                if (ReceiveData_Raw != null) ReceiveData_Raw(this, new ReceiveDataEventArgs(buffer));
                System.Text.StringBuilder LogMsg = new StringBuilder();

                if (Terminal == eTerminal.CustomParser)
                {
                    byte[] remainBuffer;
                Repeat:
                    if (CustomParser(buffer, out remainBuffer))
                    {
                        //parser ok
                        RaiseRecvData(_buffer.ToArray());
                        _buffer.Clear();
                        if (remainBuffer != null && remainBuffer.Length > 0)
                        {
                            //버퍼를 변경해서 다시 전송을 해준다.
                            buffer = new byte[remainBuffer.Length];
                            Array.Copy(remainBuffer, buffer, buffer.Length);
                            goto Repeat; //남은 버퍼가 있다면 진행을 해준다.
                        }
                    }
                }
                else
                {
                    foreach (byte bb in buffer)
                    {
                        switch (_term)
                        {

                            case eTerminal.CR:
                                if (bb == 0x0D)
                                {
                                    RaiseRecvData(_buffer.ToArray()); ;
                                    _buffer.Clear();
                                }
                                else _buffer.Add(bb);
                                break;
                            case eTerminal.LF:
                                if (bb == 0x0A)
                                {
                                    RaiseRecvData(_buffer.ToArray()); ;
                                    _buffer.Clear();
                                }
                                else _buffer.Add(bb);
                                break;
                            case eTerminal.CrLf:
                                if (bb == 0x0A)
                                {
                                    RaiseRecvData(_buffer.ToArray()); ;
                                    _buffer.Clear();
                                }
                                else if (bb == 0x0D)
                                {
                                    //0d는 그냥 넘어간다.
                                }
                                else _buffer.Add(bb);
                                break;
                            case eTerminal.Length:
                                _buffer.Add(bb);
                                if (_buffer.Count == MaxDataLength)
                                {
                                    RaiseRecvData(_buffer.ToArray()); ;
                                    _buffer.Clear();
                                }
                                else if (_buffer.Count > MaxDataLength)
                                {
                                    RaiseMessage("Buffer Length Error " + _buffer.Count.ToString() + "/" + MaxDataLength.ToString(), true);
                                    _buffer.Clear();
                                }
                                break;
                            case eTerminal.ETX: //asc타입의 프로토콜에서는 STX,ETX가 고유값이다.
                                if (bb == STX)
                                {
                                    _buffer.Clear();
                                }
                                else if (bb == ETX)
                                {
                                    RaiseRecvData(_buffer.ToArray()); ;
                                    _buffer.Clear();
                                }
                                else _buffer.Add(bb);
                                break;
                            case eTerminal.None:
                                _buffer.Add(bb);
                                break;
                        }
                    }

                    //170802
                    if (_term == eTerminal.None)
                    {
                        RaiseRecvData(_buffer.ToArray()); ;
                        _buffer.Clear();
                    }
                }
            }
            catch (Exception ex)
            {
                if (IsOpen)
                {
                    //_device.DiscardInBuffer();
                    //_device.DiscardOutBuffer();
                }
                errorMessage = ex.Message;
                RaiseMessage(ex.Message, true);
            }

        }


        #endregion

        #region "External Events"

        /// <summary>
        /// 바코드에서 들어오는 데이터의 원본 메세지
        /// </summary>
        public event EventHandler<ReceiveDataEventArgs> ReceiveData_Raw;

        /// <summary>
        /// 데이터가 들어올 경우 발생합니다 (종단기호=Termianl) 문자열이 발견된 후에 발생함
        /// </summary>
        public event EventHandler<ReceiveDataEventArgs> ReceiveData;


        /// <summary>
        /// 데이터를 전송할 때 해당 이벤트가 발생합니다.
        /// </summary>
        public event EventHandler<ReceiveDataEventArgs> SendData;

        /// <summary>
        /// 오류 및 기타 일반 메세지
        /// </summary>
        public event EventHandler<MessageEventArgs> Message;

        /// <summary>
        /// 시리얼포트의 핀 상태값이 변경될 때 발생합니다.
        /// </summary>
        //public event EventHandler<System.IO.Ports.SerialPinChangedEventArgs> serialPinchanged;


        #endregion

        #region "Properties"

        /// <summary>
        /// 식별번호(임의지정가능) - 장치 생성시 입력
        /// </summary>
        [Description("이 장치의 식별 ID(임의 지정가능)")]
        [Category("설정"), DisplayName("Device No")]
        public int ID { get; set; }

        /// <summary>
        /// terminal 이 etx 일때에만 사용하며 기본값은 0x02
        /// </summary>
        [Description("종단기호형식이 ETX일때 사용하는 데이터의 시작문자값입니다. 바이트값이므로 0~255 사이로 입력하세요.")]
        [Category("설정"), DisplayName("Data Start Byte")]
        public byte STX { get; set; }

        /// <summary>
        /// 내장 분석기(Parser)를 사용할 경우 최종 데이터에서 CR,LF를 제거할지 선택합니다.
        /// </summary>
        [Description("내장분석기(Parser)를 사용할 경우 최종 데이터에서 CR.LF를 제거할지 선택합니다.")]
        [Category("기타"), DisplayName("CRLF 제거")]
        public Boolean RemoveCRLFNULL { get; set; }

        /// <summary>
        /// 종단기호 형식
        /// </summary>
        [Description("데이터의 종단기호를 설정합니다. 지정한 데이터가 올경우")]
        [Category("설정"), DisplayName("종단기호")]
        public eTerminal Terminal { get { return _term; } set { _term = value; } }

        [Category("설정")]
        public System.IO.Ports.Parity Parity
        {
            get
            {

                return _device.Parity;
            }
            set
            {
                _device.Parity = value;
            }
        }

        [Category("설정")]
        public int DataBits
        {
            get
            {
                return _device.DataBits;
            }
            set
            {
                _device.DataBits = value;
            }
        }

        [Category("설정")]
        public System.IO.Ports.StopBits StopBits
        {
            get
            {
                return _device.StopBits;
            }
            set
            {
                _device.StopBits = value;
            }

        }

        [Category("설정")]
        public System.IO.Ports.Handshake Handshake
        {
            get
            {
                return _device.Handshake;
            }
            set
            {
                _device.Handshake = value;
            }

        }

        #region "pin state & pin setting"

        /// <summary>
        /// Data Terminal Ready
        /// </summary>
        [Description("Data Terminal Ready 신호의 사용여부")]
        [Category("PIN")]
        public Boolean DtrEnable
        {
            get
            {
                return _device.DtrEnable;

            }
            set
            {
                _device.DtrEnable = value;
            }
        }

        /// <summary>
        /// Request To Send
        /// </summary>
        [Description("Request to Send 신호의 사용여부")]
        [Category("PIN")]
        public Boolean RtsEnable
        {
            get
            {
                return _device.RtsEnable;

            }
            set
            {
                _device.RtsEnable = value;
            }
        }

        /// <summary>
        /// Data set Ready 신호 상태
        /// </summary>
        [Description("Data Set Ready 신호 상태")]
        [Category("PIN")]
        public Boolean PIN_DSR
        {
            get
            {
                if (!IsOpen) return false;
                return _device.DsrHolding;
            }
        }

        /// <summary>
        /// Carrier Detect
        /// </summary>
        [Description("Carrier Detect 신호 상태")]
        [Category("PIN")]
        public Boolean PIN_CD
        {
            get
            {
                if (!IsOpen) return false;
                return _device.CDHolding;
            }
        }

        /// <summary>
        /// Clear to Send
        /// </summary>
        [Description("Clear to Send 신호 상태")]
        [Category("PIN")]
        public Boolean PIN_CTS
        {
            get
            {
                if (!IsOpen) return false;
                return _device.CtsHolding;
            }
        }

        /// <summary>
        /// Break State
        /// </summary>
        [Description("중단신호 상태")]
        [Category("PIN")]
        public Boolean PIN_BreakState
        {
            get
            {
                if (!IsOpen) return false;
                return _device.BreakState;
            }
        }


        #endregion




        /// <summary>
        /// 포트가 열려있는지 확인
        /// </summary>
        [Description("현재 시리얼포트가 열려있는지 확인합니다")]
        [Category("정보"), DisplayName("Port Open")]
        public virtual Boolean IsOpen
        {
            get
            {
                if (_device == null) return false;
                return _device.IsOpen;
            }
        }

        /// <summary>
        /// 초기화등이 성공했는지 확인합니다.close 되었다면 실패입니다. isinit 변수값을 적절히 수정하시기 바랍니다.
        /// </summary>
        [Description("초기화성공여부 별도의 초기화 코드가없다면 isOpen 과 동일합니다.")]
        [Category("정보"), DisplayName("Init OK?")]
        public virtual Boolean IsInit
        {
            get
            {
                if (!IsOpen || !_isinit) return false;
                return true;
            }
        }

        /// <summary>
        /// 쓰기타임아웃
        /// </summary>
        [Description("쓰기명령어의 최대대기시간(단위:ms)\r\n지정 시간을 초과할 경우 오류가 발생합니다.")]
        [Category("설정"), DisplayName("쓰기 제한시간")]
        public int WriteTimeout
        {
            get { return _device.WriteTimeout; }
            set { _device.WriteTimeout = value; }
        }

        /// <summary>
        /// 읽기타임아웃
        /// </summary>
        [Description("읽기명령어의 최대대기시간(단위:ms)\r\n지정 시간을 초과할 경우 오류가 발생합니다.")]
        [Category("설정"), DisplayName("읽기 제한시간")]
        public int ReadTimeout
        {
            get { return _device.ReadTimeout; }
            set { _device.ReadTimeout = value; }
        }

        /// <summary>
        /// 포트이름
        /// </summary>
        [Description("시리얼 포트 이름")]
        [Category("설정"), DisplayName("Port Name")]
        public string PortName { get { return _device.PortName; } set { _device.PortName = value; } }
        /// <summary>
        /// RS232 Baud Rate
        /// </summary>
        [Description("시리얼 포트 전송 속도")]
        [Category("설정"), DisplayName("Baud Rate")]
        public int BaudRate { get { return _device.BaudRate; } set { _device.BaudRate = value; } }

        #endregion

        #region "Method"

        /// <summary>
        /// 쓰기버퍼비우기
        /// </summary>
        public void ClearWriteBuffer()
        {
            if (_device.IsOpen) _device.DiscardOutBuffer();
        }

        /// <summary>
        /// 읽기버퍼비우기
        /// </summary>
        public void ClearReadBuffer()
        {
            if (_device.IsOpen) _device.DiscardInBuffer();
        }

        /// <summary>
        /// 장치의 초기화작업을 수행합니다.(이 값은 기본값으로 true가 무조건 설정됩니다) 오버라이드하여 각 상황에 맞게 처리하세요.
        /// </summary>
        protected virtual void Init()
        {
            if (!IsOpen) _isinit = false;
            else _isinit = true;
        }



        protected virtual Boolean CustomParser(byte[] buf, out byte[] remainBuffer)
        {
            remainBuffer = new byte[] { };
            return true;
        }

        #region "Raise Message Events (임의로 메세지를 발생시킵니다)"


        /// <summary>
        /// 보낸메세지 이벤트를 발생
        /// </summary>
        /// <param name="data">String Data</param>
        public void RaiseSendData(string data)
        {
            RaiseSendData(System.Text.Encoding.Default.GetBytes(data));
        }

        /// <summary>
        /// 보낸메세지 이벤트를 발생 합니다.
        /// </summary>
        /// <param name="data">Byte Array</param>
        public void RaiseSendData(byte[] data)
        {
            try
            {
                if (SendData != null) SendData(this, new ReceiveDataEventArgs(data));
            }
            catch (Exception ex)
            {
                RaiseMessage("RaiseSendData:" + ex.Message, true);
            }

        }

        /// <summary>
        /// 지정한 데이터로 바코드가 수신된것처럼 발생시킵니다.
        /// </summary>
        /// <param name="b"></param>
        public void RaiseRecvData(byte[] b)
        {
            byte[] Data;

            if (RemoveCRLFNULL) //제거해야하는경우에만 처리 170822
                Data = RemoveCRLF(b);
            else
                Data = b;

            try
            {
                if (ReceiveData != null) ReceiveData(this, new ReceiveDataEventArgs(Data));
            }
            catch (Exception ex)
            {
                RaiseMessage("RaiseDataMessage:" + ex.Message, true);
            }
        }

        /// <summary>
        /// 지정한 데이터로 바코드가 수신된것처럼 발생시킵니다.
        /// </summary>
        /// <param name="data"></param>
        public void RaiseRecvData(string data)
        {
            RaiseRecvData(System.Text.Encoding.Default.GetBytes(data));
        }

        /// <summary>
        /// 메세지이벤트를 발생합니다. 오류메세지일 경우 2번째 파라미터를 true 로 입력하세요.
        /// </summary>
        /// <param name="message">메세지</param>
        /// <param name="isError">오류라면 True로 설정하세요. 기본값=False</param>
        public void RaiseMessage(string message, Boolean isError = false)
        {
            if (isError) errorMessage = message; //170920
            if (Message != null) Message(this, new MessageEventArgs(isError, message));
        }


        #endregion

        /// <summary>
        /// 포트열기(실패시 False)를 반환
        /// </summary>
        public virtual Boolean Open(Boolean runInit = true)
        {
            try
            {
                _device.Open();
                if (_device.IsOpen)
                {
                    Init();
                }
                else
                {
                    _isinit = false;
                }

                return _isinit;
            }
            catch (Exception ex)
            {
                errorMessage = ex.Message;
                RaiseMessage(ex.Message, true);
                return false;
            }
        }

        /// <summary>
        /// 포트닫기
        /// </summary>
        public virtual void Close(Boolean PortClose = true)
        {
            if (_device != null && _device.IsOpen)
            {
                _isinit = false;
                _device.DiscardInBuffer();
                _device.DiscardOutBuffer();
                if (PortClose) _device.Close(); //dispose에서는 포트를 직접 클리어하지 않게 해뒀다.
            }
        }

        /// <summary>
        /// 메세지내용중 Cr,LF 를 제거합니다.
        /// </summary>
        protected byte[] RemoveCRLF(byte[] src)
        {
            List<byte> bcdbuf = new List<byte>();
            foreach (byte by in src)
            {
                if (by == 0x00 || by == 0x0d || by == 0x0a || by == 0x02 || by == 0x03) continue;
                bcdbuf.Add(by);
            }
            return bcdbuf.ToArray();
        }

        #endregion

        #region "Method Write Data"

        /// <summary>
        /// 포트에 쓰기(barcode_DataReceived 이벤트로 메세지수신)
        /// </summary>
        public virtual Boolean WriteData(string data)
        {
            byte[] buf = System.Text.Encoding.Default.GetBytes(data);
            return WriteData(buf);
        }

        /// <summary>
        /// 포트에 쓰기 반환될 때까지 기다림(SyncTimeOut 값까지 기다림)
        /// </summary>
        public virtual byte[] WriteDataSync(string data)
        {
            byte[] buf = System.Text.Encoding.Default.GetBytes(data);
            return WriteDataSync(buf);
        }

        /// <summary>
        /// _buffer를 클리어하고 입력된 데이터를 버퍼에 추가합니다.
        /// </summary>
        /// <param name="buf"></param>
        public void setRecvBuffer(byte[] buf)
        {
            this._buffer.Clear();
            this._buffer.AddRange(buf);
        }

        public int WriteError = 0;
        public string WriteErrorMessage = string.Empty;

        /// <summary>
        /// 포트에 쓰기 반환될 때까지 기다림(SyncTimeOut 값까지 기다림)
        /// </summary>
        public virtual byte[] WriteDataSync(byte[] data, Boolean useReset = true)
        {
            errorMessage = string.Empty;
            _isSync = true;
            byte[] recvbuf = null;
         //   Boolean bRet = false;

            //171214
            if (!IsOpen)
            {
                errorMessage = "Port Closed";
                return null;
            }

            //171205 : 타임아웃시간추가
            if (useReset)
            {
                if (!_mre.WaitOne(3000))
                {
                    errorMessage = string.Format("WriteDataSync:MRE:WaitOne:TimeOut 3000ms");
                    RaiseMessage(errorMessage, true);
                    return null;
                }

                _mre.Reset();
            }


            //save last command
            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);
            Boolean sendOK = false;

            try
            {
                _device.DiscardInBuffer();
                _device.DiscardOutBuffer();
                _buffer.Clear();    //171205
                _device.Write(data, 0, data.Length);
                WriteError = 0;
                WriteErrorMessage = string.Empty;
                sendOK = true;
            }
            catch (Exception ex)
            {
                WriteError += 1;
                WriteErrorMessage = ex.Message;
            }

            if (sendOK)
            {
                try
                {
                    //171113
                    if (EnableTxMessage && SendData != null) SendData(this, new ReceiveDataEventArgs(data));

                    _wat.Restart();
                    Boolean bTimeOut = false;
                    _buffer.Clear();
                    Boolean bDone = false;
                    while (!bDone)
                    {
                        if (_wat.ElapsedMilliseconds > WriteTimeout)
                        {
                            errorMessage = "(Sync)WriteTimeOut";
                            bTimeOut = true;
                            break;
                        }
                        else
                        {
                            int RecvCnt = _device.BytesToRead;
                            if (RecvCnt > 0)
                            {
                                byte[] rbuf = new byte[RecvCnt];
                                _device.Read(rbuf, 0, rbuf.Length);
                                if (_term == eTerminal.CustomParser)
                                {
                                    byte[] remainBuffer;
                                Repeat:
                                    if (CustomParser(rbuf, out remainBuffer))
                                    {
                                        RaiseRecvData(_buffer.ToArray());
                                        bDone = true;
                                        if (remainBuffer != null && remainBuffer.Length > 0)
                                        {
                                            rbuf = new byte[remainBuffer.Length];
                                            Array.Copy(remainBuffer, rbuf, rbuf.Length);
                                            goto Repeat;
                                        }
                                    }
                                }
                                else
                                {
                                    foreach (byte b in rbuf)
                                    {
                                        switch (_term)
                                        {
                                            case eTerminal.CR:
                                                if (b == 0x0D)
                                                {
                                                    bDone = true;
                                                    break;
                                                }
                                                else _buffer.Add(b);
                                                break;
                                            case eTerminal.LF:
                                                if (b == 0x0A)
                                                {
                                                    bDone = true;
                                                    break;
                                                }
                                                else _buffer.Add(b);
                                                break;
                                            case eTerminal.CrLf:
                                                if (b == 0x0A)
                                                {
                                                    bDone = true;
                                                    break;
                                                }
                                                else if (b == 0x0d)
                                                {
                                                    //pass
                                                }
                                                else
                                                {
                                                    _buffer.Add(b);
                                                }
                                                break;
                                            case eTerminal.Length:
                                                _buffer.Add(b);
                                                if (_buffer.Count == MaxDataLength)
                                                {
                                                    bDone = true;
                                                    break;
                                                }
                                                else if (_buffer.Count > MaxDataLength)
                                                {
                                                    RaiseMessage("Buffer Length Error " + _buffer.Count.ToString() + "/" + MaxDataLength.ToString(), true);
                                                    _buffer.Clear();
                                                }

                                                break;
                                            case eTerminal.ETX:

                                                if (b == STX)
                                                {
                                                    _buffer.Clear();
                                                }
                                                else if (b == ETX)
                                                {
                                                    bDone = true;
                                                    break;
                                                }
                                                else _buffer.Add(b);
                                                break;
                                        }
                                    }
                                }
                            }

                        }
                    }
                    _wat.Stop();
                    if (!bTimeOut)
                    {
                        recvbuf = _buffer.ToArray();
                        //bRet = true;
                    }
                }
                catch (Exception ex)
                {
                    errorMessage = ex.Message;
                    //bRet = false;
                }
            }

            if (useReset)
                _mre.Set();//release

            //syncmode off
            _isSync = false;
            return recvbuf;
        }

        /// <summary>
        /// 포트에 쓰기(barcode_DataReceived 이벤트로 메세지수신)
        /// </summary>
        public virtual Boolean WriteData(byte[] data)
        {
            Boolean bRet = false;

            //171205 : 타임아웃시간추가
            if (!_mre.WaitOne(3000))
            {
                errorMessage = string.Format("WriteData:MRE:WaitOne:TimeOut 3000ms");
                RaiseMessage(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);
                _device.Write(data, 0, data.Length);

                //171113
                if (EnableTxMessage && SendData != null) SendData(this, new ReceiveDataEventArgs(data));

                bRet = true;
                WriteError = 0;
                WriteErrorMessage = string.Empty;
            }
            catch (Exception ex)
            {
                // this.isinit = false;
                RaiseMessage(ex.Message, true);// if (ReceivceData != null) ReceivceData(this, true, ex.Message);
                bRet = false;
                WriteError += 1; //연속쓰기오류횟수
                WriteErrorMessage = ex.Message;
            }
            finally
            {
                _mre.Set();
            }
            return bRet;
        }

        #endregion

        /// <summary>
        /// 장치를 생성합니다.
        /// </summary>
        public Barcode() : this(0) { }
        /// <summary>
        /// 지정한ID를 가진 장치를 생성합니다.
        /// </summary>
        /// <param name="id"></param>
        public Barcode(int id)
        {
            _mre = new ManualResetEvent(true);
            this.ID = id;
            this._device = new System.IO.Ports.SerialPort();
            _device.DataReceived += barcode_DataReceived;
            _device.ErrorReceived += barcode_ErrorReceived;
            _device.ReadTimeout = 2000;
            _device.WriteTimeout = 2000;
            _term = eTerminal.CrLf;
            STX = 0x02;
            ETX = 0x03;
            RemoveCRLFNULL = false;
            EnableTxMessage = false;
        }

        ~Barcode()
        {
            Dispose();
        }

        /// <summary>
        /// close를 호출합니다.
        /// </summary>
        public virtual void Dispose()
        {
            if (!isDisposed) //180219
            {
                _isinit = false;
                _device.DataReceived -= barcode_DataReceived;
                _device.ErrorReceived -= barcode_ErrorReceived;
                //Close(false);
                isDisposed = true;
            }
        }
    }
}
