Qt 串口编程-从入门到实战
1. Qt 串口通信流程解析
1.1 串行通信和并行通信对比
- 并行通信适合距离较短的通信,且信号容易受干扰,成本高
- 串口通讯-设备(蓝牙, wifi, gprs, gps)
1.2 Qt 串口通信具体流程
- 1. 创建 QSerialPort 对象
- 2. 配置属性(波特率, 数据位, 停止, 校验位)
- 3. 打开设备
- 4. 发送数据到串口 write
- 5. 在槽函数中读取数据(当串口有数据可读的时候会发送 readyRead 信号)
1.2.1 serialapp.pro
QT += core gui serialport
1.2.2 serialapp.h
#ifndef SERIALAPP_H
#define SERIALAPP_H#include <QWidget>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QIODevice>
#include <QByteArray>
#include <QString>
#include <QDebug>QT_BEGIN_NAMESPACE
namespace Ui { class SerialApp; }
QT_END_NAMESPACEclass SerialApp : public QWidget {Q_OBJECTpublic:SerialApp(QWidget *parent = nullptr);~SerialApp();private slots:void on_openBt_clicked();void on_sendBt_clicked();void read_data();private:Ui::SerialApp *ui;// 1、创建 QSerialPort 对象QSerialPort mSerial;
};
#endif // SERIALAPP_H
1.2.3 serialapp.cpp
#include "serialapp.h"
#include "ui_serialapp.h"SerialApp::SerialApp(QWidget *parent) : QWidget(parent), ui(new Ui::SerialApp) {ui->setupUi(this);// 2、配置设备,波特率,数据位,停止位,校验位mSerial.setPortName("COM1"); // 选择对应的端口号mSerial.setBaudRate(QSerialPort::Baud115200);mSerial.setDataBits(QSerialPort::Data8);mSerial.setStopBits(QSerialPort::OneStop);mSerial.setParity(QSerialPort::NoParity);connect(&mSerial, &QSerialPort::readyRead, this, &SerialApp::read_data);
}SerialApp::~SerialApp() {delete ui;
}// 3、打开设备 (读写)
void SerialApp::on_openBt_clicked() {if (mSerial.open(QIODevice::ReadWrite)) {qDebug() << "open success!";} else {qDebug() << "open failed!";}
}// 4、发送数据到串口 write
void SerialApp::on_sendBt_clicked() {QString data = ui->textEdit->toPlainText();mSerial.write(data.toUtf8());
}// 5、读取串口数据 read
void SerialApp::read_data() {QByteArray array = mSerial.readAll();ui->textBrowser->append(QString(array));
}
1.2.4 serialapp.ui
2. Qt 虚拟串口调试
2.1 VSPD 创建虚拟串口
-
VSPD (Virtual Serial Port Driver) 是一个虚拟串口驱动程序
- 它可以模拟多个串口设备,使得应用程序可以通过虚拟串口与物理串口设备进行通信
- 使用 VSPD 可以方便地进行串口调试、数据采集、数据转发等操作
- VSPD 还支持多种协议,例如模拟 GPS 设备、模拟调制解调器、与虚拟机通信等
-
VSPD虚拟串口软件安装及使用
2.2 SecureCRT 连接虚拟串口
- SecureCRT 是一款安全的终端模拟器,常用于远程访问服务器和网络设备
- 它可以让用户通过 SSH、Telnet、Rlogin 或者串口等协议连接到远程设备,并在本地进行命令行操作
- SecureCRT 还提供了多重会话管理、脚本编写、自动登录、加密通信等多种功能
- SecureCRT安装教程
2.3 Qt 虚拟串口实现
-
serialapp.h
#ifndef SERIALAPP_H #define SERIALAPP_H#include <QWidget> #include <QSerialPort> #include <QSerialPortInfo> #include <QIODevice> #include <QByteArray> #include <QString> #include <QDebug> #include <QList>QT_BEGIN_NAMESPACE namespace Ui { class SerialApp; } QT_END_NAMESPACEclass SerialApp : public QWidget {Q_OBJECTpublic:SerialApp(QWidget *parent = nullptr);~SerialApp();private slots:void on_openBt_clicked();void on_sendBt_clicked();void read_data();private:Ui::SerialApp *ui;// 创建 QSerialPort 对象QSerialPort mSerial; }; #endif // SERIALAPP_H
-
serialapp.cpp
#include "serialapp.h" #include "ui_serialapp.h"SerialApp::SerialApp(QWidget *parent) : QWidget(parent), ui(new Ui::SerialApp) {ui->setupUi(this);// 获取当前设备上的所有串口QList<QSerialPortInfo> list = QSerialPortInfo::availablePorts();for (int i = 0; i < list.size(); i++) {ui->comboBox->addItem(list.at(i).portName());}// 配置设备,波特率,数据位,停止位,校验位//mSerial.setPortName("COM1");mSerial.setBaudRate(QSerialPort::Baud115200);mSerial.setDataBits(QSerialPort::Data8);mSerial.setStopBits(QSerialPort::OneStop);mSerial.setParity(QSerialPort::NoParity);connect(&mSerial, &QSerialPort::readyRead, this, &SerialApp::read_data); }SerialApp::~SerialApp() {delete ui; }// 打开设备 (读写) void SerialApp::on_openBt_clicked() {if (mSerial.isOpen()) {mSerial.close();}mSerial.setPortName(ui->comboBox->currentText()); // 设置端口if (mSerial.open(QIODevice::ReadWrite)) {qDebug() << "open success!";} else {qDebug() << "open failed!";} }// 发送数据到串口 write void SerialApp::on_sendBt_clicked() {QString data = ui->textEdit->toPlainText();mSerial.write(data.toUtf8()); }// 读取串口数据 read void SerialApp::read_data() {QByteArray array = mSerial.readAll();ui->textBrowser->append(QString(array)); }
-
serialapp.ui
2.4 Qt 与 SecureCRT 建立虚拟串口连接
3. Qt 编写串口调试工具
3.1 serialportapp.h
#ifndef SERIALPORTAPP_H
#define SERIALPORTAPP_H#include <QWidget>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QList>
#include <QStringList>
#include <QString>
#include <QIODevice>
#include <QDebug>
#include <QByteArray>
#include <QMessageBox>
#include <QTimerEvent>
#include <QFileDialog>
#include <QFile>QT_BEGIN_NAMESPACE
namespace Ui { class SerialPortApp; }
QT_END_NAMESPACEclass SerialPortApp : public QWidget {Q_OBJECTpublic:SerialPortApp(QWidget *parent = nullptr);~SerialPortApp();void timerEvent(QTimerEvent *event);private slots:void on_openBt_clicked();void on_closeBt_clicked();void on_sendBt_clicked();void on_autoCheckBox_clicked(bool checked);void on_clearSendSizeBt_clicked();void on_sendHexCb_clicked(bool checked);void on_recvHexCb_clicked(bool checked);void read_data();void on_clearRecvSizeBt_clicked();void on_selectfileBt_clicked();void on_sendfileBt_clicked();void send_file_text(quint64 size);private:Ui::SerialPortApp *ui;QSerialPort mSerial;int timerid;qint32 sendsize;qint32 recvsize;QFile file; // 发送文件qint32 sendfilesize;
};
#endif // SERIALPORTAPP_H
3.2 serialportapp.cpp
#include "serialportapp.h"
#include "ui_serialportapp.h"SerialPortApp::SerialPortApp(QWidget *parent) : QWidget(parent), ui(new Ui::SerialPortApp) {ui->setupUi(this);// 遍历获取当前设备上的所有串口QList<QSerialPortInfo> infos = QSerialPortInfo::availablePorts();for (int i = 0; i < infos.size(); i++) {ui->comCb->addItem(infos.at(i).portName());}// 设置波特率显示QStringList list;list << "1200" << "2400" << "4800" << "9600" << "19200" << "38400" << "57600" << "115200";ui->rateCb->addItems(list);ui->rateCb->setCurrentIndex(7); // 设置默认波特率为 115200list.clear();// 设置数据位list << "5" << "6" << "7" << "8" << "-1";ui->dataCb->addItems(list);ui->dataCb->setCurrentIndex(3); // 设置默认数据位为 8list.clear();// 设置停止位list << "1" << "3" << "2" << "-1";ui->stopCb->addItems(list);list.clear();// 设置校验位list << "None" << "NULL" << "Even" << "Odd" << "Space" << "Mark";ui->priCb->addItems(list);list.clear();// 把关闭按钮设置失效ui->closeBt->setEnabled(false);// 当串口有数据可读时会发送 readyRead 信号connect(&mSerial, &QSerialPort::readyRead, this, &SerialPortApp::read_data);// 初始化发送、接收的字节数记录sendsize = recvsize = 0;
}SerialPortApp::~SerialPortApp() {delete ui;
}// 打开
void SerialPortApp::on_openBt_clicked() {// 配置端口,波特率,数据位,停止位,校验位mSerial.setPortName(ui->comCb->currentText());mSerial.setBaudRate(ui->rateCb->currentText().toInt());mSerial.setDataBits((QSerialPort::DataBits)ui->dataCb->currentText().toInt());mSerial.setStopBits((QSerialPort::StopBits)ui->stopCb->currentText().toInt());mSerial.setParity((QSerialPort::Parity)ui->priCb->currentText().toInt());// 打开设备if (mSerial.open(QIODevice::ReadWrite)) {ui->closeBt->setEnabled(true);ui->openBt->setEnabled(false);}
}// 关闭
void SerialPortApp::on_closeBt_clicked() {// 关闭设备mSerial.close();ui->closeBt->setEnabled(false);ui->openBt->setEnabled(true);
}// 手动发送数据
void SerialPortApp::on_sendBt_clicked() {QString data = ui->sendText->toPlainText();if (ui->sendHexCb->isChecked()) {// 转十六进制:data = 4142 --> 0x41 0x42QByteArray array;if (data.size() % 2 != 0) {data.insert(0, '0');}for (int i = 0; i < data.size() / 2; i++) {QString t = data.mid(2*i, 2);bool ok = false;int ihex = t.toInt(&ok, 16);array.append(ihex);}int size = mSerial.write(array); // 发送数据sendsize += size; // 累计发送的字节数} else {int size = mSerial.write(data.toUtf8()); // 发送数据sendsize += size;}// 设置显示已发送的字节数ui->sendsizelabel->setText(QString::number(sendsize));
}// 定时自动发送数据
void SerialPortApp::on_autoCheckBox_clicked(bool checked) {if (checked) {// 获取定时发送周期int ms = ui->autotimeEdit->text().toInt();if (ms < 100) {QMessageBox::warning(this, "time hint", "time should > 100ms");ui->autoCheckBox->setChecked(false);return;}// 启动定时器事件timerid = this->startTimer(ms);} else {// 关闭定时器事件this->killTimer(timerid);}
}// 定时器事件
void SerialPortApp::timerEvent(QTimerEvent *event) {on_sendBt_clicked();
}// 清空已发送的字节数
void SerialPortApp::on_clearSendSizeBt_clicked() {sendsize = 0;ui->sendText->clear();ui->sendsizelabel->setText("0");
}// 发送端:十六进制和十进制转换
void SerialPortApp::on_sendHexCb_clicked(bool checked) {if (checked) { // 十进制 --> 十六进制QString data = ui->sendText->toPlainText();QByteArray array = data.toUtf8().toHex();ui->sendText->setText(QString(array));} else { // 十六进制 --> 十进制QString data = ui->sendText->toPlainText();QByteArray array;if (data.size() % 2 != 0) {data.insert(0, '0');}for (int i = 0; i < data.size() / 2; i++) {QString t = data.mid(2*i, 2);bool ok = false;int ihex = t.toInt(&ok, 16);array.append(ihex);}ui->sendText->setText(QString(array));}
}// 接收端:十六进制和十进制转换
void SerialPortApp::on_recvHexCb_clicked(bool checked) {if (checked) { // 十进制 --> 十六进制QString data = ui->recvText->toPlainText();QByteArray array = data.toUtf8().toHex();ui->recvText->setText(QString(array));} else { // 十六进制 --> 十进制QString data = ui->recvText->toPlainText();QByteArray array;if (data.size() % 2 != 0) {data.insert(0, '0');}for (int i = 0; i < data.size() / 2; i++) {QString t = data.mid(2*i, 2);bool ok = false;int ihex = t.toInt(&ok, 16);array.append(ihex);}ui->recvText->setText(QString(array));}
}// 接收串口数据
void SerialPortApp::read_data() {// 读到的数据是一个个字节QByteArray array = mSerial.readAll();recvsize += array.size(); // 显示已接收到的字节数if (ui->recvHexCb->isChecked()) {ui->recvText->append(array.toHex());} else {ui->recvText->append(array);}// 设置显示已接收到的字节数ui->recvsizelabel->setText(QString::number(recvsize));
}// 清空已接收的字节数
void SerialPortApp::on_clearRecvSizeBt_clicked() {recvsize = 0;ui->recvText->clear();ui->recvsizelabel->setText("0");
}// 选择要发送的文件
void SerialPortApp::on_selectfileBt_clicked() {QString path = QFileDialog::getOpenFileName(this);ui->filepathEdit->setText(path);
}// 发送文件
void SerialPortApp::on_sendfileBt_clicked() {// 当数据发送完毕后会发出一个信号 &QSerialPort::bytesWritten// 每当有效载荷的数据写入到设备当前的写入通道时,就会发出这个信号connect(&mSerial, &QSerialPort::bytesWritten, this, &SerialPortApp::send_file_text);// 打开文件file.setFileName(ui->filepathEdit->text());if (!file.open(QIODevice::ReadOnly)) {return;}// 获取文件大小int filesize = file.size();ui->progressBar->setMaximum(filesize);// 设置进度条显示QByteArray array = file.read(128); // 每次读取 128 字节内容sendfilesize = mSerial.write(array);ui->progressBar->setValue(sendfilesize);
}// 循环(每 128 字节)发送文件
void SerialPortApp::send_file_text(quint64 size) {// 设置进度条显示QByteArray array = file.read(128);quint64 mSize = mSerial.write(array);sendfilesize += mSize;ui->progressBar->setValue(sendfilesize);// 判断文件是否发送完毕if (sendfilesize == ui->progressBar->maximum()) {file.close();disconnect(&mSerial, &QSerialPort::bytesWritten, this, &SerialPortApp::send_file_text);}
}
3.3 serialportapp.ui
相关文章:

Qt 串口编程-从入门到实战
1. Qt 串口通信流程解析 1.1 串行通信和并行通信对比 并行通信适合距离较短的通信,且信号容易受干扰,成本高串口通讯-设备(蓝牙, wifi, gprs, gps) 1.2 Qt 串口通信具体流程 1. 创建 QSerial…...

如何获得微软MVP徽章
要成为微软MVP,需要在特定领域成为专家,并积极参与社区,为其他人提供帮助和支持。以下是一些步骤可以帮助你成为MVP: 在特定领域成为专家:要成为MVP,需要在某个领域具有专业知识和经验。这可以通过阅读相关…...

Java架构师软件架构开发
目录 1 基于架构的软件开发导论2 ABSD架构方法论3 ABSD方法论具体实现4 ABSD金融业案例5 基于特定领域的软件架构开发导论6 DSSA领域分析7 DSSA领域设计和实现8 DSSA国际电商平台架构案例9 架构思维方法论概述10 AT方法论和案例想学习架构师构建流程请跳转:Java架构师系统架构…...

西南科技大学数字电子技术实验一(数字信号基本参数与逻辑门电路功能测试及FPGA 实现 )预习报告
手写报告稍微认真点写,80+随便有 目录 一、计算/设计过程 1、通过虚拟示波器观察和测量信号 2、通过实际电路(电阻、开关、发光二极管)模拟逻辑门电路 二、画出并填写实验指导书上的预表...
Java八股文面试全套真题【含答案】- SpringMVC篇
以下是一些关于Spring MVC语言的经典面试题以及它们的答案: 什么是Spring MVC框架?它的特点是什么? Spring MVC是基于Java的一种Web应用框架,用于开发基于MVC(模型-视图-控制器)模式的Web应用程序。它的特…...

Spring第二课响应的完全,如何理解前后端互联
目录 一、响应 Control,RestController 1.Controller的源码,代表什么意思 2.返回数据 Responsebody 3.返回HTML片段 4.返回JSON 5.那么假如我们使用集合会怎么样呢 设置状态码,虽然不影响展示,但是确实显示起来也就是401的情况。 2.我…...

html实现各种瀑布流(附源码)
文章目录 1.设计来源1.1 动态响应瀑布流1.2 分页瀑布流1.3 响应瀑布流 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者:xcLeigh 文章地址:https://blog.csdn.net/weixin_43151418/article/details/134613121 html实现各种瀑布流(附源码),…...

万字解析设计模式之责任链模式、状态模式
目录 一、责任链模式 1.1概述 1.2结构 1.3实现 1.4 优缺点 1.5应用场景 1.6源码解析 二、状态模式 2.1概述 2.2结构 2.3实现 2.4优缺点 2.5应用场景 三、责任链模式实验 任务描述 实现方式 编程要求 测试说明 四、状态模式实验 任务描述 实现方式 编程要…...
二十三种设计模式全面解析-深入探讨状态模式的高级应用技术:释放对象行为的无限可能
在软件开发中,状态管理是一个常见的挑战。当对象的行为随着内部状态的变化而变化时,有效地管理对象的状态和相应的行为变得至关重要。在这方面,状态模式提供了一种优雅而灵活的解决方案。它允许对象在运行时根据内部状态的改变而改变其行为&a…...

论文笔记--Toolformer: Language Models Can Teach Themselves to Use Tools
论文笔记--Toolformer: Language Models Can Teach Themselves to Use Tools 1. 文章简介2. 文章概括3 文章重点技术3.1 Toolformer3.2 APIs 4. 文章亮点5. 原文传送门 1. 文章简介 标题:Toolformer: Language Models Can Teach Themselves to Use Tools作者&#…...

stm32实现0.96oled图片显示,菜单功能
stm32实现0.96oled图片显示,菜单功能 功能展示简介代码介绍oled.coled.holedfont.h(字库文件)main函数 代码思路讲解 本期内容,我们将学习0.96寸oled的进阶使用,展示图片,实现菜单切换等功能,关…...
sqlite外键约束 保证数据一致性
1. 外键约束 在SQLite中,可以通过使用外键(Foreign Key)约束和CASCADE选项来实现通过外键删除相关信息。 CASCADE选项是指在主键表中删除记录时,相应的外键表中的相关记录也将被自动删除。 -- 创建主键表 CREATE TABLE Persons…...

Vue轻松入门,附带学习笔记和相关案例
目录 案例 一Vue基础 什么是Vue? 补充:mvvm框架 mvvm的组成 详解 Vue的使用方法 1.直接下载并引入 2.通过 CDN 使用 Vue 3.通过npm安装 4.使用Vue CLI创建项目 二插值表达式 什么是插值表达式? 插值表达式的缺点 解决方法 …...

【青蛙跳台阶问题 —— (三种算法)】
青蛙跳台阶问题 —— (三种算法) 一.题目介绍1.1.题目1.2.图示 二.解题思路三.题解及其相关算法3.1.递归分治法3.2.动态规划算法(Dynamic Programming)3.3.斐波那契数列法 四.注意细节 一.题目介绍 1.1.题目 一只青蛙一次可以跳上1级台阶&am…...
联想yoga AMD处理器 转接头无法电量外接显示器
第一次买AMD的处理器,当时就是为了yogaAMD这款的接口要比英特尔的接口多,没想到AMD处理器真的问题多。经常蓝屏不说,偶尔还点不亮外接显示器。遇到这种问题,不是什么驱动问题,可能你按照网上各种方法打开设备管理器→显…...

OSG粒子系统与阴影 - 阴影shadow(7)
OSG阴影 在虚拟现实仿真中,为了真实地模拟自然效果,阴影效果是不可缺少的,它对一个场景的真实性是非常重要的。在游戏或仿真中,一个高效的阴影往往能够提供非常强悍的视觉真实感。 osgShadow库 在OSG中专门定义了一个名字空间osg…...

vue3项目中使用富文本编辑器
前言 适配 Vue3 的富文本插件不多,我看了很多插件官网,也有很多写的非常棒的,有UI非常优雅让人耳目一新的,也有功能非常全面的。 如: Quill,简单易用,功能全面。editorjs,UI极其优…...

Java EE 进程线程
JavaEE 进程&线程 文章目录 JavaEE 进程&线程1. 进程1.1 概念1.2 进程管理1.3 PCB (Process Control Block) 2. 线程2.1 概念2.1 线程与进程的区别2.3 创建线程 1. 进程 1.1 概念 什么是进程? 进程是操作系统对一个正在执行的程序的一种抽象 我们可以打开…...
GPT写SQL的模版
表:profit_loss_sum_m_snapshot 计算字段:成本cost_whole求和,收入income_whole求和,收入求和-成本求和,成本目标cost_target求和,收入求和-成本目标求和 条件:日期statis_date在2023-11-01&…...
蓝桥杯官网练习题(平均)
问题描述 有一个长度为 n 的数组( n 是 10 的倍数),每个数 ai 都是区间 [0,9] 中的整数。小明发现数组里每种数出现的次数不太平均,而更改第 i 个数的代价为 bi,他想更改若干个数的值使得这 10 种数出现的次数相等…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...

论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...

Xela矩阵三轴触觉传感器的工作原理解析与应用场景
Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知,帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量,能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度,还为机器人、医疗设备和制造业的智…...
MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释
以Module Federation 插件详为例,Webpack.config.js它可能的配置和含义如下: 前言 Module Federation 的Webpack.config.js核心配置包括: name filename(定义应用标识) remotes(引用远程模块࿰…...

五子棋测试用例
一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏,有着深厚的文化底蕴。通过将五子棋制作成网页游戏,可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家,都可以通过网页五子棋感受到东方棋类…...

第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10+pip3.10)
第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10pip3.10) 一:前言二:安装编译依赖二:安装Python3.10三:安装PIP3.10四:安装Paddlepaddle基础框架4.1…...