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

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

  • 一、关于开发 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 串口插入:
在这里插入图片描述

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

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

相关文章

Linux—— ansible循环

1.如果有大量的变量要定义&#xff0c;如果多个变量本身类型相同或类似 再比如&#xff0c;同一个剧本&#xff0c;给主机同时安装多个软件包 按照已有的用法&#xff0c;每个软件包都对应不同变量&#xff0c;还会涉及到改剧本 2.现在可以用清单&#xff0c;以及playbook里…

【路径规划】基于粒子群结合遗传算法实现机器人栅格地图路径规划

研究方法: 基于粒子群优化算法结合遗传算法的机器人栅格地图路径规划是一种智能算法的应用。它将粒子群优化算法和遗传算法相结合,以寻找最优路径规划解决方案。 研究路线: 理论研究:了解粒子群优化算法和遗传算法的基本原理,并掌握相关的路径规划理论知识。 算法设计:…

JavaScript中 Map与reduce的应用

1. Map&#xff1a;映射新世界 Map构造函数创建一个新Map对象&#xff0c;它允许你以键值对的形式存储数据&#xff0c;提供了一种更加灵活的数据结构。与传统的对象相比&#xff0c;Map允许任何值&#xff08;包括对象&#xff09;作为键&#xff0c;而且具有更好的性能表现。…

中服云数字孪生平台

中服云数字孪生平台是基于中服云物联网平台和数据中台打造的一款实时数据2D/3D集成展示监控平台&#xff0c;旨在解决工业物联网数据的直观展示、实虚互动、仿真模拟、故障诊断、告警、预警、预测、实时观测、实时监控等问题。提供了数据采集、数据底座、监控逻辑、建模工具、展…

50ETF期权可以异地线上期权开户吗?

今天带你了解50ETF期权可以异地线上期权开户吗&#xff1f;有很多的股民、基金投资者都是有上证50ETF期权开户的需求的&#xff0c;大家都知道不能把鸡蛋放在一个篮子里&#xff0c;上证50ETF期权可以作为一种对冲大盘下跌的保险。 期权要看你在哪里开户&#xff0c;如果是在券…

React实现H5手势密码

监测应用进入前后台 在JavaScript中&#xff0c;监听H5页面是否在前台或后台运行&#xff0c;主要依赖于Page Visibility API。这个API在大多数现代浏览器中都是支持的&#xff0c;包括苹果的Safari和谷歌的Chrome&#xff08;也就基本覆盖了Android和iOS平台&#xff09;。下…

IOS逆向分析—终极详细(三)

IOS逆向分析—终极详细&#xff08;三&#xff09; 前言一、逆向分析是什么&#xff1f;二、IDA分析1.下载并安装IDA2.安装插件3.加载二进制4.代码分析5.其它 总结 前言 本文是个人完成对IOS上APP分析的整个过程&#xff0c;当然对于不同的机型还会遇到不同的情况&#xff0c;谨…

力扣144A

文章目录 1. 题目链接2. 题目代码3. 题目总结4. 代码分析 1. 题目链接 Arrival of the General 2. 题目代码 #include<iostream> using namespace std;int heightOfSoldier[110];int main(){int numberOfSoldier;cin >> numberOfSoldier;int maxHeight -1;int mi…

QT中启动窗口QSplashScreen的使用

1、前言 一个程序启动时,程序需要初始化操作比如加载数据、连接网络,一般来说比较耗时,这个时候为了用户体验,就是在程序主界面完成之前加一个启动界面。 2、关键代码 #include "SplashScreen.h" #include "ui_SplashScreen.h"SplashScreen::Splash…

【科研基础】通感一体化讲座

高斯信道下通信感知一体化的性能极限(刘凡) 通信和感知在硬件结构上相似,高效地利用资源,实现相互的增益; 感知是基于不同的任务,比如目标检测(检测概率,虚警概率),估计任务(从收到的信号中去估计有用的参数,均方误差,CRB),识别(知道目标的语义信息,就是目标…

助力草莓智能自动化采摘,基于YOLOv8全系列【n/s/m/l/x】参数模型开发构建果园种植采摘场景下草莓成熟度智能检测识别系统

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;技术已经渗透到我们生活的方方面面&#xff0c;从智能家居到自动驾驶&#xff0c;再到医疗健康&#xff0c;其影响力无处不在。然而&#xff0c;当我们把目光转向中国的农业领域时&#xff0c;一个令人惊讶的…

Go 内存模型与分配机制

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

前端开发流程与技术选型

目录 一、简介 二、前端职责 三、开发步骤 四、技术选型 五、页面展示 一、简介 做一个网站时&#xff0c;能看到的一切都是前端程序员的工作&#xff0c;负责网页或者app的结构、样式、用户操作网站时的事件逻辑&#xff08;比如点击一个按钮&#xff09;。 二、前端职…

2024广东省职业技能大赛云计算赛项实战——容器云平台搭建

容器云平台搭建 前言 容器镜像使用的是斗学培训平台提供的镜像包&#xff0c;这东西网上都没有&#xff0c;一堆人要&#xff0c;我是靠自己想的方法获取到了&#xff0c;也不敢给。你们可以通过在这个网站申请环境进行操作https://ncc.douxuedu.com/ 虚拟机使用的是自行创建…

分类算法和回归算法区别

分类算法和回归算法在机器学习中扮演着不同的角色&#xff0c;它们的主要区别体现在输出类型、应用场景以及算法目标上。以下是对两者区别和使用场景的详细分析&#xff1a; 一、区别 1.输出类型&#xff1a; 分类算法&#xff1a;输出是离散的类别标签&#xff0c;通常表示为…

【网络安全的神秘世界】文件上传、JBOSS、Struct漏洞复现

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 攻防环境搭建及漏洞原理学习 Kali安装docker 安装教程 PHP攻防环境搭建 中间件介绍 介于应用系统和系统软件之间的软件。…

class的流光效果

效果图&#xff1a; 代码示例 <!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title&g…

链表OJ

GDUFE 在期末前再刷一次链表题 ~ 203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ struct ListNode* removeElements(struct ListNode* head, int …

学习笔记——网络管理与运维——SNMP(SNMP版本)

二、SNMP版本 1、SNMP版本 SNMP共有三个版本&#xff1a;SNMPv1、SNMPv2c和SNMPv3。 (1)SNMPv1 1990年5月&#xff0c;RFC1157定义了SNMP的第一个版本SNMPv1。RFC1157提供了一种监口控和管理计算机网络的系统方法。SNMPv1基于团体名认证&#xff0c;安全性较差&#xff0c;…

快速编译安装tensorrt_yolo

快速编译安装 安装 tensorrt_yolo 通过 PyPI 安装 tensorrt_yolo 模块&#xff0c;您只需执行以下命令即可&#xff1a; pip install -U tensorrt_yolo 如果您希望获取最新的开发版本或者为项目做出贡献&#xff0c;可以按照以下步骤从 GitHub 克隆代码库并安装&#xff1a; …