You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

560 lines
15 KiB
C++

1 year ago
#pragma execution_character_set("utf-8")
#include "device/Device.h"
#include <QString>
#include <QObject>
#include <QSerialPort>
#include <QThread>
#include "Utils.h"
#include "frame/Frame.h"
#include "data/DateTimeData.h"
#include <thread>
#include <mutex>
#include "Common.h"
#include "logger/LogManager.h"
#include "crypto/CRC.h"
Device::Device()
: m_portName(QString("COM1"))
, m_baudRate(QString("9600"))
, m_dataBits(QString("8"))
, m_stopBits(QString("1"))
, m_parity("None")
{
InitRecvTimer(1000);
}
Device::Device(QString portName, QString baudRate, QString dataBits, QString stopBits, QString parity)
: m_portName(portName)
, m_baudRate(baudRate)
, m_dataBits(dataBits)
, m_stopBits(stopBits)
, m_parity(parity)
{
InitRecvTimer(1000);
m_lastLogString = "";
m_lastLogTimes = 0;
}
Device::~Device()
{
StopRecvTimer();
}
/* ****************************************
*
*
*
*
* **************************************** */
// 转换波特率
QSerialPort::BaudRate Device::convertBaudRate(QString baudRate)
{
if(baudRate == QObject::tr("1200"))
{
return QSerialPort::Baud1200;
}
else if(baudRate == QObject::tr("2400"))
{
return QSerialPort::Baud2400;
}
else if(baudRate == QObject::tr("4800"))
{
return QSerialPort::Baud4800;
}
else if(baudRate == QObject::tr("9600"))
{
return QSerialPort::Baud9600;
}
else if(baudRate == QObject::tr("19200"))
{
return QSerialPort::Baud19200;
}
else if(baudRate == QObject::tr("38400"))
{
return QSerialPort::Baud38400;
}
else if(baudRate == QObject::tr("57600"))
{
return QSerialPort::Baud57600;
}
else if(baudRate == QObject::tr("115200"))
{
return QSerialPort::Baud115200;
}
//return gDefaultBaudRate;
return QSerialPort::Baud115200;
}
// 转换数据位
QSerialPort::DataBits Device::convertDataBits(QString dataBits)
{
if(dataBits == QObject::tr("5"))
{
return QSerialPort::Data5;
}
else if(dataBits == QObject::tr("6"))
{
return QSerialPort::Data6;
}
else if(dataBits == QObject::tr("7"))
{
return QSerialPort::Data7;
}
else if(dataBits == QObject::tr("8"))
{
return QSerialPort::Data8;
}
//return gDefaultDataBits;
return QSerialPort::Data8;
}
// 转换停止位
QSerialPort::StopBits Device::convertStopBits(QString stopBits)
{
if(stopBits == QObject::tr("1"))
{
return QSerialPort::OneStop;
}
else if(stopBits == QObject::tr("1,5"))
{
return QSerialPort::OneAndHalfStop;
}
else if(stopBits == QObject::tr("2"))
{
return QSerialPort::TwoStop;
}
//return gDefaultStopBits;
return QSerialPort::OneStop;
}
// 转换校验位
QSerialPort::Parity Device::convertParity(QString parity)
{
if(parity == QObject::tr("None"))
{
return QSerialPort::NoParity;
}
else if(parity == QObject::tr("Odd"))
{
return QSerialPort::OddParity;
}
else if(parity == QObject::tr("Even"))
{
return QSerialPort::EvenParity;
}
//return gDefaultParity;
return QSerialPort::NoParity;
}
bool Device::Open()
{
m_serialPort.setPortName(m_portName);
if(!m_serialPort.open(QIODevice::ReadWrite))
{
return false;
}
// 设置波特率
m_serialPort.setBaudRate(convertBaudRate(m_baudRate));
// 设置数据位
m_serialPort.setDataBits(convertDataBits(m_dataBits));
// 设置停止位
m_serialPort.setStopBits(convertStopBits(m_stopBits));
// 设置校验位
m_serialPort.setParity(convertParity(m_parity));
m_serialPort.setFlowControl(QSerialPort::NoFlowControl);
// readyRead()信号不产生的解决方案,设置"控制管脚状态".
m_serialPort.setDataTerminalReady(true);
//m_commThread.start();
connect(&m_serialPort, SIGNAL(readyRead()), this, SLOT(slot_readData()));
logInfo(tr("Device Opened!"));
return true;
}
bool Device::Close()
{
if(m_serialPort.isOpen())
{
disconnect(&m_serialPort, SIGNAL(readyRead()), this, SLOT(slot_readData()));
m_serialPort.clear();
m_serialPort.close();
}
logInfo("Device Closed!");
return true;
}
void Device::ClearRecvData()
{
m_recvMutex.lock();
m_recvData.clear();
m_recvMutex.unlock();
logInfo("Recv Data Cleared!");
}
/*
0 1 1 head-1 = 0x5A
1 2 1 head-2 = 0xA5
2 3 1 addr-1
3 4 1 addr-2
4 5 1 cmd
5 6 1 len-1
6 7 1 len-2
7 8 length >=0 && <= 10240
8 9 1 crc-1
9 10 1 crc-2
*/
// DMS Device Machine Status
// 名称表示该状态完成
#define DMS0_INIT 0
#define DMS1_HEAD_1 1
#define DMS2_HEAD_2 2
#define DMS3_ADDR_1 3
#define DMS4_ADDR_2 4
#define DMS5_CMD 5
#define DMS6_LEN_1 6
#define DMS7_LEN_2 7
#define DMS8_CONTENT 8
#define DMS9_CRC_1 9
#define DMS10_CRC_2 10
// 5A A5 00 00 01 00 00 C0 8C
void Device::slot_readData()
{
QByteArray byteArray = m_serialPort.readAll();
m_serialPort.clear();
logInfo("Read Data!");
m_recvMutex.lock();
m_recvData.append(byteArray);
m_recvMutex.unlock();
}
// 发送数据
bool Device::SendData(int cmd, std::string str, int msec)
{
if(!IsOpen())
{
logInfo("串口未打开!");
return false;
}
Frame *frame = new Frame(cmd, FRAME_DIRECT_REQ);
if(frame == nullptr)
{
logInfo("创建协议帧失败!");
return false;
}
frame->setAddr(0x1234);
// // 设置的时间为当前时间不从text中获取
// Data *data = new DateTimeData();
// frame->setData(data);
frame->getData()->fromByteStr(str);
frame->updateLen();
std::string sendData = frame->toByteStr();
int sendDataLen = sendData.length();
//#ifdef _DEBUG
logDebug(QString::fromStdString(Utils::strToHexStr(sendData)));
//#endif
// 发送前先清理数据
m_recvMutex.lock();
m_recvData.clear();
m_serialPort.clear();
//m_serialPort.write(frame->toByteStr().c_str(), frame->toByteStr().length());
logInfo(tr("发送消息:%1").arg(gFrameCmdName[(cmd>=FRAME_CMD_START && cmd <=FRAME_CMD_MAX ? cmd : 0)].c_str()));
m_serialPort.write(sendData.c_str(), sendDataLen);
m_recvMutex.unlock();
delete frame;
// 启动定时器,等待返回
// 如果定时器时间到,尚未收到响应消息,则报超时
// 如果在定时器时间内收到响应消息,关闭定时器
StartRecvTimer(msec);
return true;
}
bool Device::SendData(int cmd, Data *data, int msec)
{
if(!IsOpen())
{
logInfo("串口未打开!");
return false;
}
Frame *frame = new Frame(cmd, FRAME_DIRECT_REQ);
if(frame == nullptr)
{
logInfo("创建协议帧失败!");
return false;
}
frame->setAddr(0x1234);
// // 设置的时间为当前时间不从text中获取
// Data *data = new DateTimeData();
// frame->setData(data);
//frame->getData()->fromByteStr(str);
frame->setData(data);
frame->updateLen();
std::string sendData = frame->toByteStr();
int sendDataLen = sendData.length();
//#ifdef _DEBUG
logDebug(QString::fromStdString(Utils::strToHexStr(sendData)));
//#endif
// 发送前先清理数据
m_recvMutex.lock();
m_recvData.clear();
m_serialPort.clear();
//m_serialPort.write(frame->toByteStr().c_str(), frame->toByteStr().length());
logInfo(tr("发送消息:%1").arg(gFrameCmdName[(cmd>=FRAME_CMD_START && cmd <=FRAME_CMD_MAX ? cmd : 0)].c_str()));
m_serialPort.write(sendData.c_str(), sendDataLen);
m_recvMutex.unlock();
delete frame;
// 启动定时器,等待返回
// 如果定时器时间到,尚未收到响应消息,则报超时
// 如果在定时器时间内收到响应消息,关闭定时器
StartRecvTimer(msec);
return true;
}
// 接收数据定时器初始化
void Device::InitRecvTimer(int msec)
{
m_recvTimer.stop();
m_recvTimer.setInterval(msec); // 1s超时
connect(&m_recvTimer, SIGNAL(timeout()), this, SLOT(slot_recvTimer()));
}
// 接收数据定时器
void Device::StartRecvTimer(int msec)
{
#ifdef _DEBUG
logDebug(tr("StartRecvTimer"));
#endif
m_recvTimer.start(msec);
}
// 接收数据定时器
void Device::StopRecvTimer()
{
#ifdef _DEBUG
logDebug(tr("StopRecvTimer"));
#endif
m_recvTimer.stop();
}
// 接收数据定时器的功能:
void Device::slot_recvTimer()
{
logInfo(tr("接收消息超时!"));
emit recv_timeout(m_portName);
StopRecvTimer();
}
// 数据解析线程
void Device::runParseThread()
{
#ifdef _DEBUG
logDebug(tr("runParseThread begin"));
#endif
while(1)
{
if(!m_bRunParseThread)
{
break;
}
if(m_recvData.length() > 0)
{
#ifdef _DEBUG
logDebug(tr("parse data, length=%1, data=[%2]")
.arg(m_recvData.length())
.arg(QString::fromStdString(Utils::strToHexStr(m_recvData.toStdString()))));
#endif
m_recvMutex.lock();
unsigned short nLen = 0;
int status = 0; // 解析状态0-初始1-head1,2-head2,
while(1)
{
if(status == DMS0_INIT) // 0:head - 1 - 0x5A
{
if(m_recvData.length() < 1)
{
break; // 长度不够,退出
}
// 判断第一个字符是否0x5A是则转入下一步否则移除该字符重新开始
if(m_recvData.at(0) == 0x5A)
{
status = DMS1_HEAD_1;
}
else
{
m_recvData.remove(0, 1);
status = DMS0_INIT;
}
}
else if(status == DMS1_HEAD_1) // 1:head - 2 - 0xA5
{
// 状态1判断第2个字符是否为0xA5
if(m_recvData.length() < 2)
{
break; // 长度不够,退出
}
// 判断第二个字符是否为0xA5是则转入下一步否则。。。
if((unsigned char)m_recvData.at(1) == 0xA5)
{
status = DMS2_HEAD_2;
}
else
{
m_recvData.remove(0, 1);
status = DMS1_HEAD_1;
}
}
else if(status == DMS2_HEAD_2) // 2:addr - 1
{
if(m_recvData.length() < 3)
{
break; // 长度不够,退出循环
}
status = DMS3_ADDR_1;
}
else if(status == DMS3_ADDR_1) // 3:addr - 2
{
// 状态2判断长度是否超过4不足则退出循环
if(m_recvData.length() < 4)
{
break; // 长度不够,退出循环
}
status = DMS4_ADDR_2;
}
else if(status == DMS4_ADDR_2) // 4:cmd
{
// 状态2判断命令
if(m_recvData.length() < 5)
{
break; // 长度不够,退出循环
}
if(isCmdValid(m_recvData.at(4)))
{
status = DMS5_CMD;
}
else
{
m_recvData.remove(0, 2); // 移除首部2个字符重新定位起始位置
status = DMS0_INIT;
}
}
else if(status == DMS5_CMD) // 5:len-1
{
// 状态2判断命令
if(m_recvData.length() < 6)
{
break; // 长度不够,退出循环
}
status = DMS6_LEN_1;
}
else if(status == DMS6_LEN_1) // 6:len - 2
{
if(m_recvData.length() < 7)
{
break; // 长度不够,退出循环
}
nLen = Utils::charToShort(m_recvData.at(5), m_recvData.at(6));
if(nLen > 10240)
{
m_recvData.remove(0, 2);
status = DMS0_INIT;
nLen = 0;
}
else
{
status = DMS7_LEN_2;
}
}
else if(status == DMS7_LEN_2) // content + CRC
{
if(m_recvData.length() < 7 + nLen + 2)
{
break; // 长度不够,退出循环
}
// 检查CRC
QByteArray tmpRecvData = m_recvData.left(7 + nLen);
unsigned short crc = CRC::crc16Table((unsigned char *)tmpRecvData.data(), tmpRecvData.length());
if(crc == Utils::charToShort(m_recvData.at(7+nLen), m_recvData.at(7+nLen+1)))
{
QByteArray recvData = m_recvData.left(7 + nLen + 2);
m_recvData.remove(0, 7+nLen+2);
//emit recv_data(m_portName, recvData); // 收到消息
// 此处直接返回类型和数据
unsigned char cmd = recvData.at(4);
recvData.remove(0, 7); // 移除帧头
recvData.remove(nLen, 2); // 移除帧尾
emit recv_data(m_portName, cmd, recvData);
StopRecvTimer(); // 停止定时器
status = DMS0_INIT;
}
else
{
m_recvData.remove(0, 2);
status = DMS0_INIT;
nLen = 0;
}
}
}
m_recvMutex.unlock();
}
QThread::msleep(10);
}
#ifdef _DEBUG
logDebug(tr("runParseThread end"));
#endif
}
// 启动解析线程
void Device::startParseThread()
{
#ifdef _DEBUG
logDebug(tr("startParseThread"));
#endif
m_bRunParseThread = true;
m_parseThread = std::thread(&Device::runParseThread, this);
//m_parseThread.detach();
}
// 停止解析线程
void Device::stopParseThread()
{
#ifdef _DEBUG
logDebug(tr("stopParseThread"));
#endif
m_bRunParseThread = false;
m_parseThread.join();
}