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
|
{
|
/// <summary>
|
/// Serial Module을 제어하기 위한 클래스
|
/// </summary>
|
public class SerialModule
|
{
|
/// <summary>
|
/// Exception Log
|
/// </summary>
|
private ILog ExceptionLog = LogManager.GetLogger("SerialException");
|
|
/// <summary>
|
/// Exception Log 사용유무 확인
|
/// </summary>
|
public bool UseExceptionLog { get; set; }
|
|
/// <summary>
|
/// Exception Log를 기록하기 위한 메서드
|
/// </summary>
|
/// <param name="msg">Error Message</param>
|
private void WriteExceptionLog(string msg)
|
{
|
if (UseExceptionLog)
|
ExceptionLog.Debug(msg);
|
}
|
|
/// <summary>
|
/// lock 제어를 하기 위함.
|
/// </summary>
|
private object thislock;
|
|
/// <summary>
|
/// lock 제어를 하기 위함.
|
/// </summary>
|
private bool lockcheck;
|
|
/// <summary>
|
/// SerialPort와 직접 통신.
|
/// </summary>
|
private SerialPort _serialPort;
|
|
/// <summary>
|
/// SerialPort에서 받은 Data를 Encoding형식에 맞게 Parsing하기 위함.
|
/// </summary>
|
public Encoding EncodeType { get { return encodeType; } private set { encodeType = value; } }
|
private Encoding encodeType;
|
|
/// <summary>
|
/// Carriage Return
|
/// </summary>
|
public char CR { get { return '\r'; }}
|
|
/// <summary>
|
/// Line Feed
|
/// </summary>
|
public char LF { get { return '\n'; }}
|
|
public char Spliiter { get { return splitter; } private set { splitter = value; } }
|
private char splitter;
|
|
/// <summary>
|
/// SerialPort의 Open 여부
|
/// </summary>
|
public bool isOpen { get { return _serialPort.IsOpen; } }
|
|
/// <summary>
|
/// Splitter로 Split하기 전 Data를 모아두는 저장소.
|
/// </summary>
|
private List<byte> RecvData;
|
|
/// <summary>
|
/// encodeType을 설정해주는 메서드
|
/// </summary>
|
/// <param name="encodeType"></param>
|
public void SetEncodeType(Encoding encodeType)
|
{
|
this.EncodeType = encodeType;
|
}
|
|
/// <summary>
|
/// SerialPort를 생성하고 내부 Data를 설정하는 생성자.(Base Encoding은 ASCII)
|
/// </summary>
|
/// <param name="PortName"> Port이름 ex)COM6, COM13 </param>
|
/// <param name="BaudRate"> 전송속도 ex)4800, 9600, 19200 </param>
|
/// <param name="Splitter"> Data의 마지막을 확인할 문자 기본 '\r' </param>
|
/// <param name="DataBits"> 바이트 당 데이터 비트의 표준길이 ex)5, 6, 7, 8 </param>
|
/// <param name="Parity">패리티 검사 프로토콜 ex)Ports.Parity.None </param>
|
/// <param name="StopBits">비트당 정지비트의 표준 개수 ex)Ports.StopBits.One </param>
|
/// <param name="Handshake">직렬 전송을 위한 핸드셰이킹 ex)Ports.Handshake.None</param>
|
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<byte>();
|
_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;
|
}
|
|
/// <summary>
|
/// SerialPort를 생성하고 내부 Data를 설정하는 생성자.(Base Encoding은 ASCII)
|
/// </summary>
|
/// <param name="PortName"> Port이름 ex)COM6, COM13 </param>
|
/// <param name="BaudRate"> 전송속도 ex)4800, 9600, 19200 </param>
|
/// <param name="DataBits"> 바이트 당 데이터 비트의 표준길이 ex)5, 6, 7, 8 </param>
|
/// <param name="Parity">패리티 검사 프로토콜 ex)Ports.Parity.None </param>
|
/// <param name="StopBits">비트당 정지비트의 표준 개수 ex)Ports.StopBits.One </param>
|
/// <param name="Handshake">직렬 전송을 위한 핸드셰이킹 ex)Ports.Handshake.None</param>
|
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<byte>();
|
_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;
|
}
|
|
/// <summary>
|
/// 사용가능한 Portname을 반환.
|
/// </summary>
|
/// <returns>Portname 값</returns>
|
public static string[] GetPorts()
|
{
|
return SerialPort.GetPortNames();
|
}
|
|
/// <summary>
|
/// 설정 Port Open
|
/// </summary>
|
/// <returns>성공 실패 여부</returns>
|
public bool Open()
|
{
|
try
|
{
|
_serialPort.Open();
|
return true;
|
}
|
catch(Exception e)
|
{
|
WriteExceptionLog(e.Message);
|
return false;
|
}
|
}
|
|
/// <summary>
|
/// 설정 Port Close
|
/// </summary>
|
/// <returns>성공 실패 여부</returns>
|
public bool Close()
|
{
|
try
|
{
|
_serialPort.Close();
|
return true;
|
}
|
catch(Exception e)
|
{
|
WriteExceptionLog(e.Message);
|
return false;
|
}
|
}
|
|
/// <summary>
|
/// SerialPort에 Data를 보내기 위한 메서드
|
/// </summary>
|
/// <param name="sCmd">string형 Data</param>
|
/// <returns></returns>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// SerialPort에 Data를 보내기 위한 메서드
|
/// </summary>
|
/// <param name="bCmd">byte형 배열 Data</param>
|
/// <returns></returns>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// SerialPort에 Data를 보내기 위한 메서드
|
/// </summary>
|
/// <param name="cCmd">char형 배열 Data</param>
|
/// <returns></returns>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// SerialPort에 Data를 보낸후 Read 대기
|
/// </summary>
|
/// <param name="sCmd">string Data</param>
|
/// <returns></returns>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// SerialPort에 Data를 보낸후 Read 대기
|
/// </summary>
|
/// <param name="bCmd">byte형 배열 Data</param>
|
/// <returns></returns>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// SerialPort에 Data를 보낸후 Read 대기
|
/// </summary>
|
/// <param name="cCmd">char형 배열 Data</param>
|
/// <returns></returns>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// Splitter Data의 유무를 판별하기 위한 메서드
|
/// </summary>
|
/// <param name="bytes">SerialPort에서 받은 Data</param>
|
/// <returns></returns>
|
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;
|
}
|
}
|
}
|
}
|