Files
0417_QT_code/DRI/Dri_Cdc.cpp

624 lines
17 KiB
C++

#include "DRI/Dri_Cdc.h"
#include "COM/Com_Protocol.h"
#include <QtCore/QDir>
#include <QtCore/QQueue>
#include <QtCore/QStringList>
#include <QtCore/QVector>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
struct Dri_Cdc_Struct_Candidate
{
QSerialPort* p_SerialPort = nullptr;
QString PortName;
QString EndpointId;
QString SystemLocation;
QByteArray PendingBytes;
QQueue<Com_Struct_RawPacket> PacketQueue;
};
struct Dri_Cdc_Struct_Context
{
QVector<Dri_Cdc_Struct_Candidate> CandidateList;
int ActiveCandidateIndex = -1;
};
namespace
{
QString Dri_Cdc_FormatPortLabel(const QSerialPortInfo& PortInfo)
{
const QString Description = PortInfo.description().trimmed();
return Description.isEmpty()
? QStringLiteral("USB CDC (%1)").arg(PortInfo.portName())
: QStringLiteral("USB CDC (%1 - %2)").arg(PortInfo.portName(), Description);
}
QString Dri_Cdc_BuildEndpointId(const QSerialPortInfo& PortInfo)
{
const QString SystemLocation = QDir::cleanPath(PortInfo.systemLocation());
if (!SystemLocation.isEmpty())
{
return SystemLocation;
}
return PortInfo.portName().trimmed();
}
bool Dri_Cdc_IsPortStillPresent(const Dri_Cdc_Struct_Candidate& Candidate)
{
for (const QSerialPortInfo& PortInfo : QSerialPortInfo::availablePorts())
{
if (!Candidate.SystemLocation.isEmpty() &&
(QDir::cleanPath(PortInfo.systemLocation())
.compare(Candidate.SystemLocation, Qt::CaseInsensitive) == 0))
{
return true;
}
}
return false;
}
void Dri_Cdc_DeleteCandidate(Dri_Cdc_Struct_Candidate* p_Candidate)
{
if (p_Candidate == nullptr)
{
return;
}
if ((p_Candidate->p_SerialPort != nullptr) && p_Candidate->p_SerialPort->isOpen())
{
p_Candidate->p_SerialPort->setDataTerminalReady(false);
p_Candidate->p_SerialPort->close();
}
delete p_Candidate->p_SerialPort;
p_Candidate->p_SerialPort = nullptr;
p_Candidate->PendingBytes.clear();
p_Candidate->PacketQueue.clear();
}
void Dri_Cdc_DeleteContext(Dri_Cdc_Struct_Context* p_Context)
{
if (p_Context == nullptr)
{
return;
}
for (int Index = 0; Index < p_Context->CandidateList.size(); ++Index)
{
Dri_Cdc_DeleteCandidate(&p_Context->CandidateList[Index]);
}
delete p_Context;
}
int Dri_Cdc_FindCandidateIndex(
const Dri_Cdc_Struct_Context* p_Context,
const QString& EndpointId)
{
if ((p_Context == nullptr) || EndpointId.trimmed().isEmpty())
{
return -1;
}
for (int Index = 0; Index < p_Context->CandidateList.size(); ++Index)
{
const Dri_Cdc_Struct_Candidate& Candidate = p_Context->CandidateList.at(Index);
if (Candidate.EndpointId.compare(EndpointId, Qt::CaseInsensitive) == 0)
{
return Index;
}
}
return -1;
}
void Dri_Cdc_RefreshPortSummary(
Dri_Cdc_Struct_Port* p_Port,
Dri_Cdc_Struct_Context* p_Context)
{
if ((p_Port == nullptr) || (p_Context == nullptr))
{
return;
}
if ((p_Context->ActiveCandidateIndex >= 0) &&
((p_Context->ActiveCandidateIndex >= p_Context->CandidateList.size()) ||
(p_Context->CandidateList.at(p_Context->ActiveCandidateIndex).p_SerialPort == nullptr) ||
!p_Context->CandidateList.at(p_Context->ActiveCandidateIndex).p_SerialPort->isOpen()))
{
p_Context->ActiveCandidateIndex = -1;
}
QStringList OpenedPortList;
for (const Dri_Cdc_Struct_Candidate& Candidate : p_Context->CandidateList)
{
if ((Candidate.p_SerialPort != nullptr) && Candidate.p_SerialPort->isOpen())
{
OpenedPortList.append(Candidate.PortName);
}
}
if (OpenedPortList.isEmpty())
{
p_Port->IsOpened = false;
p_Port->PortName.clear();
return;
}
p_Port->IsOpened = true;
if (p_Context->ActiveCandidateIndex >= 0)
{
p_Port->PortName =
p_Context->CandidateList.at(p_Context->ActiveCandidateIndex).PortName;
return;
}
p_Port->PortName = OpenedPortList.size() == 1
? OpenedPortList.first()
: QStringLiteral("USB CDC candidates (%1)").arg(OpenedPortList.join(QStringLiteral(", ")));
}
bool Dri_Cdc_WritePacket(
Dri_Cdc_Struct_Candidate* p_Candidate,
const QByteArray& PacketBody)
{
if ((p_Candidate == nullptr) ||
(p_Candidate->p_SerialPort == nullptr) ||
!p_Candidate->p_SerialPort->isOpen())
{
return false;
}
const qint64 BytesWritten = p_Candidate->p_SerialPort->write(PacketBody);
if (BytesWritten != PacketBody.size())
{
return false;
}
p_Candidate->p_SerialPort->flush();
return p_Candidate->p_SerialPort->waitForBytesWritten(100);
}
void Dri_Cdc_EnqueuePacket(
Dri_Cdc_Struct_Candidate* p_Candidate,
const QByteArray& PacketBody,
Com_Enum_ProtocolType Type)
{
if ((p_Candidate == nullptr) || PacketBody.isEmpty() ||
(Type == Com_Enum_ProtocolType_None))
{
return;
}
Com_Struct_RawPacket Packet;
Packet.IsValid = true;
Packet.Source = Com_Enum_RawPacketSource_UsbCdc;
Packet.ProtocolType = Type;
Packet.ByteArray = PacketBody;
Packet.PortName = p_Candidate->PortName;
Packet.EndpointId = p_Candidate->EndpointId;
p_Candidate->PacketQueue.enqueue(Packet);
}
void Dri_Cdc_BufferIncomingBytes(
Dri_Cdc_Struct_Candidate* p_Candidate,
const QByteArray& IncomingBytes)
{
if ((p_Candidate == nullptr) || IncomingBytes.isEmpty())
{
return;
}
p_Candidate->PendingBytes.append(IncomingBytes);
while (true)
{
QByteArray PacketBody;
Com_Enum_ProtocolType Type = Com_Enum_ProtocolType_None;
if (!Com_Protocol_TryTakePacket(&p_Candidate->PendingBytes, &PacketBody, &Type))
{
break;
}
Dri_Cdc_EnqueuePacket(p_Candidate, PacketBody, Type);
}
}
bool Dri_Cdc_DequeuePacket(
Dri_Cdc_Struct_Candidate* p_Candidate,
Com_Struct_RawPacket* p_Packet,
QString* p_TextStatus)
{
if ((p_Candidate == nullptr) || (p_Packet == nullptr) || p_Candidate->PacketQueue.isEmpty())
{
return false;
}
*p_Packet = p_Candidate->PacketQueue.dequeue();
if (p_TextStatus != nullptr)
{
*p_TextStatus =
QStringLiteral("%1 RX %2")
.arg(
p_Candidate->PortName,
QString::fromLatin1(p_Packet->ByteArray.toHex(' ').toUpper()));
}
return true;
}
} // namespace
void Dri_Cdc_Close(Dri_Cdc_Struct_Port* p_Port)
{
Dri_Cdc_DeleteContext(p_Port->p_Context);
*p_Port = Dri_Cdc_Struct_Port();
}
bool Dri_Cdc_Init(
Dri_Cdc_Struct_Port* p_Port,
const Com_Struct_DeviceConfig& DeviceConfig,
QString* p_TextStatus)
{
Dri_Cdc_Close(p_Port);
const QVector<QSerialPortInfo> PortInfoList = QSerialPortInfo::availablePorts().toVector();
if (PortInfoList.isEmpty())
{
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("No USB CDC port is available.");
}
return false;
}
auto* p_Context = new Dri_Cdc_Struct_Context();
QStringList ErrorList;
for (const QSerialPortInfo& PortInfo : PortInfoList)
{
Dri_Cdc_Struct_Candidate Candidate;
Candidate.p_SerialPort = new QSerialPort(PortInfo);
Candidate.PortName = Dri_Cdc_FormatPortLabel(PortInfo);
Candidate.EndpointId = Dri_Cdc_BuildEndpointId(PortInfo);
Candidate.SystemLocation = QDir::cleanPath(PortInfo.systemLocation());
Candidate.p_SerialPort->setBaudRate(QSerialPort::Baud115200);
Candidate.p_SerialPort->setDataBits(QSerialPort::Data8);
Candidate.p_SerialPort->setParity(QSerialPort::NoParity);
Candidate.p_SerialPort->setStopBits(QSerialPort::OneStop);
Candidate.p_SerialPort->setFlowControl(QSerialPort::NoFlowControl);
if (!Candidate.p_SerialPort->open(QIODevice::ReadWrite))
{
ErrorList.append(
QStringLiteral("%1: %2")
.arg(PortInfo.portName(), Candidate.p_SerialPort->errorString()));
Dri_Cdc_DeleteCandidate(&Candidate);
continue;
}
Candidate.p_SerialPort->setDataTerminalReady(true);
p_Context->CandidateList.append(Candidate);
}
if (p_Context->CandidateList.isEmpty())
{
if (p_TextStatus != nullptr)
{
*p_TextStatus = ErrorList.isEmpty()
? QStringLiteral("USB CDC ports were found, but all opens failed.")
: QStringLiteral("USB CDC open failed: %1").arg(ErrorList.join(QStringLiteral(" | ")));
}
Dri_Cdc_DeleteContext(p_Context);
return false;
}
p_Port->p_Context = p_Context;
Dri_Cdc_RefreshPortSummary(p_Port, p_Context);
Q_UNUSED(DeviceConfig);
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("%1 connected.").arg(p_Port->PortName);
}
return true;
}
bool Dri_Cdc_Read(
Dri_Cdc_Struct_Port* p_Port,
Com_Struct_RawPacket* p_Packet,
QString* p_TextStatus)
{
*p_Packet = Com_Struct_RawPacket();
p_Packet->Source = Com_Enum_RawPacketSource_UsbCdc;
p_Packet->PortName = p_Port->PortName;
Dri_Cdc_Struct_Context* p_Context = p_Port->p_Context;
if (!p_Port->IsOpened || (p_Context == nullptr) || p_Context->CandidateList.isEmpty())
{
return false;
}
const int StartIndex =
p_Context->ActiveCandidateIndex >= 0 ? p_Context->ActiveCandidateIndex : 0;
const int EndIndex =
p_Context->ActiveCandidateIndex >= 0
? (p_Context->ActiveCandidateIndex + 1)
: p_Context->CandidateList.size();
for (int Index = StartIndex; Index < EndIndex; ++Index)
{
Dri_Cdc_Struct_Candidate& Candidate = p_Context->CandidateList[Index];
if ((Candidate.p_SerialPort == nullptr) || !Candidate.p_SerialPort->isOpen())
{
continue;
}
if (!Dri_Cdc_IsPortStillPresent(Candidate))
{
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("%1 disconnected.").arg(Candidate.PortName);
}
Dri_Cdc_DeleteCandidate(&Candidate);
if (p_Context->ActiveCandidateIndex == Index)
{
Dri_Cdc_Close(p_Port);
return false;
}
Dri_Cdc_RefreshPortSummary(p_Port, p_Context);
continue;
}
if (Dri_Cdc_DequeuePacket(&Candidate, p_Packet, p_TextStatus))
{
return true;
}
const QByteArray IncomingBytes = Candidate.p_SerialPort->readAll();
if (IncomingBytes.isEmpty())
{
continue;
}
Dri_Cdc_BufferIncomingBytes(&Candidate, IncomingBytes);
if (Dri_Cdc_DequeuePacket(&Candidate, p_Packet, p_TextStatus))
{
return true;
}
if ((p_TextStatus != nullptr) && !Candidate.PendingBytes.isEmpty())
{
*p_TextStatus =
QStringLiteral("%1 has pending protobuf bytes and is waiting for the next CDC chunk.")
.arg(Candidate.PortName);
}
}
Dri_Cdc_RefreshPortSummary(p_Port, p_Context);
if (!p_Port->IsOpened)
{
Dri_Cdc_Close(p_Port);
}
return false;
}
bool Dri_Cdc_LockCandidate(
Dri_Cdc_Struct_Port* p_Port,
const QString& EndpointId,
QString* p_TextStatus)
{
Dri_Cdc_Struct_Context* p_Context = p_Port->p_Context;
if (!p_Port->IsOpened || (p_Context == nullptr))
{
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("USB CDC is not open, so the candidate cannot be locked.");
}
return false;
}
const int ActiveIndex = Dri_Cdc_FindCandidateIndex(p_Context, EndpointId);
if (ActiveIndex < 0)
{
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("The target USB CDC candidate was not found.");
}
return false;
}
if ((p_Context->CandidateList.at(ActiveIndex).p_SerialPort == nullptr) ||
!p_Context->CandidateList.at(ActiveIndex).p_SerialPort->isOpen())
{
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("The target USB CDC candidate is already closed.");
}
return false;
}
if (p_Context->ActiveCandidateIndex == ActiveIndex)
{
if (p_TextStatus != nullptr)
{
*p_TextStatus =
QStringLiteral("USB CDC already locked to %1.")
.arg(p_Context->CandidateList.at(ActiveIndex).PortName);
}
return true;
}
for (int Index = 0; Index < p_Context->CandidateList.size(); ++Index)
{
if (Index == ActiveIndex)
{
continue;
}
Dri_Cdc_DeleteCandidate(&p_Context->CandidateList[Index]);
}
p_Context->ActiveCandidateIndex = ActiveIndex;
Dri_Cdc_RefreshPortSummary(p_Port, p_Context);
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("USB CDC locked to %1.").arg(p_Port->PortName);
}
return true;
}
bool Dri_Cdc_DiscardCandidate(
Dri_Cdc_Struct_Port* p_Port,
const QString& EndpointId,
QString* p_TextStatus)
{
Dri_Cdc_Struct_Context* p_Context = p_Port->p_Context;
if (!p_Port->IsOpened || (p_Context == nullptr))
{
return false;
}
const int CandidateIndex = Dri_Cdc_FindCandidateIndex(p_Context, EndpointId);
if (CandidateIndex < 0)
{
return false;
}
const QString PortLabel = p_Context->CandidateList.at(CandidateIndex).PortName;
const bool IsActiveCandidate = p_Context->ActiveCandidateIndex == CandidateIndex;
Dri_Cdc_DeleteCandidate(&p_Context->CandidateList[CandidateIndex]);
if (IsActiveCandidate)
{
Dri_Cdc_Close(p_Port);
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("Discarded the locked USB CDC candidate %1.").arg(PortLabel);
}
return true;
}
Dri_Cdc_RefreshPortSummary(p_Port, p_Context);
if (!p_Port->IsOpened)
{
Dri_Cdc_Close(p_Port);
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("All USB CDC candidates were discarded.");
}
return true;
}
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("Discarded USB CDC candidate %1.").arg(PortLabel);
}
return true;
}
bool Dri_Cdc_Write(
Dri_Cdc_Struct_Port* p_Port,
const QByteArray& PacketBody,
QString* p_TextStatus)
{
Dri_Cdc_Struct_Context* p_Context = p_Port->p_Context;
if (!p_Port->IsOpened || (p_Context == nullptr) || p_Context->CandidateList.isEmpty())
{
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("USB CDC is not open. Skip send.");
}
return false;
}
if (PacketBody.isEmpty())
{
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("USB CDC packet body is empty.");
}
return false;
}
if (p_Context->ActiveCandidateIndex >= 0)
{
Dri_Cdc_Struct_Candidate& ActiveCandidate =
p_Context->CandidateList[p_Context->ActiveCandidateIndex];
if (!Dri_Cdc_WritePacket(&ActiveCandidate, PacketBody))
{
if (p_TextStatus != nullptr)
{
*p_TextStatus =
QStringLiteral("%1 鍐欏叆澶辫触锛?2")
.arg(
ActiveCandidate.PortName,
ActiveCandidate.p_SerialPort == nullptr
? QStringLiteral("port already closed")
: ActiveCandidate.p_SerialPort->errorString());
}
Dri_Cdc_Close(p_Port);
return false;
}
if (p_TextStatus != nullptr)
{
*p_TextStatus =
QStringLiteral("%1 TX %2")
.arg(
ActiveCandidate.PortName,
QString::fromLatin1(PacketBody.toHex(' ').toUpper()));
}
return true;
}
int SuccessCount = 0;
QStringList SuccessPortList;
QStringList ErrorList;
for (Dri_Cdc_Struct_Candidate& Candidate : p_Context->CandidateList)
{
if ((Candidate.p_SerialPort == nullptr) || !Candidate.p_SerialPort->isOpen())
{
continue;
}
if (Dri_Cdc_WritePacket(&Candidate, PacketBody))
{
++SuccessCount;
SuccessPortList.append(Candidate.PortName);
continue;
}
ErrorList.append(
QStringLiteral("%1: %2")
.arg(Candidate.PortName, Candidate.p_SerialPort->errorString()));
}
if (SuccessCount > 0)
{
if (p_TextStatus != nullptr)
{
*p_TextStatus =
QStringLiteral("USB CDC 骞挎挱 TX %1锛岀洰鏍囷細%2")
.arg(
QString::fromLatin1(PacketBody.toHex(' ').toUpper()),
SuccessPortList.join(QStringLiteral(", ")));
}
return true;
}
if (p_TextStatus != nullptr)
{
*p_TextStatus = ErrorList.isEmpty()
? QStringLiteral("USB CDC send failed.")
: QStringLiteral("USB CDC 鍙戦€佸け璐ワ細%1").arg(ErrorList.join(QStringLiteral(" | ")));
}
return false;
}