Commit f213db5d authored by FeniceLiu's avatar FeniceLiu :speech_balloon:
Browse files

update ui

issue: should get ui thread and serial thread departed
parent 9b4ebc97
No related merge requests found
Showing with 164 additions and 28 deletions
+164 -28
......@@ -3,7 +3,7 @@
#include <QRegularExpression>
#include "constants.h"
//instance pointer initialized as nullptr
/// @brief instance pointer initialized as nullptr
std::shared_ptr<Serial> Serial::instance = nullptr;
/**
......@@ -216,13 +216,14 @@ QList<QSerialPortInfo> Serial::searchPorts() {
}
/**
* @brief This function creates a new instance of Serial if it's not created yet, otherwise return the existing one. Simultaneously, it also describe how to delete instance properly. Parameter increment & limit are optional, if not given, it will use the default values. Only the first time this function is called, the parameters will be used.
* @brief This function creates a new instance of Serial if it's not created yet, otherwise return the existing one. Simultaneously, it also describe how to delete instance properly. Parameter increment, limit, and breakInterval are optional, if not given, it will use the default values. Only the first time this function is called, the parameters will be used.
* @param increment Size increment of the records circular-queue, default value store in const_num.
* @param limit Size limit of the records circular-queue, default value store in const num.
* @param breakInterval Set auto-break frame interval, unit in millisecond, 0 means disable auto-break.
* @return Single Instance of this class
*/
std::shared_ptr<Serial> Serial::getInstance(size_t increment,size_t limit){
if(!instance) instance = std::shared_ptr<Serial>(new Serial(increment,limit),Serial::SerialDeleter());
std::shared_ptr<Serial> Serial::getInstance(size_t increment,size_t limit,unsigned breakInterval){
if(!instance) instance = std::shared_ptr<Serial>(new Serial(increment,limit,breakInterval),Serial::SerialDeleter());
return instance;
}
......@@ -230,11 +231,19 @@ std::shared_ptr<Serial> Serial::getInstance(size_t increment,size_t limit){
* @brief Constructor of Serial class. It initializes the records vector with default size increment and set count-variables to init a circular-queue.
* @param increment Size increment of the records circular-queue.
* @param limit Size limit of the records circular-queue.
* @param breakInterval Set auto-break frame interval, unit in millisecond, 0 means disable auto-break.
*/
Serial::Serial(size_t increment,size_t limit):records(increment){
Serial::Serial(size_t increment,size_t limit,unsigned breakInterval):records(increment){
this->increment = increment;
this->limit = limit;
head = 0; tail = 0; count = 0;
this->buffer.clear();
this->autoBreakInterval = breakInterval;
this->autoBreakTimer = new QTimer(this);
this->autoBreakTimer->setSingleShot(true);
if(autoBreakInterval!=0)
autoBreakTimer->setInterval(static_cast<int>(autoBreakInterval));
connect(autoBreakTimer, &QTimer::timeout, this, &Serial::receiveFrame);
}
/**
......@@ -332,6 +341,25 @@ std::optional<Serial::Record> Serial::recordsPop() {
return value;
}
/**
* @brief This function clears all records in the circular-queue.
*/
void Serial::recordsClear() {
head = 0;
tail = 0;
count = 0;
}
/**
* @brief This function sets the auto-break frame interval. If the interval is 0, it will disable the auto-break frame. Otherwise, it will set the interval.
* @param interval Target auto-break frame interval, unit in millisecond.
*/
void Serial::setAutoBreak(unsigned int interval) {
this->autoBreakInterval = interval;
if(autoBreakInterval!=0)
autoBreakTimer->setInterval(static_cast<int>(autoBreakInterval));
}
/**
* @brief This function opens a new serial port with given parameters. Parameters is restricted to the defined values in constants.h, any other values will return PORT_ERR_PARAM. This function doesn't allow connect below a connected situation, so it will return PORT_ERR_USING if the port is already opened. If the port is opened successfully, it will connect the errorOccurred and readyRead signals to handleError and handleRxData slots respectively.
* @param name Name of port to be connected.
......@@ -373,6 +401,7 @@ Serial::PortResult Serial::openPort(const std::string& name, int baudRate, int d
default: return PORT_ERR_OPEN;
}
}else{
buffer.clear();
//connect callback functions
connect(&connection, &QSerialPort::errorOccurred, this, &Serial::handleError);
connect(&connection, &QSerialPort::readyRead, this, &Serial::handleRxData);
......@@ -389,6 +418,9 @@ void Serial::closePort() {
disconnect(&connection, &QSerialPort::errorOccurred, this, &Serial::handleError);
disconnect(&connection, &QSerialPort::readyRead, this, &Serial::handleRxData);
}
if(autoBreakTimer->isActive())
this->autoBreakTimer->stop();
buffer.clear();
}
/**
......@@ -401,16 +433,23 @@ void Serial::handleError(QSerialPort::SerialPortError error){
this->errorOccurred(error);
}
/**
* @brief This function handles the received data from the connection. It reads all data from the connection and push it to the records circular-queue. The data will be pushed as a record with current time and emit signal in recordsPush().
* @brief This function handles the received data from the connection. If auto-break frame is disabled or buffer data pass ASCII/RTU frame-structure check, it'll trigger push signal immediately. Otherwise, it will start the auto-break timer.
*/
void Serial::handleRxData(){
QByteArray data = connection.readAll();
recordsPush({
QDateTime::currentDateTime(),
false,data.length()==0||data[0]!=':',data
});
buffer.append(data);
//if auto-break is disabled, push data immediately
if(autoBreakInterval==0) receiveFrame();
else{
//Break frame according to frame-structure(ASCII/RTU)
bool asciiBreak = buffer.startsWith(':')&&buffer.endsWith("\r\n");
bool rtuBreak = buffer.length()>=4
&&CRC(buffer.left(buffer.length()-2))==(static_cast<uint16_t>(static_cast<unsigned char>(buffer[buffer.length()-2]))<<8|(static_cast<unsigned char>(buffer[buffer.length()-1])));
if(asciiBreak||rtuBreak) receiveFrame();
//waiting for auto-break
else autoBreakTimer->start();
}
}
/**
......@@ -427,4 +466,16 @@ Serial::PortResult Serial::sendFrame(const QByteArray& frame) {
true,frame.length()==0||frame[0]!=':',frame
});
return PORT_SENT;
}
/**
* @brief This function actually trigger the circular-queue push signal, which means a frame is received and pushed to the circular-queue. It will also stop the auto-break timer if it's active and clean rx buffer simultaneously.
*/
void Serial::receiveFrame() {
recordsPush({
QDateTime::currentDateTime(),
false,buffer.length()==0||buffer[0]!=':',buffer
});
if(autoBreakTimer->isActive()) autoBreakTimer->stop();
buffer.clear();
}
\ No newline at end of file
......@@ -7,6 +7,7 @@
#include <QDateTime>
#include <vector>
#include <QColor>
#include <QTimer>
#include "constants.h"
class Serial : public QObject{
......@@ -17,7 +18,8 @@ public:
//User can get instance pointer from here
static std::shared_ptr<Serial>getInstance(
size_t increment = const_num::default_recordSizeIncrement,
size_t limit = const_num::default_recordSizeLimit);
size_t limit = const_num::default_recordSizeLimit,
unsigned breakInterval = const_num::default_breakFrameInterval);
//public destructor due to private default destructor
struct SerialDeleter{void operator()(Serial* ptr){delete ptr;}};
//ban copy constructor and assignment operator
......@@ -27,7 +29,7 @@ private:
//single-instance pointer
static std::shared_ptr<Serial> instance;
//private constructor and destructor
Serial(size_t increment,size_t limit);
Serial(size_t increment,size_t limit,unsigned breakInterval);
~Serial() override;
//data structure
......@@ -64,6 +66,12 @@ private:
size_t increment,limit;
//circular queue count variables
size_t head,tail,count;
//Rx buffer before auto-break frame
QByteArray buffer;
//auto-break frame interval
unsigned autoBreakInterval;
//auto-break frame timer
QTimer* autoBreakTimer;
//Circular Queue Operation
public:
......@@ -73,6 +81,7 @@ public:
void recordsPush(const Record& record);
std::optional<Record> getRecord(size_t index);
std::optional<Record> recordsPop();
void recordsClear();
private:
//resize record buffer, claimed as private due to dangerous operation
bool resize(size_t targetCapacity);
......@@ -86,6 +95,9 @@ public:
PortResult openPort(const std::string& name, int baudRate, int dataBit, float stopBit,const std::string& parity);
void closePort();
PortResult sendFrame(const QByteArray& frame);
void setAutoBreak(unsigned interval);
private:
void receiveFrame();
private slots:
void handleRxData();
void handleError(QSerialPort::SerialPortError error);
......
......@@ -46,6 +46,7 @@ namespace const_str{
const std::string button_sync = "同步数据";
const std::string suffix_bit = "位";
const std::string suffix_byte = "字节";
const std::string sorted_tabName[] = {"纯文本显示","富文本表格"};
const std::string sorted_tableHeader[] = {"时间戳","方向","协议类型","地址码","功能码","数据长度","数据内容","校验码","帧校验","原始数据"};
......@@ -91,6 +92,12 @@ namespace const_str{
const std::string table_methodIllegal = "[未知功能]";
const std::string table_lengthDefined = "[计算]";
const std::string table_lengthCalculate = "[合规]";
const std::string status_tx = "发送: ";
const std::string status_rx = "接收: ";
const std::string status_connected = "已连接";
const std::string status_disconnected = "未连接";
const std::string status_inaccessible = "未选择端口";
}
namespace const_num{
......@@ -117,6 +124,7 @@ namespace const_num{
const uint16_t default_crc16 = 0xA001;
const int default_recordSizeIncrement = 1000;
const int default_recordSizeLimit = 1000000;
const int default_breakFrameInterval = 10;
}
namespace app_color{
......
......@@ -2,5 +2,6 @@
<qresource>
<file>style/toast.qss</file>
<file>style/table.qss</file>
<file>style/status.qss</file>
</qresource>
</RCC>
\ No newline at end of file
QStatusBar { background-color: lightgray; } QLabel { border: 1px solid white; }
\ No newline at end of file
......@@ -17,6 +17,29 @@ MainWindow::MainWindow(QWidget *parent) :
connect(serial.get(), &Serial::errorOccurred, this, &MainWindow::onError);
connect(serial.get(),&Serial::newRecordPushed, this, &MainWindow::onMessage);
txLabel = new QLabel(this);
txCount = 0;
QPalette txPalette = txLabel->palette();
txPalette.setColor(QPalette::WindowText,QColor(app_color::color_transmit.c_str()));
txLabel->setPalette(txPalette);
txLabel->setText(const_str::status_tx.c_str()+QString::number(txCount)+const_str::suffix_byte.c_str());
txLabel->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);
ui->statusbar->addPermanentWidget(txLabel);
rxLabel = new QLabel(this);
rxCount = 0;
QPalette rxPalette = rxLabel->palette();
rxPalette.setColor(QPalette::WindowText,QColor(app_color::color_receive.c_str()));
rxLabel->setPalette(rxPalette);
rxLabel->setText(const_str::status_rx.c_str()+QString::number(rxCount)+const_str::suffix_byte.c_str());
rxLabel->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);
ui->statusbar->addPermanentWidget(rxLabel);
statusLabel = new QLabel(this);
statusLabel->setText(const_str::status_inaccessible.c_str());
statusLabel->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);
ui->statusbar->addPermanentWidget(statusLabel);
allocateViewSize();
loadViewStyles();
loadViewConstants();
......@@ -86,6 +109,29 @@ MainWindow::MainWindow(QWidget *parent) :
else if(res==Serial::PORT_ERR_TIMEOUT) toast->showToast(const_str::toast_transmitTimeout, const_num::default_toastTimeout);
});
connect(ui->clearData,&QPushButton::clicked, this, [this](){
ui->plainText->clear();
ui->hyperTable->clear();
ui->hyperTable->setRowCount(0);
txCount = 0;
rxCount = 0;
txLabel->setText(const_str::status_tx.c_str()+QString::number(txCount)+const_str::suffix_byte.c_str());
rxLabel->setText(const_str::status_rx.c_str()+QString::number(rxCount)+const_str::suffix_byte.c_str());
serial->recordsClear();
});
connect(ui->syncData,&QPushButton::clicked,this,[this](){
ui->plainText->clear();
ui->hyperTable->clear();
for(int i=0;i<serial->recordsCount();i++){
auto record = serial->getRecord(i);
if(!record.has_value()) continue;
updateText(record.value());
updateTable(record.value());
}
ui->hyperTable->resizeColumnsToContents();
});
QTimer::singleShot(1000,this,&MainWindow::onSearchPorts);
}
......@@ -220,7 +266,6 @@ void MainWindow::loadViewConstants() {
ui->sendButton->setText(const_str::button_send.c_str());
ui->sendButton->setEnabled(false);
ui->syncData->setText(const_str::button_sync.c_str());
ui->syncData->setEnabled(false);
}
void MainWindow::onSearchPorts(){
......@@ -247,7 +292,11 @@ void MainWindow::onSearchPorts(){
void MainWindow::onSelected(int index){
auto *sender = qobject_cast<QComboBox*>(QObject::sender());
if(sender==ui->serialPortInput) ui->serialConnect->setEnabled(index!=-1);
if(sender==ui->serialPortInput){
if(index!=-1) statusLabel->setText(ui->serialPortInput->currentText()+" "+const_str::status_disconnected.c_str());
else statusLabel->setText(const_str::status_inaccessible.c_str());
ui->serialConnect->setEnabled(index!=-1);
}
else if(sender==ui->contextInput){
ui->sendContent->clear();
ui->checkSumInput->clear();
......@@ -265,12 +314,6 @@ void MainWindow::onSelected(int index){
}
void MainWindow::onOpen(){
qDebug() << "Opening Serial Port: ";
qDebug() << "\tName:" << ui->serialPortInput->currentText();
qDebug() << "\tBaudRate:" << ui->baudRateInput->currentText();
qDebug() << "\tDataBit:" << ui->dataBitInput->currentText();
qDebug() << "\tStopBit:" << ui->stopBitInput->currentText();
qDebug() << "\tParity:" << ui->parityBitInput->currentText();
Serial::PortResult result = serial->openPort(
ui->serialPortInput->currentText().toStdString(),
const_num::enum_baudRate[ui->baudRateInput->currentIndex()],
......@@ -288,6 +331,7 @@ void MainWindow::onOpen(){
connect(ui->serialConnect, &QPushButton::clicked, this, &MainWindow::onClose);
ui->serialSearch->setEnabled(false);
ui->sendButton->setEnabled(true);
statusLabel->setText(ui->serialPortInput->currentText()+" "+const_str::status_connected.c_str());
return;
}
switch(result){
......@@ -322,6 +366,7 @@ void MainWindow::onClose(){
disconnect(ui->serialConnect, &QPushButton::clicked, this, &MainWindow::onClose);
connect(ui->serialConnect, &QPushButton::clicked, this, &MainWindow::onOpen);
ui->sendButton->setEnabled(false);
statusLabel->setText(ui->serialPortInput->currentText()+" "+const_str::status_disconnected.c_str());
}
void MainWindow::onError(QSerialPort::SerialPortError error){
......@@ -344,6 +389,7 @@ void MainWindow::onError(QSerialPort::SerialPortError error){
}
toast->showToast(const_str::toast_exceptionClosed+reason, const_num::default_toastTimeout);
ui->sendButton->setEnabled(false);
statusLabel->setText(ui->serialPortInput->currentText()+" "+const_str::status_disconnected.c_str());
}
void MainWindow::onSwitch(bool checked){
......@@ -537,6 +583,18 @@ QByteArray MainWindow::generateFrame() {
}
void MainWindow::onMessage(const Serial::Record& record) {
if(record.isTransmit){
txCount += static_cast<int>(record.frameRaw.size());
txLabel->setText(const_str::status_tx.c_str()+QString::number(txCount)+const_str::suffix_byte.c_str());
}else{
rxCount += static_cast<int>(record.frameRaw.size());
rxLabel->setText(const_str::status_rx.c_str()+QString::number(rxCount)+const_str::suffix_byte.c_str());
}
if(record.isTransmit&&!ui->txSwitch->isChecked()) return;
updateText(record);
}
void MainWindow::updateText(const Serial::Record& record) {
QTextCursor cursor(ui->plainText->textCursor());
cursor.movePosition(QTextCursor::End);
QTextCharFormat format;
......@@ -551,6 +609,9 @@ void MainWindow::onMessage(const Serial::Record& record) {
}
text.append(" ]\n");
cursor.insertText(text,format);
}
void MainWindow::updateTable(const Serial::Record &record) {
auto recordAnalyze = Serial::analyzeRecord(record);
int rowCount = ui->hyperTable->rowCount();
ui->hyperTable->insertRow(rowCount);
......@@ -561,16 +622,13 @@ void MainWindow::onMessage(const Serial::Record& record) {
item->setTextAlignment(Qt::AlignCenter);
ui->hyperTable->setItem(rowCount,i,item);
}
ui->hyperTable->resizeColumnsToContents();
}
//TODO: send Button x1
//TODO: interval transmit
//TODO: rxData & txData show in tableArea
//TODO: script transmit
//TODO: show tx switch
//TODO: clear data button
//TODO: sync data button
//TODO: menubar & globalSettings
//TODO: Status Bar
//TODO: auto-break ui
//TODO: change baudrate... params during connecting
\ No newline at end of file
......@@ -21,6 +21,8 @@ private:
Ui::MainWindow *ui;
Toast *toast;
std::shared_ptr<Serial> serial;
QLabel *txLabel, *rxLabel, *statusLabel;
int txCount, rxCount;
static QRegularExpression hexRegex;
......@@ -28,6 +30,9 @@ private:
void loadViewStyles();
void allocateViewSize();
void updateText(const Serial::Record& record);
void updateTable(const Serial::Record& record);
QString syncDataLength(QString text = nullptr);
QByteArray generateFrame();
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment