搜索
bottom↓
回复: 15

C# 串口定时轮询采集中如何插入能及时响应的控制命令

[复制链接]

出0入4汤圆

发表于 2020-8-10 23:19:56 | 显示全部楼层 |阅读模式
大家好,我有个软件C#开发的,有一个定时的任务,一条485总线上modbus协议采集一批设备数据。目前工作方式是这样的,定时器超时事件发生后,先关闭定时器,在超时事件中采集一批,然后打开定时器,等待下次超时采集。现有有个需求,有个设备需要控制,用户点击时发送控制命令,用户可以随便点。
现在有个问题,用户点击控制时候,定时器的采集任务可能在运行,就会可能出现多条命令同时发的情况,导致接收有问题。现在把所有控制命令缓存起来,等到定时器超时时候再运行,这样不会有多条命令同时发送的情况,但是定时器的定时周期比较长,导致控制命令延时比较大。如果有控制命令修改定时器超时,又会导致原来定时采集的间隔不准确。
在保证定时采集间隔不变,控制又能及时响应的情况下,有没有什么好的机制,既能让定时器的任务始终按照设定的时间运行,又能让插入的控制命令及时的运行?

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入22汤圆

发表于 2020-8-10 23:23:10 来自手机 | 显示全部楼层
多线程不好吗,定时器用起来好像没那么安逸。

出110入170汤圆

发表于 2020-8-10 23:44:23 来自手机 | 显示全部楼层
没办法吧,硬件阻塞了。
而且485的架构,
只有等上一帧接收完成或者超时。

你可以接收完毕就响应。

出615入1076汤圆

发表于 2020-8-11 00:18:52 来自手机 | 显示全部楼层
本帖最后由 dukelec 于 2020-8-11 00:20 编辑

高級語言都有 queue 可以用的吧,所有命令入 queue,然後只要 queue 有命令就第一時間按照順序下發至總線(下發程序被 queue 中的數據喚醒),並在收到設備回覆的時候喚醒入 queue 者(可以通過另一個 queue 或者信號量之類的東西,或者採用回調的方式)。定時器定時時間到就把命令入 queue,用戶操作按鈕也把命令入 queue。queue 可以使用多線程方式,也可以使用 asyncio 異步方式。反正 Python 這樣操作很簡單。

出0入0汤圆

发表于 2020-8-11 09:19:39 | 显示全部楼层
点击按钮发送控制命令时,加个定时器是否关闭的判断就行了吧
while(定时器关闭==1)
{
Thread。sleep(1000);
}
发送命令

出0入8汤圆

发表于 2020-8-11 09:39:25 | 显示全部楼层
dukelec 发表于 2020-8-11 00:18
高級語言都有 queue 可以用的吧,所有命令入 queue,然後只要 queue 有命令就第一時間按照順序下發至總線( ...

正解,有UI有后台的多线程都是采用这种Queue的方式来解耦的

不过你这个太复杂了,需要专业的软件开发人员才行
半路出家的嵌入式开发人员还是得想点别的办法

出40入42汤圆

发表于 2020-8-11 09:40:00 | 显示全部楼层
1.通信线路用线程来处理,一直激活,带一个缓冲区,有数据就发,没数据就自己一边玩
2.无论定时器还是用户界面,都通过接口把数据写入缓冲区,等线程完成交互返回结果
3.如果接口不是阻塞的,而是异步返回就比较麻烦,写程序的时候要不断查询结果

出0入4汤圆

 楼主| 发表于 2020-8-11 22:43:13 | 显示全部楼层
落叶知秋 发表于 2020-8-11 09:40
1.通信线路用线程来处理,一直激活,带一个缓冲区,有数据就发,没数据就自己一边玩
2.无论定时器还是用户 ...

这位兄弟的回复,让我想到思路了。回头测试下

出0入20汤圆

发表于 2020-8-11 23:10:58 | 显示全部楼层
落叶知秋 发表于 2020-8-11 09:40
1.通信线路用线程来处理,一直激活,带一个缓冲区,有数据就发,没数据就自己一边玩
2.无论定时器还是用户 ...

这个时候就体现出C#的优势了,语言原生的对异步操作有良好支持
实际上我自己写的程序现在到处是随手起task干活(匿名函数用着太爽了),用task类自带的各种附加功能做调度,快速把功能码出来才是最重要的
真正耗时间的操作,要么放到后台去跑,有结果了更新UI数据缓存,或者直接阻塞UI,反正task.wait也好,信号量也好,在C#里面用起来都非常简单

实现这些功能需要一些事件响应式编程基础,这个对习惯了单片机中断的嵌入式工程师来说也不是很难的事情,且不说代码写起来比C爽太多.

我甚至在之前的一个项目里面专门写了一个form只有一个功能就是弹出来显示上面转圈圈的gif,但凡是有阻塞UI时间比较长的操作的就把这个窗口弹出来然后禁用UI,等活干完了再解除掉.实际上那个项目里面因为用户需要手动触发orm做数据库模糊查询,因为有这个功能,我根本不用跟用户去解释为啥UI卡住了.

出0入20汤圆

发表于 2020-8-11 23:26:45 | 显示全部楼层
顺便回一下LZ,做从c#少用定时器,我的程序里面一般只有一个定时器用来定期刷UI,其他的操作从来不用定时器,本来winform里面的定时器就不是用来做精确定时和触发事件的.
你这个问题上面大家已经给了正确的方法了,就是用queue做命令缓存,独立线程操作IO,返回值通过事件函数耦合到状态机或者其他具体功能.另外C#提供了一个线程安全的Queue组件,这个在通信这种可能中断的情况下是非常有用的,基本的queue还是有一定的线程安全风险的,当然如果有控制queue长度的操作可以直接用基本的queue,不用担心因为消费者挂了生产者一直往queue里面塞东西把内存撑爆.或者queue都空了还在继续尝试往外弹数据.
Invoke也少用(不是不用),对性能影响挺大的,如果做过一屏刷新几百个变量的这种程序你就知道直接刷UI是非常痛苦的,把窗体都刷撕裂了还很难保证逻辑上的正确性,对UI建模做buffer才是正确的方法.

出0入0汤圆

发表于 2020-8-12 03:59:27 | 显示全部楼层
解決"多条命令同时发送的情况"最基本的方式就是lock,算是resource contention的問題。進階的方式就是 dukelec 提到的 quence。
其實C#中quence的用法也是很簡單。

出0入0汤圆

发表于 2020-8-12 09:07:11 | 显示全部楼层
可以把控制命令单独设置一个缓存区,在定时器发送正常轮询指令前优先发送这个控制命令缓存区的数据就是了

出0入76汤圆

发表于 2020-9-9 17:31:14 | 显示全部楼层
多线程+队列

出0入0汤圆

发表于 2020-9-9 18:08:10 来自手机 | 显示全部楼层
多线程+事件,收到数据发布一个事件,然后建个方法订阅这个事件。

出0入0汤圆

发表于 2020-9-10 07:26:16 来自手机 | 显示全部楼层
落叶知秋 发表于 2020-8-11 09:40
1.通信线路用线程来处理,一直激活,带一个缓冲区,有数据就发,没数据就自己一边玩
2.无论定时器还是用户 ...

条理清晰,指导性强!
数据交互线程是同步或者异步对主进程处理其实都差不多的。相当于tcp通信的同步异步模式

出0入58汤圆

发表于 2020-9-10 16:16:41 | 显示全部楼层
简单啊,发送指令的操作放在独立线程里面。这个线程就负责不断查询发送指令队列,如果有就顺序发送。

然后你的定时器里面和用户点击就负责把指令塞到发送队列就可以了。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-24 10:33

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表