编写水文专业串口通讯软件的开发经历

编写水文专业串口通讯软件的开发经历

  • 一、关于开发 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>打开串口过程</summary>private 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-1"serialPort.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命令</summary>private 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 串口插入:
在这里插入图片描述

五、软件下载

百度云盘:
https://pan.baidu.com/s/1ExIa8IE6q98mZ3_A_ROQQw?pwd=ZYYU

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/356539.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android Studio无法连接夜神模拟器的解决方案

一、AS检测不到夜神模拟器 1、问题描述 在按照教程【如何安装和使用Android夜神模拟器】进入夜神的bin目录&#xff0c;输入连接命令回车后&#xff0c;终端显示的already connected to 127.0.0.1:62001&#xff0c;但是AS的Running Devices并没有显示夜神模拟器。 2、解决方…

鸿蒙HarmonyOS NEXT角落里的知识:ArkTS高性能编程实践

概述 本文主要提供应用性能敏感场景下的高性能编程的相关建议&#xff0c;助力开发者开发出高性能的应用。高性能编程实践&#xff0c;是在开发过程中逐步总结出来的一些高性能的写法和建议&#xff0c;在业务功能实现过程中&#xff0c;我们要同步思考并理解高性能写法的原理…

【Java】已解决java.nio.channels.ClosedChannelException异常

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决java.nio.channels.ClosedChannelException异常 在Java的NIO&#xff08;New I/O&#xff09;编程中&#xff0c;java.nio.channels.ClosedChannelException是一个常见的异常…

韩国裸机云站群服务器托管租用方案

随着网络技术的飞速发展&#xff0c;站群服务器在网站运营中扮演着越来越重要的角色。韩国裸机云站群服务器&#xff0c;以其独特的优势&#xff0c;如地理位置优越、价格相对较低、技术实力雄厚等&#xff0c;吸引了众多企业的关注。本文将为您详细介绍韩国裸机云站群服务器的…

PostgreSQL配置SSL连接

1、系统需要有openssl、openssl-devel包 yum -y install openssl openssl-devel 2、查看当前数据库是否使用openssl编译 pg_config|grep CONFIGURE 如果没有重新编译 make clean make && make install 3、服务器端证书配置 服务器端需生成三个文件: root.crt(根证…

[RK-Linux] RK3562 I2C驱动TP芯片GSL3680

TP芯片GSL3680 1、简介 GSL3680是一款电容屏多点触摸控制单芯片,其芯片内部框架图如下所示: GSL3680采用了独特的互电容感应技术,可以在1ms内测量多达192个节点,支持广泛的传感器选择,包括单层或双层ITO,玻璃或薄膜,采用了先进的抗电磁干扰信号处理技术,使其对各种干…

新火种AI|Claude 3.5一夜封王超越GPT-4o!留给OpenAI的时间真的不多了...

AI大模型更新换代的速度&#xff0c;的确快到令人难以想象。 相信很多人现在对“最先进AI大模型”的印象还停留在GPT-4&#xff0c;但事实上&#xff0c;大模型领域的头把交椅早已悄然易主了好几回。就在GPT-4惊艳全球不久之后&#xff0c;其“死对头” Anthropic发布了Claude…

Tomcat基础详解

第一篇&#xff1a;Tomcat基础篇 lecture&#xff1a;邓澎波 一、构建Tomcat源码环境 工欲善其事必先利其器&#xff0c;为了学好Tomcat源码&#xff0c;我们需要先在本地构建一个Tomcat的运行环境。 1.源码环境下载 源码有两种下载方式&#xff1a; 1.1 官网下载 https://…

IDEA、PyCharm等基于IntelliJ平台的IDE汉化方式

PyCharm 或者 IDEA 等编辑器是比较常用的&#xff0c;默认是英文界面&#xff0c;有些同学用着不方便&#xff0c;想要汉化版本的&#xff0c;但官方没有这个设置项&#xff0c;不过可以通过插件的方式进行设置。 方式1&#xff1a;插件安装 1、打开设置 File->Settings&a…

ArcGIS实现不同地块分类与面积汇总

​ 点击下方全系列课程学习 点击学习—>ArcGIS全系列实战视频教程——9个单一课程组合系列直播回放 点击学习——>遥感影像综合处理4大遥感软件ArcGISENVIErdaseCognition 我们要做一个不同地块面积汇总&#xff01; 你有一批地块&#xff0c;不同面积&#xff0c;我们需…

Android中的Audio系统框架分析(一)

概述 Audio系统是Android 平台重要的组成部分&#xff0c;我们将从以下几个方面来讲解&#xff1a; 一Audio基础知识讲解 二、Android系统中Audio框架 Audio基础知识讲解 我们大家知道声音是由物体振动产生的声波。是通过介质&#xff08;空气或固体、液体&#xff09;传播并…

python18 正则表达式

python18 正则表达式 正则表达式 re.match(),re.search(),re.findall(),re.sub(),re.split() 元字符 具有特殊意义的专用字符 导入模块 improt re代码 正则表达式 re.match(),re.search(),re.findall(),re.sub(),re.split() 元字符 具有特殊意义的专用字符 导入模块 improt rei…

内容安全复习 6 - 白帽子安全漏洞挖掘披露的法律风险

文章目录 安全漏洞的法律概念界定安全漏洞特征白帽子安全漏洞挖掘面临的法律风险“白帽子”安全漏洞挖掘的风险根源“白帽子”的主体边界授权行为边界关键结论 安全漏洞的法律概念界定 可以被利用来破坏所在系统的网络或信息安全的缺陷或错误&#xff1b;被利用的网络缺陷、错…

Linux系统查看程序内存及CPU占用

文章目录 1.free命令2.top命令3.PS命令3.1 查看内存占用前10位&#xff1a;3.2 查看CPU占用前10位 参考文档 1.free命令 可以通过free命令查看物理内存占用情况 #单位KB free #单位MB free -m #单位GB free -h 2.top命令 输入top命令&#xff0c;会输出定时刷新的程序PID、内…

【TB作品】MSP430G2553单片机,转速测量系统,转速测量仪,霍尔传感器

文章目录 题目器件原理霍尔传感器测速原理&#xff1a;电机如何调节速度程序设计 实验报告实验题目实验目的实验器材实验原理霍尔传感器测速原理电机调速原理定时器中断原理 硬件连接软件设计定时器配置主程序中断处理程序 实验结果实验总结 题目 设计基于MSP430的转速测量仪的…

深度剖析ElasticSearch分页原理与深分页问题|ES深分页问题|ES分页原理剖析

文章目录 ES分页|Paginate search resultsES深分页的问题一页获取数据量太大&#xff0c;报错分页深度太大&#xff0c;报错官方解释 其他解决方案Search after解决两个问题 有没有深分页查询的必要性&#xff1f;search after & PIT的使用方式1.创建pit2.首次查询3.之后的…

21.智能指针(上)

目录 一、概念二、Box\<T\>2.1 概念与应用场景2.2 简单应用2.3 递归类型的创建 三、通过Deref trait将智能指针当作常规引用处理3.1 常规引用3.2 像引用一样使用Box\<T\>3.3 自定义智能指针3.4 函数和方法的隐式解引用强制转换3.5 解引用强制转换与可变性交互 四、…

YOLOv8中的C2f模块

文章目录 一、结构概述二、模块功能 一、结构概述 C2f块:首先由一个卷积块(Conv)组成&#xff0c;该卷积块接收输入特征图并生成中间特征图特征图拆分:生成的中间特征图被拆分成两部分&#xff0c;一部分直接传递到最终的Concat块&#xff0c;另一部分传递到多个Botleneck块进…

用VScode打开keil下的文件中文编码乱码的问题,以及利用VScode转换字符编码的方法

目录 问题描述 解决方法 利用VScode转换字符编码的方法 问题描述 keil中默认的编码是ANIS如下图所示。 而VScode中默认的编码为UTF-8 &#xff0c;打开后如下。 解决方法 建议另存后&#xff0c;再打开目标文件&#xff0c;防止误操作&#xff01; 在VScode的最下方可以找…

一文读懂数据仓库ODS层

数据仓库一般分为三层&#xff0c;分别为数据贴源层&#xff08;ODS&#xff0c;Operation Data Store&#xff09;、数据公共层&#xff08;CDM&#xff0c;Common Data Model&#xff09;和数据应用层&#xff08;ADS&#xff0c;Application Data Service&#xff09;。其中…