SerialBase.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.IO.Ports;
  6. using System.Threading;
  7. using MECF.Framework.RT.Core.ThreadLock;
  8. using log4net.Core;
  9. namespace MECF.Framework.RT.Core.IoProviders.Common.Serial
  10. {
  11. /// <summary>
  12. /// 所有串行通信类的基类,提供了一些基础的服务
  13. /// </summary>
  14. public class SerialBase
  15. {
  16. #region Constructor
  17. /// <summary>
  18. /// 实例化一个无参的构造方法
  19. /// </summary>
  20. public SerialBase( )
  21. {
  22. SP_ReadData = new SerialPort( );
  23. hybirdLock = new SimpleHybirdLock( );
  24. }
  25. #endregion
  26. #region Public Method
  27. /// <summary>
  28. /// 初始化串口信息,9600波特率,8位数据位,1位停止位,无奇偶校验
  29. /// </summary>
  30. /// <param name="portName">端口号信息,例如"COM3"</param>
  31. public void SerialPortInni( string portName )
  32. {
  33. SerialPortInni( portName, 9600 );
  34. }
  35. /// <summary>
  36. /// 初始化串口信息,波特率,8位数据位,1位停止位,无奇偶校验
  37. /// </summary>
  38. /// <param name="portName">端口号信息,例如"COM3"</param>
  39. /// <param name="baudRate">波特率</param>
  40. public void SerialPortInni( string portName, int baudRate )
  41. {
  42. SerialPortInni( portName, baudRate, 8, StopBits.One, Parity.None );
  43. }
  44. /// <summary>
  45. /// 初始化串口信息,波特率,数据位,停止位,奇偶校验需要全部自己来指定
  46. /// </summary>
  47. /// <param name="portName">端口号信息,例如"COM3"</param>
  48. /// <param name="baudRate">波特率</param>
  49. /// <param name="dataBits">数据位</param>
  50. /// <param name="stopBits">停止位</param>
  51. /// <param name="parity">奇偶校验</param>
  52. public void SerialPortInni( string portName, int baudRate, int dataBits, StopBits stopBits, Parity parity )
  53. {
  54. if (SP_ReadData.IsOpen)
  55. {
  56. return;
  57. }
  58. SP_ReadData.PortName = portName; // 串口
  59. SP_ReadData.BaudRate = baudRate; // 波特率
  60. SP_ReadData.DataBits = dataBits; // 数据位
  61. SP_ReadData.StopBits = stopBits; // 停止位
  62. SP_ReadData.Parity = parity; // 奇偶校验
  63. }
  64. /// <summary>
  65. /// 根据自定义初始化方法进行初始化串口信息
  66. /// </summary>
  67. /// <param name="initi">初始化的委托方法</param>
  68. public void SerialPortInni( Action<SerialPort> initi )
  69. {
  70. if (SP_ReadData.IsOpen)
  71. {
  72. return;
  73. }
  74. SP_ReadData.PortName = "COM5";
  75. SP_ReadData.BaudRate = 9600;
  76. SP_ReadData.DataBits = 8;
  77. SP_ReadData.StopBits = StopBits.One;
  78. SP_ReadData.Parity = Parity.None;
  79. initi.Invoke( SP_ReadData );
  80. }
  81. /// <summary>
  82. /// 打开一个新的串行端口连接
  83. /// </summary>
  84. public void Open( )
  85. {
  86. if (!SP_ReadData.IsOpen)
  87. {
  88. SP_ReadData.Open( );
  89. InitializationOnOpen( );
  90. }
  91. }
  92. /// <summary>
  93. /// 获取一个值,指示串口是否处于打开状态
  94. /// </summary>
  95. /// <returns>是或否</returns>
  96. public bool IsOpen( )
  97. {
  98. return SP_ReadData.IsOpen;
  99. }
  100. /// <summary>
  101. /// 关闭端口连接
  102. /// </summary>
  103. public void Close( )
  104. {
  105. if(SP_ReadData.IsOpen)
  106. {
  107. ExtraOnClose( );
  108. SP_ReadData.Close( );
  109. }
  110. }
  111. /// <summary>
  112. /// 读取串口的数据
  113. /// </summary>
  114. /// <param name="send">发送的原始字节数据</param>
  115. /// <returns>带接收字节的结果对象</returns>
  116. public OperateResult<byte[]> ReadBase(byte[] send)
  117. {
  118. hybirdLock.Enter( );
  119. if (IsClearCacheBeforeRead) ClearSerialCache( );
  120. OperateResult sendResult = SPSend( SP_ReadData, send );
  121. if (!sendResult.IsSuccess)
  122. {
  123. hybirdLock.Leave( );
  124. return OperateResult.CreateFailedResult<byte[]>( sendResult );
  125. }
  126. OperateResult<byte[]> receiveResult = SPReceived( SP_ReadData, true );
  127. hybirdLock.Leave( );
  128. return receiveResult;
  129. }
  130. /// <summary>
  131. /// 清除串口缓冲区的数据,并返回该数据,如果缓冲区没有数据,返回的字节数组长度为0
  132. /// </summary>
  133. /// <returns>是否操作成功的方法</returns>
  134. public OperateResult<byte[]> ClearSerialCache( )
  135. {
  136. return SPReceived( SP_ReadData, false );
  137. }
  138. #endregion
  139. #region virtual Method
  140. /// <summary>
  141. /// 检查当前接收的字节数据是否正确的
  142. /// </summary>
  143. /// <param name="rBytes">输入字节</param>
  144. /// <returns>检查是否正确</returns>
  145. protected virtual bool CheckReceiveBytes(byte[] rBytes )
  146. {
  147. return true;
  148. }
  149. #endregion
  150. #region Initialization And Extra
  151. /// <summary>
  152. /// 在打开端口时的初始化方法,按照协议的需求进行必要的重写
  153. /// </summary>
  154. /// <returns>是否初始化成功</returns>
  155. protected virtual OperateResult InitializationOnOpen( )
  156. {
  157. return OperateResult.CreateSuccessResult( );
  158. }
  159. /// <summary>
  160. /// 在将要和服务器进行断开的情况下额外的操作,需要根据对应协议进行重写
  161. /// </summary>
  162. /// <returns>当断开连接时额外的操作结果</returns>
  163. protected virtual OperateResult ExtraOnClose( )
  164. {
  165. return OperateResult.CreateSuccessResult( );
  166. }
  167. #endregion
  168. #region Private Method
  169. /// <summary>
  170. /// 发送数据到串口里去
  171. /// </summary>
  172. /// <param name="serialPort">串口对象</param>
  173. /// <param name="data">字节数据</param>
  174. /// <returns>是否发送成功</returns>
  175. protected virtual OperateResult SPSend( SerialPort serialPort, byte[] data )
  176. {
  177. if (data != null && data.Length > 0)
  178. {
  179. try
  180. {
  181. serialPort.Write( data, 0, data.Length );
  182. return OperateResult.CreateSuccessResult( );
  183. }
  184. catch(Exception ex)
  185. {
  186. return new OperateResult( ex.Message );
  187. }
  188. }
  189. else
  190. {
  191. return OperateResult.CreateSuccessResult( );
  192. }
  193. }
  194. /// <summary>
  195. /// 从串口接收一串数据信息,可以指定是否一定要接收到数据
  196. /// </summary>
  197. /// <param name="serialPort">串口对象</param>
  198. /// <param name="awaitData">是否必须要等待数据返回</param>
  199. /// <returns>结果数据对象</returns>
  200. protected virtual OperateResult<byte[]> SPReceived( SerialPort serialPort, bool awaitData )
  201. {
  202. byte[] buffer = new byte[1024];
  203. System.IO.MemoryStream ms = new System.IO.MemoryStream( );
  204. DateTime start = DateTime.Now; // 开始时间,用于确认是否超时的信息
  205. while (true)
  206. {
  207. System.Threading.Thread.Sleep( sleepTime );
  208. try
  209. {
  210. if (serialPort.BytesToRead < 1)
  211. {
  212. if ((DateTime.Now - start).TotalMilliseconds > ReceiveTimeout)
  213. {
  214. ms.Dispose( );
  215. return new OperateResult<byte[]>( $"Time out: {ReceiveTimeout}" );
  216. }
  217. else if (ms.Length > 0)
  218. {
  219. break;
  220. }
  221. else if (awaitData)
  222. {
  223. continue;
  224. }
  225. else
  226. {
  227. break;
  228. }
  229. }
  230. // 继续接收数据
  231. int sp_receive = serialPort.Read( buffer, 0, buffer.Length );
  232. ms.Write( buffer, 0, sp_receive );
  233. }
  234. catch (Exception ex)
  235. {
  236. ms.Dispose( );
  237. return new OperateResult<byte[]>( ex.Message );
  238. }
  239. }
  240. // resetEvent.Set( );
  241. byte[] result = ms.ToArray( );
  242. ms.Dispose( );
  243. return OperateResult.CreateSuccessResult( result );
  244. }
  245. #endregion
  246. #region Object Override
  247. /// <summary>
  248. /// 返回表示当前对象的字符串
  249. /// </summary>
  250. /// <returns>字符串</returns>
  251. public override string ToString()
  252. {
  253. return "SerialBase";
  254. }
  255. #endregion
  256. #region Public Properties
  257. /// <summary>
  258. /// 接收数据的超时时间,默认5000ms
  259. /// </summary>
  260. public int ReceiveTimeout
  261. {
  262. get { return receiveTimeout; }
  263. set { receiveTimeout = value; }
  264. }
  265. /// <summary>
  266. /// 连续串口缓冲数据检测的间隔时间,默认20ms
  267. /// </summary>
  268. public int SleepTime
  269. {
  270. get { return sleepTime; }
  271. set { if (value > 0) sleepTime = value; }
  272. }
  273. /// <summary>
  274. /// 是否在发送数据前清空缓冲数据,默认是false
  275. /// </summary>
  276. public bool IsClearCacheBeforeRead
  277. {
  278. get { return isClearCacheBeforeRead; }
  279. set { isClearCacheBeforeRead = value; }
  280. }
  281. #endregion
  282. #region Private Member
  283. private SerialPort SP_ReadData = null; // 串口交互的核心
  284. private SimpleHybirdLock hybirdLock; // 数据交互的锁
  285. private int receiveTimeout = 5000; // 接收数据的超时时间
  286. private int sleepTime = 20; // 睡眠的时间
  287. private bool isClearCacheBeforeRead = false; // 是否在发送前清除缓冲
  288. #endregion
  289. }
  290. }