当前位置: 首页 > news >正文

电力协议处理框架C++版(三)

        前两篇聊了下此框架的架构和启动机制,本篇,我们继续深入讨论下跟电力业务相关的,通道设计。

一、通道的设计

        我们的电力协议中,有很多规约,比如DLT645 ModBus IEC104 CDT等,网上单体的开源库也有很多。但是我们现在需要一个框架封装他们的底层通讯,在这些协议中,有基于串口的,网口的,串口的介质又分为485 232,而网口分为具体实现可以是TCP UDP,而TCP中,又能分为是客户端还是服务端。但是总体来说,这么多介质发送与接收数据,接口可以大致设计如下:

bool sendFrame(const std::string &name, const std::string &sendBuf);
bool recvFrame(const std::string &name, std::string &recvBuf);

1.1 同步还是异步 

        上面的接口看起来是同步的,那就有同学疑问了,为什么我不能设计成异步,比如sendFrame后,再通过回调函数进来,这样效率不是更高吗?我们首先考虑一下485串口,这个是半双工的,要么通道用作发,要么通道用作收,而不能收发同时进行。所以85协议一般都是一发一收制。

        试想当一个串口下,挂两个设备,那这个时候,异步是不行的,设备一起发送接收容易出现报文错乱,从而没法组装成一个完整的报文。那同步报文是不是意味着我们要一直等下去,那肯定也不行,需要有一个超时时间,超过这个时间,报文则无效。否则一直等下去,其他设备就没法交互了。

        那又有同学开始疑问了,既然串口不行,网口呢,比如TCP协议。那这个是可以的,做成异步的方式,但是在框架中,如果是一对一的场景,比如TCP客户端,我们仍然使用同步接口,对于TCP服务端,一对多的情况下,我们使用异步接口。在使用异步接口的情况下,同步接口是无效的。异步接口的设计如下:

/*** @brief 消息处理回调函数* void 返回值* int socket fd* const char * 接收的报文信息* int 报文长度*/
using msgFunc = TFunction<void, int, const char*, int>;/**
* @brief 注册消息回调函数,这个是异步调用,注册后,recvFrame函数不可用,消息与发送异步处理。仅用于tcpServer
* 多客户端的情况下
*
* @param name 转发或设备名称
* @param func 消息回调
* @return true 成功
* @return false 失败
*/
virtual bool attachMsgAsyncFunc(const std::string &name, const msgFunc &func) = 0;

这样我们就吃下了不同通道的发送接收问题,同时也解决了一对多的问题,比如很多场景是104一个服务端,但是会有多个客户端来连接,如果仅靠一对一的同步发送接收端口也是不行的

1.2 通道安全

        仍然以485通道为例,试想如下的场景,同一个485通道下面挂了温湿度(Modbus),电表(DLT645),这是两个不同的规约,正常情况下,我们的框架是轮询进入不同的设备的,不同的通道是不同的线程,这样已经保证了线程和数据交互的安全。但是如果此时正在处理DLT645的报文,而平台下发了modbus的遥控指令,这个时候我们应该怎么处理。如果为了同步安全性,可以等到645的设备处理完,到modbus的设备处理时再处理这个遥控的响应,但是这样就丧失了及时性。对一些要求及时响应的客户,这个是不能接受的。

        那有些用户会怎么做呢,他在各自的规约里起一个线程,去处理遥控或遥调信号,然后中断发送相应报文,这个及时性满足了,但是会暴露安全性的问题,如果在处理modbus报文的时候,中断处理是没问题的,但是如果在处理645,这个时候中断去发送modbus控制报文,就会和645的冲突,导致报文混乱。所以这个时候需要通道锁,将整个通道管住,这样的话,中断处理也不会有问题。核心的代码如下:

        

void CProtocolManager::process(base::ThreadImpl *pThread, std::vector<ProtocolInfo> vecProt)
{std::mutex mutex;for (auto &iter : vecProt){/// 将锁设置给通道上面的每个规约iter.pProt->setLockSource(&mutex);for (auto &iter1 : iter.vecDevName){CChannelManager::instance()->attachFixFunc(iter1, base::function(&IProtocol::fixFrame, iter.pProt));}}while (pThread->looping()){for (auto &iter : vecProt){/// 不同的规约轮询进入处理iter.pProt->process(iter.vecDevName);}std::this_thread::sleep_for(std::chrono::milliseconds(20));}
}

在使用的时候,只需要用下面的接口就行

/**
* @brief 同步发送报文并接受返回报文,用于不仅数据处理的协议,还有遥控遥调协议的设备。
*
* @param name 设备名称
* @param mutex 锁
* @param sendBuf 发送缓冲区
* @param recvBuf 接受报文
* @return true 成功
* @return false 失败
*/
virtual bool sendSyncRecv(const std::string &name, std::mutex *mutex, DATA_TYPE type, const std::string &sendBuf, std::string &recvBuf, int timeOut) = 0;

这样的话,我们的操作就可以满足遥控遥调的及时性和安全性。

二、任务,通道,规约,线程的关系

        整个框架都是以这四个概念设计的,配置首先驱动任务,随后创建通道规约,并跑起整个业务。何为任务呢,这是一个虚拟的概念,这个和通道是一对一的关系。而通道对于规约是一对多的关系,正如前面所述,一个485通道可以挂不同的设备,而这些设备又可以跑不同的规约,所以是一对多,线程则是和通道一对一的。

        为什么不设计成多进程模式,一个规约一个进程,很多小伙伴也这么问我,说这样定位问题比较简单,一个进程的话,出问题难定位。这里我仅表达自己的观点,不喜勿喷。

2.1 性能,效率

        博主设计这套框架呢,最初是嵌入式架构的,嵌入式呢也就是资源有限,性能也有限。首先对于单体结构和微服务架构(多进程)相比,肯定是单体性能最高,基于内存的交互。而多进程之间的交互,总逃不过共享内存、管道、socket等,而在socket上面还可以制定一些私有协议,比如基于tcp的、http的一些rpc交互方式。就其中的效率最高的方式共享内存而言,也不得不为同步安全使用上进程锁。所以在效率是低于单体程序的,尤其在很多进程一起交互的时候,往往表现的不是很好。当然机器性能足够强的话,这些其实也不是啥瓶颈,用户也感知不到。所以框架的设计还是依据自己的设备环境而考虑

2.2 问题定位

        问题定位怎么说呢,我感觉是难者不会会者不难,我觉得90%的问题一般都可以通过gdb的bt 、info thread、t num(切换到某个线程)、f num(切换到某个栈帧)、p打印、n/s单步调试即可。极少数会用到寄存器、汇编级别的调试。但是问题只需要解决一次,而运行效率确实一直存在的,我觉得为了让问题容易定位,而损失性能这个我是觉得不划算的。再者随着开发人员的水平提高,辅以CR、静态扫描等手段,是可以将很多问题进行规避的。 

那是不是说多进程就没有优点呢,也不是,我们也会用多进程但是颗粒度没有那么细,这就是存在一个设计平衡了。我们整个数据处理是单独一个进程,当配置更新时,比如点表、规约或者通道参数改变时,这个时候用多进程就比较方便,可以热更新,维护起来也比较方便。

http://www.lryc.cn/news/588079.html

相关文章:

  • 打破空间边界!Nas-Cab用模块化设计重构个人存储逻辑
  • SwiftUI 全面介绍与使用指南
  • AI数字人正成为医药行业“全场景智能角色”,魔珐科技出席第24届全国医药工业信息年会
  • 【微信小程序】
  • 1.2.2 高级特性详解——AI教你学Django
  • vue3 服务端渲染时请求接口没有等到数据,但是客户端渲染是请求接口又可以得到数据
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘sqlite3’问题
  • 第一章编辑器开发基础第一节绘制编辑器元素_4输入字段(4/7)
  • Django基础(一)———创建与启动
  • Django Admin 配置详解
  • uni-app 选择国家区号
  • 第二章 uniapp实现兼容多端的树状族谱关系图,封装tree-item子组件
  • 《星盘接口2:NVMe风暴》
  • Python 变量与简单输入输出:从零开始写你的第一个交互程序
  • Spring的`@Value`注解使用详细说明
  • vue3+uniapp 使用vue-plugin-hiprint中实现打印效果
  • 【数据同化案例1】ETKF求解参数-状态联合估计的同化系统(完整MATLAB实现)
  • 微算法科技技术创新,将量子图像LSQb算法与量子加密技术相结合,构建更加安全的量子信息隐藏和传输系统
  • 简单易用的资产跟踪器DumbAssets
  • uni-app在安卓设备上获取 (WIFI 【和】以太网) ip 和 MAC
  • 游戏设备软件加密锁复制:技术壁垒与安全博弈
  • 高安全前端架构:Rust-WASM 黑盒技术揭秘
  • 多云环境下的统一安全架构设计
  • 从 JSON 到 Python 对象:一次通透的序列化与反序列化之旅
  • Eplan API Creating projects or pages
  • .net winfrom 获取上传的Excel文件 单元格的背景色
  • 使⽤Pytorch构建⼀个神经⽹络
  • Solid Edge多项目并行,浮动许可如何高效调度?
  • 在项目中集成开源的表单设计器FcDesigner源码
  • mongodb原理及其实现