QThread + QSerialPort! Крутим в отдельном потоке работу с СOM портом ( продолжение). + чучуть протокола DCON

Так и не дошли руки (стыдно мне очень перед тов. Alatar ) доработать его.
Задача: Организовать стабильный обмен данными (важен сам прием) дописать немножко терминалку. Получить критику и подзатыльники(если будет за что).
Посылка с устройства идет байтами и не всегда это неразрывный поток. В большинстве случаев между байтами проскакивают паузы. По этой причине нужно немножко подзаморочится… Приведенный в предыдущем моем посте способ приема – банальная подпорка палкой.
Чучуть кода:
connect(&thisPort, SIGNAL(readyRead()),this,SLOT(ReadInPort()));
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Port :: WriteToPort(QByteArray data){
if(thisPort.isOpen()){
thisPort.write(data);
}
}
void Port :: ReadInPort(){
QByteArray data;
data.append(thisPort.readAll());
outPort(data);
}
Тут все просто=))
WriteToPort Если порт открыт то отсылаем в него наши данные (эту магию я не знаю, что и как там происходит).
Прием осуществляется по сигналу readyRead где сразу считывается все принятое из порта. И можно (лучше не надо) отправлять данные парсить и росковыривать дальше. Вот здесь и лежит большая Ж.=))
Приступим: Пример буду делать для протокола DCON (чуть извращенства) команды (да и сам) модуля ввода вывода ADAM4055
1. Правильно(надеюсь) отправляем данные
bool Port :: WriteToPort(QByteArray data)
{//Непосредственно пишем в порт
if(thisPort.isOpen())
{//если порт открыт то пишем
thisPort.write(data);
return thisPort.waitForBytesWritten(TIMEOUT);//ждем пока не запишутся все данные
} else return false;// Если совсем плохо все или порт закрыт
}
При отправке данных WriteToPort вернет нам true – если все прошло хорошо! или же false – если все плохо(произошел сбой при отправке или закончилось время на отправку)
2. Правильно(надеюсь) принимаем данные:
QByteArray Port :: ReadInPort(){
QByteArray data;
while (thisPort.waitForReadyRead(TIMEOUT))
{//Пока не считается все ждем
data +=thisPort.readAll();
int read_num = data.indexOf(END_OF_LINE);//читаем до прихода конца посылки
if (read_num > 0)
{
data = data.left(read_num + 1);//лишние данные после конца посылки убираем
if (Veryfi_CRC(data))//проверка контрольной суммы
{
return data;//Если все в порядке - возвращвем нашу посылку
}
else
{
data.clear();
return data;//Если все в плохо - отправляем "пустоту"
}
}
}
data.clear();
return data;//Если все в плохо - отправляем "пустоту"
}
Здесь тоже ждем:
while (thisPort.waitForReadyRead(TIMEOUT))
{//Пока не считается все ждем
data +=thisPort.readAll();
Ждем промежуток времени TIMEOUT и считываем данные валяющиеся в приемном буфере порта – суммируя их в data. Из цикла выйдем только в двух случая: если закончится время на считывание или же если приняли конец символ конца посылки. Тут уже идет разветвление, если контрольная сумма посылки сошлась – радостно возвращаем нашу посылку, при несовпадении контрольной суммы – возвращается 0.
З.Ы. Тут Следовало бы добавить проверку начала посылки и также обрезать лишнее (все может быть, мало ли что за мусор по каналу связи повалит).
3. Склеиваем все в кучу. Небольшая «служба отправки приема»:
Ну с отправкой все понятно=)) расписывать простыню не буду.
Для приема? Тут уж вариантов несколько, можно принимать по сигналу ReadyRead (как в первом посте, только чучуть модифицировав процедуру) или же после отправки данных сразу смотреть порт.
В протоколе DCON несколько команд не требующих подтверждения о выполнении или же ответа состояния со стороны контроллера. По этой причине напишем прием данными сразу после отправки:
void Port::WriteOut(QByteArray request)
{//Запись в порт данных
bool OK_write = WriteToPort(request);//Запись в порт посылки
QByteArray read_data = ReadInPort();//Считывание данных
if (!(read_data.isNull()) || OK_write)//Проверка на считывание/запипись
outPort("Все отлично!: " + (QString)read_data);
else outPort("Все плохо!");
}
Ну и сама программа:

В текстовом поле:
1) первую команду (два раза) $016CRCcr отправили запрос состояния портов в ответ получили !FF0000CRCcr и !FFС000CRCcr, где 00 а потом С0 — состояние входного порта!
2) вторую команду #0100FFCRCcr записали в выходной порт значение FF. В ответ получили >3E — команда успешно выполнена.
З.Ы. Код примера (Это всего лишь маленький простенький пример) сильно упрошен и для использования нужно добавлять еще кучу всяких проверок, примочек, плюшек, и т.д.
- +1
- 26 марта 2014, 18:09
- kalik
- 1
Файлы в топике:
terminalka2.zip
void Port :: WriteToPort(QByteArray data)
так лучше не делать, ибо будет происходить копирование объекта QbyteArray (а конструктор копирования там ресурсоемкий т. к. выделяется память, копируется буфер и т. д.). Лучше использовать ссылку, в данном лучше случае const ссылку.
void Port :: WriteToPort(const QByteArray & data)
Честно говоря у Вас в программе много мелких «косяков»
Например:
Если метод WriteToPort() вернул false (OK_write), то нет смысла дальше ждать ответа от устройства (ReadInPort()), запрос «не доставлен», ответа на него можно не ожидать, можно сразу возвращать ошибку (выводить «Все плохо»).
А почему Вы не используете механизм исключений? Из-за накладных расходов или просто из-за стиля?
Например:
void Port::WriteOut(QByteArray request)
{//Запись в порт данных
bool OK_write = WriteToPort(request);//Запись в порт посылки
QByteArray read_data = ReadInPort();//Считывание данных
if (!(read_data.isNull()) || OK_write)//Проверка на считывание/запипись
outPort("Все отлично!: " + (QString)read_data);
else outPort("Все плохо!");
}
Если метод WriteToPort() вернул false (OK_write), то нет смысла дальше ждать ответа от устройства (ReadInPort()), запрос «не доставлен», ответа на него можно не ожидать, можно сразу возвращать ошибку (выводить «Все плохо»).
А почему Вы не используете механизм исключений? Из-за накладных расходов или просто из-за стиля?
Честно говоря у Вас в программе много мелких «косяков»Есть такое=)) Стыдно… но есть! От хорошего и правильного пинка по нужному направлению не откажусь=)) Изначально указал:
Получить критику и подзатыльники(если будет за что).Пока учусь — заполняю те пустоты, которые после ВУЗа образовались(стыдно за систему образования).
А почему Вы не используете механизм исключений? Из-за накладных расходов или просто из-за стиля?По многим причинам! Именно в этом случае? Попытка немного упростить код убрав навороты всякие. В основной программе она реализована (кривовато пока) жаль сроки давят… могут руки не дойти поправить
github.com/pixraider/featured-serial-terminal
Уверен, вам будет интересно.
Если отбросить там всякие свистелки, то реализация довольно проста, работает стабильно, можно адаптировать под свой протокол.
Уверен, вам будет интересно.
Если отбросить там всякие свистелки, то реализация довольно проста, работает стабильно, можно адаптировать под свой протокол.
надо бы мне как-нить дооформить статейку про многопоточный обмен с последовательным портом из лазаруза + библиотечки синапс. тоже интересно было курнуть многопоточность лазаруса, а то фризы окошка достали :)
вот тут я слегка разжевал про многопоточность в лазарусе blindage.org/?p=4467
а вот прога, которая использует это dev.blindage.org/home-light-controller/src/333f7bd07e95cdced5c7d8da41f059f913d934dc/multiplatform%20client-mt/?at=default
а вот прога, которая использует это dev.blindage.org/home-light-controller/src/333f7bd07e95cdced5c7d8da41f059f913d934dc/multiplatform%20client-mt/?at=default
вот тут я слегка разжевал про многопоточность в лазарусеПроще говоря — «как в дельфи, только дефайн UseCThreads добавить».
Я только одного не понял — зачем уничтожать объект ser (да и прочие действия выполнять) в обработке каждой ошибки, если у тебя используется try-finally? И нафига вообще использовать try-finally, если блок finally пустой?
опа. не замечал этого. помог ошибку найти. ну а try нужен чтоб ошибки ловить и флажки поднимать нужные. в этой проге оно просто не используется, но обрабатывать ошибки нужно в любом случае, даже если обработки внутри обработки нет ;)
try-finally с пустым finally бесполезен. Это не try-except, оторый с пустым except выполняет функцию подавления исключений. try-finally же только гарантирует выполнение кода finally при выходе и блока try (штатно, по исклюению или команде Exit — неважно).
Но в любом случае, классическая конструкция — это
Кроме того, вылетевшая в таком коде в блоке try птичка не погасится, а полетит прямо к следующему уровню перехвата — вплоть до системной ловушки «программа выполнила недопустимую операцию и будет закрыта», если по пути не встретится блок try-except.
Впрочем, это все верно для Delphi. FPC может отличаться в плане обработки ошибок.
Но в любом случае, классическая конструкция — это
Ser:=TSomeClass.Create;
try
//code
//Здесь можно смело использовать Exit - он выкинет в finally
finally
FreeAndNil(Ser);
//Опционально можно сюда же запихать и прочую общую деинициализацию, тогда проверка ошибок сводится к if Error then Exit;
end;
Кроме того, вылетевшая в таком коде в блоке try птичка не погасится, а полетит прямо к следующему уровню перехвата — вплоть до системной ловушки «программа выполнила недопустимую операцию и будет закрыта», если по пути не встретится блок try-except.
Впрочем, это все верно для Delphi. FPC может отличаться в плане обработки ошибок.
Какая же єто проблема? Так и должно быть=))
Лечится это с помощью пары строк=))
Запись в порт:
Чтение с порта:
Примерно так сделать (на 100 % идеальность кода не гарантирую) будет у Вас все приниматься нормально.
Лечится это с помощью пары строк=))
Запись в порт:
{//Запись данных в порт
if(Port.isOpen())
{
Port.write(data);
while(Port.waitForBytesWritten(Time))//ожидаем завершения записи в порт
{
if(Port.isDataTerminalReady())
return true;//Все в порядке
}
}
return false;//запись не завершена
}
Чтение с порта:
if (Port.isOpen())//если порт открыт
while (Port.waitForReadyRead(Time))//пока не считаем все данные
{
dataRead += Port.readAll();// +
if(dataRead.length() > 0)//если считали хоть что то
{
if (dataRead.indexOf(EOT) > 0 && dataRead.indexOf(STX) >= 0 && //EOT = h04, STX = h02
dataRead.indexOf(EOT) >dataRead.indexOf(STX))//нахождение конца посылки
//&& нахождение начала посылки
{
//Тут ковыряем принятую посылку или еще чего...
}
}
}
Примерно так сделать (на 100 % идеальность кода не гарантирую) будет у Вас все приниматься нормально.
Комментарии (20)
RSS свернуть / развернуть