本章来写一个插件,插件功能为通过NETLINK读取linux系统中的hotplug信息,比如usb、SD卡、磁盘等设备的插拔事件产生的信息,将读到的信息通过插件间通信的方式发出。
1. eventadmin库编译
CTK Plugin Framework下插件间通信是通过事件管理机制实现的,其代码位于CTK/Libs/PluginFramework/service/event目录下,使能事件管理机制,首先要在编译CTK的时候选择生成org.commontk.eventadmin库。如下图所示,打开cmake-gui,搜索plugin,然后把几个相关的库勾选上,重新编译CTK。
同样,参考上一篇博客,将编译生成的库文件,拷贝到Qt工程的"plugindepends/lib-平台名称"目录下,windows-x64-msvc平台下文件列表如下图所示。
2. eventadmin库环境配置
eventadmin插件属于ctk库自带的插件,可以通过如下方式启动。
ctkPluginFrameworkLauncher::addSearchPath(ctkPluginLibsPath, false); // 添加services插件目录
ctkPluginFrameworkLauncher::start("org.commontk.eventadmin"); // 启动插件框架,使能eventadmin service
需要先添加eventadmin插件库的所在目录,然后再启动。可以将liborg_commontk_eventadmin.dll文件拷贝到系统的库路径下,比如/usr/lib,然后在程序中修改路径,不过这样不利于代码做迁移。我们以在.pro文件中添加宏的形式来配置库路径,将库文件放在源码目录下。
首先,配置Plugindepends.pri文件,添加CTK_PLUGIN_LIBS宏,指向库路径,配置如下。
win32-msvc*{
# for windows visual studio 2015 x64 msvc compiler
CONFIG(debug, debug|release){
equals(QT_ARCH, x86_64): LIBS += -L$${PWD}/lib-windows-x64-msvc-debug -lCTKCore -lCTKPluginFramework
DEFINES += CTK_PLUGIN_LIBS=$${PWD}/lib-windows-x64-msvc-debug
}else{
equals(QT_ARCH, x86_64): LIBS += -L$${PWD}/lib-windows-x64-msvc-release -lCTKCore -lCTKPluginFramework
DEFINES += CTK_PLUGIN_LIBS=$${PWD}/lib-windows-x64-msvc-release
}
}
linux{
# for linux gcc x64 compiler
equals(QT_ARCH, x86_64){
LIBS += -L$$PWD/../plugindepends/lib-gcc-x64/ -lCTKCore -lCTKPluginFramework
DEFINES += CTK_PLUGIN_LIBS=$${PWD}/lib-gcc-x64
}
# for linux gcc arm64 compiler
equals(QT_ARCH, arm64){
LIBS += -L$$PWD/../plugindepends/lib-gcc-arm64/ -lCTKCore -lCTKPluginFramework
DEFINES += CTK_PLUGIN_LIBS=$${PWD}/lib-gcc-arm64
}
}
在源码中,可以通过宏转字符串的方式获取CTK_PLUGIN_LIBS指向的路径。
#define MACRO2STR(R) #R
#define STR_MACRO(R) MACRO2STR(R)
QString ctkPluginLibsPath = QString(STR_MACRO(CTK_PLUGIN_LIBS));
qDebug() << QString("add search path: %1").arg(ctkPluginLibsPath);
3. hotplug插件编写
通过拷贝的方式新建一个plugin-hotplug库,在plugin-hotplug库工程目录项,右键选择新建一个C++ class,取名为HotplugDetect,设置该类继承于QThread。在HotplugDetect类程序中,首先打开一个数据报socket,协议簇设置为AF_NETLINK,protocol为NETLINK_KOBJECT_UEVENT,代码如下。
const int buffersize = 2048;
int ret;
struct sockaddr_nl snl;
bzero(&snl, sizeof(struct sockaddr_nl));
snl.nl_family = AF_NETLINK;
snl.nl_pid = getpid();
snl.nl_groups = 1;
socket_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if (socket_fd == -1)
{
perror("socket");
}
setsockopt(socket_fd, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize));
ret = bind(socket_fd, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl));
if (ret < 0)
{
perror("bind");
close(socket_fd);
}else{
qDebug() << "raw sock bind success.";
}
然后在QThread线程中,循环读取socket数据,然后将读出的信息简单过滤后以插件事件通信的方式发出去。发送事件信息代码如下,首先通过插件contex获取ctkEventAdmin服务,然后通过该服务发送一个ctkEvent事件,该事件中指定名称跟字典cdiry,字典中可以插入自定义数据。
QString recvStr;
recvLen = recv(socket_fd, &buf, sizeof(buf), 0);
if(recvLen > 0){
recvStr.sprintf("%s", buf);
...
ctkServiceReference csref = pcontex->getServiceReference();
ctkEventAdmin *eventAdmin = pcontex->getService(csref);
ctkDictionary cdiry;
cdiry.insert("plug_info", QString("[%1]%2").arg(QTime::currentTime().toString("mm:ss.zzz")).arg(recvStr));
ctkEvent event("testsop/hotplug", cdiry);
eventAdmin->postEvent(event);
}
4. 事件接收
事件接收程序可以单独写一个插件,也可以写到应用程序中。首先需要新建一个类,继承于ctkEventHandler。这里定义一个类Subscriber,定义代码如下。
#ifndef SUBSCRIBER_H
#define SUBSCRIBER_H
#include
#include
#include
class Subscriber : public QObject, public ctkEventHandler
{
Q_OBJECT
Q_INTERFACES(ctkEventHandler)
public:
Subscriber(ctkPluginContext *context);
// 将事件处理程序注册为服务
void registerServece();
// 处理事件
void handleEvent(const ctkEvent& event) Q_DECL_OVERRIDE;
private:
ctkPluginContext* pcontext;
};
#endif // SUBSCRIBER_H
注册事件处理服务时,要指定的事件名称需要与事件发送者指定的一致。
// 将事件处理程序注册为服务
void Subscriber::registerServece()
{
ctkDictionary cdiry;
cdiry.insert(ctkEventConstants::EVENT_TOPIC, "testsop/hotplug");
pcontext->registerService(this, cdiry);
}
事件处理函数handleEvent也比较简单,直接获取对应的属性值即可。
// 处理事件
void Subscriber::handleEvent(const ctkEvent &event)
{
QString infostr = event.getProperty("plug_info").toString();
qDebug() << QString("handleEvent_info: %1").arg(infostr);
}
5. 运行示例
这里以linux-x86_64平台运行下示例,测试插件运行情况。