SerialCommunication.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.ComponentModel;
  4. using System.IO.Ports;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using Newtonsoft.Json;
  8. using TeamAAS.Communication.Attributes;
  9. using TeamAAS.Communication.Base;
  10. using TeamAAS.Communication.Interfaces;
  11. using TeamAAS.Communication.Enums;
  12. namespace TeamAAS.Communication.Devices
  13. {
  14. [Communication("串口", "基础通讯", "RS232/RS485 串口通讯")]
  15. public class SerialCommunication : BindableCommunicationBase
  16. {
  17. [JsonIgnore]
  18. private SerialPort _serial;
  19. private readonly ConcurrentQueue<string> _received = new ConcurrentQueue<string>();
  20. [JsonIgnore]
  21. private readonly StringBuilder _receiveBuffer = new StringBuilder();
  22. [JsonIgnore]
  23. private System.Threading.Timer _reconnectTimer;
  24. [JsonIgnore]
  25. private volatile bool _hasConnectedOnce;
  26. [JsonIgnore]
  27. private volatile bool _manualDisconnect;
  28. [JsonIgnore]
  29. private volatile bool _isReconnecting;
  30. private string _portName = "COM1";
  31. [Category("II.客户端配置"), DisplayName("1.端口名称"), Description("串口名称,如 COM1")]
  32. [TypeConverter(typeof(ComPortListConverter))]
  33. public string PortName
  34. {
  35. get { return _portName; }
  36. set { if (SetProperty(ref _portName, value)) Notify(nameof(EndpointUrl)); }
  37. }
  38. private BaudRates _baudRate = BaudRates.BR_9600;
  39. [Category("II.客户端配置"), DisplayName("2.波特率"), Description("串口波特率")]
  40. public BaudRates BaudRate
  41. {
  42. get { return _baudRate; }
  43. set { if (SetProperty(ref _baudRate, value)) Notify(nameof(EndpointUrl)); }
  44. }
  45. private Parity _parity = Parity.None;
  46. [Category("II.客户端配置"), DisplayName("3.校验位"), Description("串口校验位")]
  47. public Parity Parity
  48. {
  49. get { return _parity; }
  50. set { SetProperty(ref _parity, value); }
  51. }
  52. private StopBits _stopBits = StopBits.One;
  53. [Category("II.客户端配置"), DisplayName("4.停止位"), Description("串口停止位")]
  54. public StopBits StopBits
  55. {
  56. get { return _stopBits; }
  57. set { SetProperty(ref _stopBits, value); }
  58. }
  59. private int _dataBits = 8;
  60. [Category("II.客户端配置"), DisplayName("5.数据位"), Description("串口数据位")]
  61. public int DataBits
  62. {
  63. get { return _dataBits; }
  64. set { SetProperty(ref _dataBits, value); }
  65. }
  66. private Terminator _terminator = Terminator.None;
  67. [Category("III.数据格式"), DisplayName("1.结束符"), Description("发送时自动附加、接收时自动去除的结束符")]
  68. public Terminator Terminator
  69. {
  70. get { return _terminator; }
  71. set { SetProperty(ref _terminator, value); }
  72. }
  73. private DataEncoding _dataEncoding = DataEncoding.Default;
  74. [Category("III.数据格式"), DisplayName("2.编码格式"), Description("收发数据的编码格式")]
  75. public DataEncoding DataEncoding
  76. {
  77. get { return _dataEncoding; }
  78. set { SetProperty(ref _dataEncoding, value); }
  79. }
  80. [Browsable(false)]
  81. public override string EndpointUrl
  82. {
  83. get { return $"serial://{PortName}?baud={BaudRate}"; }
  84. set
  85. {
  86. if (!string.IsNullOrWhiteSpace(value) && value.StartsWith("serial://"))
  87. {
  88. var uri = new Uri(value);
  89. PortName = uri.Host;
  90. }
  91. }
  92. }
  93. [JsonIgnore, Browsable(false)]
  94. public override bool IsConnected => (_serial != null && _serial.IsOpen);
  95. public override event Action<object, bool> ConnectChangedEvent;
  96. public override event Action<object, string> DataReceivedEvent;
  97. public override void Connect()
  98. {
  99. Disconnect();
  100. _serial = new SerialPort(PortName, (int)BaudRate, Parity, DataBits, StopBits);
  101. _serial.Encoding = GetEncoding();
  102. _serial.DataReceived += Serial_DataReceived;
  103. _serial.Open();
  104. _manualDisconnect = false;
  105. _hasConnectedOnce = true;
  106. StopReconnectTimer();
  107. ConnectChangedEvent?.Invoke(this, true);
  108. Notify(nameof(IsConnected));
  109. }
  110. public override Task ConnectAsync()
  111. {
  112. return Task.Run(() => Connect());
  113. }
  114. public override void Disconnect()
  115. {
  116. StopReconnectTimer();
  117. _manualDisconnect = true;
  118. if (_serial != null)
  119. {
  120. try { _serial.Close(); } catch { }
  121. _serial.Dispose();
  122. _serial = null;
  123. }
  124. ConnectChangedEvent?.Invoke(this, false);
  125. Notify(nameof(IsConnected));
  126. }
  127. public override void Dispose()
  128. {
  129. Disconnect();
  130. }
  131. private void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
  132. {
  133. try
  134. {
  135. var sp = sender as SerialPort;
  136. if (sp == null) return;
  137. var text = sp.ReadExisting();
  138. if (!string.IsNullOrEmpty(text))
  139. {
  140. _receiveBuffer.Append(text);
  141. FlushReceivedMessages(_receiveBuffer);
  142. }
  143. }
  144. catch
  145. {
  146. if (_hasConnectedOnce && !_manualDisconnect)
  147. StartAutoReconnect();
  148. }
  149. }
  150. /// <summary>
  151. /// 获取编码
  152. /// </summary>
  153. public Encoding GetEncoding()
  154. {
  155. switch (DataEncoding)
  156. {
  157. case DataEncoding.ASCII: return Encoding.ASCII;
  158. case DataEncoding.UTF7: return Encoding.UTF7;
  159. case DataEncoding.UTF8: return Encoding.UTF8;
  160. case DataEncoding.UTF32: return Encoding.UTF32;
  161. case DataEncoding.Unicode: return Encoding.Unicode;
  162. case DataEncoding.BigEndianUnicode: return Encoding.BigEndianUnicode;
  163. case DataEncoding.GB2312: return Encoding.GetEncoding("gb2312");
  164. default: return Encoding.Default;
  165. }
  166. }
  167. /// <summary>
  168. /// 获取结束符字符串
  169. /// </summary>
  170. public string GetTerminatorString()
  171. {
  172. switch (Terminator)
  173. {
  174. case Terminator.CR: return "\r";
  175. case Terminator.LF: return "\n";
  176. case Terminator.CRLF: return "\r\n";
  177. case Terminator.None:
  178. default: return string.Empty;
  179. }
  180. }
  181. /// <summary>
  182. /// 去除数据尾部结束符
  183. /// </summary>
  184. public string TrimTerminator(string text)
  185. {
  186. if (string.IsNullOrEmpty(text)) return text;
  187. var term = GetTerminatorString();
  188. if (string.IsNullOrEmpty(term)) return text;
  189. return text.EndsWith(term) ? text.Substring(0, text.Length - term.Length) : text;
  190. }
  191. /// <summary>
  192. /// 按结束符分帧:收到完整结束符才触发接收事件;无结束符时直接触发
  193. /// </summary>
  194. private void FlushReceivedMessages(StringBuilder receiveBuffer)
  195. {
  196. var term = GetTerminatorString();
  197. if (string.IsNullOrEmpty(term))
  198. {
  199. if (receiveBuffer.Length > 0)
  200. {
  201. var text = receiveBuffer.ToString();
  202. receiveBuffer.Clear();
  203. _received.Enqueue(text);
  204. DataReceivedEvent?.Invoke(this, text);
  205. }
  206. return;
  207. }
  208. string content = receiveBuffer.ToString();
  209. int idx;
  210. while ((idx = content.IndexOf(term, StringComparison.Ordinal)) >= 0)
  211. {
  212. var msg = content.Substring(0, idx);
  213. content = content.Substring(idx + term.Length);
  214. _received.Enqueue(msg);
  215. DataReceivedEvent?.Invoke(this, msg);
  216. }
  217. receiveBuffer.Clear();
  218. receiveBuffer.Append(content);
  219. }
  220. /// <summary>
  221. /// 自动重连:初始化连接成功过,掉线后每5秒尝试连接一次
  222. /// </summary>
  223. private void StartAutoReconnect()
  224. {
  225. if (_reconnectTimer != null) return;
  226. if (_serial != null)
  227. {
  228. try { _serial.Close(); } catch { }
  229. _serial.Dispose();
  230. _serial = null;
  231. }
  232. ConnectChangedEvent?.Invoke(this, false);
  233. Notify(nameof(IsConnected));
  234. _reconnectTimer = new System.Threading.Timer(ReconnectTimer_Elapsed, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
  235. }
  236. private void ReconnectTimer_Elapsed(object state)
  237. {
  238. if (_manualDisconnect) { StopReconnectTimer(); return; }
  239. if (_isReconnecting) return;
  240. _isReconnecting = true;
  241. try
  242. {
  243. if (_serial != null)
  244. {
  245. try { _serial.Close(); } catch { }
  246. _serial.Dispose();
  247. _serial = null;
  248. }
  249. var serial = new SerialPort(PortName, (int)BaudRate, Parity, DataBits, StopBits);
  250. serial.Encoding = GetEncoding();
  251. serial.DataReceived += Serial_DataReceived;
  252. try
  253. {
  254. serial.Open();
  255. _serial = serial;
  256. StopReconnectTimer();
  257. ConnectChangedEvent?.Invoke(this, true);
  258. Notify(nameof(IsConnected));
  259. }
  260. catch
  261. {
  262. try { serial.Dispose(); } catch { }
  263. }
  264. }
  265. finally
  266. {
  267. _isReconnecting = false;
  268. }
  269. }
  270. private void StopReconnectTimer()
  271. {
  272. var t = _reconnectTimer;
  273. _reconnectTimer = null;
  274. if (t != null) { try { t.Dispose(); } catch { } }
  275. }
  276. public void Send(string text)
  277. {
  278. if (_serial == null || !_serial.IsOpen)
  279. throw new InvalidOperationException("串口未连接。");
  280. _serial.Write((text ?? string.Empty) + GetTerminatorString());
  281. }
  282. public override object ReadValue(string address)
  283. {
  284. _received.TryDequeue(out var text);
  285. return text;
  286. }
  287. public override Task<object> ReadValueAsync(string address)
  288. {
  289. return Task.FromResult(ReadValue(address));
  290. }
  291. public override void WriteValue(string address, object value)
  292. {
  293. Send(value?.ToString() ?? string.Empty);
  294. }
  295. public override Task WriteValueAsync(string address, object value)
  296. {
  297. Send(value?.ToString() ?? string.Empty);
  298. return Task.CompletedTask;
  299. }
  300. }
  301. }