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