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;
}
}
}
}