#include "DRI/Dri_Cdc.h" #include "COM/Com_Protocol.h" #include #include #include #include #include #include struct Dri_Cdc_Struct_Candidate { QSerialPort* p_SerialPort = nullptr; QString PortName; QString EndpointId; QString SystemLocation; QByteArray PendingBytes; QQueue PacketQueue; }; struct Dri_Cdc_Struct_Context { QVector 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 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; }