上海网站建设找摩彼,姑娘视频在线观看免费完整版高清,哈尔滨seo优化,手表网站排名前十编写水文专业串口通讯软件的开发经历 一、关于开发 YAC9900 水位雨量 RTU 通讯软件二、软件开发遇到的问题和困难1、开发架构的适应2、开发语言的学习3、.net core 8 架构中串口构建的难点4、YAC9900 水位雨量 RTU 通讯软件开发中的 UI 冻结 三、发现问题解决问题的具体办法1、… 编写水文专业串口通讯软件的开发经历 一、关于开发 YAC9900 水位雨量 RTU 通讯软件二、软件开发遇到的问题和困难1、开发架构的适应2、开发语言的学习3、.net core 8 架构中串口构建的难点4、YAC9900 水位雨量 RTU 通讯软件开发中的 UI 冻结 三、发现问题解决问题的具体办法1、预置是否没有执行完 invoke 的 bool 开关是否关闭串口的 bool 开关是否连续发送命令的 bool 开关2、在串口打开或关闭中处理上面第一项的 bool 关系3、在串口SerialDataReceivedEventHandler(GetMessageFromEquipment)事件中处理中断 四、程序界面 一、关于开发 YAC9900 水位雨量 RTU 通讯软件
YAC9900 水位雨量 RTU 是长江一方公司开发的一款用于水文测量的水位雨量记录 RTU能接入多种水位传感器。新版 YAC9900 主板重新设计后功能强大但用于 YAC9900 通讯和设置参数的软件很旧尽管用起来不错。于是开发一款新的软件采用 Microsoft Visual Studio C# 开发分别采用 .net core 8 架构和 .net Framework 4.8 进行编译。
二、软件开发遇到的问题和困难
1、开发架构的适应
以前学习 .net 开发都是在 .net Framework 4.8 进行这次迁移到 .net core 8 架构学习了不少知识。.net core 8 语言更加简练和方便提示更加全面。唯一的遗憾是编译后产生的库文件太多尽管程序用到库不多但 Visual Studio 还没有智能到只生成程序依赖的库所以一股脑的把很多库都给塞进了编译输出目录其中很多都是不需要的库。.net core 8 架构可以编译发布产生单文件执行软件这个单文件也是一股脑的把很多库都给包进了编译的单文件导致单文件有 145M其实不包含库程序不到 1M。
2、开发语言的学习
.net core 8 架构中学习了很多如 ? 和 ?? 运算符、三元条件运算符索引和范围范围运算符 […index]替代了很多 if else 、 Substring 、IndexOf、LastIndexOf 语句包括检索字符串 Contains 语句等等。学习了与 .net Framework 4.8 很多的不同点。
3、.net core 8 架构中串口构建的难点
.net core 8 架构中System.IO.Ports 组件不再像 .net Framework 那样内置需要通过管理 NuGet 程序包下载。高版本的.net core 8 架构更多涉及软件的安全性能所以在串口访问中对于数据处理的结果运用到 UI 界面不能再像 Visual Studio 2017 以前那样处理处理不好就导致程序界面冻结、卡死、死锁因为 UI 界面的刷新必须使用和考虑线程和委托的开始和终结。如何让 UI 主线程与串口通讯线程和委托互不影响和干扰就很重要了。
4、YAC9900 水位雨量 RTU 通讯软件开发中的 UI 冻结
由于软件开发中采用各种通讯指令连续多发在关闭串口或窗口时极易发生 UI 冻结软件死锁卡死只能在任务管理器中终结进程。原因在于窗口通讯线程任务在进行中没有处理好中断任务导致线程打架引起程序 UI 冻结。
三、发现问题解决问题的具体办法
出现最大的问题就是 UI 界面刷新和界面冻结关键在于串口的事件 SerialDataReceivedEventHandler 和 UI 界面刷新的委托 Invoke 处理完善。问题就得到完美的解决。
1、预置是否没有执行完 invoke 的 bool 开关是否关闭串口的 bool 开关是否连续发送命令的 bool 开关 public partial class Form1 : Form{string[] StationType new string[] { 雨量站, 并行水位站, 并行水文站, 串行水位站, 串行水文站, 水温站 };string[] Channel new string[] { 无效, PSTN, 北斗卫星, GSM, GPRS };string[] DebugMsg new string[] { 打开, 关闭 };string[] SensorType new string[] {SDI-12 WL3100 (HS40),SDI-12 WFX-40 (伟思浮子式),RS485 WFX-40 (伟思浮子式),RS485 OTT (德国HACH),RS485 ISO,Sens,RS485 MPM (麦克压阻式),RS485 Tem,Sens8 (武汉环宇压阻式),VEGA,Sens10 (XYJ固件VEGAM),Sens11 (XYJ固件HXDLS),VEGAM (XYJ固件HXRad),Sens13 (XYJ固件WFX40G),Sens14,Sens15,Sens16,Sens17,Sens18};private static SerialPort serialPort new SerialPort();private bool WhenInvokeg false;//是否没有执行完invoke相关操作 private bool closing false;//是否正在关闭串口执行Application.DoEvents并阻止再次invokeprivate bool SendCommand false;//是否连续发送命令public Form1(){InitializeComponent();InitializeCustom();}} 2、在串口打开或关闭中处理上面第一项的 bool 关系
关键在于及时取消连续发送命令 SendCommand false并中断串口 GetMessageFromEquipment 事件继续 closing true 。 /// summary打开串口过程/summaryprivate void OpenPort(){try{if (serialPort ! null serialPort.IsOpen){SendCommand false;//取消连续发送命令closing true;//是要关闭串口中断串口 GetMessageFromEquipment 事件继续serialPort.DataReceived - GetMessageFromEquipment;while (WhenInvokeg) Application.DoEvents();//执行完 invoke 才能关闭串口serialPort.Close();closing false;//StatusMessage2.Text ;StatusMessage4.Text 已经关闭端口 serialPort.PortName;StatusMessage6.Text 可以将消息框内容转换为16进制了;OpenPortToolButton.Image Resources.Close;}else{if (serialPort null) serialPort new SerialPort();//如果不存在则新建端口serialPort.ReadBufferSize 4096;//缓冲大小serialPort.WriteBufferSize 4096;//缓冲大小serialPort.PortName _PortName; // 设置串口名称serialPort.BaudRate _BaudRate; // 设置波特率serialPort.Parity _Parity; // 设置奇偶校验serialPort.DataBits _DataBit; // 设置数据位数serialPort.StopBits _StopBits; // 设置停止位serialPort.Handshake _Handshake; // 设置握手协议//serialPort.ReadIntervalTimeout 100;// serialPort.NewLine \r\n;//解释 ReadLine( )和WriteLine( )方法调用结束的值 默认值“\n”//serialPort.RtsEnable true;// serialPort.Encoding Encoding.GetEncoding(iso-8859-1); //支持汉字显示//GB2312//iso-8859-1serialPort.DataReceived new SerialDataReceivedEventHandler(GetMessageFromEquipment);serialPort.Open(); // 打开串口StatusMessage2.Text ;StatusMessage4.Text 已经打开端口 serialPort.PortName;StatusMessage6.Text ;OpenPortToolButton.Image Resources.Open;}}catch (Exception ex){if (serialPort null) serialPort new SerialPort();//如果不存在则新建端口serialPort.PortName _PortName; // 设置串口名称serialPort.BaudRate _BaudRate; // 设置波特率StatusMessage2.Text ex.Message;//StatusMessage4.Text Messaging(ex.Message);// 处理异常消息VS2022使用Trace进行调试显示//Trace.WriteLine(ex.Source); Trace.WriteLine(ex.StackTrace); Trace.WriteLine(ex.Message);// Trace.WriteLine(ex.GetType().Name);Trace.WriteLine(ex.ToString());//MessageBox.Show(ex.Message \r ex.Source \r ex.StackTrace, 错误消息);}}3、在串口SerialDataReceivedEventHandler(GetMessageFromEquipment)事件中处理中断
关键在于 closing true 中断串口 GetMessageFromEquipment 事件继续委托线程。使 WhenInvokeg false 不再发生委托。 private void GetMessageFromEquipment(object sender, SerialDataReceivedEventArgs e)//从设备中获取信息(串口){if (closing) return;//如果正在关闭忽略操作直接返回string PortMessage ;//串口消息WhenInvokeg true;//设置在委托调用标记已经开始接收数据if (InvokeRequired){//更新UI的同步委托//BeginInvoke(new Action(() Invoke(new Action(() // Invoke((EventHandler)(delegate{try{ // 更新UI的代码StatusMessage4.Text 串口正在通讯线程委托正在更新UI界面;Application.DoEvents();Delayed(PortBufferInterval);//等待缓冲数据PortMessage serialPort.ReadLine().Trim();//去前后空格和回车后的端口消息 读行// int nums serialPort.BytesToRead;// byte[] receiveBytes new byte[nums];// serialPort.Read(receiveBytes, 0, nums);//读字节// PortMessage Encoding.ASCII.GetString(receiveBytes);if (PortMessage.Length 0){switch (TabWorkbenches.SelectedIndex){case 0:case 1:Yac9900PortMessaging(PortMessage);//消息处理和界面更新break;case 2:break;case 3:break;}}}catch (Exception ex){ // 处理异常StatusMessage2.Text 程序线程上执行的委托异常 ex.Message;//Trace.WriteLine(ex.Message);//VS2022使用 Trace 显示调试//Console.WriteLine(ex.Message);MessageBox.Show(ex.Message \r ex.Source \r ex.StackTrace, 错误消息);}finally{StatusMessage4.Text 通讯串口消息已经完成! 等待新的指令或消息!;WhenInvokeg false;//没有调用了UI可以关闭串口了。 }}));}else{try{ // 更新UI的代码StatusMessage4.Text 串口正在通讯并更新UI界面;Application.DoEvents();Delayed(PortBufferInterval);//等待缓冲数据PortMessage serialPort.ReadLine().Trim();if (PortMessage.Length 0){switch (TabWorkbenches.SelectedIndex){case 0:case 1:Yac9900PortMessaging(PortMessage);break;case 2:break;case 3:break;}}}catch (Exception ex){// 处理异常StatusMessage2.Text ex.Message;// StatusMessage4.Text Messaging(ex.Message);//Trace.WriteLine(ex.Message);//Console.WriteLine(ex.Message);MessageBox.Show(ex.Message \r ex.Source \r ex.StackTrace, 错误消息);}finally{StatusMessage4.Text 通讯串口消息已经完成! 等待新的指令或消息!;WhenInvokeg false;//没有调用了UI可以关闭串口了。 }}}4、在窗体关闭前处理消息 关键在于及时取消连续发送命令 SendCommand false 。避免继续产生串口事件产生委托线程打架。 private void Form1_FormClosing(object sender, FormClosingEventArgs e){if (serialPort.IsOpen){try{if (WhenInvokeg)//如果没有委托可以关闭程序{SendCommand false;//取消连续发送命令StatusMessage6.Text 已取消剩下的发送命令再次点击就退出; e.Cancel true; // 暂时不能退出窗体串口指令和消息完成后再关闭窗口}else{if (serialPort ! null serialPort.IsOpen) OpenPort(); // 关闭串口e.Cancel false;}}catch (Exception ex){MessageBox.Show(无法关闭串口 ex.Message);}}}5、串口连续指令发送的中断处理 关键在于 SendCommand false 取消连续发生指令不再发生新的串口事件。 /// summary执行可以读写的YAC9900命令/summaryprivate void CanRwCommands()//可读写命令区{try{string setStr ;if (checkDT.Checked SendCommand)//是否选中时间读写和允许发送指令{if (CheckSet.Checked)//是否设置{if (CheckUseSystemTime.Checked){setStr DateTime.Now.ToString(yyyyMMddHHmmss).Trim();}else{setStr DT_Picker.Value.ToString(yyyyMMddHHmmss).Trim();}}serialPort.Write(DT setStr);Delayed(SendCommandInterval);}if (checkStationCode.Checked SendCommand){//发送指令与上雷同}if (checkStorageWater.Checked SendCommand){//发送指令与上雷同}if (checkWaterBase.Checked SendCommand){//以下省略很多指令}}catch (Exception ex){// 处理异常StatusMessage2.Text ex.Message;StatusMessage4.Text Messaging(ex.Message);Trace.WriteLine(ex.Source);//Console.WriteLine(ex.Message);//MessageBox.Show(ex.Message \r ex.Source \r ex.StackTrace, 错误消息);}}四、程序界面
程序主界面 串口设置界面自动搜索串口集合自动捕获 USB 串口插入