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++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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();
}