示波器接口调用案例¶
一、新建Qt项目并编写.pro文件载入必要库¶
1. 在Qt(5.12版本)菜单“文件”→“新建项目”→ “选择Qt Widgets Application”,启动创建项目向导。

-
设置项目名称以及一些项目配置。这里注意在build system中选择qmake版本,Kit Selection中选中64位,其它配置使用默认就好。



-
找到厂家提供的SDK库,路径如下(64位库为例)。

函数库具体路径如下


-
在Qt的项目文件(.pro)中添加头文件路径以及库路径,相对或绝对路径都可以。

-
声明用到的头文件
二、查看函数手册,熟悉相关函数接口¶

1.连接控制器接口:
| 函数名 | 功能 |
| connect | 连接控制器 |
2.电子齿轮比相关接口:
| 函数名 | 功能 |
| startCollect | 启动示波器采集功能 |
| stopCollect | 停止示波器采集功能 |
| setScopeCallback | 回调传回示波器采集到的数据 |
三、基础示波器演示案例¶
1. 先在Qt的UI设计界面中,放置好之后会用到的组件,包含三个Push Button组件和一个Widget组件


控件说明:
| 控件 | 对象名 | 说明 |
| QPushButton | btnConnect | 连接 |
| QPushButton | btnStart | 开始采集 |
| QPushButton | btnStop | 停止采集 |
| Widget | oscWidget | 显示示波曲线 |
-
创建相关头文件及源代码文件

其中oscilloscopewidget为示波器窗口类,qcustomplot详细介绍见下文
-
提升widget为oscilloscopewidget类
①在UI设计界面选中widget,右键点击“提升为”,在弹出的窗口中填写类名:OscilloscopeWidget,头文件:oscilloscopewidget.h,后点击添加(Add) → 提升(Promote)

-
使用Qt C++的一个绘图小部件QCustomPlot实现波形显示的组件
从官网qcustomplot.com中下载源码解压后加入项目目录下,包括如下文件。这是一个用于2D绘图和数据可视化的部件,这里仅作示例用,主要关注示波器函数相关调用即可,如果在有其它控件可以实现类似效果或非Qt端项目想调用示波器功能,可以此做参考。

同时还需要注意,由于QCustomPlot 在内部使用了QPrinter,所以在.pro文件中,需要添加QtPrintSupport模块

-
实现效果

-
具体代码实现
mainwindow.h:
mainwindow.cpp:#pragma once #include <QMainWindow> #include <CNCApi/CncDef.h> #include <CNCApi/CommApi.h> #include <CNCApi/ProxySys.h> #include <CNCApi/ProxyEntry.h> #include <CNCApi/ProxyMotion.h> #include <CNCApi/ProxyStatus.h> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void onStartClicked(); void onStopClicked(); void on_btnConnect_clicked(); private: Ui::MainWindow *ui; /* UI对象指针 */ weconcnc::CCommApi *commApi; /* 通信API对象指针,用于与CNC设备交互 */ };oscilloscopewidget.h:#include "mainwindow.h" #include "ui_mainwindow.h" #include "oscilloscopewidget.h" #include <QDebug> /* 构造函数:初始化主窗口,设置UI、通信API和信号槽连接 */ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) , commApi(new weconcnc::CCommApi()) { ui->setupUi(this); /* 连接按钮和滑块的信号与槽函数 */ connect(ui->btnStart, &QPushButton::clicked, this, &MainWindow::onStartClicked); connect(ui->btnStop, &QPushButton::clicked, this, &MainWindow::onStopClicked); WSADATA wsaData; int iResult; /* 初始化 Winsock */ iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); if (iResult != 0) { printf("WSAStartup failed with error: %d\n", iResult); } } MainWindow::~MainWindow() { delete ui; } /* 槽函数:启动示波器数据收集 */ void MainWindow::onStartClicked() { ui->oscWidget->startCollect(); } /* 槽函数:停止示波器数据收集 */ void MainWindow::onStopClicked() { ui->oscWidget->stopCollect(); } /* 槽函数:触发示波器连接操作 */ void MainWindow::on_btnConnect_clicked() { ui->oscWidget->btnConnect(); }oscilloscopewidget.cpp:#pragma once #include <QWidget> #include <QTimer> #include <QVector> #include "qcustomplot.h" #include <CNCApi/CncDef.h> #include <CNCApi/CommApi.h> #include <CNCApi/ProxySys.h> #include <CNCApi/ProxyEntry.h> #include <CNCApi/ProxyMotion.h> #include <CNCApi/ProxyStatus.h> #include <CNCApi/ProxyScope.h> class OscilloscopeWidget : public QWidget { Q_OBJECT public: explicit OscilloscopeWidget(QWidget *parent = nullptr); void startCollect(); void stopCollect(); void btnConnect(); private: QCustomPlot *plot; QTimer updateTimer; QVector<QVector<QPointF>> channelData; qint64 baseTimestamp = -1; int channelCount = 2; double timeScale = 5.0; weconcnc::CCommApi *commApi; static int onScopeData(const char *pData, int32_t nLen, void *param); void parseData(const char *pData, int len, int channelCount); };#include "oscilloscopewidget.h" #include <QVBoxLayout> #include <QDebug> using namespace weconcnc; /* 构造函数:初始化示波器控件,设置图表、布局和定时器 */ OscilloscopeWidget::OscilloscopeWidget(QWidget *parent) : QWidget(parent), plot(new QCustomPlot(this)) , commApi(new weconcnc::CCommApi()) { auto layout = new QVBoxLayout(this); layout->setMargin(0); layout->addWidget(plot); /* 将图表添加到布局 */ /* 初始化通道数据存储,这里采用2个通道仅作示例用 */ channelData.resize(channelCount); for (int i = 0; i < channelCount; ++i) { plot->addGraph(); /* 为每个通道添加图表 */ if (i == 1) { plot->yAxis2->setVisible(true); /* 为第二个通道启用右侧Y轴 */ plot->graph(i)->setValueAxis(plot->yAxis2); } plot->graph(i)->setName(QString("Ch%1").arg(i + 1)); /* 设置通道名称 */ } /* 配置图表显示属性 */ plot->legend->setVisible(true); plot->xAxis->setLabel("Time (ms)"); plot->yAxis->setLabel("Value"); plot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom); /* 定期更新图表数据 */ connect(&updateTimer, &QTimer::timeout, [this]() { for (int i = 0; i < plot->graphCount(); ++i) { QVector<double> x, y; for (const auto &p : channelData[i]) { x.append(p.x()); /* 时间数据 */ y.append(p.y()); /* 值数据 */ } plot->graph(i)->setData(x, y); } plot->xAxis->setRange(0, timeScale * 1000); plot->rescaleAxes(true); plot->replot(); /* 设置通道线条样式 */ QPen pen1(Qt::red); QPen pen2(Qt::green); pen1.setWidth(2); pen2.setWidth(2); plot->graph(0)->setPen(pen1); plot->graph(1)->setPen(pen2); }); updateTimer.start(100); } /* 连接函数 */ void OscilloscopeWidget::btnConnect(){ std::string sIp = "192.168.54.98"; uint16_t port = 19996; WECONCNC_ERROR_E ret = commApi->connect(sIp, port); if (WECONCNC_ERROR_SUCCESS != ret) { std::cout << "connect failed!" << std::endl; } else { std::cout << "connect success!" << std::endl; } } /* 开始数据收集,设置回调并启动收集 */ void OscilloscopeWidget::startCollect() { CProxyScope *scope = CProxyScope::getInstance(commApi); /* 获取示波器实例 */ std::vector<std::string> channels = {"axis.0.motor-pos-cmd", "ini.0.max_limit"}; /* 定义收集的通道 */ scope->setScopeCallback(onScopeData, this); /* 设置数据回调函数 */ scope->startCollect(10, channels); // 10ms间隔 } /* 停止数据收集 */ void OscilloscopeWidget::stopCollect() { CProxyScope *scope = CProxyScope::getInstance(commApi); scope->stopCollect(); updateTimer.stop(); } /* 回调函数,处理从CNC设备接收的原始数据 */ int OscilloscopeWidget::onScopeData(const char *pData, int32_t nLen, void *param) { auto *self = reinterpret_cast<OscilloscopeWidget*>(param); self->parseData(pData, nLen, self->channelCount); return 0; } /* 解析原始数据,提取时间戳和通道值 */ void OscilloscopeWidget::parseData(const char *pData, int len, int channelCount) { int pos = 0; while (pos + 8 <= len) { qint64 timestamp; memcpy(×tamp, pData + pos, 8); /* 读取8字节时间戳 */ pos += 8; if (baseTimestamp < 0) baseTimestamp = timestamp; /* 设置基准时间戳 */ double elapsed = (timestamp - baseTimestamp) / 1000.0; for (int i = 0; i < channelCount; ++i) { if (pos + 8 > len) break; double value; memcpy(&value, pData + pos, 8); /* 读取8字节通道值 */ pos += 8; channelData[i].append(QPointF(elapsed, value)); /* 添加数据点到通道 */ if (channelData[i].size() > 2000) channelData[i].removeFirst(); } } }