我的第一个上位机软件

       2019年即将过去,这一年最值得开心、高兴的事就是我参与研发的“全自动生化分析仪”终于上市了,并受到市场的欢迎;由于有非常给力的销售团队,机器的订单一直不断。当然机器研制成功是项目经理和团队的功劳,而我只是参与其中的一部分而已,但这对我而言有特殊的意义;因为这是我的第一个基于linux的商用上位机软件。虽然以前在windows平台折腾过java、C#、MFC的小上位机,但那些上位机无论从代码量和功能上都无法和这个商用上位机相提并论。当年在做毕业设计时,见识到了linux系统功能的强大,一直想从事linux系统的软件开发;但为了尽快找到工作,只能先找擅长的单片机和stm32的软件开发工作。然后利用业余时间再学习linux系统相关知识。由于缺乏正确的引导,学习linux系统的应用程序开发走了很多弯路;选择了一套课程,根据课程先学了linux的设备驱动开发,学习了linux的某个模块源代码;虽然这部分在后来的工作中也用到了一点点,但这毕竟和linux应用程序开发是两个方向的工作;到了2016年初才学会如何在linux上开发Qt应用程序;造化弄人,到了2016年底终于有机会让我开发基于linux的应用程序。

        由于加入项目组的时间比较晚,加上是第一次开发商用上位机,缺乏经验,没有针对需求做详细的软件架构设计,一上来就根据需求开始编码;这种方式开发一些学习软件没有问题,但是开发功能繁多的商用软件肯定不可取;就像垒一个鸡窝前期可以不用设计图纸,但是建一栋高楼大厦,如果前期没详细规划和设计图纸,那将会带来无尽的问题。前期没有详细得设计软件架构,就根据开发单片机软件的经验用C语言的思维去开发C++软件,在刚开始开发一些调试执行机构的软件时,还没察觉到有什么问题。随着不断的新增功能,代码量越来越大,特别是在处理测试数据时,这时就不断的暴露出一些致命的问题。导致项目经理在测试机器性能时,整个软件bug满天飞,黑屏不断。项目经理虽然一直在催进度,但并没给我很大的压力,而是一直相信我能改好。刚开始调试bug时手段比较单一,只是把程序的运行情况打印到控制台上,虽然这是很常用的调试手段,但局限性也很大;因为当机器在不同地方使用时就得在那的电脑安装串口调试助手才能知道机器运行情况,若是交到客户那使用呢?经过不断查询资料发现Qt提供了运行日志的功能,虽然之前有通信日志,但那远远不够;熟练掌握了Qt运行日志的功能后,就在软件加上这个功能;果然收到了很好的效果,不管机器在哪里运行,只要一出问题,就可以根据运行日志和通信日志很快定位到原因,而打印运行日志时就很考验经验了,打印多了影响软件性能,打印少了不利于定位原因。但在开发软件的阶段时要查看一些变量的值时,每次都得登录后台打开运行日志进行查看,显得很繁琐。根据开发单片机软件的经验,每次遇到bug都是仿真,因此就引进了GDB调试,由于所用开发环境版本比较低,无法使用一键远程调试,但是使用gdbserver的方式也可达到同样的目的;使用了GDB调试后可定位到软件运行的具体语句并查看变量的值;大大提高了开发效率。有次朋友问我当linux的应用程序崩溃时有没有获取Linux的core文件进行定位程序崩溃的原因;我直接回答:“没有!”;然后朋友就用鄙视的眼神看了我一眼,那种鄙视的眼神让我久久不能忘怀;没方法学艺不精,只能在鄙视中成长!然后查阅了大量资料并做了相关实验,了解linux的core文件是什么以及如何使用;原来linux的core文件就像飞机失事后的黑匣子;当linux的应用程序崩溃时导致黑屏,core文件会记录下应用程序最后运行的各种情况,然后再用GDB调试core文件即可定位到出问题的原因,甚至定位到具体哪一语句导致程序崩溃;有时根据core文件定位出问题的类型,然后根据运行日志再定位到具体原因;两种调试手段结合使用。在开发软件过程中总会遇到各种各样的bug,而丰富的调试手段将决定是否能很快定位原因并解决bug。从一开始遇到bug的慌张到最后的淡定;上述几种调试方法给了我很大的底气。

        虽然掌握了几种调试方法,可以比较快速定位bug原因;但保证不了整个工程的代码质量。由于前期缺乏整体软件架构设计,到了中后期,进入代码维护和新增需求时,修改代码时经常是“牵一发而动全身”,修复了一个bug往往会引进另一个bug;新增一个功能则是会牵动到其他的功能;整个软件工程就像一个豆腐渣工程。为了扭转这种颓势,买了很多书和在网上找各种相关的视频教程。经常有人问买那么多书看得完吗?当然是看不完,但一个本书只要帮助解决一个小问题就赚了;更何况有的书不但可以协助解决问题还可以带来有体系的知识点。找各种相关视频教程是为了看看其他大神写的代码,因为提高写代码的能力就是看大神写的优秀代码然后模仿;在以前购买的教程学习群里有人介绍了狄泰软件学院的课程,由于以前购买课程的阴影,刚开始我也犹豫了一段时间,后来遇到的问题实在太多,抱着试试的态度而且价格也不贵就买了一套Qt课程;看了课程后就有种“相见恨晚”的感觉。课程通过写几个小小项目来讲解知识点;看老师写小项目代码时经常感到醍醐灌顶,原来还可以这样写代码;其中项目界面的全用代码生成,为用代码生成界面提供了模板;还有把C/C++的各种基础知识巧妙灵活配合使用而不是简单的“if else”和“switch case”;例如把C/C++的“短路原则”、“条件表达式”和“逗号语句”的作用发挥得淋漓尽致使得代码简洁清晰;因之前看过整套C++视频,这些基础知识虽然都懂,之前却不知怎么样使用;这让我坚信了基础知识的重要,只有基础知识扎实才能写出有质量的代码和设计出工业水平的软件架构。看完了这些再对照自己写的代码,然后通过模仿课程的写法几乎重构了整个软件代码,也重新设计了整个软件的编码风格。

        虽然整个软件有了良好的编码风格,但却找不到解决界面与数据分离的方法;直到看了课程中讲解的自定义模型类,用分层思想实现了数据到视图的控制流程,每层用一个类表示,用自定义的模型类组合了标准模型类,灵活显示各种数据;定义数据源类操作底层数据文件,用于把数据对象转换成字符串写入数据文件,让上层代码只需操作数据对象即可,妥妥的面向对象编程;这样就使得程序结构层次分明,代码写起来简单明了便于维护。这套设计我只需做简单的修改,增加少量的代码,即可用于解决我的软件界面与数据分离的问题;这套设计还让我学会了如何面向对象编程,就是在解决问题时,先把问题抽象成类,然后再定义操作类解决问题后返回相应问题对象。这种思想在课程实现简易网络聊天软件时得到了进一步的体现。在解决网络通信的数据粘连时,定义了通信协议类,然后定义协议对象装配类用于把通信数据装配成协议对象并返回给上层代码处理;通过举一反三修改少量代码即可用于解决我软件的串口通信数据粘连的问题,相比于原来用C语言思维方式解决问题,代码显得有层次有模块,完全符合“强内聚弱耦合”的设计原则。

        有了灵活巧妙运用各种基础知识的模板,有了如何用代码生成界面的模板,有了界面和数据分离的模板,有了运用面向对象编程思维解决通信数据粘连的模板;接下来就是模仿这些模板写法,重构自己的软件代码;而整个工程代码将近十万行,几乎是全推倒式的重构;而现实情况不可能是接下来几个月不新增需求和修复bug;所以只能“乱中求治”,在新增功能和修复 bug时坚持“抠”一点时间或利用加班时间用于逐渐改善代码设计,逐渐使代码的质量越来越好。最终使整个工程代码层次分明,结构简洁清晰,模块化,为后期维护打坚实基础。

        在完成整个软件的功能中另一个困难就是各种非线性曲线的拟合,刚开始面对这种只有在研究生课程才会学到的数值分析束手无策;后来由于时间关系,这部分就交给另一个同事,他用开源库搞定了。但由于对其中各种拟合曲线的兴趣,我开始翻阅各种资料,慢慢发现可以看懂简易的样条曲线拟合推导过程;这更激起了我的兴趣,更加认真学样条曲线拟合推导的全部过程,学习的过程就像柯南破案一样,一点一点的接近真相,每次有进步一点都能让我兴奋不已;先是在马东升和董宁编写的《数值计算方法》学了样条曲线拟合的推导理论知识,并看懂其中的几道例题;然后在王晓华编写的《算法的乐趣》学会了把方程组变化成矩阵,通过克洛脱法分解成两个三角矩阵,再用追赶法求得方程组的解。最后在John H.Mathews和Kurtis D.Fink著的《Numberical Methodes Using MATLAB》了解了样条曲线端点约束的几种情况,对样条曲线有了全面的认识;同时也知道机器中样本曲线拟合适那种端点约束。结合几本书学到的知识点,最后编写成了代码,并且计算结果和用开源库拟合的曲线参数是一样的,这让我成就感爆棚。拟合曲线完成后,在计算测试结果时,则是让我学会了用牛顿迭代法求解一元三次方程;还有如何快速判断抛物线在一个区间内的单调性,妥妥复习了一下高中数学。

        从写下第一行代码到软件跟随机器上市,这个过程遇到过千千万万的问题,而每一个诡异的bug和问题背后都隐藏着一个低级错误,每当解决一个诡异的bug都能带来一点点成就感和一点点进步;这也是我从毕业后这几年来,一直坚持学习linux应用开发的原因,虽然这几年让我失去其他的很多东西,思想也有过波动,最后庆幸坚持了下来,终于有一款基于linux的商用上位机,也算是随了几年前的心愿。

                                                                                                                                                                               2019年12月31号

 

 

<think>嗯,用户问的是如何用VS2022设计一个上位机软件。首先,我需要理解用户的需求。上位机软件通常用于与硬件设备通信,比如通过串口、USB或者网络,所以用户可能是在做一个需要与下位机(比如单片机、传感器)交互的项目。可能用户是工程师、学生或者爱好者,想要开发一个监控、控制或数据采集的界面。 接下来,我得考虑使用什么框架。VS2022支持WinForms、WPF、UWP等。WinForms比较简单,适合快速开发;WPF更现代,界面更灵活,但学习曲线稍高。可能用户需要基础功能,所以先推荐WinForms,如果有更复杂的需求再考虑WPF。 然后,通信部分。常用的有串口和网络通信。可能需要用到SerialPort类或者TCP/IP套接字。得提醒用户安装相应的库,比如SerialPort在WinForms中自带,网络通信可能需要更多配置。 数据处理的建议,比如多线程或异步操作,避免界面卡顿。用户可能不了解后台线程的重要性,需要强调这一点,防止UI无响应。 数据展示部分,图表控件可以用自带的Chart或者第三方库如LiveCharts,OxyPlot。保存数据可能需要文件操作或数据库,这里可以提到SQLite或CSV文件,根据用户需求选择。 调试和测试方面,虚拟串口工具或者网络调试助手可能有用,用户可能没有实际硬件,所以模拟工具很重要。 最后,打包和部署,ClickOnce或安装程序项目,方便用户分发软件。 可能用户是第一次开发上位机,所以步骤要详细,从新建项目到各个模块的实现,给出示例代码,比如串口和TCP的简单代码,让用户能快速上手。同时提醒常见问题,比如UI线程阻塞,数据处理方法。 还要考虑用户可能的后续需求,比如扩展功能,日志记录,插件系统,或者跨平台考虑,虽然VS2022主要是Windows,但可以提到.NET MAUI或其他框架。 需要检查是否有遗漏的步骤,比如界面设计中的具体控件拖拽,属性设置,事件处理等,可能需要更详细的指导,但考虑到回答长度,可能给出关键点,让用户知道基本流程。 总之,回答需要覆盖开发流程、框架选择、通信实现、数据处理、界面设计、调试方法和部署,同时给出示例代码和注意事项,帮助用户顺利开始项目。</think>使用 Visual Studio 2022 (VS2022) 设计一个上位机软件需要结合界面设计、通信协议实现(如串口、TCP/IP等)以及数据处理功能。以下是具体步骤和关键点: --- ### **1. 环境准备** - **安装 VS2022** 从官网下载并安装,选择 `.NET 桌面开发` 工作负载(支持 WinForms/WPF)。 - **可选工具** 若需高级图表或控件,可安装 NuGet 包(如 `LiveCharts`、`OxyPlot` 用于绘图)。 --- ### **2. 选择开发框架** - **WinForms** 适合快速开发简单界面,拖拽控件即可。 - **WPF** 适合复杂界面和动画,支持 MVVM 模式,更现代化。 - **.NET MAUI**(跨平台) 如果需要跨平台支持(如移动端),但上位机通常以 Windows 为主。 --- ### **3. 创建新项目** 1. 打开 VS2022 → 新建项目 → 选择模板(如 `Windows Forms 应用` 或 `WPF 应用`)。 2. 命名项目,例如 `SerialPortMonitor` 或 `TCPClientTool`。 --- ### **4. 界面设计** - **控件拖拽** 添加按钮(Button)、文本框(TextBox)、图表(Chart)、列表框(ListBox)等。 - **关键控件** - 串口通信:`ComboBox`(选择端口)、`Button`(打开/关闭端口)。 - 网络通信:`TextBox`(输入 IP 和端口)、`Button`(连接/断开)。 - 数据显示:`DataGridView`(表格)、`Chart`(实时曲线)。 --- ### **5. 通信功能实现** #### **串口通信** ```csharp using System.IO.Ports; // 初始化串口 SerialPort serialPort = new SerialPort("COM3", 9600, Parity.None, 8, StopBits.One); // 打开串口 serialPort.Open(); // 数据接收事件 serialPort.DataReceived += (sender, e) => { string data = serialPort.ReadExisting(); // 跨线程更新 UI(WinForms) this.Invoke((MethodInvoker)delegate { textBoxReceivedData.AppendText(data); }); }; // 发送数据 serialPort.Write("Hello Device"); ``` #### **TCP/IP 通信** ```csharp using System.Net.Sockets; TcpClient client = new TcpClient(); client.Connect("192.168.1.100", 8080); NetworkStream stream = client.GetStream(); byte[] sendData = Encoding.ASCII.GetBytes("Hello Server"); stream.Write(sendData, 0, sendData.Length); // 异步接收数据 byte[] buffer = new byte[1024]; stream.BeginRead(buffer, 0, buffer.Length, (ar) => { int bytesRead = stream.EndRead(ar); string receivedData = Encoding.ASCII.GetString(buffer, 0, bytesRead); // 更新 UI }, null); ``` --- ### **6. 数据处理与显示** - **多线程/异步操作** 使用 `async/await` 或 `BackgroundWorker` 避免 UI 卡顿。 - **图表更新** 使用 `System.Windows.Forms.DataVisualization.Chart`(WinForms)或第三方库。 - **数据存储** 保存到 CSV、Excel 或 SQLite 数据库。 --- ### **7. 调试与测试** - **虚拟串口工具** 使用 [Virtual Serial Port Driver](https://www.eltima.com/vspd.html) 模拟串口通信。 - **网络调试助手** 使用 TCP/UDP 测试工具(如 `NetAssist` 或 `Hercules`)验证通信。 --- ### **8. 打包与部署** - 生成发布版本(`Release` 模式)。 - 使用 `ClickOnce` 或 `InstallShield` 创建安装包。 --- ### **示例项目结构(WinForms)** ``` - MainForm.cs(主界面) - 控件事件(按钮点击、数据接收) - SerialManager.cs(封装串口操作) - DataProcessor.cs(处理接收的数据) - Logging.cs(记录日志) ``` --- ### **注意事项** 1. **线程安全** UI 更新必须通过 `Invoke` 或 `Dispatcher` 切回主线程。 2. **异常处理** 捕获通信中的异常(如端口占用、断开连接)。 3. **性能优化** 大数据量时避免频繁刷新 UI,可采用定时批量更新。 --- 通过以上步骤,你可以快速搭建一个基础的上位机软件。根据需求扩展功能(如协议解析、自定义控件、多语言支持等)。如果需要更具体的代码示例或功能实现细节,可以进一步说明需求!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

划清界限

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值