| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- using System;
- using System.Collections.Concurrent;
- using System.ComponentModel;
- using System.IO.Ports;
- using System.Text;
- using System.Threading.Tasks;
- using Newtonsoft.Json;
- using TeamAAS.Communication.Attributes;
- using TeamAAS.Communication.Base;
- using TeamAAS.Communication.Interfaces;
- using TeamAAS.Communication.Enums;
- namespace TeamAAS.Communication.Devices
- {
- [Communication("串口", "基础通讯", "RS232/RS485 串口通讯")]
- public class SerialCommunication : BindableCommunicationBase
- {
- [JsonIgnore]
- private SerialPort _serial;
- private readonly ConcurrentQueue<string> _received = new ConcurrentQueue<string>();
- [JsonIgnore]
- private readonly StringBuilder _receiveBuffer = new StringBuilder();
- [JsonIgnore]
- private System.Threading.Timer _reconnectTimer;
- [JsonIgnore]
- private volatile bool _hasConnectedOnce;
- [JsonIgnore]
- private volatile bool _manualDisconnect;
- [JsonIgnore]
- private volatile bool _isReconnecting;
- private string _portName = "COM1";
- [Category("II.客户端配置"), DisplayName("1.端口名称"), Description("串口名称,如 COM1")]
- [TypeConverter(typeof(ComPortListConverter))]
- public string PortName
- {
- get { return _portName; }
- set { if (SetProperty(ref _portName, value)) Notify(nameof(EndpointUrl)); }
- }
- private BaudRates _baudRate = BaudRates.BR_9600;
- [Category("II.客户端配置"), DisplayName("2.波特率"), Description("串口波特率")]
- public BaudRates BaudRate
- {
- get { return _baudRate; }
- set { if (SetProperty(ref _baudRate, value)) Notify(nameof(EndpointUrl)); }
- }
- private Parity _parity = Parity.None;
- [Category("II.客户端配置"), DisplayName("3.校验位"), Description("串口校验位")]
- public Parity Parity
- {
- get { return _parity; }
- set { SetProperty(ref _parity, value); }
- }
- private StopBits _stopBits = StopBits.One;
- [Category("II.客户端配置"), DisplayName("4.停止位"), Description("串口停止位")]
- public StopBits StopBits
- {
- get { return _stopBits; }
- set { SetProperty(ref _stopBits, value); }
- }
- private int _dataBits = 8;
- [Category("II.客户端配置"), DisplayName("5.数据位"), Description("串口数据位")]
- public int DataBits
- {
- get { return _dataBits; }
- set { SetProperty(ref _dataBits, value); }
- }
- private Terminator _terminator = Terminator.None;
- [Category("III.数据格式"), DisplayName("1.结束符"), Description("发送时自动附加、接收时自动去除的结束符")]
- public Terminator Terminator
- {
- get { return _terminator; }
- set { SetProperty(ref _terminator, value); }
- }
- private DataEncoding _dataEncoding = DataEncoding.Default;
- [Category("III.数据格式"), DisplayName("2.编码格式"), Description("收发数据的编码格式")]
- public DataEncoding DataEncoding
- {
- get { return _dataEncoding; }
- set { SetProperty(ref _dataEncoding, value); }
- }
- [Browsable(false)]
- public override string EndpointUrl
- {
- get { return $"serial://{PortName}?baud={BaudRate}"; }
- set
- {
- if (!string.IsNullOrWhiteSpace(value) && value.StartsWith("serial://"))
- {
- var uri = new Uri(value);
- PortName = uri.Host;
- }
- }
- }
- [JsonIgnore, Browsable(false)]
- public override bool IsConnected => (_serial != null && _serial.IsOpen);
- public override event Action<object, bool> ConnectChangedEvent;
- public override event Action<object, string> DataReceivedEvent;
- public override void Connect()
- {
- Disconnect();
- _serial = new SerialPort(PortName, (int)BaudRate, Parity, DataBits, StopBits);
- _serial.Encoding = GetEncoding();
- _serial.DataReceived += Serial_DataReceived;
- _serial.Open();
- _manualDisconnect = false;
- _hasConnectedOnce = true;
- StopReconnectTimer();
- ConnectChangedEvent?.Invoke(this, true);
- Notify(nameof(IsConnected));
- }
- public override Task ConnectAsync()
- {
- return Task.Run(() => Connect());
- }
- public override void Disconnect()
- {
- StopReconnectTimer();
- _manualDisconnect = true;
- if (_serial != null)
- {
- try { _serial.Close(); } catch { }
- _serial.Dispose();
- _serial = null;
- }
- ConnectChangedEvent?.Invoke(this, false);
- Notify(nameof(IsConnected));
- }
- public override void Dispose()
- {
- Disconnect();
- }
- private void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
- {
- try
- {
- var sp = sender as SerialPort;
- if (sp == null) return;
- var text = sp.ReadExisting();
- if (!string.IsNullOrEmpty(text))
- {
- _receiveBuffer.Append(text);
- FlushReceivedMessages(_receiveBuffer);
- }
- }
- catch
- {
- if (_hasConnectedOnce && !_manualDisconnect)
- StartAutoReconnect();
- }
- }
- /// <summary>
- /// 获取编码
- /// </summary>
- public Encoding GetEncoding()
- {
- switch (DataEncoding)
- {
- case DataEncoding.ASCII: return Encoding.ASCII;
- case DataEncoding.UTF7: return Encoding.UTF7;
- case DataEncoding.UTF8: return Encoding.UTF8;
- case DataEncoding.UTF32: return Encoding.UTF32;
- case DataEncoding.Unicode: return Encoding.Unicode;
- case DataEncoding.BigEndianUnicode: return Encoding.BigEndianUnicode;
- case DataEncoding.GB2312: return Encoding.GetEncoding("gb2312");
- default: return Encoding.Default;
- }
- }
- /// <summary>
- /// 获取结束符字符串
- /// </summary>
- public string GetTerminatorString()
- {
- switch (Terminator)
- {
- case Terminator.CR: return "\r";
- case Terminator.LF: return "\n";
- case Terminator.CRLF: return "\r\n";
- case Terminator.None:
- default: return string.Empty;
- }
- }
- /// <summary>
- /// 去除数据尾部结束符
- /// </summary>
- public string TrimTerminator(string text)
- {
- if (string.IsNullOrEmpty(text)) return text;
- var term = GetTerminatorString();
- if (string.IsNullOrEmpty(term)) return text;
- return text.EndsWith(term) ? text.Substring(0, text.Length - term.Length) : text;
- }
- /// <summary>
- /// 按结束符分帧:收到完整结束符才触发接收事件;无结束符时直接触发
- /// </summary>
- private void FlushReceivedMessages(StringBuilder receiveBuffer)
- {
- var term = GetTerminatorString();
- if (string.IsNullOrEmpty(term))
- {
- if (receiveBuffer.Length > 0)
- {
- var text = receiveBuffer.ToString();
- receiveBuffer.Clear();
- _received.Enqueue(text);
- DataReceivedEvent?.Invoke(this, text);
- }
- return;
- }
- string content = receiveBuffer.ToString();
- int idx;
- while ((idx = content.IndexOf(term, StringComparison.Ordinal)) >= 0)
- {
- var msg = content.Substring(0, idx);
- content = content.Substring(idx + term.Length);
- _received.Enqueue(msg);
- DataReceivedEvent?.Invoke(this, msg);
- }
- receiveBuffer.Clear();
- receiveBuffer.Append(content);
- }
- /// <summary>
- /// 自动重连:初始化连接成功过,掉线后每5秒尝试连接一次
- /// </summary>
- private void StartAutoReconnect()
- {
- if (_reconnectTimer != null) return;
- if (_serial != null)
- {
- try { _serial.Close(); } catch { }
- _serial.Dispose();
- _serial = null;
- }
- ConnectChangedEvent?.Invoke(this, false);
- Notify(nameof(IsConnected));
- _reconnectTimer = new System.Threading.Timer(ReconnectTimer_Elapsed, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
- }
- private void ReconnectTimer_Elapsed(object state)
- {
- if (_manualDisconnect) { StopReconnectTimer(); return; }
- if (_isReconnecting) return;
- _isReconnecting = true;
- try
- {
- if (_serial != null)
- {
- try { _serial.Close(); } catch { }
- _serial.Dispose();
- _serial = null;
- }
- var serial = new SerialPort(PortName, (int)BaudRate, Parity, DataBits, StopBits);
- serial.Encoding = GetEncoding();
- serial.DataReceived += Serial_DataReceived;
- try
- {
- serial.Open();
- _serial = serial;
- StopReconnectTimer();
- ConnectChangedEvent?.Invoke(this, true);
- Notify(nameof(IsConnected));
- }
- catch
- {
- try { serial.Dispose(); } catch { }
- }
- }
- finally
- {
- _isReconnecting = false;
- }
- }
- private void StopReconnectTimer()
- {
- var t = _reconnectTimer;
- _reconnectTimer = null;
- if (t != null) { try { t.Dispose(); } catch { } }
- }
- public void Send(string text)
- {
- if (_serial == null || !_serial.IsOpen)
- throw new InvalidOperationException("串口未连接。");
- _serial.Write((text ?? string.Empty) + GetTerminatorString());
- }
- public override object ReadValue(string address)
- {
- _received.TryDequeue(out var text);
- return text;
- }
- public override Task<object> ReadValueAsync(string address)
- {
- return Task.FromResult(ReadValue(address));
- }
- public override void WriteValue(string address, object value)
- {
- Send(value?.ToString() ?? string.Empty);
- }
- public override Task WriteValueAsync(string address, object value)
- {
- Send(value?.ToString() ?? string.Empty);
- return Task.CompletedTask;
- }
- }
- }
|