using System; using System.Collections.Generic; using System.IO.Ports; using System.Linq; using System.Text; using System.Threading; namespace POSV.Utils { public class SerialPortExtension { static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private SerialPort sp = new SerialPort(); public event SerialPortEventHandler ReceiveDataEvent = null; public event SerialPortEventHandler OpenPortEvent = null; public event SerialPortEventHandler ClosePortEvent = null; private Object thisLock = new Object(); /// /// When serial received data, will call this method /// /// /// private void DataReceived(object sender, SerialDataReceivedEventArgs e) { if (sp.BytesToRead <= 0) { return; } //Thread Safety explain in MSDN: // Any public static (Shared in Visual Basic) members of this type are thread safe. // Any instance members are not guaranteed to be thread safe. // So, we need to synchronize I/O lock (thisLock) { int len = sp.BytesToRead; Byte[] data = new Byte[len]; try { sp.Read(data, 0, len); } catch (System.Exception) { //catch read exception } SerialPortEventArgs args = new SerialPortEventArgs(); args.ReceivedBytes = data; if (ReceiveDataEvent != null) { ReceiveDataEvent.Invoke(this, args); } } } /// /// Send bytes to device /// /// /// public bool Send(Byte[] bytes) { if (!sp.IsOpen) { return false; } try { sp.Write(bytes, 0, bytes.Length); } catch (System.Exception) { return false; //write failed } return true; //write successfully } /// /// Open Serial port /// /// /// /// /// /// /// public bool Open(string portName, String baudRate, string dataBits, string stopBits, string parity, string handshake) { if (sp.IsOpen) { Close(); } sp.PortName = portName; sp.BaudRate = Convert.ToInt32(baudRate); sp.DataBits = Convert.ToInt16(dataBits); /** * If the Handshake property is set to None the DTR and RTS pins * are then freed up for the common use of Power, the PC on which * this is being typed gives +10.99 volts on the DTR pin & +10.99 * volts again on the RTS pin if set to true. If set to false * it gives -9.95 volts on the DTR, -9.94 volts on the RTS. * These values are between +3 to +25 and -3 to -25 volts this * give a dead zone to allow for noise immunity. * http://www.codeproject.com/Articles/678025/Serial-Comms-in-Csharp-for-Beginners */ if (handshake == "None") { //Never delete this property sp.RtsEnable = true; sp.DtrEnable = true; } SerialPortEventArgs args = new SerialPortEventArgs(); try { sp.StopBits = (StopBits)Enum.Parse(typeof(StopBits), stopBits); sp.Parity = (Parity)Enum.Parse(typeof(Parity), parity); sp.Handshake = (Handshake)Enum.Parse(typeof(Handshake), handshake); sp.WriteTimeout = 1000; /*Write time out*/ sp.Open(); sp.DataReceived += new SerialDataReceivedEventHandler(DataReceived); args.IsOpend = true; } catch (System.Exception ex) { args.IsOpend = false; } if (OpenPortEvent != null) { OpenPortEvent.Invoke(this, args); } return args.IsOpend; } /// /// 丢弃接收到的未处理的缓冲 /// public void DiscardOutBuffer() { if (sp.IsOpen) { sp.DiscardOutBuffer(); } } /// /// 判断串口是否打开 /// /// public bool IsOpen { get { return sp.IsOpen; } } /** * Take care to avoid deadlock when calling Close on the SerialPort * in response to a GUI event. * An app involving the UI and the SerialPort freezes up when closing the SerialPort * Deadlock can occur if Control.Invoke() is used in serial port event handlers * * The typical scenario we encounter is occasional deadlock in an app * that has a data received handler trying to update the GUI at the * same time the GUI thread is trying to close the SerialPort (for * example, in response to the user clicking a Close button). * * The reason deadlock happens is that Close() waits for events to * finish executing before it closes the port. You can address this * problem in your apps in two ways: * * (1)In your event handlers, replace every Control.Invoke call with * Control.BeginInvoke, which executes asynchronously and avoids * the deadlock condition. This is commonly used for deadlock avoidance * when working with GUIs. * * (2)Call serialPort.Close() on a separate thread. You may prefer this * because this is less invasive than updating your Invoke calls. */ /// /// Close serial port /// public void Close() { Thread closeThread = new Thread(new ThreadStart(CloseSpThread)); closeThread.Start(); } /// /// Close serial port thread /// private void CloseSpThread() { SerialPortEventArgs args = new SerialPortEventArgs(); args.IsOpend = false; try { sp.Close(); //close the serial port sp.DataReceived -= new SerialDataReceivedEventHandler(DataReceived); } catch (Exception) { args.IsOpend = true; } if (ClosePortEvent != null) { ClosePortEvent.Invoke(this, args); } } } public delegate void SerialPortEventHandler(Object sender , SerialPortEventArgs e); public class SerialPortEventArgs : EventArgs { public bool IsOpend = false; public Byte[] ReceivedBytes = null; } }