496 lines
15 KiB
C#
496 lines
15 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading;
|
|
|
|
|
|
public abstract partial class arRS232 : IDisposable
|
|
{
|
|
protected System.IO.Ports.SerialPort _device;
|
|
protected ManualResetEvent _mre;
|
|
protected byte[] LastReceiveBuffer = new byte[] { };
|
|
/// <summary>
|
|
/// 최종 전송 메세지
|
|
/// </summary>
|
|
public byte[] lastSendBuffer = new byte[] { };
|
|
//public int ValidCheckTimeMSec { get; set; } = 5000;
|
|
protected List<byte> tempBuffer = new List<byte>();
|
|
protected Boolean findSTX = false;
|
|
public string errorMessage { get; set; }
|
|
public DateTime LastConnTime { get; set; }
|
|
public DateTime LastConnTryTime { get; set; }
|
|
public DateTime lastSendTime;
|
|
/// <summary>
|
|
/// 메세지 수신시 사용하는 내부버퍼
|
|
/// </summary>
|
|
protected List<byte> _buffer = new List<byte>();
|
|
/// <summary>
|
|
/// 데이터조회간격(초)
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 마지막으로 데이터를 받은 시간
|
|
/// </summary>
|
|
public DateTime lastRecvTime;
|
|
|
|
|
|
public int WriteError = 0;
|
|
public string WriteErrorMessage = string.Empty;
|
|
public int WaitTimeout { get; set; } = 1000;
|
|
public int MinRecvLength { get; set; } = 1;
|
|
/// <summary>
|
|
/// 포트이름
|
|
/// </summary>
|
|
[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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 포트가 열려있는지 확인
|
|
/// </summary>
|
|
[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);
|
|
}
|
|
/// <summary>
|
|
/// 수신받은 메세지를 발생 시킵니다
|
|
/// </summary>
|
|
/// <param name="data"></param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 수신받은 자료를 처리한다
|
|
/// </summary>
|
|
/// <param name="data"></param>
|
|
/// <returns></returns>
|
|
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"
|
|
|
|
|
|
/// <summary>
|
|
/// 오류 및 기타 일반 메세지
|
|
/// </summary>
|
|
public event EventHandler<MessageEventArgs> Message;
|
|
|
|
#endregion
|
|
|
|
#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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 메세지를 강제 발생
|
|
/// </summary>
|
|
/// <param name="mt"></param>
|
|
/// <param name="message"></param>
|
|
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;
|
|
|
|
/// <summary>
|
|
/// Recv,Send,Normal,Error 모두 지원
|
|
/// </summary>
|
|
public string Message { get { return _message; } }
|
|
|
|
private byte[] _data = null;
|
|
|
|
/// <summary>
|
|
/// Recv,Send에서만 값이 존재 합니다
|
|
/// </summary>
|
|
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);
|
|
|
|
/// <summary>
|
|
/// 포트가 열려있거나 데이터 수신시간이 없는경우 false를 반환합니다
|
|
/// </summary>
|
|
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 string ByteListToHexString(List<byte> src)
|
|
{
|
|
return string.Join(" ", src.Select(t => t.ToString("X2")));
|
|
}
|
|
|
|
protected bool WriteData(string cmd)
|
|
{
|
|
return WriteData(System.Text.Encoding.Default.GetBytes(cmd));
|
|
}
|
|
/// <summary>
|
|
/// 포트에 쓰기(barcode_DataReceived 이벤트로 메세지수신)
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
|