Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.219.236.62] |
|
Сообщ.
#1
,
|
|
|
В С++ Builder 6 создана программа обмена по COM порту в асинхронном режиме. Программа выполнена на трех потоках VinApi. Ничего особенного. Все просто. Основные функции ниже по тексту
#include <vcl.h> #pragma hdrstop #include "Unit1.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" #include <iostream> #include <windows.h> #include <stdio.h> #include <Math.hpp> #include <math.h> TForm1 *Form1; //--------------------------------------------------------------------------- // дефайны //ёмкость буфера приема #define BUFSIZE_RD 5000 //ёмкость буфера передачи #define BUFSIZE_WR 128 //--------------------------------------------------------------------------- // дескрипторы HANDLE COMport; HANDLE writer; HANDLE reader; HANDLE main; ///будем использовать для операций чтения (см. поток ReadThread) OVERLAPPED overlapped; //будем использовать для операций записи (см. поток WriteThread) OVERLAPPED overlappedwr; //--------------------------------------------------------------------------- // глобальные переменные // строка определения com порта AnsiString com_port; // флаг приема bool FlComRead; // флаг цикла в потоке приема bool flag; bool FlBeg; //приёмный и передающий буферы unsigned char bufrd[BUFSIZE_RD], bufwr[BUFSIZE_WR]; // контрольная сумма пакета передачи unsigned char TxBufCrc; unsigned char PrCanal; unsigned char PrSect; // счетчик приемов unsigned int counterRX; unsigned int counterTX; unsigned int counterRP; unsigned int Dist; unsigned int Distm; // скорость бод unsigned long BaudR; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- // функции //--------------------------------------------------------------------------- // задержка void wait (int wt) { while (wt--); } //--------------------------------------------------------------------------- // Принятые байты в HEX void hexdumpstrRead ( char src[], size_t size) { Form1->Memo2->Clear(); static const char hex[] = "0123456789abcdef"; char *result = (char *)malloc((size > 0)? size*3: 1), *p = result; if (result) { while (size-- > 0) { *p++ = hex[(*src >> 4) & 0x0f]; *p++ = hex[*src++ & 0x0f]; *p++ = ' '; } p[(p > result)? -1: 0] = 0; } Form1->Memo2->Lines->Add(result); } //--------------------------------------------------------------------------- // Переданные байты в HEX void hexdumpstrWrite ( char src[], size_t size) { Form1->Memo1->Clear(); static const char hex[] = "0123456789abcdef"; char *result = (char *)malloc((size > 0)? size*3: 1), *p = result; if (result) { while (size-- > 0) { *p++ = hex[(*src >> 4) & 0x0f]; *p++ = hex[*src++ & 0x0f]; *p++ = ' '; } p[(p > result)? -1: 0] = 0; } Form1->Memo1->Lines->Add(result); } //--------------------------------------------------------------------------- // Вывод принятых байтов на экран void ReadPrinting(DWORD btr) { char Buffer[20]; unsigned int nb, tm; String stroka, temp_str; counterRP++; // sprintf(Buffer," %d", counterRP); // Form1->Label9->Caption = Buffer; // Перенос буфера в строку. for(UINT32 i=0; i<btr ; i++) { stroka = stroka + IntToHex(bufrd[i], 2) + ' '; } // кличество принятых байт // Form1->Panel3->Caption = "Всего принято байт: " + IntToStr(counterRX); // контрольные символы for (UINT32 i = 0; i < sizeof(bufrd); i++) { if(bufrd[i] == 0xa1 && bufrd[i + 1] == 0xa2 && !FlBeg) { FlBeg = 1; nb = ((bufrd[i + 2] << 8) | bufrd[i + 3]); sprintf(Buffer,"Кол. инфо байт в пакете: %d", nb); Form1->Panel2->Caption = Buffer; Form1->Memo2->Clear(); // wait(30000000); // ЗАПЛАТКА. if(Form1 -> CheckBox4 -> Checked) Sleep(100); else Sleep(400); Form1->Panel1->Caption = ""; // if(!FlComRead) FlComRead = 1; } // определение последнего канала else if(bufrd[i] == 0x55 && bufrd[i + 1] == 0xaa && bufrd[i + 3] == 0x75) { // tm = bufrd[i + 6]; // Sleep(100); // wait(300000); // ЗАПЛАТКА. if(!FlComRead) FlComRead = 1; break; } } // количество принятых байт Form1->Panel3->Caption = "Всего принято байт: " + IntToStr(counterRX); // Вывод Form1->Memo2->Lines->Add(stroka); // Очистить буфер (чтобы данные не накладывались друг на друга). memset(bufrd, 0, BUFSIZE_RD); } //--------------------------------------------------------------------------- // Поток чтения DWORD WINAPI ReadThread( LPVOID lpParam ) { // буфер для вывода строк char Buffer[20]; // структура текущего состояния порта, // используется для определения количества принятых в порт байт COMSTAT comstat; // переменная temp используется в качестве заглушки DWORD btr, temp, mask, signal; // создать сигнальный объект-событие для асинхронных операций overlapped.hEvent = CreateEvent(NULL, true, true, NULL); // установить маску на срабатывание по событию приёма байта в порт SetCommMask(COMport, EV_RXCHAR); // пока поток не будет прерван, выполняем цикл // while(1) while(!flag) { //ожидать события приёма байта (это и есть перекрываемая операция) WaitCommEvent(COMport, &mask, &overlapped); //приостановить поток до прихода байта signal = WaitForSingleObject(overlapped.hEvent, INFINITE); //если событие прихода байта произошло if(signal == WAIT_OBJECT_0) { //проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent if(GetOverlappedResult(COMport, &overlapped, &temp, true)) { //если произошло именно событие прихода байта if((mask & EV_RXCHAR)!=0) { // Sleep(10); // нужно заполнить структуру COMSTAT ClearCommError(COMport, &temp, &comstat); // и получить из неё количество принятых байтов btr = comstat.cbInQue; // если действительно есть байты для чтения if(btr) { // Sleep(10); // прочитать байты из порта в буфер программы ReadFile(COMport, bufrd, btr, &temp, &overlapped); // увеличиваем счётчик байтов counterRX+=btr; // sprintf(Buffer," %d", counterRX); // Form1->Label9->Caption = Buffer; // вызываем функцию для вывода данных на экран ReadPrinting(btr); // hexdumpstrRead(bufrd, 846); } } } } Sleep(1); } // таймаут потока // Sleep(1); CloseHandle(overlapped.hEvent); } //--------------------------------------------------------------------------- // Поток записи WriteThread //главная функция потока, выполняет передачу байтов из буфера в COM-порт DWORD WINAPI WriteThread(LPVOID lpParam) { CONST HANDLE hMutex = (CONST HANDLE)lpParam; // temp - переменная-заглушка DWORD temp, signal; // текущий рабочий буфер char Buffer[20]; //создать событие overlappedwr.hEvent = CreateEvent(NULL, true, true, NULL); while(1) { //очистить передающий буфер порта PurgeComm(COMport, PURGE_TXCLEAR); //записать байты в порт (перекрываемая операция!) // WriteFile(COMport, bufwr, strlen(bufwr), &temp, &overlappedwr); WriteFile(COMport, bufwr, sizeof(bufwr), &temp, &overlappedwr); //приостановить поток, пока не завершится перекрываемая операция WriteFile signal = WaitForSingleObject(overlappedwr.hEvent, INFINITE); //если операция завершилась успешно if((signal == WAIT_OBJECT_0) && (GetOverlappedResult(COMport, &overlappedwr, &temp, true))) { //вывести сообщение об этом в строке состояния sprintf(Buffer,"Передача %d прошла успешно", counterTX); Form1->Panel1->Caption = Buffer; } //иначе вывести в строке состояния сообщение об ошибке else {Form1->Panel1->Caption = "Ошибка передачи";} // Приостановить поток записи SuspendThread(writer); Sleep(1); } // таймаут в потоке // Sleep(1); } //--------------------------------------------------------------------------- // Поток main DWORD WINAPI MainThread(LPVOID lpParam) { // текущий рабочий буфер char Buffer[20]; while(1) { // если принят пакет if(FlComRead) { FlBeg = 0; FlComRead = 0; sprintf(Buffer,"Дистанция: %d м", Distm); Form1->Label16->Caption = Buffer; // включение режима снятия характеристики if(Form1->CheckBox1 -> Checked) { bufwr[126] |= (1 << 7); Form1->CSpinEdit3->Enabled = true; Form1->CSpinEdit4->Enabled = true; } else { bufwr[126] &= ~(1 << 7); Form1->CSpinEdit3->Enabled = false; Form1->CSpinEdit4->Enabled = false; } // включение режима снятия характеристики if(Form1->CheckBox2 -> Checked) { bufwr[123] |= (1 << 2); bufwr[126] = 80; } else { bufwr[123] &= ~(1 << 2); } // контрольная сумма в 0 TxBufCrc = 0; // подсчет контрольной суммы пакета управл. for(int i = 1; i < 127; i++) { TxBufCrc = TxBufCrc + bufwr[i]; } // контрольная сумма упр. пакета bufwr[127] = TxBufCrc; // вывод управл пакета в Memo 1 hexdumpstrWrite(bufwr,128); // номер передачи counterTX++; // количество принятых байт в 0 counterRX = 0; //чтобы данные не накладывались друг на друга //очистить передающий буфер порта PurgeComm(COMport, PURGE_TXCLEAR); // включение передачи if(Form1->CheckBox3 -> Checked) { //активировать поток записи данных в порт ResumeThread(writer); } } Sleep(1); } // таймаут в потоке // Sleep(1); } //--------------------------------------------------------------------------- // Com установки void Com_Setting() { COMMTIMEOUTS timeouts; com_port += "\\\\.\\"; // com_port.c_str() com_port += Form1->ComboBox1->Text; COMport = CreateFile(Form1->ComboBox1->Text.c_str(),GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if(COMport==INVALID_HANDLE_VALUE) { if(GetLastError()==ERROR_FILE_NOT_FOUND) { MessageBox(NULL,"serial port does not exist.","",MB_OK); } MessageBox(NULL,"serial port error","", MB_OK); } DCB dcbSerialParams = {0}; dcbSerialParams.DCBlength=sizeof(dcbSerialParams); if (!GetCommState(COMport, &dcbSerialParams)) { MessageBox(NULL,"getting state error\n","",MB_OK); } dcbSerialParams.BaudRate=BaudR; dcbSerialParams.ByteSize=8; dcbSerialParams.StopBits=ONESTOPBIT; dcbSerialParams.Parity=NOPARITY; if(!SetCommState(COMport, &dcbSerialParams)) { MessageBox(NULL,"error setting serial port state\n","",MB_OK); } // Установка таймаутов. timeouts.ReadIntervalTimeout = 10; // таймаут между двумя символами timeouts.ReadTotalTimeoutMultiplier = 0; // общий таймаут операции чтения timeouts.ReadTotalTimeoutConstant = 0; // константа для общего таймаута операции чтения timeouts.WriteTotalTimeoutMultiplier = 0; // общий таймаут операции записи timeouts.WriteTotalTimeoutConstant = 0; // константа для общего таймаута операции записи // Загрузить структуру таймаутов в порт. if(!SetCommTimeouts(COMport, &timeouts)) { // Если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния. MessageBox(NULL,"Не удалось установить тайм-ауты\n","",MB_OK); return; } // Установить размеры очередей приёма и передачи SetupComm(COMport,2000,2000); // Очистка буферов порта PurgeComm(COMport,PURGE_RXCLEAR); // очистить строку имени порта com_port = ""; } Теперь стоит задаче переместить все в VisualStudio 2008 поскольку к данному коду необходимо будет приклепать сложную графику GLUT. Подскажите пожалуйста как данный код заставить работать в VisualStudio 2008? Пока совершенно не врубаюсь каким образом это можно сделать. Пробовал делать в WindowsForm используя компонент serialPort. Может я не прав но похоже полная ерунда. Компонент не приспособлен для асинхронного режима. А нужно повторить именно асинхронный режим COM порта. Это связано с жесткими временными рамками приема и ответной передачи данных. Если не сложно, подскажите пожалуйста коротко путь по которому функции данного кода + потоки можно приспособить в WindowsForm VisualStudio 2008. Спасибо. |
Сообщ.
#2
,
|
|
|
тупо копипаст не пробовал?
|
Сообщ.
#3
,
|
|
|
В VisualStudio 2008 можно писать как на практически том же C++, что и Билдере, так и на шарпе.
Воспроизвести С++ код из Билдера несложно, т.к. в данном случае VCL-ность не задействована (правда, и синхронизации с главным потоком я не заметил) Компонент serialPort относится к шарпу, и работает именно асинхронно. |
Сообщ.
#4
,
|
|
|
Для обмена в асинхронном режиме зачем три потока? Ну один, ну два, ну никак не три.
Можно это в шарпе сделать с SerialPort, легко и удобно, только со сложной графикой windowsforms не очень подходит в плане производительности. |
Сообщ.
#5
,
|
|
|
Спасибо. Если так, тогда как это делается на практике? WindowsForm необходим, поскольку нужен интерфейс с кнопками и окнами вывода информации. Сам интерфейс составить несложно. Не врубаюсь как теперь туда подключить потоки с кодом внутри и необходимыми функциями. Если можно коротко по пунктам как это сделать.
|
Сообщ.
#6
,
|
|
|
Поток приёма уже есть внутри serialPort. Его событие DataReceived вызывается из этого (вторичного) потока, так что не забыть про синхронизацию.
|
Сообщ.
#7
,
|
|
|
Цитата Acvarif @ Спасибо. Если так, тогда как это делается на практике? WindowsForm необходим, поскольку нужен интерфейс с кнопками и окнами вывода информации. Сам интерфейс составить несложно. Не врубаюсь как теперь туда подключить потоки с кодом внутри и необходимыми функциями. Если можно коротко по пунктам как это сделать. Сделай сначала каркас на шарпе с сериалпортом, чтением записью кнопками, а потом переноси туда свои функции с байтовыми операциями, ну тут рутина, менять кое-что придется, это ты сам. |
Сообщ.
#8
,
|
|
|
Пробововал serialPort Может толком не разобрался. Но получилась ерунда. Полнейшая временная разсинхронизация. В чем суть. Потоки приема и передачи в программе на Builder жестко привязаны по времени. Тоесть как только закончился прием непрерывного пакета данных формируется флаг разрешения для передачи обратно так называемого пакета управления. Время между окончанием приема и началом обратной передачи должно уложиться в 5..10 ms. Там организован прием по событию прихода байта на порт. Поэтому скорее всего придется запускать подобные потоки в WindowsForm Вопрос. Можно-ли будет встроить в потоки код типа, где имеются функции типа WaitCommEvent и т.п.?
// буфер для вывода строк char Buffer[20]; // структура текущего состояния порта, // используется для определения количества принятых в порт байт COMSTAT comstat; // переменная temp используется в качестве заглушки DWORD btr, temp, mask, signal; // создать сигнальный объект-событие для асинхронных операций overlapped.hEvent = CreateEvent(NULL, true, true, NULL); // установить маску на срабатывание по событию приёма байта в порт SetCommMask(COMport, EV_RXCHAR); // пока поток не будет прерван, выполняем цикл // while(1) while(!flag) { //ожидать события приёма байта (это и есть перекрываемая операция) WaitCommEvent(COMport, &mask, &overlapped); //приостановить поток до прихода байта signal = WaitForSingleObject(overlapped.hEvent, INFINITE); //если событие прихода байта произошло if(signal == WAIT_OBJECT_0) { //проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent if(GetOverlappedResult(COMport, &overlapped, &temp, true)) { //если произошло именно событие прихода байта if((mask & EV_RXCHAR)!=0) { // Sleep(10); // нужно заполнить структуру COMSTAT ClearCommError(COMport, &temp, &comstat); // и получить из неё количество принятых байтов btr = comstat.cbInQue; // если действительно есть байты для чтения if(btr) { // Sleep(10); // прочитать байты из порта в буфер программы ReadFile(COMport, bufrd, btr, &temp, &overlapped); // увеличиваем счётчик байтов counterRX+=btr; // sprintf(Buffer," %d", counterRX); // Form1->Label9->Caption = Buffer; // вызываем функцию для вывода данных на экран ReadPrinting(btr); // hexdumpstrRead(bufrd, 846); } } } } Sleep(1); Кстати, а почему WindowsForm c графикой GLUT непроизводителен? Что производительнее? Да, вот источник который использовался для построения кода http://piclist.ru/S-COM-THREAD-RUS/S-COM-THREAD-RUS.html#22 |
Сообщ.
#9
,
|
|
|
>Пробововал serialPort
Как именно пробовал? |
Сообщ.
#10
,
|
|
|
Цитата Acvarif @ Потоки приема и передачи в программе на Builder жестко привязаны по времени. Тоесть как только закончился прием непрерывного пакета данных формируется флаг разрешения для передачи обратно так называемого пакета управления. Вот в этом-то тебе и повезо. Дело в том, что ф-ии ReadFile/WriteFile потоко-не-безопасны. В твоём примере синхронизации я не заметил, т.е. ты фактически синхронизируешь операции общим ходом алгоритма. Значит, что-нибудь когда-нибудь заглючит.. |
Сообщ.
#11
,
|
|
|
Вот поток в котором осуществляется прием пакета с последующей передачей. Прием принимает, но не всегда, а передача идет не сразу за примом. Разрывы большие, не стабильные по времени. Иногда до 1 сек. В то время как обмен между компом и устройством производится один раз в сек.
void Form1::ThreadWork(){ buf_rez = Convert::ToInt32( textBox1->Text ); while(ThreadWork_Stop){ //this->Invoke(delInstatnce); mesRead = this->serialPort1->ReadExisting(); BufferSendCom[0] = bit1; BufferSendCom[1] = bit2; BufferSendCom[2] = bit3; //BufferSendCom[3] = bit4; BufferSendCom[124] = bit126; //timer1->Enabled = true; //this->Invoke(delInstatnce); if(mesRead->Length > 1 ){ std::cout << Convert::ToInt32(mesRead[0]) << "ddddsdsd" << std::endl; if( Convert::ToByte(mesRead[0]) == 63){ //form2->Send_Buffer(mesRead); for(int i = 0; i <= 127; i++){ bufer += BufferSendCom[i]; BF += BufferSendCom[i]; } BufferSendCom[127] = bufer; BF += BufferSendCom[127]; this->serialPort1->Write(BF); //Show_Send(); BF = ""; bufer = NULL; } } //std::cout << "//////////////////////" << std::endl; //***************************** mesRead = ""; Sleep(1100); }//while } Да собственно в коде нет ничего секретного, да к тому же он сырой. В срепке проект в архиве. Прикреплённый файлAmuleTest.rar (159,24 Кбайт, скачиваний: 116) |
Сообщ.
#12
,
|
|
|
похоже, что осуществляется поллинг (опрос, нету ли чего в порту) - это нехорошо. Как уже сказано, у порта есть событие OnDataReceived, ему нужно назначить обработчик, и при получении данных обработчик вызовется (по сути - аналог WaitCommEvent)
(и с синхронизхацией опять беда - в поточной, как я понимаю, функции, прямой доступ к GUI Convert::ToInt32( textBox1->Text )) |
Сообщ.
#13
,
|
|
|
В общих чертах понятно. Если OnDataReceived сработает так как WaitCommEvent то должно быть то, что нужно. Если не сложно, можно пример кода serialPort + OnDataReceived.
Да, синхронизация никакая. Буду поправлять. Это второй этам. Пока нужно понять какой дорожкой пойти. Дальше работать с serialPort или.. |
Сообщ.
#15
,
|
|
|
Спасибо. Становится понятней. Для проверки работы по событию сделал так
private: System::Void serialPort1_DataReceived(System::Object^ sender, System::IO::Ports::SerialDataReceivedEventArgs^ e) { counterRX++; std::cout << Convert::ToInt32(counterRX) << std::endl; } Работает. Счетчик считает событя по очереди как и должно быть. Попробую подключить функцию накопления данных в буфере. Добавлено Для дальнейшей проверки пытаюсь вывести часть принятых по сом данных в окно textBox2 (со свойством Multiline - true) private: System::Void serialPort1_DataReceived(System::Object^ sender, System::IO::Ports::SerialDataReceivedEventArgs^ e) { mesRead = this->serialPort1->ReadExisting(); for (int i = 0; i < 30; i++) { this->textBox2->Text += mesRead[i].ToString()+"\r\n"; } } Возникает ошибка Cross-thread operation not valid: Control 'textBox2' accessed from a thread other than the thread it was created on. В чем тут хитрость? Почему операция кросс-поточная? |
Сообщ.
#16
,
|
|
|
кто-то не читает, что ему пишут, например, в посте #6
http://stackoverflow.com/questions/1159094...r-of-serialport |
Сообщ.
#17
,
|
|
|
Спасибо. Понял. Пробую так http://stackoverflow.com/questions/1077536...m-a-thread-othe Только у меня очевидно проблемы с синтаксисом в VisualStudio2008
delegate void SetTextCallback(string text); private: System::Void SetText(string text) { // InvokeRequired required compares the thread ID of the // calling thread to the thread ID of the creating thread. // If these threads are different, it returns true. if (this->textBox2->InvokeRequired) { SetTextCallback d = new SetTextCallback(SetText); this->Invoke(d, new object[] { text }); } else { this->textBox2->Text = text; } } private: System::Void serialPort1_DataReceived(System::Object^ sender, System::IO::Ports::SerialDataReceivedEventArgs^ e) { txt += serialPort1.ReadExisting().ToString(); SetText(txt.ToString()); } Такой код выдает ошибки Error 1 error C3149: 'AmuleTest::Form1::SetTextCallback' : cannot use this type here without a top-level '^' d:\mydesignsxp\visualstprj\visualc++prj\amuletest\amuletest\Form1.h 984 AmuleTest Error 2 error C3867: 'AmuleTest::Form1::SetText': function call missing argument list; use '&AmuleTest::Form1::SetText' to create a pointer to member d:\mydesignsxp\visualstprj\visualc++prj\amuletest\amuletest\Form1.h 984 AmuleTest Error 3 error C2750: 'AmuleTest::Form1::SetTextCallback' : cannot use 'new' on the reference type; use 'gcnew' instead d:\mydesignsxp\visualstprj\visualc++prj\amuletest\amuletest\Form1.h 984 AmuleTest Error 4 error C3350: 'AmuleTest::Form1::SetTextCallback' : a delegate constructor expects 2 argument(s) d:\mydesignsxp\visualstprj\visualc++prj\amuletest\amuletest\Form1.h 984 AmuleTest Error 5 error C2061: syntax error : identifier 'object' d:\mydesignsxp\visualstprj\visualc++prj\amuletest\amuletest\Form1.h 985 AmuleTest Error 6 error C2143: syntax error : missing ')' before '{' d:\mydesignsxp\visualstprj\visualc++prj\amuletest\amuletest\Form1.h 985 AmuleTest Error 7 error C2143: syntax error : missing ';' before '{' d:\mydesignsxp\visualstprj\visualc++prj\amuletest\amuletest\Form1.h 985 AmuleTest Error 8 error C2143: syntax error : missing ';' before '}' d:\mydesignsxp\visualstprj\visualc++prj\amuletest\amuletest\Form1.h 985 AmuleTest Error 9 error C2059: syntax error : ')' d:\mydesignsxp\visualstprj\visualc++prj\amuletest\amuletest\Form1.h 985 AmuleTest Error 10 error C2664: 'void System::Windows::Forms::Control::Text::set(System::String ^)' : cannot convert parameter 1 from 'std::string' to 'System::String ^' d:\mydesignsxp\visualstprj\visualc++prj\amuletest\amuletest\Form1.h 989 AmuleTest Error 11 error C2065: 'txt' : undeclared identifier d:\mydesignsxp\visualstprj\visualc++prj\amuletest\amuletest\Form1.h 995 AmuleTest Error 12 error C2228: left of '.ReadExisting' must have class/struct/union d:\mydesignsxp\visualstprj\visualc++prj\amuletest\amuletest\Form1.h 995 AmuleTest Error 13 error C2228: left of '.ToString' must have class/struct/union d:\mydesignsxp\visualstprj\visualc++prj\amuletest\amuletest\Form1.h 995 AmuleTest Error 14 error C2065: 'txt' : undeclared identifier d:\mydesignsxp\visualstprj\visualc++prj\amuletest\amuletest\Form1.h 996 AmuleTest Error 15 error C2228: left of '.ToString' must have class/struct/union d:\mydesignsxp\visualstprj\visualc++prj\amuletest\amuletest\Form1.h 996 AmuleTest |
Сообщ.
#18
,
|
|
|
ну ты и чайник
|
Сообщ.
#19
,
|
|
|
Сделал небольшой тест на C#
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; namespace CrossThreadDemo { public partial class Form1 : Form { String txt; int counterRP; void ComSetings(){ this.serialPort1.BaudRate = 115200; this.serialPort1.PortName = "COM1"; this.serialPort1.ReadBufferSize = 4800; try{ this.serialPort1.Open(); this.label1.BackColor = System.Drawing.Color.Green; } catch { this.label1.BackColor = System.Drawing.Color.Red; } } void StopCom(){ this.serialPort1.Close(); this.label1.BackColor = System.Drawing.Color.Red; } delegate void SetTextCallback(string text); private void SetText(string text) { // InvokeRequired required compares the thread ID of the // calling thread to the thread ID of the creating thread. // If these threads are different, it returns true. if (this.textBox1.InvokeRequired) { SetTextCallback d = new SetTextCallback(SetText); this.Invoke(d, new object[] { text }); } else { this.textBox1.Text = text; } } public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { } private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { txt = serialPort1.ReadExisting().ToString(); SetText(txt); } private void button1_Click(object sender, EventArgs e) { ComSetings(); } private void button2_Click(object sender, EventArgs e) { StopCom(); } } } Работает. В окне textBox1 передаваемая строка. Но опять проблема. Как теперь в окне textBox1 показать принимаемые данные в HEX виде, примерно так как в программе на Builder Прикреплённая картинка
Еще загвоздка. Как по собитию прихода очередного байта получить количество уже пришедших на порт байт? WinApi это просто //проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent if(GetOverlappedResult(COMport, &overlapped, &temp, true)) { //если произошло именно событие прихода байта if((mask & EV_RXCHAR)!=0) { // нужно заполнить структуру COMSTAT ClearCommError(COMport, &temp, &comstat); // и получить из неё количество принятых байтов btr = comstat.cbInQue; // если действительно есть байты для чтения if(btr) { // прочитать байты из порта в буфер программы ReadFile(COMport, bufrd, btr, &temp, &overlapped); // увеличиваем счётчик байтов counterRX+=btr; // вызываем функцию для вывода данных на экран ReadPrinting(btr); } } } |
Сообщ.
#20
,
|
|
|
да какой С# здесь раздел Visual С++ MFC
я твой пример тупо копипаст + MFC и все |
Сообщ.
#21
,
|
|
|
Копипаст это как? Что такое копипаст?
Поясните пожалуйста. Это типа ворованный код? Код проги на Builder мой Прикреплённый файлTest.rar (11,68 Кбайт, скачиваний: 72) . Прога работает без проблем, кроме одной - тяжело работать с графикой OpenGl. Поэтому пытаюсь перевести все в VisualStudio. C# или C++ без разницы. Если у Вас имеется пример как в Visual Studio работать с VinApi и COM портом могли-бы поделиться... |
Сообщ.
#22
,
|
|
|
Цитата Acvarif @ Еще загвоздка. Как по собитию прихода очередного байта получить количество уже пришедших на порт байт? WinApi это просто //проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent if(GetOverlappedResult(COMport, &overlapped, &temp, true)) { //если произошло именно событие прихода байта if((mask & EV_RXCHAR)!=0) { // нужно заполнить структуру COMSTAT ClearCommError(COMport, &temp, &comstat); // и получить из неё количество принятых байтов btr = comstat.cbInQue; // если действительно есть байты для чтения if(btr) { // прочитать байты из порта в буфер программы ReadFile(COMport, bufrd, btr, &temp, &overlapped); // увеличиваем счётчик байтов counterRX+=btr; // вызываем функцию для вывода данных на экран ReadPrinting(btr); } } } Нет. Содержимое "overlapped" структуры прямо указывает на переданное количество байт. "GetOverlappedResult" потрошит эту структуру, хотя это можно было делать и вручную. Но поскольку конкретная структура использовалась для операции чтения, значит "GetOverlappedResult(COMport, &overlapped, &Bytes, true)" сразу передаёт число полученных байт, готовых для чтения. Дальше: if(Bytes) { // ... /// добываем данные - в указанном операцией ReadFile (выполненной ранее) буфере /// есть количество "Bytes" байт, полученных с порта } |
Сообщ.
#23
,
|
|
|
Спасибо. Понятно. Можно и так попробовать. В любом случае код Builder6 + WinApi работает без проблем. Перенос кода на VisualStudio нужен для дальнейшей работы с GLUT. На Builder6 с этим делом неважно. Поэтому и прощупываю варианты. Вариант с serialPort на первый взгляд работает. Но опять уткнулся в проблему подсчета и отображения принятых байт в HEX виде в окне textBox. Хотя строку принимает и отображает как надо. По ходу еще проблема как очищать окно textBox перед очередным приемом пакета байт. (период прихода пакетов 1 сек.)
И все же главный вопрос. Можно-ли в VisualStudio использовать непосредственно функции WaitCommEvent(COMport, &mask, &overlapped) GetOverlappedResult(COMport, &overlapped, &Bytes, true); и им подобные? Если да то несколько слов о том как. Какой вид проекта для этого закладывать WindowsForm C++, MFC, win32... ? |
Сообщ.
#24
,
|
|
|
Цитата Acvarif @ И все же главный вопрос. Можно-ли в VisualStudio использовать непосредственно функции WaitCommEvent(COMport, &mask, &overlapped) GetOverlappedResult(COMport, &overlapped, &Bytes, true); и им подобные? Если да то несколько слов о том как. Какой вид проекта для этого закладывать WindowsForm C++, MFC, win32... ? Естествено это же Win32 API! MFC в зубы и вперед с песней! WinForms это для .NET и C# |
Сообщ.
#25
,
|
|
|
В зубы это да. Какую библиотеку подключить? Куда подключить? Можно несколько слов, коротко, по шагам.
|
Сообщ.
#26
,
|
|
|
Цитата Acvarif @ И все же главный вопрос. Можно-ли в VisualStudio использовать непосредственно функции WaitCommEvent(COMport, &mask, &overlapped) GetOverlappedResult(COMport, &overlapped, &Bytes, true); и им подобные? Если да то несколько слов о том как. Какой вид проекта для этого закладывать WindowsForm C++, MFC, win32... ? Да, конечно можно. Только "WaitCommEvent" практически не нужна. Для ожидания событий достаточно использовать штатные функции ожидания Виндус. Общая схема такая: 1. Открываем порт в режиме "OVERLAPPED" 2. Заполняем все какие надо структуры, программируем ими порт. 3. Не забываем создать объекты-эвенты. 4. Хэндлы этих объектов заносятся в OVERLAPPED-структуру перед выполнением конкретной операции (чтения или записи). 5. Дальше эти хэндлы массивом отдаём "WaitForMultipleObjects". 6. Когда событие наступит, ожидание закончится. Запустим соотв. обработчик. 7. Распотрошим результат из OVERLAPPED-структуры и начнём всё сначала. Как-то так. Одного потока более чем достаточно. Почитай книгу Агурова. Там хотя и на Паскале, но по API-вызовам понятно, что он делает. --- Лично я использую базовый класс COMM-порт, использующий WINAPI. (с++) После изготовления он занесён в библиотеку, после чего о тонкостях его функционирования можно забыть. Именно поэтому мой рассказ о том, "как это работает" носит самый общий характер. На его основе (наследуясь от него) делается класс COMM-порт для конкретных нужд конкретного приложения. |
Сообщ.
#27
,
|
|
|
Все примерно то же самое я уже делал в проге на Builder6. Правда потоков несколько. Может и лишнее. Все работает.
Я не врубаюсь как код с WINAPI прописать в Wisual Studio. Какой вид проекта для этого открыть MFC, WindovsForm, или...? Собственно мой вопрос не по сути, а по технологии. С хендлами, событиями и т. п справлюсь. Если начинать проект MFC то куда и чего прописывать Напнример в Builder все прописывается в одном едиственном модуле c расширением .cpp #include <vcl.h> #pragma hdrstop #include "Unit1.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" #include <iostream> #include <windows.h> #include <stdio.h> #include <Math.hpp> #include <math.h> TForm1 *Form1; //--------------------------------------------------------------------------- // дефайны //ёмкость буфера приема #define BUFSIZE_RD 5000 //ёмкость буфера передачи #define BUFSIZE_WR 128 //--------------------------------------------------------------------------- // дескрипторы HANDLE COMport; HANDLE writer; HANDLE reader; HANDLE main; ///будем использовать для операций чтения (см. поток ReadThread) OVERLAPPED overlapped; //будем использовать для операций записи (см. поток WriteThread) OVERLAPPED overlappedwr; //--------------------------------------------------------------------------- // глобальные переменные // строка определения com порта AnsiString com_port; // флаг приема bool FlComRead; // флаг цикла в потоке приема bool flag; bool FlBeg; //приёмный и передающий буферы unsigned char bufrd[BUFSIZE_RD], bufwr[BUFSIZE_WR]; // контрольная сумма пакета передачи unsigned char TxBufCrc; unsigned char PrCanal; unsigned char PrSect; // счетчик приемов unsigned int counterRX; unsigned int counterTX; unsigned int counterRP; unsigned int Dist; unsigned int Distm; // скорость бод unsigned long BaudR; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- // функции //--------------------------------------------------------------------------- // задержка void wait (int wt) { while (wt--); } //--------------------------------------------------------------------------- // Принятые байты в HEX void hexdumpstrRead ( char src[], size_t size) { Form1->Memo2->Clear(); static const char hex[] = "0123456789abcdef"; char *result = (char *)malloc((size > 0)? size*3: 1), *p = result; if (result) { while (size-- > 0) { *p++ = hex[(*src >> 4) & 0x0f]; *p++ = hex[*src++ & 0x0f]; *p++ = ' '; } p[(p > result)? -1: 0] = 0; } Form1->Memo2->Lines->Add(result); } //--------------------------------------------------------------------------- // Переданные байты в HEX void hexdumpstrWrite ( char src[], size_t size) { Form1->Memo1->Clear(); static const char hex[] = "0123456789abcdef"; char *result = (char *)malloc((size > 0)? size*3: 1), *p = result; if (result) { while (size-- > 0) { *p++ = hex[(*src >> 4) & 0x0f]; *p++ = hex[*src++ & 0x0f]; *p++ = ' '; } p[(p > result)? -1: 0] = 0; } Form1->Memo1->Lines->Add(result); } //--------------------------------------------------------------------------- // Вывод принятых байтов на экран void ReadPrinting(DWORD btr) { char Buffer[20]; unsigned int nb, tm; String stroka, temp_str; counterRP++; // sprintf(Buffer," %d", counterRP); // Form1->Label9->Caption = Buffer; // Перенос буфера в строку. for(UINT32 i=0; i<btr ; i++) { stroka = stroka + IntToHex(bufrd[i], 2) + ' '; } // кличество принятых байт // Form1->Panel3->Caption = "Всего принято байт: " + IntToStr(counterRX); // контрольные символы for (UINT32 i = 0; i < sizeof(bufrd); i++) { if(bufrd[i] == 0xa1 && bufrd[i + 1] == 0xa2 && !FlBeg) { FlBeg = 1; nb = ((bufrd[i + 2] << 8) | bufrd[i + 3]); sprintf(Buffer,"Кол. инфо байт в пакете: %d", nb); Form1->Panel2->Caption = Buffer; Form1->Memo2->Clear(); // wait(30000000); // ЗАПЛАТКА. if(Form1 -> CheckBox4 -> Checked) Sleep(100); else Sleep(400); Form1->Panel1->Caption = ""; // if(!FlComRead) FlComRead = 1; } // определение последнего канала else if(bufrd[i] == 0x55 && bufrd[i + 1] == 0xaa && bufrd[i + 3] == 0x75) { // tm = bufrd[i + 6]; // Sleep(100); // wait(300000); // ЗАПЛАТКА. if(!FlComRead) FlComRead = 1; break; } } // количество принятых байт Form1->Panel3->Caption = "Всего принято байт: " + IntToStr(counterRX); // Вывод Form1->Memo2->Lines->Add(stroka); // Очистить буфер (чтобы данные не накладывались друг на друга). memset(bufrd, 0, BUFSIZE_RD); } //--------------------------------------------------------------------------- // Поток чтения DWORD WINAPI ReadThread( LPVOID lpParam ) { // буфер для вывода строк char Buffer[20]; // структура текущего состояния порта, // используется для определения количества принятых в порт байт COMSTAT comstat; // переменная temp используется в качестве заглушки DWORD btr, temp, mask, signal; // создать сигнальный объект-событие для асинхронных операций overlapped.hEvent = CreateEvent(NULL, true, true, NULL); // установить маску на срабатывание по событию приёма байта в порт SetCommMask(COMport, EV_RXCHAR); // пока поток не будет прерван, выполняем цикл // while(1) while(!flag) { //ожидать события приёма байта (это и есть перекрываемая операция) WaitCommEvent(COMport, &mask, &overlapped); //приостановить поток до прихода байта signal = WaitForSingleObject(overlapped.hEvent, INFINITE); //если событие прихода байта произошло if(signal == WAIT_OBJECT_0) { //проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent if(GetOverlappedResult(COMport, &overlapped, &temp, true)) { //если произошло именно событие прихода байта if((mask & EV_RXCHAR)!=0) { // Sleep(10); // нужно заполнить структуру COMSTAT ClearCommError(COMport, &temp, &comstat); // и получить из неё количество принятых байтов btr = comstat.cbInQue; // если действительно есть байты для чтения if(btr) { // Sleep(10); // прочитать байты из порта в буфер программы ReadFile(COMport, bufrd, btr, &temp, &overlapped); // увеличиваем счётчик байтов counterRX+=btr; // sprintf(Buffer," %d", counterRX); // Form1->Label9->Caption = Buffer; // вызываем функцию для вывода данных на экран ReadPrinting(btr); // hexdumpstrRead(bufrd, 846); } } } } Sleep(1); } // таймаут потока // Sleep(1); CloseHandle(overlapped.hEvent); } //--------------------------------------------------------------------------- // Поток записи WriteThread //главная функция потока, выполняет передачу байтов из буфера в COM-порт DWORD WINAPI WriteThread(LPVOID lpParam) { CONST HANDLE hMutex = (CONST HANDLE)lpParam; // temp - переменная-заглушка DWORD temp, signal; // текущий рабочий буфер char Buffer[20]; //создать событие overlappedwr.hEvent = CreateEvent(NULL, true, true, NULL); while(1) { //очистить передающий буфер порта PurgeComm(COMport, PURGE_TXCLEAR); //записать байты в порт (перекрываемая операция!) // WriteFile(COMport, bufwr, strlen(bufwr), &temp, &overlappedwr); WriteFile(COMport, bufwr, sizeof(bufwr), &temp, &overlappedwr); //приостановить поток, пока не завершится перекрываемая операция WriteFile signal = WaitForSingleObject(overlappedwr.hEvent, INFINITE); //если операция завершилась успешно if((signal == WAIT_OBJECT_0) && (GetOverlappedResult(COMport, &overlappedwr, &temp, true))) { //вывести сообщение об этом в строке состояния sprintf(Buffer,"Передача %d прошла успешно", counterTX); Form1->Panel1->Caption = Buffer; } //иначе вывести в строке состояния сообщение об ошибке else {Form1->Panel1->Caption = "Ошибка передачи";} // Приостановить поток записи SuspendThread(writer); Sleep(1); } // таймаут в потоке // Sleep(1); } //--------------------------------------------------------------------------- // Поток main DWORD WINAPI MainThread(LPVOID lpParam) { // текущий рабочий буфер char Buffer[20]; while(1) { // если принят пакет if(FlComRead) { FlBeg = 0; FlComRead = 0; sprintf(Buffer,"Дистанция: %d м", Distm); Form1->Label16->Caption = Buffer; // включение режима снятия характеристики if(Form1->CheckBox1 -> Checked) { bufwr[126] |= (1 << 7); Form1->CSpinEdit3->Enabled = true; Form1->CSpinEdit4->Enabled = true; } else { bufwr[126] &= ~(1 << 7); Form1->CSpinEdit3->Enabled = false; Form1->CSpinEdit4->Enabled = false; } // включение режима снятия характеристики if(Form1->CheckBox2 -> Checked) { bufwr[123] |= (1 << 2); bufwr[126] = 80; } else { bufwr[123] &= ~(1 << 2); } // контрольная сумма в 0 TxBufCrc = 0; // подсчет контрольной суммы пакета управл. for(int i = 1; i < 127; i++) { TxBufCrc = TxBufCrc + bufwr[i]; } // контрольная сумма упр. пакета bufwr[127] = TxBufCrc; // вывод управл пакета в Memo 1 hexdumpstrWrite(bufwr,128); // номер передачи counterTX++; // количество принятых байт в 0 counterRX = 0; //чтобы данные не накладывались друг на друга //очистить передающий буфер порта PurgeComm(COMport, PURGE_TXCLEAR); // включение передачи if(Form1->CheckBox3 -> Checked) { //активировать поток записи данных в порт ResumeThread(writer); } } Sleep(1); } // таймаут в потоке // Sleep(1); } //--------------------------------------------------------------------------- // Com установки void Com_Setting() { COMMTIMEOUTS timeouts; com_port += "\\\\.\\"; // com_port.c_str() com_port += Form1->ComboBox1->Text; COMport = CreateFile(Form1->ComboBox1->Text.c_str(),GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if(COMport==INVALID_HANDLE_VALUE) { if(GetLastError()==ERROR_FILE_NOT_FOUND) { MessageBox(NULL,"serial port does not exist.","",MB_OK); } MessageBox(NULL,"serial port error","", MB_OK); } DCB dcbSerialParams = {0}; dcbSerialParams.DCBlength=sizeof(dcbSerialParams); if (!GetCommState(COMport, &dcbSerialParams)) { MessageBox(NULL,"getting state error\n","",MB_OK); } dcbSerialParams.BaudRate=BaudR; dcbSerialParams.ByteSize=8; dcbSerialParams.StopBits=ONESTOPBIT; dcbSerialParams.Parity=NOPARITY; if(!SetCommState(COMport, &dcbSerialParams)) { MessageBox(NULL,"error setting serial port state\n","",MB_OK); } // Установка таймаутов. timeouts.ReadIntervalTimeout = 10; // таймаут между двумя символами timeouts.ReadTotalTimeoutMultiplier = 0; // общий таймаут операции чтения timeouts.ReadTotalTimeoutConstant = 0; // константа для общего таймаута операции чтения timeouts.WriteTotalTimeoutMultiplier = 0; // общий таймаут операции записи timeouts.WriteTotalTimeoutConstant = 0; // константа для общего таймаута операции записи // Загрузить структуру таймаутов в порт. if(!SetCommTimeouts(COMport, &timeouts)) { // Если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния. MessageBox(NULL,"Не удалось установить тайм-ауты\n","",MB_OK); return; } // Установить размеры очередей приёма и передачи SetupComm(COMport,2000,2000); // Очистка буферов порта PurgeComm(COMport,PURGE_RXCLEAR); // очистить строку имени порта com_port = ""; } Пытаюсь создать проект MFC Создается модуль .cpp с кодом типа // MFCtest.cpp : Defines the class behaviors for the application. // #include "stdafx.h" #include "MFCtest.h" #include "MFCtestDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CMFCtestApp BEGIN_MESSAGE_MAP(CMFCtestApp, CWinApp) ON_COMMAND(ID_HELP, &CWinApp::OnHelp) END_MESSAGE_MAP() // CMFCtestApp construction CMFCtestApp::CMFCtestApp() { // TODO: add construction code here, // Place all significant initialization in InitInstance } // The one and only CMFCtestApp object CMFCtestApp theApp; // CMFCtestApp initialization BOOL CMFCtestApp::InitInstance() { // InitCommonControlsEx() is required on Windows XP if an application // manifest specifies use of ComCtl32.dll version 6 or later to enable // visual styles. Otherwise, any window creation will fail. INITCOMMONCONTROLSEX InitCtrls; InitCtrls.dwSize = sizeof(InitCtrls); // Set this to include all the common control classes you want to use // in your application. InitCtrls.dwICC = ICC_WIN95_CLASSES; InitCommonControlsEx(&InitCtrls); CWinApp::InitInstance(); AfxEnableControlContainer(); // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need // Change the registry key under which our settings are stored // TODO: You should modify this string to be something appropriate // such as the name of your company or organization SetRegistryKey(_T("Local AppWizard-Generated Applications")); CMFCtestDlg dlg; m_pMainWnd = &dlg; INT_PTR nResponse = dlg.DoModal(); if (nResponse == IDOK) { // TODO: Place code here to handle when the dialog is // dismissed with OK } else if (nResponse == IDCANCEL) { // TODO: Place code here to handle when the dialog is // dismissed with Cancel } // Since the dialog has been closed, return FALSE so that we exit the // application, rather than start the application's message pump. return FALSE; } Куда в какое место в нем прописываются хендлы, глобальные переменные, функции, да собственно и потоки? Пробую так // MFCtest.cpp : Defines the class behaviors for the application. // #include "stdafx.h" #include "MFCtest.h" #include "MFCtestDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CMFCtestApp BEGIN_MESSAGE_MAP(CMFCtestApp, CWinApp) ON_COMMAND(ID_HELP, &CWinApp::OnHelp) END_MESSAGE_MAP() // CMFCtestApp construction CMFCtestApp::CMFCtestApp() { // TODO: add construction code here, // Place all significant initialization in InitInstance } // The one and only CMFCtestApp object CMFCtestApp theApp; // CMFCtestApp initialization BOOL CMFCtestApp::InitInstance() { // InitCommonControlsEx() is required on Windows XP if an application // manifest specifies use of ComCtl32.dll version 6 or later to enable // visual styles. Otherwise, any window creation will fail. INITCOMMONCONTROLSEX InitCtrls; InitCtrls.dwSize = sizeof(InitCtrls); // Set this to include all the common control classes you want to use // in your application. InitCtrls.dwICC = ICC_WIN95_CLASSES; InitCommonControlsEx(&InitCtrls); CWinApp::InitInstance(); AfxEnableControlContainer(); // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need // Change the registry key under which our settings are stored // TODO: You should modify this string to be something appropriate // such as the name of your company or organization SetRegistryKey(_T("Local AppWizard-Generated Applications")); CMFCtestDlg dlg; m_pMainWnd = &dlg; INT_PTR nResponse = dlg.DoModal(); if (nResponse == IDOK) { // TODO: Place code here to handle when the dialog is // dismissed with OK } else if (nResponse == IDCANCEL) { // TODO: Place code here to handle when the dialog is // dismissed with Cancel } // Since the dialog has been closed, return FALSE so that we exit the // application, rather than start the application's message pump. return FALSE; //--------------------------------------------------------------------------- // дефайны //ёмкость буфера приема #define BUFSIZE_RD 5000 //ёмкость буфера передачи #define BUFSIZE_WR 128 //--------------------------------------------------------------------------- // дескрипторы HANDLE COMport; HANDLE writer; HANDLE reader; HANDLE main; ///будем использовать для операций чтения (см. поток ReadThread) OVERLAPPED overlapped; //будем использовать для операций записи (см. поток WriteThread) OVERLAPPED overlappedwr; //--------------------------------------------------------------------------- // глобальные переменные // строка определения com порта // String com_port; // флаг приема bool FlComRead; // флаг цикла в потоке приема bool flag; bool FlBeg; //приёмный и передающий буферы unsigned char bufrd[BUFSIZE_RD], bufwr[BUFSIZE_WR]; // контрольная сумма пакета передачи unsigned char TxBufCrc; unsigned char PrCanal; unsigned char PrSect; // счетчик приемов unsigned int counterRX; unsigned int counterTX; unsigned int counterRP; unsigned int Dist; unsigned int Distm; // скорость бод unsigned long BaudR; //--------------------------------------------------------------------------- // функции //--------------------------------------------------------------------------- // задержка void wait (int wt) { while (wt--); } //--------------------------------------------------------------------------- // Принятые байты в HEX void hexdumpstrRead ( char src[], size_t size) { // Form1->Memo2->Clear(); static const char hex[] = "0123456789abcdef"; char *result = (char *)malloc((size > 0)? size*3: 1), *p = result; if (result) { while (size-- > 0) { *p++ = hex[(*src >> 4) & 0x0f]; *p++ = hex[*src++ & 0x0f]; *p++ = ' '; } p[(p > result)? -1: 0] = 0; } // Form1->Memo2->Lines->Add(result); } //--------------------------------------------------------------------------- // Переданные байты в HEX void hexdumpstrWrite ( char src[], size_t size) { // Form1->Memo1->Clear(); static const char hex[] = "0123456789abcdef"; char *result = (char *)malloc((size > 0)? size*3: 1), *p = result; if (result) { while (size-- > 0) { *p++ = hex[(*src >> 4) & 0x0f]; *p++ = hex[*src++ & 0x0f]; *p++ = ' '; } p[(p > result)? -1: 0] = 0; } // Form1->Memo1->Lines->Add(result); } } Ошибки типа Error 1 error C2601: 'wait' : local function definitions are illegal |
Сообщ.
#28
,
|
|
|
Цитата Acvarif @ Ошибки типа Так тебе компилятор прямым текстом сообщает об ошибке Добавлено Кстати сама функция -- это фигня какая-то. Я уж молчу про то, что без volatile компилятор вправе вообще твой while потереть. |
Сообщ.
#29
,
|
|
|
Цитата Acvarif @ Я не врубаюсь как код с WINAPI прописать в Wisual Studio. Какой вид проекта для этого открыть MFC, WindovsForm, или...? Есть три вида Win32-проект, MFC-проект и WinForms-проект. Последний лучше писать на С#. Создаешь MFC-проект, самое простое, по типу С++Builder, это диалоговое приложение. Потом кидаешь контролы на форму, и всязываешь с обработчиками почти как С++Builder там VCL, тут MFC. Цитата Acvarif @ Если начинать проект MFC то куда и чего прописывать Напнример в Builder все прописывается в одном едиственном модуле c расширением .cpp в случае создания диалогового приложения, изначально можно все запихать в файл срр, где находиться определение главного окна приложения. Как создать обработчик в студии, да все просто выбираешь конрол и дабл клик на нем студия сама создаст Добавлено если еще не понял то открывай учебник по MFC и Visual Studio и читай там все описано. |
Сообщ.
#30
,
|
|
|
Спасибо. Полегчало. Visual Studio слишком насыщен разными подходами. С наскоку растерялся. MFC подойдет.
|
Сообщ.
#31
,
|
|
|
Потихоньку врубаюсь. Глубоко понять MFC - это еще не скоро. Создал пустой проект с названием MFCtest. В результате имеются 2 файла типа CMFCtest.cpp MFCtestDlg.cpp
Пока пытаюсь тупо перенести код из Builder6 в файл типа CMFCtest.cpp .Глобальные переменные и функции разместил сразу после #include "stdafx.h" #include "MFCtest.h" #include "MFCtestDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #endif void __fastcall TForm1::FormCreate(TObject *Sender) reader = CreateThread( NULL, 0,ReadThread, NULL, CREATE_SUSPENDED, NULL); Хотя читал, что MFC требует такого создателя потока reader = AfxBeginThread(ReadThread, NULL, THREAD_PRIORITY_IDLE); И тем не менее подскажите please куда необходимо разместить функцию создания потока? Вариант размещения создателя потока так ... // CMFCtestApp BEGIN_MESSAGE_MAP(CMFCtestApp, CWinApp) ON_COMMAND(ID_HELP, &CWinApp::OnHelp) END_MESSAGE_MAP() // CMFCtestApp construction CMFCtestApp::CMFCtestApp() { // TODO: add construction code here, // Place all significant initialization in InitInstance // скорость бод по умолчанию BaudR = CBR_115200; //reader = CreateThread( NULL, 0, ReadThread, NULL, CREATE_SUSPENDED, NULL); reader = AfxBeginThread(ReadThread, NULL, THREAD_PRIORITY_IDLE); ::Sleep(10); } // The one and only CMFCtestApp object CMFCtestApp theApp; // CMFCtestApp initialization BOOL CMFCtestApp::InitInstance() { // InitCommonControlsEx() is required on Windows XP if an application // manifest specifies use of ComCtl32.dll version 6 or later to enable // visual styles. Otherwise, any window creation will fail. INITCOMMONCONTROLSEX InitCtrls; InitCtrls.dwSize = sizeof(InitCtrls); // Set this to include all the common control classes you want to use // in your application. InitCtrls.dwICC = ICC_WIN95_CLASSES; InitCommonControlsEx(&InitCtrls); ... выдает ошибку Error 1 error C2665: 'AfxBeginThread' : none of the 2 overloads could convert all the argument types |
Сообщ.
#32
,
|
|
|
Цитата Acvarif @ Дальше пытаюсь понять где находится аналог билдеровского создателя формы void __fastcall TForm1::FormCreate(TObject *Sender) reader = CreateThread( NULL, 0,ReadThread, NULL, CREATE_SUSPENDED, NULL); в файл MFCtestDlg.cpp можно в конструктор или вирт функцию OnInitilize Добавлено Цитата Acvarif @ Хотя читал, что MFC требует такого создателя потока reader = AfxBeginThread(ReadThread, NULL, THREAD_PRIORITY_IDLE); код который создал мастер студии вообще не трогай, чтобы там что то менять надо хорошо разбираться в MFC |
Сообщ.
#33
,
|
|
|
Код не трогаю. Перенес глобальные переменные и функции из CMFCtest.cpp в MFCtestDlg.cpp
Создание потока разместил после коммента // TODO: Add extra initialization here BOOL CMFCtestDlg::OnInitDialog() { CDialog::OnInitDialog(); // Add "About..." menu item to system menu. // IDM_ABOUTBOX must be in the system command range. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here reader = AfxBeginThread(ReadThread, NULL, THREAD_PRIORITY_IDLE); return TRUE; // return TRUE unless you set the focus to a control } Опять ошибка Error 1 error C2665: 'AfxBeginThread' : none of the 2 overloads could convert all the argument types Если не сложно, покажите пожалуйста в коде как сделать конструктор для ReadThread, чтобы правильно разместить его вызов в CMFCtestDlg::OnInitDialog(). |
Сообщ.
#34
,
|
|
|
зачем AfxCreateThread? юзай CreateThread ты же её ранее вызывал.
функция AfxBeginThread возвращет указатель на CWndThread: CWndThread* pThread = AfxBeginThread(ReadThread,GetSafeHwnd(),THREAD_PRIORITY_IDLE); у тебя reader это HANDLE а не указатель. ReaderThread должен объявлен так: UINT ReaderThread(LPVOID); |
Сообщ.
#35
,
|
|
|
Спасибо.
Поток вроде запустился, и даже COMport = CreateFile... сработал. Интересно есть-ли в MFC аналог билдеровского Form1->Memo2->Lines->Add(stroka); которое работает непосредственно в функции потока чтения COM? |
Сообщ.
#36
,
|
|
|
Цитата Acvarif @ Интересно есть-ли в MFC аналог билдеровского Form1->Memo2->Lines->Add(stroka); конечно есть насколько я понял Memo2 это текстовый контрол, в MFC это контрол Edit. |
Сообщ.
#37
,
|
|
|
Да. Нашел. Все же MFC тяжелый для пнимания. Совершенно не врубаюсь как повторить простейший билдеровский ввод текста из ComboBox1 типа
Form1->ComboBox1->Text.c_str() Как это выглядит для MFC? |
Сообщ.
#38
,
|
|
|
Цитата Acvarif @ Да. Нашел. Все же MFC тяжелый для пнимания. Совершенно не врубаюсь как повторить простейший билдеровский ввод текста из ComboBox1 типа Form1->ComboBox1->Text.c_str() Как это выглядит для MFC? есть несколько вариантов самый простой: SetDlgItemText(IDC_EDIT, _T("Привет мир!!!")); IDC_EDIT это идентификатор эдита или так CComboBox* pCombo = (CComboBox*)GetDlgItem(IDC_COMBO); pCombo->InsertString(-1, _T("Текст")); |
Сообщ.
#39
,
|
|
|
Спасибо. Функция SetDlgItemText рабтает. Наверняка для вывода данных в оно Edit Control тоже пойдет.
Очевидно я неверно задал вопрос. Имелось ввиду обратное - из окна ввода (или списка ComboBox) перенести текст, напрмер в переменную-строку типа CString com_port ... Спасибо. Понял. Работает. |
Сообщ.
#40
,
|
|
|
получить текст можно с помощью GetDlgItemText.
|
Сообщ.
#41
,
|
|
|
Цитата Acvarif @ Потихоньку врубаюсь. Глубоко понять MFC - это еще не скоро. Это довольно старая библиотека. А это значит, что можно найти немало книг и статей. Интересная статья Хороший сайт |
Сообщ.
#42
,
|
|
|
Спасибо. Тоже работает. Попытаюсь этой функцией перенести текст типа "COM1" в переменную CString com_pot, а затем в функцию
COMport = CreateFile(com_port,GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); oid Com_Setting() { COMMTIMEOUTS timeouts; // com_port += ComboBox1->Text; // COMport = CreateFile(L"COM1",GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); COMport = CreateFile(com_port,GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if(COMport==INVALID_HANDLE_VALUE) { if(GetLastError()==ERROR_FILE_NOT_FOUND) { MessageBox(NULL,(LPCWSTR)L"serial port does not exist.",(LPCWSTR)L"",MB_OK); } MessageBox(NULL,(LPCWSTR)L"serial port error",(LPCWSTR)L"", MB_OK); } DCB dcbSerialParams = {0}; dcbSerialParams.DCBlength=sizeof(dcbSerialParams); if (!GetCommState(COMport, &dcbSerialParams)) { MessageBox(NULL,(LPCWSTR)L"getting state error\n",(LPCWSTR)L"",MB_OK); } dcbSerialParams.BaudRate=BaudR; dcbSerialParams.ByteSize=8; dcbSerialParams.StopBits=ONESTOPBIT; dcbSerialParams.Parity=NOPARITY; if(!SetCommState(COMport, &dcbSerialParams)) { MessageBox(NULL,(LPCWSTR)L"error setting serial port state\n",(LPCWSTR)L"",MB_OK); } // Установка таймаутов. timeouts.ReadIntervalTimeout = 10; // таймаут между двумя символами timeouts.ReadTotalTimeoutMultiplier = 0; // общий таймаут операции чтения timeouts.ReadTotalTimeoutConstant = 0; // константа для общего таймаута операции чтения timeouts.WriteTotalTimeoutMultiplier = 0; // общий таймаут операции записи timeouts.WriteTotalTimeoutConstant = 0; // константа для общего таймаута операции записи // Загрузить структуру таймаутов в порт. if(!SetCommTimeouts(COMport, &timeouts)) { // Если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния. MessageBox(NULL,(LPCWSTR)L"Не удалось установить тайм-ауты\n",(LPCWSTR)L"",MB_OK); return; } // Установить размеры очередей приёма и передачи SetupComm(COMport,2000,2000); // Очистка буферов порта PurgeComm(COMport,PURGE_RXCLEAR); // очистить строку имени порта // com_port = ""; } |
Сообщ.
#43
,
|
|
|
ну получилось?
|
Сообщ.
#44
,
|
|
|
Получается. Для проверки делаю так: По клику Button1 формирую строку типа "COM1" переношу ее в IDC_COMBO1 затем из IDC_COMBO1 извлекаюь строку в переменную str и потом вызываю функцию установок и открытия COM порта, где переменная str попадает в
COMport = CreateFile(str, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);. void CMFCtestDlg::OnBnClickedButton1() { CString strText(_T("COM1")); SetDlgItemText(IDC_COMBO1, strText); CString str; GetDlgItemText(IDC_COMBO1, str); // установки порта Com_Setting(str); // активировать поток чтения данных в порт ResumeThread(reader); } Порт открывается. Поток вроде запускаетс. Пока не врубаюсь как составить список в ComboBox1 типа ("COM1" "COM2" "COM3" ..) |
Сообщ.
#45
,
|
|
|
Цитата Acvarif @ Пока не врубаюсь как составить список в ComboBox1 типа ("COM1" "COM2" "COM3" ..) а что сложного? CComboBox* pCombo = (CComboBox*)GetDlgItem(IDC_COMBOID); pCombo->AddString("COM1"); pCombo->AddString("COM2"); pCombo->AddString("COM3"); |
Сообщ.
#46
,
|
|
|
Спасибо. Сработало. Интересно работает ComboBox При каждом нажатии на стрелку выбора вниз - собственно это вызов функции
// выбор COM порта void CMFCtestDlg::OnCbnDropdownCombo1() { CComboBox* pCombo = (CComboBox*)GetDlgItem(IDC_COMBO1); pCombo->AddString(L"COM1"); pCombo->AddString(L"COM2"); pCombo->AddString(L"COM3"); } в окне выбора появляется повторение типа Прикреплённая картинка
А.. врубился. Это нужно делать при запуске формы. CMFCtestDlg::OnCbnDropdownCombo1() не нужна. |
Сообщ.
#47
,
|
|
|
Цитата Acvarif @ А.. врубился. Это нужно делать при запуске формы. CMFCtestDlg::OnCbnDropdownCombo1() не нужна. в OnInitDialog еще если это список не меняется то можно в ресурсах его прописать в свойство Data: COM1;COM2;COM3 |
Сообщ.
#48
,
|
|
|
Очередной затык. Окно Edit Control.
Имеется функция void ReadPrinting(DWORD btr) которая вызывается непосредственно в функции-потоке чтения. Функция принимает данные о количестве прочитанных байт (DWORD btr) COM портом и тупо выводит данные из глобального буфера bufrd в окно Memo в HEX виде. Выглядит это так for(UINT32 i=0; i<btr ; i++) { stroka = stroka + IntToHex(bufrd[i], 2) + ' '; } Form1->Memo2->Lines->Add(stroka); Подскажите пожалуйста как это можно повторить в MFC - вывести данные в окно Edit Control. Для теста пробовал несколько типичных вариантов , например типа SetWindowText(IDC_EDIT1, "Text"); Неработает. Ошибка Error 1 'SetWindowTextW' : cannot convert parameter 1 from 'int' to 'HWND' |
Сообщ.
#49
,
|
|
|
www.ya.ru, ищем "CEdit MFC" -> находим описание:
CEdit Цитата .. CEdit наследует значительные возможности из CWnd. Для установки и извлечения текста из объекта CEdit, используйте функции-члены SetWindowText и GetWindowText ... |
Сообщ.
#50
,
|
|
|
Такая конструкция работает
CWnd* pWnd = GetDlgItem(IDC_EDIT1); pWnd->SetWindowText(_T("Gerald Samper")); Но только если вызывается в OnInitDialog или в любом обработчике (кнопки или т. п) Если поместить это в любую внешнюю функцию то возникает ошибка Error 1 error C2660: 'GetDlgItem' : function does not take 1 arguments В чем хитрость? В билдре там просто добавляется Form1->... В MFC как? Похоже элемент IDC_EDIT1 или вся функция GetDlgItem(IDC_EDIT1), если можно так выразиться, не попадает во внешнюю функцию. |
Сообщ.
#51
,
|
|
|
Цитата Acvarif @ В чем хитрость? В билдре там просто добавляется Form1->... В MFC как? Похоже элемент IDC_EDIT1 или вся функция GetDlgItem(IDC_EDIT1), если можно так выразиться, не попадает во внешнюю функцию. при создании потока в качестве параметра lParam передается дескриптор на главное окно: DWORD WINAPI WorkThread(LPVOID lParam) { CWnd* pWnd = CWnd::FromHandle((HWND)lParam); CEdit* pEdit = (CEdit*)pWnd->GetDlgItem(IDC_EDIT1); pEdit->SetWindowText(_T("Hello from thread!")); return 0; } также в MFC есть указатель на главное окно сохраненое в члене m_pMainWnd класса CWinApp: AfxGetApp()->m_pMainWnd. судя по всему Form1 это тоже указатель на главное окно? |
Сообщ.
#52
,
|
|
|
Спасибо. Пнятно. Для проверки поместил код в функцию-поток чтения в то место где собственно производится загрузка принятого байта в буфер bufrd
if(btr) { // прочитать байты из порта в буфер программы ReadFile(COMport, bufrd, btr, &temp, &overlapped); // увеличиваем счётчик байтов counterRX+=btr; // вывод данных на экран CWnd* pWnd = CWnd::FromHandle((HWND)lpParam); CEdit* pEdit = (CEdit*)pWnd->GetDlgItem(IDC_EDIT1); pEdit->SetWindowText(_T("Hello from thread!")); } Один раз в секунду на COM прт приходит пакет данных. Явно btr становится не 0 - вызывается код CWnd* pWnd = CWnd::FromHandle((HWND)lpParam); CEdit* pEdit = (CEdit*)pWnd->GetDlgItem(IDC_EDIT1); pEdit->SetWindowText(_T("Hello from thread!")); Просходит странная ошибка, вернее исключение, которое указывает на файл winocc.cpp Прикреплённая картинка
Что-бы это мгло быть. Ведь в общем ничего особо не делается. Просто попытка вывести в окно Edit Control коротий текст. Может это связано с тем, что на порт приходит порядка 800 байт и по сути каждый раз в нем должно отображаться "Hello from thread!" (800 раз) Чего-то не помещается? Сделал Auto scrol true. Не помогло... Уменьшил количество байт до 24. То же самое, в отладчике застревает на строке CEdit* pEdit = (CEdit*)pWnd->GetDlgItem(IDC_EDIT1); |
Сообщ.
#53
,
|
|
|
Цитата Acvarif @ Просходит странная ошибка, вернее исключение, которое указывает на файл winocc.cpp Что-бы это мгло быть. обращение к несуществующему объекту или попросту по нулевому указателю ты дескриптор окна-владельца передаешь при создании потока? Цитата Acvarif @ То же самое, в отладчике застревает на строке CEdit* pEdit = (CEdit*)pWnd->GetDlgItem(IDC_EDIT1); невалидный указатель pWnd. |
Сообщ.
#54
,
|
|
|
Функция создания потока
reader = CreateThread( NULL, 0,ReadThread, NULL, CREATE_SUSPENDED, NULL); Только имя потоковой функции ReadThread. Дескриптор нет. Как это выглядит правильно? |
Сообщ.
#55
,
|
|
|
Цитата Acvarif @ В чем хитрость? В билдре там просто добавляется Form1->... В MFC как? Похоже элемент IDC_EDIT1 или вся функция GetDlgItem(IDC_EDIT1), если можно так выразиться, не попадает во внешнюю функцию. По всей вероятности, в том, один из методов CWnd имеет имя "GetDlgItem". Которое совпадает с именем WINDOWS-API функции "GetDlgItem". Они имеют разное число параметров. Когда функция с таким именем вызывается внутри класса, используется функция класса. Снаружи класса вызывается API-функция, которая имеет другой прототип. Поэтому появляется сообщение об ошибке. |
Сообщ.
#56
,
|
|
|
Цитата Acvarif @ Функция создания потока reader = CreateThread( NULL, 0,ReadThread, NULL, CREATE_SUSPENDED, NULL); Только имя потоковой функции ReadThread. Дескриптор нет. Как это выглядит правильно? 4 параметр есть указатель на данные для рабочего потока, передаём в него дескриптор главного окна через GetSafeHwnd(): reader = CreateThread( NULL, 0,ReadThread, GetSafeHwnd(), CREATE_SUSPENDED, NULL); |
Сообщ.
#57
,
|
|
|
Спасибо. Работает. Информация в окне Edit Contol отображается нормально, причем с обновлением.
Хотя Micosoft пишет про четвертый параметр optional lpParameter [in, optional] A pointer to a variable to be passed to the thread. Дааа. В MFC все достаточно жестко.. |
Сообщ.
#58
,
|
|
|
Цитата Acvarif @ Дааа. В MFC все достаточно жестко.. а мне MFC по кайфу |
Сообщ.
#59
,
|
|
|
Надеюсь на MFC удастся повторить билдеровсую прогу с потоками на API. Во всяком случае прием уже точно работает.
Данные COM порта накапливаются в глобальном буфере bufrd ReadFile(COMport, bufrd, btr, &temp, &overlapped); Далее их нужно отобразить в окне Edit Contol в HEX виде. В билдере это просто преобразовывалось функцией + пробелы между байтами // Перенос буфера в строку. for(UINT32 i=0; i<btr ; i++) { stroka = stroka + IntToHex(bufrd[i], 2) + ' '; } затем строка выводилась в окно Memo Сейчас выводится в окно Edit Control Как в MFC можно повторить это: stroka = stroka + IntToHex(bufrd[i], 2) + ' '; |
Сообщ.
#60
,
|
|
|
Цитата Acvarif @ Как в MFC можно повторить это: stroka = stroka + IntToHex(bufrd[i], 2) + ' '; CString s; s.Format("%X", bufrd[i]); |
Сообщ.
#61
,
|
|
|
Спасибо. Работает. Сделал так:
for (int i = 0; i < btr; i++) { temp_str.Format(_T(" %X"), bufrd[i]); stroka = stroka + temp_str + ' '; } CWnd* pWnd = CWnd::FromHandle((HWND)lpParam); CEdit* pEdit = (CEdit*)pWnd->GetDlgItem(IDC_EDIT1); pEdit->SetWindowText(stroka); Выводит данные так Прикреплённая картинка
В билдере так Прикреплённая картинка
В MFC некоторые нюансы - не прописывает один 0 в HEX числе - очевидно из-за построчного обновления пропускаются первые несколько данных Как можно устранить эти нюансы? |
Сообщ.
#62
,
|
|
|
Цитата Acvarif @ В MFC некоторые нюансы - не прописывает один 0 в HEX числе Это ни к MFC, ни к Билдеру отношения не имеет. Это форматный вывод в "С". Попробуй так: temp_str.Format(_T(" %02X"), bufrd[i]); Или даже так: TCHAR buf[128]; _sntprintf_s(buf,ARRAYSIZE(buf),_TRUNCATE,_T(" %02X"),bufrd[i]); Почитай про printf/sprintf. тут, например |
Сообщ.
#63
,
|
|
|
Да. Зарапортовался с форматированием. Спасибо.
В билдере было так stroka = stroka + IntToHex(bufrd[i], 2) + ' '; Все же остался один непонятный нюанс - Похоже каждая новая строка записывается на место старой. Все выводится в одну едиственную строку. Как с этим бороться? |
Сообщ.
#64
,
|
|
|
Цитата Acvarif @ Все же остался один непонятный нюанс - Похоже каждая новая строка записывается на место старой. Все выводится в одну едиственную строку. Как с этим бороться? Эти "технологии" я тебе уже показывал: www.ya.ru, ищем "CEdit MFC" и находим описание тут. Цитата ... CEdit наследует значительные возможности из CWnd. Для установки и извлечения текста из объекта CEdit, используйте функции-члены SetWindowText и GetWindowTextCWnd, устанавливающие или получающие все содержимое элемента управления "Поле ввода", даже если многострочный элемент управления. Линии многополосном текста в элементе управления разделены последовательности символов "\r\n". Кроме того, если элемент управления "Поле ввода" многополосн, получение и установка часть текста элемента управления путем вызова функции-члены GetLine, SetSel, GetSel и ReplaceSelCEdit. ... |
Сообщ.
#65
,
|
|
|
Эта ссылка у меня открыта. Но всеравно не врубаюсь.
Цитата Линии многополосном текста в элементе управления разделены последовательности символов "\r\n". Это что означает? На практике получается, что каждый новый вызов кода // Перенос буфера в строку. for (DWORD i = 0; i < btr; i++) { temp_str.Format(_T(" %02X"), bufrd[i]); stroka = stroka + temp_str + ' '; } CWnd* pWnd = CWnd::FromHandle((HWND)lpParam); CEdit* pEdit = (CEdit*)pWnd->GetDlgItem(IDC_EDIT1); pEdit->SetWindowText(stroka); стирает предыдущую строку и печатает новую на ее место. Что в коде нужно добавить, отнять, изменить, чтобы просто каждый новый его вызов печатал строку с новой строки? |
Сообщ.
#66
,
|
|
|
Цитата Acvarif @ Эта ссылка у меня открыта. Но всеравно не врубаюсь. я не использую MFC. Но судя по этому описанию - как-то так: // ... for (DWORD i = 0; i < btr; i++) { temp_str.Format(_T(" %02X"), bufrd[i]); stroka = stroka + temp_str + ' '; } stroka += "\r\n"; //... |
Сообщ.
#67
,
|
|
|
Спасибо. Мысль неплохая.
Но все оказалось круто закручено. Итак понятно, что каждое выполнение кода CString stroka, temp_str; // Перенос буфера в строку. for (DWORD i = 0; i < btr; i++) { temp_str.Format(_T(" %02X"), bufrd[i]); stroka = stroka + temp_str + ' '; } CWnd* pWnd = CWnd::FromHandle((HWND)lpParam); CEdit* pEdit = (CEdit*)pWnd->GetDlgItem(IDC_EDIT1); pEdit->SetWindowText(stroka); приводит к новому выводу строки в Edit Control Это значит, что если непрерывный пакет данных в буфере приемника COM будет появляться с некоторыми временными разрывами то это уже будет не непрерывный пакет данных и функция ReadPrinting(btr, lpParam); вывода данных в Edit Control (так какраз и выполняется вышеприведенный код) будет вызываться многократно. Все заложено в функции потока чтения //--------------------------------------------------------------------------- // Поток чтения DWORD WINAPI ReadThread( LPVOID lpParam ) { // структура текущего состояния порта, // используется для определения количества принятых в порт байт COMSTAT comstat; // переменная temp используется в качестве заглушки DWORD btr, temp, mask, signal; // создать сигнальный объект-событие для асинхронных операций overlapped.hEvent = CreateEvent(NULL, true, true, NULL); // установить маску на срабатывание по событию приёма байта в порт SetCommMask(COMport, EV_RXCHAR); // пока поток не будет прерван, выполняем цикл while(1) { //ожидать события приёма байта (это и есть перекрываемая операция) WaitCommEvent(COMport, &mask, &overlapped); //приостановить поток до прихода байта signal = WaitForSingleObject(overlapped.hEvent, INFINITE); //если событие прихода байта произошло if(signal == WAIT_OBJECT_0) { //проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent if(GetOverlappedResult(COMport, &overlapped, &temp, true)) { //если произошло именно событие прихода байта if((mask & EV_RXCHAR)!=0) { ::Sleep(100); // нужно заполнить структуру COMSTAT ClearCommError(COMport, &temp, &comstat); // и получить из неё количество принятых байтов btr = comstat.cbInQue; // если действительно есть байты для чтения if(btr) { // прочитать байты из порта в буфер программы ReadFile(COMport, bufrd, btr, &temp, &overlapped); // вызываем функцию для вывода данных на экран ReadPrinting(btr, lpParam); } } } } Sleep(1); } return 1; } Собственно, чтобы избежать повторений нужно или задержать вход в if(btr) или CString stroka, temp_str; сделать глобальными, чтобы очищать их как-то, когда в Edit Control отобразится нужное количество принятых данных. Пока помогла не вдаваться в такие дебри небольшая задержка if((mask & EV_RXCHAR)!=0) { ::Sleep(100); Теперь вход в if(btr) { осуществляется один раз при непрерывном пакете данных на COM (порядка 800 байт с перидом в 1 сек) Функция ReadPrinting(btr, lpParam); выполняется один раз - окно Edit Control отображает корректно весь пакет принятых данных. Если у кого есть по этому поводу еще какие мысли, буду признателен. |
Сообщ.
#68
,
|
|
|
Цитата Acvarif @ Но все оказалось круто закручено. Итак понятно, что каждое выполнение кода ... приводит к новому выводу строки в Edit Control Вообще непонятно, что ты хочешь. Если ты получаешь не строки, а байт - так и выводи байты. Поставь непрерывный счётчик байт, равный размеру строки. По достижению максимального значения - сбрасывай счётчик и добавляй два байта в Эдит '\r','\n'. Дополнительные операции со строками только время тратят. |
Сообщ.
#69
,
|
|
|
Цитата Acvarif @ Все же остался один непонятный нюанс - Похоже каждая новая строка записывается на место старой. Все выводится в одну едиственную строку. Как с этим бороться? Цитата Acvarif @ Что в коде нужно добавить, отнять, изменить, чтобы просто каждый новый его вызов печатал строку с новой строки? Вместо CEdit::SetWindowText: int len = pEdit->GetWindowTextLength(); pEdit->SetSel(len, len); pEdit->ReplaceSel(_T("\r\nНовая строка")); |
Сообщ.
#70
,
|
|
|
Цитата Вообще непонятно, что ты хочешь. Если ты получаешь не строки, а байт - так и выводи байты. Поставь непрерывный счётчик байт, равный размеру строки. По достижению максимального значения - сбрасывай счётчик и добавляй два байта в Эдит '\r','\n'. Может я сразу не написал почему используется асинхронный прием - прием по событию прихода каждого байта. Все дело в том, что пакеты данных переменной длины (от 800 до 4000 байт). Каждый новый пакет - другая длина. Поэтому подсчитать что-то конкретное невозможно. Вот и выкручиваюсь... Кроме того, ответная передача пакета управления (128 байт информации) должна быть по времени (разрыв не более 10 мс)жестко привязана к концу приема. Конец к счастью можно определить по конкретным конечным символам в пакете прима. Пока в MFC прием работает не хуже чем в Builder. В Builder имеется возможность выводить строку в Label, Panel типа Form1->Panel3->Caption = "Всего принято байт: " + IntToStr(counterRX); sprintf(Buffer,"Дистанция: %d м", Distm); Form1->Label16->Caption = Buffer; В MFC имеется Text Control. Можно-ли в Text Control выводить текст примерно как Panel3->Caption =.. или Label16->Caption = ..? |
Сообщ.
#71
,
|
|
|
Цитата Acvarif @ В Builder имеется возможность выводить строку в Label, Panel типа Form1->Panel3->Caption = "Всего принято байт: " + IntToStr(counterRX); sprintf(Buffer,"Дистанция: %d м", Distm); Form1->Label16->Caption = Buffer; В MFC имеется Text Control. Можно-ли в Text Control выводить текст примерно как Panel3->Caption =.. или Label16->Caption = ..? Естестно CStatic* pText = (CStatic*)GetDlgItem(IDC_STATIC1); pText->SetWindowText(_T("Hello")); также имеется возможность определить объект соотвествующего класса и обращаться непросредствено через него: class CDialog1 : public CDialog { public: CStatic label1; }; void CDialog1::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_STATIC1, label1); } BOOL CDialog1::OnInitDialog() { CDialog::OnInitDialog(); label1.SetWindowText(_T("Hello")); } |
Сообщ.
#72
,
|
|
|
Цитата Acvarif @ Может я сразу не написал почему используется асинхронный прием - прием по событию прихода каждого байта. Все дело в том, что пакеты данных переменной длины (от 800 до 4000 байт). Каждый новый пакет - другая длина. 1. Или выводи по-байтно. 2. Или принимай по-одному, но накапливай в выводной буфер число байт, равное длине строки твоего "виртуального" дисплея, и выводи. 3. Или читай из буфера порта операцией чтения максимальный размер (допустим 4000 байт). С указанным тайм-аутом по приходу байт. Почитай подробнее про тайм-ауты и выбери подходящий. Будешь принимать или по максимальному количеству или по тайм-ауту некоторое количество байт. Получишь - выводи. --- Все 3 способа будут работать - 100%. Единственное требование - чтобы твой компьютер мог в принципе справится с таким потоком байт. |
Сообщ.
#73
,
|
|
|
Цитата Так и делаю. Вроде работает.Или принимай по-одному.. Цитата CStatic* pText = (CStatic*)GetDlgItem(IDC_STATIC1); pText->SetWindowText(_T("Hello")); Понятно. Спасибо. По ходу разобрался со сменой фонта в IDC_EDIT1 // фонт для IDC_EDIT1 CFont *font = new CFont; CEdit* pEditFont = (CEdit*)pWnd->GetDlgItem(IDC_EDIT1); font->CreateFont ( 20, // nHeight 0, // nWidth 0, // nEscapement 0, // nOrientation FW_NORMAL, // nWeight FALSE, // bItalic FALSE, // bUnderline 0, // cStrikeOut ANSI_CHARSET, // nCharSet OUT_DEFAULT_PRECIS, // nOutPrecision CLIP_DEFAULT_PRECIS, // nClipPrecision DEFAULT_QUALITY, // nQuality DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily L"Arial"); // lpszFacename pEditFont->SetFont(font); Похоже удастся весь процесс приема передачи сделать в одном потоке. В билдере элемент выбора типа ComboBox работает непосредственно с выбранным в окне текстом. В MFC код выбора работает с опозданием на строку. void CMFCtestDlg::OnCbnSelchangeCombo3() { CString strCombo3; GetDlgItemText(IDC_COMBO3, strCombo3); if(strCombo3 == "откл.") { bufwr[1] &= ~(1 << 0); bufwr[1] &= ~(1 << 1); } else if(strCombo3 == "мин.") { bufwr[1] |= (1 << 0); bufwr[1] &= ~(1 << 1); } else if(strCombo3 == "макс.") { bufwr[1] &= ~(1 << 0); bufwr[1] |= (1 << 1); } } При выборе в bufwr[1] записываются данные соответствующие предыдущему состоянию окна IDC_COMBO3 Как сделать так чтобы срабатывало то, что выбрано в настоящий момент? |
Сообщ.
#74
,
|
|
|
Цитата Acvarif @ В билдере элемент выбора типа ComboBox работает непосредственно с выбранным в окне текстом. В MFC код выбора работает с опозданием на строку. Как сделать так чтобы срабатывало то, что выбрано в настоящий момент? дело в том что событие OnCbnSelchangeCombo3 срабатывает до замены текста в редакторе комбо, поэтому вызов GetDlgItemText считывает предыдущее значение. Выход считывать текущее значение на основе выбранного пункта из списка значений комбо с помощью GetLBText. void CMFCtestDlg::OnCbnSelchangeCombo3() { CString strCombo3; CComboBox* pCombo = (CComboBox*)GetDlgItem(IDC_COMBO3); pCombo->GetLBText(pCombo->GetCurSel(),strCombo3); } |
Сообщ.
#75
,
|
|
|
Спасибо. Работает.
В билдере имеется функция закрытия формы типа void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) В ней обычно останавливаются потоки, закрывается порт и т. п. Как можно сделать то-же в MFC? |
Сообщ.
#76
,
|
|
|
Цитата Acvarif @ В билдере имеется функция закрытия формы типа void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) В ней обычно останавливаются потоки, закрывается порт и т. п. Как можно сделать то-же в MFC? обработать сообщение WM_CLOSE пс. на самом деле MFC является всего лишь классовой надстройкой над Win32 API, поэтому все то что делалось в чистом Win32 API применимо и к MFC. |
Сообщ.
#77
,
|
|
|
Возник один неприятный момент. При переносе проекта из одного компа на другой (с домашнего на рабочий) при компиляции возникает ошибка:
Error 1 fatal error C1853: 'Debug\MFCtest.pch' precompiled header file is from a previous version of the compiler, or the precompiled header is C++ and you are using it from C (or vice versa) d:\mydesignsxp\visualstprj\mfctest\mfctest\mfctestdlg.cpp 1 MFCtest В чем фишка? На компах разные версии компиляторов? (не уверен, что ставил Студию из одного и того-же источника) Как с этим бороться? Проект пока еще не такой большой. Можно начать заново. Но потом наверняка ситуация повторится если на выходные буду переносить проект обранто с рабочего компа на домашний. |
Сообщ.
#78
,
|
|
|
Цитата Acvarif @ Возник один неприятный момент. При переносе проекта из одного компа на другой (с домашнего на рабочий) при компиляции возникает ошибка: Error 1 fatal error C1853: 'Debug\MFCtest.pch' precompiled header file is from a previous version of the compiler, or the precompiled header is C++ and you are using it from C (or vice versa) d:\mydesignsxp\visualstprj\mfctest\mfctest\mfctestdlg.cpp 1 MFCtest В чем фишка? На компах разные версии компиляторов? (не уверен, что ставил Студию из одного и того-же источника) Как с этим бороться? Проект пока еще не такой большой. Можно начать заново. Но потом наверняка ситуация повторится если на выходные буду переносить проект обранто с рабочего компа на домашний. просто удали этот файл и снова откомпилируй вообще когда переносишь проект куда-нибудь, то делай перед этим очистку командой Build -> Clean, это действие удаляет почти все вспомогательные файлы, в том числе файл предкомпилированого заголовка pch, а также объектные и exe файлы, которые слову сказать много весят, особенно pch. |
Сообщ.
#79
,
|
|
|
Спасибо. Заработал прием и передача в одном потоке. Вывод данных на Edit Control в норме.
Имеются еще некоторые нюансы. Например Static Text почему-то не имеет своего ID. Любой Static Text (1,2 или 9 )имеет ID IDC_STATIC вместо IDC_STATIC1, IDC_STATIC2, ..IDC_STATIC9 Прикреплённая картинка
Попытка обратиться к нему по CStatic* pText = (CStatic*)GetDlgItem(IDC_STATIC9); pText->SetWindowText(_T("Hello")); Error 4 error C2065: 'IDC_STATIC9' : undeclared identifier |
Сообщ.
#80
,
|
|
|
Цитата Acvarif @ Спасибо. Заработал прием и передача в одном потоке. Вывод данных на Edit Control в норме. Имеются еще некоторые нюансы. Например Static Text почему-то не имеет своего ID. Любой Static Text (1,2 или 9 )имеет ID IDC_STATIC вместо IDC_STATIC1, IDC_STATIC2, ..IDC_STATIC9 Это ошибка самой среды Visual Studio2008+MFC? по умолчанию статик контролы имеют одинаковый ID: IDC_STATIC, поэтому чтобы обратиться к ним надо просто указать ID отличный от IDC_STATIC, например IDC_STATIC1 |
Сообщ.
#81
,
|
|
|
Спасибо. Static работает. В билдере имеется компонент тпа CSpinEdit
void __fastcall TForm1::CSpinEdit6Change(TObject *Sender) { bufwr[124] = (CSpinEdit6->Value - 1) | ((Dist >> 1) & 0x80); } Работает он по событию клика на верхнюю или нижнюю стрелку прокрутки Прикреплённая картинка
Значение в нем меняется на 1 вверх или вниз в пределах заранее установленных границ в свойствах компонента (например min 1 max 120). Как в MFC можно создать подобный компонент. Имеется в наличии Spin Control но без окна Edit. Очевидно придется компоновать Spin Control + Edit Control? |
Сообщ.
#82
,
|
|
|
Цитата Acvarif @ Как в MFC можно создать подобный компонент. Имеется в наличии Spin Control но без окна Edit. Очевидно придется компоновать Spin Control + Edit Control? надо у спин конторла установить свойства Auto Buddy = True и Set Buddy Integer = True, клики на кнопках обрабатываются через сообщение WM_VSCROLL, которое отлавливается окном диалога. Начальную инициализацию "спина" можно сделать в OnInitDialog. Edit Control надо ставить рядом слева от Spin контрола согласно Tab-порядку. BOOL CMyDialog::OnInitDialog() { CDialog::OnInitDialog(); static_cast<CSpinButtonCtrl*>(GetDlgItem(IDC_SPIN1))->SetRange(0,100); return TRUE; } void CMyDialog::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { if (pScrollBar->GetDlgCtrlID() == IDC_SPIN1) { //int spinValue = reinterpret_cast<CSpinButtonCtrl*>(pScrollBar)->GetPos32(); bufwr[124] = (nPos - 1) | ((Dist >> 1) & 0x80); } CDialog::OnVScroll(nSBCode, nPos, pScrollBar); } |
Сообщ.
#83
,
|
|
|
Спасибо. С установкой Spin+Edit Control разобрался. Все нормально ставится. С обработкой клика не получается.
В функции BOOL CMFCtestDlg::OnInitDialog() добавил строку static_cast<CSpinButtonCtrl*>(GetDlgItem(IDC_SPIN2))->SetRange(0,100); Появилась ошибка Error 5 error C2061: syntax error : identifier 'CSpinButtonCtr1' m_spin2Value.SetRange(1,120); Подключение в основной файл MFCtestDld.cpp функции void CMFCtestDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { if (pScrollBar->GetDlgCtrlID() == IDC_SPIN2) { bufwr[124] = (nPos - 1) | ((Dist >> 1) & 0x80); } CDialog::OnVScroll(nSBCode, nPos, pScrollBar); } вызывает ошибку Error 6 error C2509: 'OnVScroll' : member function not declared in 'CMFCtestDlg' Тут не совсем врубился. У Edit Control есть событие типа void CMFCtestDlg::OnEnVscrollEdit5() Что представляет из себя функция void CMFCtestDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)? Я ее просто прописал в файл MFCtestDld.cpp. |
Сообщ.
#84
,
|
|
|
Цитата Acvarif @ У Edit Control есть событие типа void CMFCtestDlg::OnEnVscrollEdit5() Edit Control в данном случае можно вообще игнорировать все делает Spin control. Цитата Acvarif @ Error 6 error C2509: 'OnVScroll' : member function not declared in 'CMFCtestDlg' Что представляет из себя функция void ::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)? это обработчик сообщения WM_VSCROLL. Цитата Acvarif @ Я ее просто прописал в файл MFCtestDld.cpp. можно вручную, но тогда надо сделать по шагам следующее: 1) объявить в классе диалога функцию-обработчик void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); 2) определить ее; 3) ну и обязательно разместить след код в таблице сообщений: BEGIN_MESSAGE_MAP(CMFCtestDlg, CDialog) ... ON_WM_VSCROLL() ... END_MESSAGE_MAP() но проще воспользоваться соотвествующим мастером ClassView, который добавит весь этот код сам. Для этого открываешь окно ClassView, выбираешь класс CMFCtestDlg, далее в окне Properties -> Messages и там находишь сообщение WM_VSCROLL Цитата Acvarif @ В функции BOOL CMFCtestDlg::OnInitDialog() добавил строку static_cast<CSpinButtonCtrl*>(GetDlgItem(IDC_SPIN2))->SetRange(0,100); Появилась ошибка Error 5 error C2061: syntax error : identifier 'CSpinButtonCtr1' хз должно работать. CSpinButtonCtrl это обертка для виндового spin контрола. Цитата Acvarif @ Хотя потом создал переменную типа m_spin2Value и сделал запись m_spin2Value.SetRange(1,120); а как создал? код приведи. |
Сообщ.
#85
,
|
|
|
Спасибо. Разобрался. Добавил через Properties -> Messages
void CMFCtestDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { // TODO: Add your message handler code here and/or call default if (pScrollBar->GetDlgCtrlID() == IDC_SPIN2) { //int spinValue = reinterpret_cast<CSpinButtonCtrl*>(pScrollBar)->GetPos32(); bufwr[124] = (nPos - 1) | ((Dist >> 1) & 0x80); } CDialog::OnVScroll(nSBCode, nPos, pScrollBar); } Работает. static_cast<CSpinButtonCtrl*>(GetDlgItem(IDC_SPIN2))->SetRange(0,100); Это тоже работает. Очевидно была механ. ошибка. В CSpinButtonCtrl вместо l(L) стояла 1(1) Переменную m_spin2Value Прикреплённая картинка
|
Сообщ.
#86
,
|
|
|
Цитата Acvarif @ Переменную m_spin2Value правильно, но в данном случае она не нужна это в C++Builder их лепят без надобности, тут все по взрослому |
Сообщ.
#87
,
|
|
|
Цитата Acvarif @ "Частота бод" - звучит, как фраза из далёкого прошлого. Сечас пишут "bps" - bits per second - бит в секунду. --- я вот чего хотел узнать - в среде Борланд Билдера надо-ли каким-то образом указывать ID контрола ? (Того-же статика, как у тебя в этом проекте, например) Или там всё автомотизировано ? |
Сообщ.
#88
,
|
|
|
В Билдере все автоматизировано.
Например клик по ComboBox5 (который уже выставлен на форму) - появляется функция в которой производятся операции с переменными, локальными или глобальными void __fastcall TForm1::ComboBox5Click(TObject *Sender) { if(ComboBox5->Text == "15-25м") { bufwr[1] &= ~(1 << 5); } else if(ComboBox5->Text == "25-40м") { bufwr[1] |= (1 << 5); } } В качестве ID просто названия типа ComboBox5. По поводу Контрола. Что имеется ввиду? Если например CSpinEdit который повторяет Edit Control+Spin Control MFC то тоже все просто. Например изменение данных в окне на 1 void __fastcall TForm1::CSpinEdit2Change(TObject *Sender) { PrCanal = CSpinEdit2->Value; } Value - готовая переменная, одинаковая почти для всех Контролов, в которой данные меняются при клике вверх или вниз. PrCanal - пользовательская переменная А Статика вообще проще-простого Например вывод данных в Статик под названием Label16 (это то же что и ID)из пременной Distm sprintf(Buffer,"Дистанция: %d м", Distm); Form1->Label16->Caption = Buffer; Buffer - обычно char Buffer[20] - достаточно для небольшого текста |
Сообщ.
#89
,
|
|
|
Цитата Acvarif @ А Статика вообще проще-простого Если в MFC "этого" нет, ты можешь сам "это" сделать. Прямо сейчас, и даже ещё лучше.. Наследуемся от класса... эээ.. который "статик". Который, наверное, CStatic. Перегружаем метод SetWindowText. Или пишем метод SetWindowTextEx. И пишем что-то вроде: // --------------------------------------------------------------------------------- void WINAPI CStaticEx::SetWindowTextEx(const TCHAR* pFmt,...) { if(!pFmt) return; va_list ap; //Указатель на список параметров va_start(ap,pFmt); //Настроились на список параметров TCHAR sss[32768]; size_t sizeBuff = ARRAYSIZE(sss); _vsntprintf_s(sss,sizeBuff,_TRUNCATE,pFmt,ap); SetWindowText(sss); va_end(ap); //Завершаем работу с макрокомандами } // --------------------------------------------------------------------------------- В результате ты сможешь писать: CStaticEx xxmm; //... xxmm.SetWindowTextEx(_T("Дистанция: %d м"), Distm); //... В результате число таких конструкций: char Buffer[20]; sprintf(Buffer,"Дистанция: %d м", Distm); //Buffer - обычно - достаточно для небольшого текста резко уменьшится. |
Сообщ.
#90
,
|
|
|
Может быть. Мне трудно судить. Былдер в свое время был идеальной средой для разработки визуальных приложений. Если в метку типа Label1 (Static Control)нужно поместить текст то без всяких заморочек
Label1->Caption = "текст"; |
Сообщ.
#91
,
|
|
|
Цитата Acvarif @ Былдер в свое время был идеальной средой для разработки визуальных приложений. Если в метку типа Label1 (Static Control)нужно поместить текст то без всяких заморочек Label1->Caption = "текст"; MFC код длинее, но не сложнее SetDlgItemText(IDC_STATIC1,"текст"); IDC_STATIC1 - это идентификатор ресурса в файле rc, SetDlgItemText - функция-член класса CWnd. Все просто Цитата Acvarif @ MFC неплохо придуман. На первый взгляд небольшое неудобство - мало компонентов. Хотя может в большинстве случаев того, что есть, вполне достаточно.. их (компонентов) вообще нет, это контролы - классы для управления объектами виндос (окнами) |
Сообщ.
#92
,
|
|
|
Спасибо за помощь.
Билдеровскую прогу удалось повторить на MFC. Все (прием-передача) крутится в одном потоке. Немного причешу и выложу в архиве. Может кому будет полезна. Далее задачка разобрать по косточкам буфер приема и отобразить на цветной панели цветные прямоугольники. Панель разбита сеткой прямоугольных ячеек. 120 по горизонтали, 500 по вертикали 120Х500. Принимаемые данные грубо говоря это координаты ячеек в сетке которые нужно засвечивать цветом отличным от цвета сетки и цвета пустой ячейки. Пока это задача минимум. Как правильно поступиь? Прикручивать к проекту OpenGl (GLUT и т.п.) или можно обойтись средствами MFC? |
Сообщ.
#93
,
|
|
|
И ради этого ты с Билдера ушёл?
Цитата приклепать сложную графику |
Сообщ.
#94
,
|
|
|
Нет. Дальше графика действительно сложная. Сетка это пока начало.
|
Сообщ.
#95
,
|
|
|
Цитата Acvarif @ Нет. Дальше графика действительно сложная. Сетка это пока начало. я бы GLUT прикрутил для реально сложной графики |
Сообщ.
#96
,
|
|
|
В скрепке архив проекта MFCtest.
Прикреплённый файлMFCtest.rar (146,87 Кбайт, скачиваний: 81)
Может кому пригодится в качестве примера приема-передачи по COM в асинхронном режиме. Также в архиве есть текстовый файл MFCtest.txt который является тестовой текстовой строкой для известной программы работы с COM портом ComPortTolkit. На ПК должно быть как минимум 2 COM порта соединенных перекрестным кабелем (выход передачи одного на вход приема другого). Один работает на передачу из программы ComPortTolkit. Другой на прием с пом. проги из проекта MFCtest.
ComPortTolkit передает порядка 850 байт на MFCtest обратно получает ответ 128 байт. Если где чего не так не судите строго. |
Сообщ.
#97
,
|
|
|
Цитата Acvarif @ Прикручивать к проекту OpenGl (GLUT и т.п.) или можно обойтись средствами MFC? Скорее всего MFC (т.е. обычного GDI) хватит. |
Сообщ.
#98
,
|
|
|
Все же попробую GLUT. Скачал и раскидал нужные файлы по нужным местам http://rucodes.com/opengl-microsoft-visual-studio-c.html
Дальше немного растерялся. В проекте имеется глобальный буфер bufrd[] где каждую секунду обновляются данные с COM порта, которые нуно разобрать и отобразить в графическом окне. Каким образом создается графическое окно с GLUT? Это должен быть отдельный файл в составе существующего проекта? Какова технология добавления графического окна к уже существующему проекту? |
Сообщ.
#99
,
|
|
|
Цитата Acvarif @ Все же попробую GLUT. ... В проекте имеется глобальный буфер bufrd[]... Дальше немного растерялся. ... Какова технология добавления графического окна к уже существующему проекту? Упс... Получается, что ты даже отдалённо не знаешь, какова должна быть структура проекта. А выбор уже сделал.. Глобальный буфер с данными в многопоточной программе я бы не посоветовал. Это и не нужно ни с какой точки зрения. |
Сообщ.
#100
,
|
|
|
Структуру не представляю. Простейший поиск по сети - приложения GLUT консольные WIN32. Какова должна быть структура MFC GLUT пока смутно представляю. Очевидно должен быть еще один поток. В имеющемся файле ..Dlg или.. не врубаюсь. Чем плох глобальный буфер?
|
Сообщ.
#101
,
|
|
|
Цитата Acvarif @ Очевидно должен быть еще один поток. В имеющемся файле ..Dlg или.. не врубаюсь. Чем плох глобальный буфер? Этим вот и плох. Из одного потока буфер заполняется. Из другого читается. Как будешь синхронизировать эти потоки для доступа к данным ? |
Сообщ.
#102
,
|
|
|
Думаю с помщью флага. Ведь период обмена 1 сек. Буфер заполняется (115200) примерно через 100 мс. Далее установка флага и работа с буфером до конца секунды. Но обычно делаются качели - 2 глобальных буфера которые заполняются попеременно. Пока один заполняется с другим можно работать. в следующую секунду все меняется местами. Ничего особенного.. Не врубаюсь где нужно писать код с GLUT, в каком файле? Где запускать поток, в каком файле? Каким образом в проекте должно появиться графическое окно? Совсем смутно с этим.
По поводу двух потоков - понял. Спасибо. Не в тему. С наступающим Новым Годом! Всех благ! Удачи! |
Сообщ.
#103
,
|
|
|
Цитата Acvarif @ Но обычно делаются качели - 2 глобальных буфера которые заполняются попеременно. Пока один заполняется с другим можно работать. в следующую секунду все меняется местами. Ничего особенного.. ..И тут вдруг будет обнаружено, что эта "естественная синхронизация" может, хотя и изредко, нарушаться. Когда второй буфер уже заполнен и происходит обмен буферами, первый ещё не считан и, как раз в этот момент, считывается. |
Сообщ.
#104
,
|
|
|
Цитата Acvarif @ Не врубаюсь где нужно писать код с GLUT, в каком файле? Где запускать поток, в каком файле? Каким образом в проекте должно появиться графическое окно? Совсем смутно с этим. определяешь функцию потока, в которой пишешь код glut, потом вызываешь через AfxBeginThread из какого места? например по нажатию кнопки. Добавлено с наступающим Новым годом петуха! |
Сообщ.
#105
,
|
|
|
Цитата С Новым Годом! Всех благ!определяешь функцию потока, в которой пишешь код glut, потом вызываешь через AfxBeginThread из какого места? например по нажатию кнопки. с наступающим Новым годом петуха! Определил функцию потока рисования и входящие в нее функции void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glRotatef(ang, 0.0, 1.0, 0.0); glutSolidTeapot(1.0); glPopMatrix(); glutSwapBuffers(); } void reshape(int x, int y) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60, (float)x/(float)y, 0.1, 100); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); } void idle() { glutPostRedisplay(); } DWORD WINAPI ImageThread(LPVOID lpParam) { glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); glutCreateWindow("test"); glutDisplayFunc(display); glutReshapeFunc(reshape); glutIdleFunc(idle); glutMainLoop(); return 0; } По запуску программы создал поток image = CreateThread( NULL, 0, ImageThread, GetSafeHwnd(), CREATE_SUSPENDED, NULL); Sleep(10); По нажатию кнопки поток запускается void CMFCtestDlg::OnBnClickedButton4() { // активировать поток рисования ResumeThread(image); } При запуске 'Build' все застревает на функции создания окна glutCreateWindow("test"); Error 1 error LNK2019: unresolved external symbol ___glutCreateWindowWithExit@8 referenced in function _glutCreateWindow_ATEXIT_HACK@4 MFCtestDlg.obj MFCtest Error 2 fatal error LNK1120: 1 unresolved externals Не врубаюсь, что не определено? |
Сообщ.
#106
,
|
|
|
Сообщ.
#107
,
|
|
|
Цитата Acvarif @ Фух.. Запустилось. |
Сообщ.
#108
,
|
|
|
По ходу возник актуальный вопрос. Если возникнет необходимость на GLUT нарисовать весь интерфейс с сеткой, кнопками, окнами прокрутки - можно-ли будет управлять кнопками, окнами с помощью мышки и клавиатуры? Или можно-ли встроить интерфейс сделанный в MFC в графическое окно GLUT?
|
Сообщ.
#109
,
|
|
|
Цитата Acvarif @ Или можно-ли встроить интерфейс сделанный в MFC в графическое окно GLUT? У GLUT есть свой цикл обработки сообщений, как их совместить с MFC циклом хз. Надо лезть в сорсы глут и изучать. |
Сообщ.
#110
,
|
|
|
Можно-ли поместить окно GLUT в окно Edit Contol MFC? Или просто жестко привязать - разместить на форме MFC.
|
Сообщ.
#111
,
|
|
|
Glut неплохо все рисует.
Прикреплённая картинка
Но возникла проблема с прорисовкой движения мети (зеленая полоска на изображении). Оказалось, что функция glutMainLoop() не имеет возврата. Если она вызывается, например, в потоке рисования то на ней все и застревает. // сетка с меткой void Display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glBegin(GL_LINES); glColor3f(1.0, 1.0, 1.0); // по горизонтали for(int i = -WinWidth/2; i <= WinWidth/2; i += 5) { if(i%100 == 0 || i == 0) glColor3f(0.0, 1.0, 1.0); else glColor3f(0.5, 0.5, 0.5); glVertex2f(i, -WinHeight); glVertex2f(i, WinWidth); } glColor3f(0.5, 0.5, 0.5); // по вертикали for(int i = -WinWidth/2; i <= WinWidth/2; i += 100) { glVertex2f(-WinHeight, i); glVertex2f(WinHeight, i); } glEnd(); glColor3f( 0.0, 1.0, 0.0 ); glPolygonMode( GL_FRONT, GL_FILL ); glBegin( GL_POLYGON ); glVertex2i( x+0, y+1 ); glVertex2i( x+5, y+1 ); glVertex2i( x+5, y+0 ); glVertex2i( x+0, y+0 ); glEnd(); glFlush(); } //--------------------------------------------------------------------------- // Поток рисования DWORD WINAPI ImageThread(LPVOID lpParam) { counterRX = counterRX + 5; CString str_static10; // CWnd* pWnd = CWnd::FromHandle((HWND)lpParam); // получить указатель на IDC_STATIC10 CStatic* pText10 = (CStatic*)pWnd->GetDlgItem(IDC_STATIC10); str_static10.Format(_T("Прием %d"), counterRX); pText10->SetWindowText(str_static10); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glViewport(0, 0, WinWidth, WinHeight); glutInitWindowSize(WinWidth, WinHeight); glutCreateWindow("Drawing with GLUT"); glutDisplayFunc(Display); glutMainLoop(); Sleep(1); return 0; } Для проверки в поток добавил счетчик counterRX с выводом на IDC_STATIC10. После запуска программы появляется рисунок но дальше явно все застревает на glutMainLoop(); поскольку счетчик останавливается на числе 5. При этом поток приема-передачи по COM работает нормально. Получается что в данном случае можно нарисовать только статическую картинку. Понимаю, что вопрос не в тему MFC, но может кто сталкивался с MFC+GLUT и динамическими картинками. Как решить вопрос MFC+GLUT_динамическое? |
Сообщ.
#112
,
|
|
|
думаю надо юзать glutIdleFunc и передать ей колбяк функцию:
void MyIdleProc() { /* Код, который меняет переменные, определяющие следующий кадр */ .... }; void MyDisplayProc() { /* Код OpenGL, который отображает кадр */ .... /* После отрисовки мы переставляем (заменяем) буфера */ glutSwapBuffers(); }; void main(int argcp, char **argv) { .... /* Регистрация вызываемых функций */ glutDisplayFunc(MyDisplay); glutIdleFunc(MyIdle); /* Запуск механизма обработки событий */ glutMainLoop(); } |
Сообщ.
#113
,
|
|
|
Спасибо. Пробую так. Весь код в файле MFCtestDlg.cpp
... int x = 0, y = 0; int counterTr; ... void Idle() { /* Код, который меняет переменные, определяющие следующий кадр */ x = x + 5; } Функция тображающая сетку с меткой в координатах x y void Display() { /* Код OpenGL, который отображает кадр */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-WinWidth/2, WinWidth/2, -WinHeight/2, WinHeight/2, -1.0, 1.0); // горизонтальные и вертикальные линии сетки glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glBegin(GL_LINES); glColor3f(1.0, 1.0, 1.0); // по горизонтали for(int i = -WinWidth/2; i <= WinWidth/2; i += 5) { if(i%100 == 0 || i == 0) glColor3f(0.0, 1.0, 1.0); else glColor3f(0.5, 0.5, 0.5); glVertex2f(i, -WinHeight); glVertex2f(i, WinWidth); } glColor3f(0.5, 0.5, 0.5); // по вертикали for(int i = -WinWidth/2; i <= WinWidth/2; i += 100) { glVertex2f(-WinHeight, i); glVertex2f(WinHeight, i); } glEnd(); // зеленая мета с координатами x, y glColor3f( 0.0, 1.0, 0.0 ); glPolygonMode( GL_FRONT, GL_FILL ); glBegin( GL_POLYGON ); glVertex2i( x+0, y+2 ); glVertex2i( x+5, y+2 ); glVertex2i( x+5, y+0 ); glVertex2i( x+0, y+0 ); glEnd(); glFlush(); /* После отрисовки мы переставляем (заменяем) буфера */ glutSwapBuffers(); } Далее поток рисования DWORD WINAPI ImageThread(LPVOID lpParam) { // контроль работы потока по изменению counterRX counterTr = counterTr + 5; CString str_static10; // CWnd* pWnd = CWnd::FromHandle((HWND)lpParam); // получить указатель на IDC_STATIC10 CStatic* pText10 = (CStatic*)pWnd->GetDlgItem(IDC_STATIC10); //вывести сообщение об этом в строке состояния str_static10.Format(_T("Прием %d"), counterTr); pText10->SetWindowText(str_static10); // выполняется один раз после запуса программы if(!win) { win = 1; glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glViewport(0, 0, WinWidth, WinHeight); glutInitWindowSize(WinWidth, WinHeight); glutCreateWindow("Drawing with GLUT"); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } // вызов функций рисования Display и смены координаты x Idle glutDisplayFunc(Display); glutIdleFunc(Idle); glutMainLoop(); Sleep(1); return 0; } Все прорисовывается нормально, но застревает на glutMainLoop() Счетчик counterTr останавливается на положении 5. Не врубаюсь, что я сделал не верно? |
Сообщ.
#114
,
|
|
|
Спасибо. Работает. Метка начала двигаться. По поводу встраивания окна Glut в MFC http://www.codeguru.com/cpp/cpp/cpp_mfc/tu...trol.htm#page-2
|
Сообщ.
#115
,
|
|
|
|
Сообщ.
#116
,
|
|
|
Цитата Acvarif @ По поводу встраивания окна Glut в MFC http://www.codeguru.com/cpp/cpp/cpp_mfc/tu...trol.htm#page-2 Хочу внести уточнение, на самом деле GLUT это независимая от платформы библиотека со своей оконной системой, поэтому ее нельзя было встроить в окно MFC или еще куда. А пример по ссылке приводится описание интеграции OpenGL и GLU (это не GLUT) в MFC через механизм Windows GL (WGL) На самом деле это даже более правильный способ использования OpenGL с MFC GLUT как правило используется в программировании игр, ну и хорошо подходит для изучения OpenGL. Сделал пример отрисовки OpenGL в окно MFC (максимально упростил ): Прикреплённый файлExample5.rar (67,49 Кбайт, скачиваний: 106) |
Сообщ.
#117
,
|
|
|
Спасибо за уточнение и пример.
|