namespace Common.Hardware.Ports { using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO.Ports; using System.Threading; using SysSerialPort = System.IO.Ports.SerialPort; public sealed class SerialPort { private static readonly ConcurrentDictionary serialPorts = new ConcurrentDictionary(); private readonly ManualResetEventSlim readAwaiter; private readonly ManualResetEventSlim closeAwaiter; public SerialPort(string name, int baudRate, Parity parity, int dataBits, StopBits stopBits) { this.COMPort = name; this.readAwaiter = new ManualResetEventSlim(); this.closeAwaiter = new ManualResetEventSlim(); var sp = new SysSerialPort(this.COMPort, baudRate, parity, dataBits, stopBits); serialPorts.AddOrUpdate(this.COMPort, sp, (key, value) => { value?.Close(); return sp; }); } public event Action Connected; public event Action Disconnected; public event Action Logging; public string COMPort { get; } public bool IsOpen => serialPorts.TryGetValue(this.COMPort, out var sp) && sp.IsOpen; public void Close() { if (serialPorts.TryGetValue(this.COMPort, out var sp)) { try { sp.Close(); this.closeAwaiter.Wait(5000); this.closeAwaiter.Reset(); sp.DataReceived -= this.SP_DataReceived; sp.Disposed -= this.SP_Disposed; } catch (Exception e) { this.Logging?.Invoke(e.ToString()); } } } public void Open() { if (serialPorts.TryGetValue(this.COMPort, out var sp) && !sp.IsOpen) { try { sp.DataReceived += this.SP_DataReceived; sp.Disposed += this.SP_Disposed; sp.Open(); this.Connected?.Invoke(); } catch (Exception e) { this.Logging?.Invoke(e.ToString()); } } } public IEnumerable ReadBytes() { var sp = this.GetOpenedSP(); var size = sp.ReadBufferSize; var buffer = new byte[size]; var length = 0; using (sp) { while (sp.IsOpen) { this.readAwaiter.Wait(); try { buffer = new byte[size]; length = sp.Read(buffer, 0, size); Array.Resize(ref buffer, length); } catch (Exception e) { this.Logging?.Invoke(e.ToString()); } this.readAwaiter.Reset(); yield return buffer; } sp.Close(); } } public IEnumerable ReadLines() { var sp = this.GetOpenedSP(); var text = default(string); using (sp) { while (sp.IsOpen) { this.readAwaiter.Wait(); try { text = sp.ReadLine(); } catch (Exception e) { this.Logging?.Invoke(e.ToString()); } this.readAwaiter.Reset(); yield return text; } } } public void Remove() { if (serialPorts.TryRemove(this.COMPort, out var sp)) { sp.Close(); } } public void Write(byte[] bytes) { var sp = this.GetOpenedSP(); try { sp.Write(bytes, 0, bytes.Length); } catch (Exception e) { this.Logging?.Invoke(e.ToString()); } } public void Write(string text) { var sp = this.GetOpenedSP(); try { sp.Write(text); } catch (Exception e) { this.Logging?.Invoke(e.ToString()); } } private SysSerialPort GetOpenedSP() { if (!serialPorts.TryGetValue(this.COMPort, out var sp)) { throw new InvalidOperationException($"{nameof(SerialPort)} on {this.COMPort} dose not exists!"); } if (!sp.IsOpen) { this.Close(); this.Open(); } return sp; } private void SP_DataReceived(object _, SerialDataReceivedEventArgs args) => this.readAwaiter.Set(); private void SP_Disposed(object _, EventArgs _1) { this.closeAwaiter.Set(); this.Disconnected?.Invoke(); } public static string[] GetPortNames() => SysSerialPort.GetPortNames(); } }