Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Не могу прочитать данные с COM-порта


Автор: EKoshelev 26.12.2012, 14:18
Всем привет.
Встала задача - работать с COM-портом. Надыбал тут h-ник и cpp-шник, которые всё это позволяют. Инициализация и запись у меня получаются. А вот с чтением возникли проблемы. Там всё сделано через CreateFile. Вот инициализация:

Код

COMPort::COMPort ( const char * const portName )
  : theDCB (NULL)
{

thePortHandle = (unsigned ) CreateFileA ( portName
                                       , GENERIC_READ | GENERIC_WRITE
                                       , 0
                                       , NULL
                                       , OPEN_EXISTING
                                       , FILE_FLAG_NO_BUFFERING
                                       , NULL
                                       );
if (thePortHandle == HFILE_ERROR)
{
   throw runtime_error ("COMPort: failed to open.");
} // endif

theDCB = new char [sizeof(DCB)];
getState();
setBlockingMode();
setHandshaking();

} // end constructor


А вот чтение (немного измененное мной):

Код

bool COMPort::read(char &ch)
{
//    char buffer;
    DWORD charsRead = 0;

       if (! ReadFile ( (HANDLE(thePortHandle))
                      , &ch
                      , sizeof(char)
                      , &charsRead
                      , NULL
                      )
          )    return false;
       else return true;
}


Дак вот, когда прога доходит до ReadFile она сидит, собака, и ждёт, пока на порт что-нибудь придёт. А до этого её даже через диспетчер задач не вырубить. Собственно вопрос: можно ли сделать так, чтобы ReadFile завершал работу даже в том случае, если на порт ничего не пришло и возвращал false?

Автор: kuzulis 26.12.2012, 16:47
Читай до просветления: http://msdn.microsoft.com/en-us/library/ff802693.aspx

Или в гугле забанили?  smile 

Автор: Dem_max 27.12.2012, 04:33
Вот готовая реализация
http://www.sources.ru/cpp/cpp_com_port_m.shtml

Автор: EKoshelev 26.4.2013, 15:52
Не прошло и полгода, ага.

kuzulis, читал, спасибо.

Слабал следующее (на Qt):

.h
Код

#include <windows.h> 

enum QParity {pNone, pOdd, pEven, pMark, pSpace};
enum QDataBits {db5 = 5, db6, db7, db8};
enum QStopBits {sb1, sb15, sb2};
enum QFlowControl {fcNone, fcSoftware, fcHardware};

struct QComPortParams
{
    QString portName;
    unsigned long baudRate;
    QDataBits dataBits;
    QStopBits stopBits;
    QParity parity;
    QFlowControl flowControl;
};


class QComPorts : public QObject
{
    Q_OBJECT

public:
    QComPorts();
    ~QComPorts();

    // Достать список всех COM-портов
    QStringList getComPortsNames();
    // Открыть порт
    bool open(QComPortParams* params);
    // Закрыть порт
    bool close();
    // Отправить данные
    void write(QByteArray* sendedData);

private:
    // Таймер, который читает
    QTimer* m_pReceiveTime;
    // Хэндл COM-порта
    HANDLE m_hComm;
    // Настройки порта
    char* m_theDCB;
    
private slots:
    // Слот на таймер
    void timerSlot();

signals:
    // Сигнал на получение 
    void receivePackSignal(QByteArray* sendedData);
};



.cpp
Код

QComPorts::QComPorts()
{
    // Таймер, который читает
    m_pReceiveTime = new QTimer();

    connect(m_pReceiveTime, SIGNAL(timeout()), this, SLOT(timerSlot()));
}


QComPorts::~QComPorts()
{
    delete m_pReceiveTime;
}


// Достать список всех COM-портов
QStringList QComPorts::getComPortsNames()
{
    TCHAR szDevices[65535];
    DWORD dwChars = QueryDosDevice(NULL, szDevices, 65535);

    // Перекинем эту строку в QString
    QString szDeviceList = "";
    QChar chTmp;
    QStringList strList;
    QStringList result;


    if (dwChars) 
    {
        int nZeroCnt = 0;
        int i = 0;
        for (;;)
        {
            // Если текущий символ нулевой, то увеличим к-во нулевых символов
            if (szDevices[i] == 0) nZeroCnt++;
            // Иначе обнулим счетчик нулей
            else nZeroCnt = 0;

            // Если более одного нуля подряд, то выходим из цикла
            if (nZeroCnt > 1) break;

            chTmp = szDevices[i];
            szDeviceList += chTmp;

            i++;
        }
    }

    // Разобьем эту строку на несколько по символу #0
    strList = szDeviceList.split(QChar(0));

    // Выберем только COM-порты
    for (int i = 0; i < strList.size(); i++)
    {
        // Если строка начинается с "COM"
        if ((strList[i].left(3).toUpper() == "COM") &&
            // А оставшийся кусок - это число от 1 до 255
            (strList[i].mid(3).toInt() >= 1) &&
            (strList[i].mid(3).toInt() <= 255))
        {
            result.push_back(strList[i]);
        }
    }

    return result;
}


// Открыть порт
bool QComPorts::open(QComPortParams* params)
{
    QString szPort;
    szPort = QString("\\\\.\\") + params->portName;

    // Связаться с портом
    m_hComm = CreateFileA(szPort.toAscii().data(),  
                        GENERIC_READ | GENERIC_WRITE, 
                        0, 
                        0, 
                        OPEN_EXISTING,
                        FILE_FLAG_OVERLAPPED,
                        0);

    // 
    if (m_hComm == INVALID_HANDLE_VALUE)
    {
        showMessage("Метод bool QComPorts::open" + QString(10) + 
                    "Ошибка в при открытии порта");
        return false;
    }

    m_theDCB = new char [sizeof(DCB)];

    // Достанем состояние порта
    if (!GetCommState(m_hComm, LPDCB(m_theDCB)))
    {
        showMessage("Метод bool QComPorts::open" + QString(10) + 
                    "Ошибка при исполнения GetCommState()");
        return false;
    }


    // Устанавливаем параметры соединения
    LPDCB(m_theDCB)->BaudRate = params->baudRate;
    LPDCB(m_theDCB)->ByteSize = params->dataBits;
    LPDCB(m_theDCB)->StopBits = params->stopBits;
    LPDCB(m_theDCB)->Parity = params->parity;

    // FlowControl
    // Эти всегда одинаковые
    LPDCB(m_theDCB)->XonChar = 17;
    LPDCB(m_theDCB)->XoffChar = 19;
    LPDCB(m_theDCB)->fOutxDsrFlow = false;
    LPDCB(m_theDCB)->fDtrControl = 0;
    LPDCB(m_theDCB)->fDsrSensitivity = false;
    LPDCB(m_theDCB)->fTXContinueOnXoff = false;

    // Эти почти не меняются
    LPDCB(m_theDCB)->fOutxCtsFlow = false;
    LPDCB(m_theDCB)->fRtsControl = 0;
    LPDCB(m_theDCB)->fOutX = false;
    LPDCB(m_theDCB)->fInX = false;

    switch (params->flowControl)
    {
    case fcNone:
        break;

    case fcHardware:
        LPDCB(m_theDCB)->fOutxCtsFlow = true;
        LPDCB(m_theDCB)->fRtsControl = 2;
        break;

    case fcSoftware:
        LPDCB(m_theDCB)->fOutX = true;
        LPDCB(m_theDCB)->fInX = true;
        break;
    }


    // Изменим настройки порта
    if (!SetCommState(m_hComm, LPDCB(m_theDCB)))
    {
        showMessage("Метод bool QComPorts::open" + QString(10) + 
                    "Ошибка при исполнения SetCommState()");
        return false;
    }

    // Запускаем таймер
    m_pReceiveTime->setInterval(50);
    m_pReceiveTime->start();

    return true;
}


// Закрыть порт
bool QComPorts::close()
{
    delete [] m_theDCB;
    if (CloseHandle ((HANDLE)m_hComm))
    {
        showMessage("Метод bool QComPorts::close" + QString(10) + 
                    "Ошибка при закрытии порта");
        return false;
    } 

    // Останавливаем таймер
    m_pReceiveTime->stop();

    return true;
}


// Отправить данные
void QComPorts::write(QByteArray* sendedData)
{
    DWORD dwToWrite = sendedData->size();

    OVERLAPPED osWrite = {0};
    DWORD dwWritten;
    DWORD dwRes;
    BOOL fRes;
    int nLastError;

    // Create this write operation's OVERLAPPED structure's hEvent.
    osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (osWrite.hEvent == NULL)
    {
        showMessage("Ошбика создания события");
        return;
    };


    // Issue write.
    if (!WriteFile(m_hComm, sendedData->data(), dwToWrite, &dwWritten, &osWrite)) {
        nLastError = GetLastError();
        if (nLastError != ERROR_IO_PENDING) { 
            // WriteFile failed, but isn't delayed. Report error and abort.
            fRes = FALSE;
    }
      else
      {
         // Write is pending.
         dwRes = WaitForSingleObject(osWrite.hEvent, INFINITE);

         switch(dwRes)
         {
            // OVERLAPPED structure's event has been signaled. 
            case WAIT_OBJECT_0:
                 if (!GetOverlappedResult(m_hComm, &osWrite, &dwWritten, FALSE))
                       fRes = FALSE;
                 else
                  // Write operation completed successfully.
                  fRes = TRUE;
                 break;
            
            default:
                 // An error has occurred in WaitForSingleObject.
                 // This usually indicates a problem with the
                // OVERLAPPED structure's event handle.
                 fRes = FALSE;
                 break;
         }
      }
   }
   else
      // WriteFile completed immediately.
      fRes = TRUE;

   CloseHandle(osWrite.hEvent);
}


// Слот на таймер
void QComPorts::timerSlot()
{
    _COMSTAT cs;
    DWORD error;
    ClearCommError(m_hComm, &error, &cs);
    QByteArray result;


    // Если пришли данные
    if (cs.cbInQue != 0) 
    {
        char* lpBuf;
        lpBuf = (char*)(malloc(cs.cbInQue));

        DWORD dwRead;
        BOOL fWaitingOnRead = FALSE;
        OVERLAPPED osReader = {0};
        bool bReadResult;

        osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

        if (osReader.hEvent == NULL)
        {
//            showMessage("Ошбика создания события");
            return;
        }

        bReadResult = ReadFile(m_hComm, lpBuf, cs.cbInQue, &dwRead, &osReader);
        if (bReadResult)
        {
            result.append(lpBuf, cs.cbInQue);
        }

        ClearCommError(m_hComm, &error, &cs);

        free(lpBuf);
    }

    if (result.size() > 0) 
        emit receivePackSignal(&result);
}


Автор: Alca 26.4.2013, 17:47
Цитата(EKoshelev @ 26.4.2013,  15:52)
Не прошло и полгода, ага.

kuzulis, читал, спасибо.

Слабал следующее (на Qt):

http://qt-project.org/wiki/QtSerialPort

Автор: EKoshelev 29.4.2013, 07:37
Alca, боян. 

В смысле, работает криво местами.

Автор: kuzulis 29.4.2013, 13:32
Цитата

В смысле, работает криво местами. 


Так надожеж баг-репорты слать, а не лялякать, а то так и будет криво работать.  smile 

Автор: EKoshelev 30.4.2013, 07:12
Цитата

а то так и будет криво работать

Ну и пусть. Я свои проблемы решил. Вроде.

Автор: bsa 30.4.2013, 07:26
EKoshelev, если не секрет, а что же там криво работает? Этот класс уже часть Qt, так что может поделишься?

Автор: xvr 30.4.2013, 12:01
Цитата(EKoshelev @  29.4.2013,  07:37 Найти цитируемый пост)
В смысле, работает криво местами. 

Твоя версия работает криво не местами, а всегда.  smile На каждое чтение из порта гарантированная утечка памяти и ресурсов.

Автор: EKoshelev 7.5.2013, 12:08
bsa, чё, серьёзно? А какой версии Qt? 

Криво работало? Сам не проверял толком, но коллеги говорят, что не всегда устанавливались настройки порта (в частности скорость передачи) и осцилограф вместо прямоугольной диаграммы местами показывал короткие аналоговые. Возможно, я где-то накосячил, но там вроде и ошибаться-то негде.

Я, кстати, почему спрашиваю про версию Qt. Не помню где, слышал, что в пятую SerialPort не попал. По крайней мере в самую первую из пятых. И если я не ошибаюсь, значит на то, видимо, были причины.




xvr, Может подскажешь в каком месте? Если malloc, то уже поправил.

Автор: chaos 7.5.2013, 12:40
Если не изменяет память: конфигурировать порт нужно после его открытия! "Наступал" давненько

Автор: Dem_max 8.5.2013, 06:44
Цитата

Если не изменяет память: конфигурировать порт нужно после его открытия! "Наступал" давненько

Все верное. А иначе что конфигурировать то ???

Автор: bsa 8.5.2013, 14:10
Цитата(EKoshelev @  7.5.2013,  13:08 Найти цитируемый пост)
bsa, чё, серьёзно? А какой версии Qt? 

Если не ошибаюсь, будет в 5.1: http://doc-snapshot.qt-project.org/qt5-stable/qtdoc/modules.html
Цитата(EKoshelev @  7.5.2013,  13:08 Найти цитируемый пост)
Я, кстати, почему спрашиваю про версию Qt. Не помню где, слышал, что в пятую SerialPort не попал.
Пропихнули туда только после выхода 5-й версии.

Автор: xvr 16.5.2013, 10:42
Цитата(EKoshelev @  7.5.2013,  12:08 Найти цитируемый пост)
xvr, Может подскажешь в каком месте? Если malloc, то уже поправил.

Угу, а еще в строке 256:
Код

        osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
не закрывается handle на osReader.hEvent - будет утекать по 1 Event'у на каждый вызов

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)