前言

中间经历了几次波折,最终才算是有时间把软件开发的框架确定下来了。现在开发才终于算是开始有了个起头。

其实在使用Qt做大型软件的时候会遇到一些问题,为此也要不得不做一些妥协。关于这个,我觉得你可能需要看一下这两篇文章

[Qt开发思想探幽]QObject、模板继承和多继承

[Qt开发探幽(二)]浅谈关于元对象,宏和Q_ENUM

越是深入开发越需要注意一点:Qt只是一个库,一个开发框架,它和boost库,MFC库并没有本质区别,它并不一定能撑起你整个的开发框架,你甚至可能要中途引入很多各种各样的库。

唯一的区别是,Qt算是一个相当重型的库,其中很多内容的生态都是封闭的(当然了,这都是建立在C++狗屎一样的String,如果string的问题可以解决,那么我相信这些库不能兼容的问题都将不复存在),这就意味着如果你使用了Qt的库,你大部分时间都将在这些Q打头的头文件里跌跌撞撞。

做出你想要做的事情,很难走出去或者走进来。这也是为什么大部分情况下用了Qt就不会用别的库了,即使你知道会有这样那样的问题:一是没必要,二是懒得弄。

说明

目前TSG项目开发使用Qt 5.14.2 + vs2019,请按照指定配置来开发,否则出现编译不过的情况请及时更换配置。

本文件将简单介绍一下软件框架及开发要求。框架没什么好说的,软件的要求是:所有新开发的模块都应该继承trunk\TSG_Base\PublicTemplate\TSG_DeviceTemplate.h中给定的指定类型,并在指定应用程序的kernel层中调用其父类,而不关心类的细节。

比如Device类型有很多很多,但是我在应用程序层中并不关心你的Device类型的细节,不管你是Faro,HikCam还是Imu,我只需要用QList将你所有的设备类型给打包,然后只需要管我的Device接口即可。

框架 TSG_Framework

一、底层信号机制 TSG_Caller

请所有的类至少需要继承../Framework/TSG_Framework/目录下的TSG_Framework.h,其中包含了基本的信号和槽,本框架所有消息转发机制都是通过此回调函数进行的:

class TSG_Framework
{
public:
using CallMethod = void(*)(const QString& sModule, const QString& sDescribe, const QString& sVariable, const QVariant& extra);
using SendCMD = void(*)(const QString& sModule, const QString& sDescribe, const QString& sVariable, const QVariant& extra); void RegisterCallMethod(CallMethod callback) {
callbacks_.append(callback);
}
void RegisterSendCMD(SendCMD callback) {
sendcmds_.append(callback);
} void Signal_CallMethod(const QString& sModule, const QString& sDescribe, const QString& sVariable, const QVariant& extra) {
for (CallMethod callback : callbacks_) {
if (callback) {
callback(sModule, sDescribe, sVariable, extra);
}
}
}
void Signal_SendCMD(const QString& sModule, const QString& sDescribe, const QString& sVariable, const QVariant& extra) {
for (SendCMD callback : callbacks_) {
if (callback) {
callback(sModule, sDescribe, sVariable, extra);
}
}
}
virtual qint32 slot_ReturnValue(const QString& sModule, const QString& sDescribe, const QString& sVariable, const QVariant& extra) { return -1; };
virtual qint32 slot_GetCMD(const QString& sModule, const QString& sDescribe, const QString& sVariable, const QVariant& extra) { return -1; };
private: QList<CallMethod> callbacks_;
QList<SendCMD> sendcmds_;
};

在这个父类中,提供了一个最基础的回调函数注册机制和发送控制命令的函数,后续所有继承这个类的子类都需要覆写这个方法,以便能够获得上层的消息。

这里我可能需要做出一些解释:



这就是CallMethod和ReturnValue在上下级中的映射关系,整套系统的控制实际上相当简单,上层控制层(后续包括业务层)通过CallMethod(回调)和ReturnValue方法在Kernel中交换消息,来实现控制层对底层的调用。

也就是说,逻辑上层的设备类如果需要调用底层的功能DLL,比如日志,网络通信等DLL,则需要使用CallMethod方法,如果底层DLL或者是从网络层发来的消息,则需要通过ReturnValue方法向上反应。

这一套信号槽机制构成了这个框架的基本,而上下信息尽量传递Json字符串,而不是一个简单的字符串,这个我们后面设计的时候会提到。

为什么不用信号和槽?因为信号和槽在继承中会出很多问题,而且会导致很多编译问题,这些问题我在做了大量调研后发现是解决不了的,所以只能用回调函数。

除了最基础的类,以下的类大多是建立在TSG_Framework的基石上。

我要先给出其他几个类之间的关系,再来说明这几个类的细节



一个设备的控制大体如上,Interface类实际上只是一个转发和接口,提供给主要进程一个最主要的控制接口,其实主要是信号收发机制。ConfigController更多的是像一个控制类内的Kernel类,但是Device类的成员并不是放在ConfigController类内。

ConfigController是一个真正承上启下的部分,管理设备信息的本地登记、各种信息的获取和修改,都留在本地,然后通过GetCMD获得各种各样的修改,否则都是默认值。这么做是为了保证在没有设置参数的前提下也能够正常执行任务。

当然了,Device并不会是一个单独的,而是会分为两部分,一个是设备本身的控制,第二个是业务的流程控制。原因很简单,设备并不一定是只有一个,而可能是一组设备。

接下来的介绍不分先后,名称也并不会按照上面的来。

二、参数类型声明 TSG_Params

class TSG_Params :public QObject {

public:
QMap<QString, QString> getKeys() {
QMap<QString, QString> ret;
const QMetaObject* metaObject = this->metaObject();
int propertyCount = metaObject->propertyCount();
for (int i = 0; i < propertyCount; ++i)
{
QMetaProperty property = metaObject->property(i);
if (property.isValid() && property.isReadable() && property.isWritable())
{
qDebug() << "Property name: " << property.name();
qDebug() << "Property type: " << property.typeName();
ret.insert(property.typeName(), property.name());
}
}
return ret;
} QString toJson() {
return Lev_Json::JsonSerialization(this);
}
bool isValid(QString strMessage) {
return Lev_Json::ValidateJsonKeys(strMessage, this);
}
};

实际上对于一个参数类,并没有做什么实际上的操作,因为每一个参数类都是通过元对象去操作这个Object本身,所以这个类可以无限制的拓展。当然了,至于想拓展成什么样还得看在开发中的具体需求。

要求所有在设备控制层中都必须传递参数类,比如

但是在控制层之上的管理层中却无法使用模板类规范输入,这是因为QObject无法支持模板类继承导致的,详情见文章开头的两篇文章

这导致了部分在管理层中只能通过Json字符串来控制控制层中的参数传递,所以必须要对所有的参数传递提供一定的验证手段和转换手段,详情见下图:



另外,在Lev_Json模块中,我提供了一整套的检查方法,可以直接将Json字符串和类内的所有成员变量之间做一个基本的筛查,这也是TSG_Params的基石。没有注释,函数名称就 是注释:

三、设备类声明 TSG_Device

不管设备内部的运行细节,一个设备都应该有以下这些接口:这里不关心设备的具体细节,但是设备本身都必须为这一套工作提供流程,因为这些接口都是在上层中需要被调用的,以这个类为基础去注册具体设备操作。

当然了这样的声明仅限于在框架内部操作,实际上只有业务层提供一个操作接口。

四、设备配置文件控制 TSG_ConfigHelper

ConfigHelper类只需要给定一个路径和指定设备的名称组就可以去自己初始化和获得设备的配置文件了。

class TSG_ConfigHelper : public TSG_Framework {
public:
virtual bool Init(const QString& path, const QList<QString>& list_device_names) = 0;
virtual bool Init(const QString& path) = 0;
virtual QList<QString> getConfigFiles() = 0;
virtual QJsonObject getConfigContain(const QString& config_name, const QString& device_name) = 0;
virtual bool setConfigContain(const QString& config_name, const QString& contains, const QString& device_name) = 0;
virtual QList<QString> getDeviceNames() = 0; virtual qint32 slot_ReturnValue(const QString& sModule, const QString& sDescribe, const QString& sVariable, const QVariant& extra) { return -1; };
virtual qint32 slot_GetCMD(const QString& sModule, const QString& sDescribe, const QString& sVariable, const QVariant& extra) { return -1; };
};

注:对每一个设备来说,其设备的配置文件类型和配置文件模式都应该是已经写死写固定了的,所以只给定一个应用程序当前的运行路径,然后自己去其中检索目录。

五、设备管理类 TSG_Device_Controller

这个就是Interface,没什么好说的,每个模块都是自洽的,so,不用传参了,直接来吧。

class TSG_Device_Controller : public TSG_Framework {
//输入当前进程的路径作为根路径,用于初始化控制类,完全是交由自己控制的
virtual bool Init(const QString& ApplicationPath) = 0; virtual QString getDeviceType() = 0;
virtual bool PreStartMission() = 0;
virtual bool StartMission() = 0;
virtual bool PauseMission() = 0;
virtual bool EndMission() = 0;
virtual WorkState Get_WorkState() = 0; qint32 slot_ReturnValue(const QString& sModule, const QString& sDescribe, const QString& sVariable, const QVariant& extra) { return -1; };
qint32 slot_GetCMD(const QString& sModule, const QString& sDescribe, const QString& sVariable, const QVariant& extra) { return -1; };
};

[TSG开发日志](一)软件基础框架的更多相关文章

  1. 如莲开发平台(MIS基础框架、Java技术、B/S结构)

    关于     「如莲」是一套MIS类系统基础框架,主要用于各类“管理信息系统”的开发,也适合做网站后台开发.可省去开发时的框架搭建.规范约定.权限管理等基础工作,直接专注于业务功能实现.     「如 ...

  2. SlickOne 敏捷开发框架介绍(二) -- 多用户/多租户/SAAS软件基础框架实现

    前言:在应用于集团版客户或SAAS平台服务的业务系统中,流程管理系统需要支持多用户组织模型.其中包括角色数据.流程定义数据和流程实例数据的多用户标识绑定.本文旨在全面描述如何基于SlickOne敏捷开 ...

  3. 接口开发-基于SpringBoot创建基础框架

    说到接口开发,能想到的开发语言有很多种,像什么Java啊..NET啊.PHP啊.NodeJS啊,太多可以用.为什么选择Java,究其原因,最后只有一个解释,那就是“学Java的人多,人员招聘范围大,有 ...

  4. C#实现多级子目录Zip压缩解压实例 NET4.6下的UTC时间转换 [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案 .NET Core开发日志

    C#实现多级子目录Zip压缩解压实例 参考 https://blog.csdn.net/lki_suidongdong/article/details/20942977 重点: 实现多级子目录的压缩, ...

  5. 准备.Net转前端开发-WPF界面框架那些事,搭建基础框架

    题外话 最近都没怎么写博客,主要是最近在看WPF方面的书<wpf-4-unleashed.pdf>,挑了比较重要的几个章节学习了下WPF基础技术.另外,也把这本书推荐给目前正在从事WPF开 ...

  6. LayIM.AspNetCore Middleware 开发日记(三)基础框架搭建

    前言 在上一篇中简单讲了一些基础知识,例如Asp.Net Core Middleware 的使用,DI的简单使用以及嵌入式资源的使用方法等.本篇就是结合基础知识来构建一个基础框架出来. 那么框架有什么 ...

  7. PHP 设计模式 笔记与总结(2)开发 PSR-0 的基础框架

    [PSR-0 规范的三项约定]: ① 命名空间必须与绝对路径一致 ② 类名的首字母必须大写 ③ 除入口文件外,其他".php"必须只有一个类(不能有可执行的代码) [开发符合 PS ...

  8. PHP扩展开发(1)-创建基础框架

    生成PHP扩展开发的基础框架.   一.Linux下   $>cd ~/{php源码}/ext $>./ext_skel --extname=simple   Creating direc ...

  9. 快速接入 Android BLE 开发的基础框架

    代码地址如下:http://www.demodashi.com/demo/12092.html ** Android BLE基础操作框架,基于回调,操作简单.包含扫描.多连接.广播包解析.服务读写及通 ...

  10. IOS开发 基础框架(Fondation Framework)的线程安全

    有一种误解,认为基础框架(Foundation framework)是线程安全的,而Application Kit是非线程安全的.不幸的是,这是一个总的概括,从而造成一点误导.每个框架都包含了线程安全 ...

随机推荐

  1. 【题解】[蓝桥杯] [基础练习VIP]矩形面积交

    题目描述 平面上有两个矩形,它们的边平行于直角坐标系的X轴或Y轴.对于每个矩形,我们给出它的一对相对顶点的坐标,请你编程算出两个矩形的交的面积. 输入 输入仅包含两行,每行描述一个矩形. 在每行中,给 ...

  2. 手动封装XMLHttpRequest

    自己动手封装一个XMLHttpRequest, 兼容低版本浏览器,自动检测post与get 类型请求,自动参数拼接,参数类型辨别 <!DOCTYPE html> <html> ...

  3. RoCE多网卡时,报文可以过去,但是回不来

    摘要:虽然网卡是接入RoCE网络,但其实问题本身是单纯路由相关的,所以看的时候,不用关注RoCE,只当做一个独立子网就行了 本文分享自华为云社区<<跟唐老师学习云网络> - RoCE ...

  4. python 环境下使用PIP 报错的解决方法

    最近做一个小程序项目,使用djangorestframework,安装restframework 出现错误,安装环境Python2.7:出现错误如下:  "UnicodeEncodeErro ...

  5. 云原生时代Go最受欢迎Web开源框架Gin原理与实战

    @ 目录 概述 定义 特点 概览导图 使用 快速入门 HTTP 方法使用 参数获取 参数绑定 自定义日志输出 自定义中间件 路由组 HTML渲染 设置和获取Cookie XML.YAML.ProtoB ...

  6. Python自学指南-第一章-安装运行

    1.1 [环境]快速安装 Python 与PyCharm "工欲善其事,必先利其器",为了自学之路的顺利顺利进行.首先需要搭建项目的开发环境. 1. 下载解释器 进入 Python ...

  7. 【python基础】input函数

    1.初识input函数 大多数程序都旨在解决最终用户的问题,为此通常需要从用户那里获取一些信息.例如假设有人要判断自己是否到了投票的年龄,要编写回答这个问题的程序,就需要知道用户的年龄,这样才能给出答 ...

  8. CKS 考试题整理 (02)-Apparmor

    Context Apparmor 已在 cluster 的工作节点 node02 上被启用.一个 Apparmor 配置文件已存在,但尚未被实施. Task 在 cluster 的工作节点 node0 ...

  9. CF1817E Half-sum 另解与 Trygub Number

    一题水两篇怎么说. 上一篇中我们采用智慧方法减少了比较次数,避免了使用复杂的高精度数.现在我们有高论!可以做到 \(\mathrm O(\log_B V\log_2 n)\) 在某一位加或者减一个大小 ...

  10. AI室内设计:提升效率、消除沟通障碍,满足客户需求

    前言 免费AI绘图工具:https://www.topgpt.one 随着人工智能(AI)技术的不断发展,室内设计行业也开始受益于这一技术的应用.其中,AI绘画工具在室内设计中的应用正日益受到关注.这 ...