在QT中添加消息队列(向QT里添加点C代码),可实现与其他应用程序的通信,比如读取传感器数据在板子的屏幕上显示

在QT中添加消息队列,实现与其他应用程序的通信

概要

问题的来源:想用QT显示一下Im6xull上采集到的传感器信息,比如说板载的AP3216C,正点原子的QT应用手册上给出了一个显示板载AP3216C数据的例程,这个例程是将之前AP3216C的应用程序部分,和QT的相关业务逻辑都融合在一起。

整体架构流程

  • 上述例程存在的问题

这个例程可以实现在板载屏幕上显示AP3216C数据的功能,但我认为它比较冗长,耦合度较高,不方便移植;

  • 程序优化的思路
    首先在应用程序的功能架构上,传感器的数据(比如AP3216C)可以划分为一个输入任务;板载的屏幕可以划分为人机交互任务;

在一个测控系统中,输入任务可能有多个,人机交互任务可能有一个,也可能有若干的控制类任务(或者叫输出任务),为了降低系统中业务逻辑间的耦合性,可以将任务独立为一个进程,任务间使用进程间通信机制,进行通信,系统框图如下所示:
Alt

  • 进程间通信机制,这里选择使用消息队列,消息队列自带阻塞机制,阻塞是个好东西

前置准备

  • 配置好了AP3216C的驱动程序和应用程序(正点原子教程)
  • 配置好了imx6ull上触摸屏的各种设置,触摸屏可以正常使用(正点原子教程)
  • 配置好了imx6ull上QT的开发环境,可以交叉编译QT程序到板子上执行(正点原子教程)
  • 了解基本的QT的控件的使用
  • 基本了解消息队列的相关API。可以看这个贴子消息队列相关细节

技术细节

下面开始移植消息队列到QT,整体包括传感器输入端程序、 QT端;

  • 首先是输入任务的程序,工程结构如下图所示,主要注意comm.h和comm.c
    在这里插入图片描述
  • comm.h
#ifndef _COMM_H_
#define _COMM_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/msg.h>

struct msgbuf
{
    long mtype;
    //char mtext[1024];
    //这个数组还是大一点比较好,大一点没有问题 
    unsigned short mtext[30];
};

#define SERVER_TYPE 1
#define CLIENT_TYPE 2

int commMsgQueue(int flags,int prj_id);
int createMsgQueue(int prj_id);
int getMsgQueue(int prj_id);
int destoryMsgQueue(int msg_id);
int sendMsgQueue_short_arry(int msg_id, int who, short* msg, int arrysize);
int recvMsgQueue_short_arry(int msg_id, int recvType, unsigned short *databuf, int date_cnt);

#endif

  • comm.c
#include "comm.h"

int commMsgQueue(int flags, int prj_id)
{
    key_t key = ftok("/tmp", prj_id);
    if (key < 0)
    {
        perror("ftok");
        return -1;
    }

    int msg_id = msgget(key, flags);
    if (msg_id < 0)
    {
        perror("msgget");
    }
    return msg_id;
}

int createMsgQueue(int prj_id)
{
    // return commMsgQueue(IPC_CREAT|IPC_EXCL|0666, prj_id);
    return commMsgQueue(IPC_CREAT | 0666, prj_id);
}

int getMsgQueue(int prj_id)
{
    return commMsgQueue(IPC_CREAT, prj_id);
}

int destoryMsgQueue(int msg_id)
{
    if (msgctl(msg_id, IPC_RMID, NULL) < 0)
    {
        perror("msgctl");
        return -1;
    }
    return 0;
}
int sendMsgQueue_short_arry(int msg_id, int who, short *msg, int arrysize)
{
    struct msgbuf buf;
    buf.mtype = who;
    int i;
    for (i = 0; i < arrysize; i++)
        buf.mtext[i] = msg[i];

    if (msgsnd(msg_id, (void *)&buf, sizeof(buf.mtext), 0) < 0)
    {
        perror("msgsnd");
        return -1;
    }
    return 0;
}

int recvMsgQueue_short_arry(int msg_id, int recvType, unsigned short *databuf, int date_cnt)
{
    struct msgbuf buf;

    // buf.mtype = recvType;

    int i;

    if (msgrcv(msg_id, (void *)&buf, sizeof(buf.mtext), recvType, 0) < 0)
    {
        perror("msgrcv");
        return -1;
    }
    for (i = 0; i < date_cnt; i++)
    {
        databuf[i] = buf.mtext[i];
    }

    return 0;
}

  • sem_ap3216app.c
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include <poll.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
#include "comm.h"

#define date_cnt 1

int ir_id = 5;
int als_id = 6;

int main(int argc, char *argv[])
{
	int fd;
	char *filename;
	// unsigned short databuf[date_cnt];
	unsigned short databuf[3];
	unsigned short ir, als, ps;

	unsigned short ir_databuf[1];
	unsigned short als_databuf[1];

	int ret = 0;

	// 创建消息队列

	int ir_msgid = createMsgQueue(ir_id);
	int als_msgid = createMsgQueue(als_id);

	pid_t pid = getpid();
	printf("pid = %d\r\n", pid);

	printf("ir_msgid = %d\r\n", ir_msgid);
	printf("als_msgid = %d\r\n", als_msgid);
	printf("我吐了啊\r\n");

	if (argc != 2)
	{
		printf("Error Usage!\r\n");
		return -1;
	}

	// 手动执行进程
	filename = argv[1];
	fd = open(filename, O_RDWR);
	if (fd < 0)
	{
		printf("can't open file %s\r\n", filename);
		return -1;
	}

	while (1)
	{
		ret = read(fd, databuf, sizeof(databuf));
		if (ret == 0)
		{

			// 看一下这样行不行,这样可以
			ir_databuf[0] = databuf[0];
			als_databuf[0] = databuf[1];

			if (sendMsgQueue_short_arry(ir_msgid, SERVER_TYPE, databuf, date_cnt) < 0)
			{
				printf("sendMsgQueue_short_arry failed\r\n");
				return -1;
			}

			usleep(20);

			if (sendMsgQueue_short_arry(als_msgid, SERVER_TYPE, als_databuf, date_cnt) < 0)
			{
				printf("sendMsgQueue_short_arry failed\r\n");
				return -1;
			}
		}
		usleep(200000); /*200ms向队列发一次 */
	}

	close(fd);
	return 0;
}

  • 移植完毕后的QT工程的文件目录如下图所示,
    在这里插入图片描述
  • QT这里的基本思路是,我在comm.h里声明了一个定时器槽函数timer_timeout();以及一个自定义的信号ap3216cDataChanged();
    在comm.cpp中,设置定时器200ms触发一次定时器槽函数;在定时器槽函数中,发射ap3216cDataChanged()信号去读取消息队列的数据。

comm.h

#ifndef COMM_H
#define COMM_H
#include <QObject>
#include <QTimer>

#define SERVER_TYPE 1
#define CLIENT_TYPE 2


class comm : public QObject
{
    Q_OBJECT
public:
    explicit comm(QObject *parent = nullptr);

    Q_INVOKABLE void setcapture(bool str);

    int commMsgQueue(int flags,int prj_id);
    int createMsgQueue(int prj_id);
    int getMsgQueue(int prj_id);
    int destoryMsgQueue(int msg_id);
    int sendMsgQueue_short_arry(int msg_id, int who, unsigned short* msg, int arrysize);
    int recvMsgQueue_short_arry(int msg_id, int recvType, unsigned short *databuf, int date_cnt);

public slots:
    void timer_timeout();
signals:
    void ap3216cDataChanged();
};

#endif // COMM_H

  • comm.cpp
#include "comm.h"
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <QDebug>
#include "unistd.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include <poll.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/msg.h>


//#define _GNU_SOURCE
struct msgbuf
{
    long mtype;
    //char mtext[1024];
    //这个数组要偏大一点
    unsigned short mtext[30];
};

//定义一个计时器,200ms发射一个ap3216cDataChanged()信号,
//这样就可以200ms读一次消息队列
QTimer* timer = new QTimer;

comm::comm(QObject *parent) : QObject(parent)
{
    //QTimer* timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(timer_timeout()));
}

void comm::timer_timeout(){
    //这里暂时不做处理
    emit ap3216cDataChanged();
}

void comm::setcapture(bool str){
    if(str){
        timer -> start(200);
    }else{
        timer -> stop();
    }
}

//开始定义相关的消息队列函数
int comm::commMsgQueue(int flags, int prj_id)
{
    key_t key = ftok("/tmp", prj_id);
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }

    int msg_id = msgget(key, flags);
    if(msg_id < 0)
    {
        perror("msgget");
    }
    return msg_id;
}

int comm::createMsgQueue(int prj_id)
{
    // return commMsgQueue(IPC_CREAT|IPC_EXCL|0666, prj_id);
    return commMsgQueue(IPC_CREAT|0666, prj_id);
}

int comm::getMsgQueue(int prj_id)
{
    return commMsgQueue(IPC_CREAT, prj_id);
}

int comm::destoryMsgQueue(int msg_id)
{
    if(msgctl(msg_id, IPC_RMID, NULL) < 0)
    {
        perror("msgctl");
        return -1;
    }
    return 0;
}
int comm::sendMsgQueue_short_arry(int msg_id, int who, unsigned short *msg, int arrysize){
    struct msgbuf buf;
    buf.mtype = who;
    int i;
    for(i = 0; i < arrysize; i++) buf.mtext[i] = msg[i];

    if(msgsnd(msg_id, (void*)&buf, sizeof(buf.mtext), 0) < 0){
        perror("msgsnd");
        return -1;
    }
    return 0;
}

int comm::recvMsgQueue_short_arry(int msg_id, int recvType, unsigned short *databuf, int date_cnt){
    struct msgbuf buf;

    //buf.mtype = recvType;

    int i;

    if(msgrcv(msg_id, (void*)&buf, sizeof(buf.mtext), recvType, 0) < 0){
        perror("msgrcv");
        return -1;
    }
    for(i = 0; i < date_cnt; i++){
        databuf[i] = buf.mtext[i];
    }

    return 0;
}

  • mainwidow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QObject>
#include <QThread>
#include <QTimer>
#include <QDebug>
#include<iostream>

using namespace std;

//QT作为消息队列的接受者
#define  __arm__  1
bool str = 1;

//接收消息的队列
#define date_cnt 1
int pro_id = 5;
int als_id = 6;

//定义消息队列
unsigned short ir_databuf[date_cnt];
unsigned short als_databuf[date_cnt];

unsigned short ir, als, ps;
int ret = 0;

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

ap3216c = new comm(this);
#if __arm__//开启定时器,200毫秒接收一次
    ap3216c->setcapture(str);
#endif
    connect(ap3216c, SIGNAL(ap3216cDataChanged()),this, SLOT(getAp3216cData()));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::getAp3216cData(){

    int ir_msgid = ap3216c->getMsgQueue(pro_id);
    int als_msgid = ap3216c->getMsgQueue(als_id);

    cout<<"读取了一次队列"<<endl;
    if(ap3216c->recvMsgQueue_short_arry(ir_msgid, SERVER_TYPE, ir_databuf, date_cnt) < 0 ){

        cout<<"ir recvMsgQueue_short_arry is fail"<<endl;
        //return -1;
    }
    //usleep(20);
    QThread::msleep(20);

    if(ap3216c->recvMsgQueue_short_arry(als_msgid, SERVER_TYPE, als_databuf, date_cnt) < 0 ){

        cout<<"als recvMsgQueue_short_arry is fail"<<endl;
        //return -1;
    }

    cout<<"ir is :"<<ir_databuf[0]<<endl;
    cout<<"als is :"<<als_databuf[0]<<endl;

    QString ir_str = QString::number(ir_databuf[0]);
    QString als_str = QString::number(als_databuf[0]);

    //想办法,把得到的值打印出来
    ui->ir_databuf_text->setText(ir_str);
    ui->als_databuf_text->setText(als_str);
}
  • QT 的UI文件的设计(也很简单)
    用了两个 Qlabe 和Qlinedit
    在这里插入图片描述
    在这里插入图片描述

  • 然后是交叉编译AP3216C和QT的应用程序,这里注意要先运行 QT(也就是消息队列接收程序),再运行AP3216C的,不然读到的数据具有滞后性,具体原因,在于消息队列的内部机制,这里可不做赘述

  • 实验效果如下:
    先运行AP3216C的应用程序,在终端上打印出了新建的队列号
    在这里插入图片描述
    开发板端的现象
    在这里插入图片描述

小结

ps:如果发现qt里面报struct msgbuf结构体的重定义错误,去修改,features.h文件,注释一个宏定义

  • 31
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt 是一个跨平台的 C++ 应用程序开发框架,而 ZeroMQ(简称 ZMQ)是一个高性能、异步的消息传递库。在 Qt 使用 ZMQ 实现消息队列需要进行以下几个步骤: 1. 首先,下载并安装 ZMQ 库。你可以从 ZeroMQ 的官方网站(https://zeromq.org/)下载适合你的操作系统的库文件,并按照安装指南进行安装。 2. 在 Qt 项目添加 ZMQ 的头文件路径和库文件路径。在项目的 .pro 文件添加以下内容: ```pro INCLUDEPATH += /path/to/zmq/include LIBS += -L/path/to/zmq/lib -lzmq ``` 将上述路径替换为你实际安装 ZMQ 库的路径。 3. 在 Qt 代码引入 ZMQ 相关的头文件: ```cpp #include <zmq.hpp> ``` 4. 创建一个 ZMQ 的上下文(context)对象和一个 socket 对象: ```cpp zmq::context_t context(1); zmq::socket_t socket(context, ZMQ_PUB); // 这以发布者(publisher)为例,如果是订阅者(subscriber)则使用 ZMQ_SUB ``` 5. 配置 socket 的连接参数(可选): ```cpp socket.bind("tcp://localhost:5555"); // 绑定到本地地址和端口 ``` 6. 发送消息到消息队列: ```cpp std::string message = "Hello, ZMQ!"; zmq::message_t zmqMessage(message.size()); memcpy(zmqMessage.data(), message.data(), message.size()); socket.send(zmqMessage); ``` 可以根据具体需求自定义消息的格式和内容。 7. 关闭 socket 和 context: ```cpp socket.close(); context.close(); ``` 这样,你就可以在 Qt 使用 ZMQ 实现消息队列的功能了。当然,这只是一个简单的示例,实际应用可能需要更复杂的逻辑来处理接收和处理消息等操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值