C++Builder
  Начало   Форум   Помощь Войти Регистрация  
Страниц: [1]   Вниз
  Печать  
Автор Тема: Как отобразить в окне Memo принятые данные по Com  (Прочитано 3363 раз)
brdm
Участник
**

Сказали спасибо: +0/-0
Offline Offline

Сообщений: 59


« : 06 октября 2016, 14:35:18 »

Небольшой проект содержит Com прием с последующей передачей. На приеме может приходить от 844 до 4800 байт. на передаче только 128 байт. Никак не получается правильно отображать в окне Memo принятые байты, поскольку каждый раз их приходит разное количество.
Код
#include <vcl.h>
#pragma hdrstop
 
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "CSPIN"
#pragma resource "*.dfm"
#include <iostream>
TForm1 *Form1;
 
DWORD nBytesRead, rb;
unsigned char Ibuffer[6000];
unsigned char buf_r[128];
char Data[6000];
using namespace std;
bool NbRead = false;
BOOL fSuccess;
char BufferSendCom[128];
unsigned int nBytesToRead = 6000;
 
//---------------------------------------------------------------------------
 
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);
}
//---------------------------------------------------------------------------
 
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);
}
 
//---------------------------------------------------------------------------
// Com
 
DWORD WINAPI Input_thread( LPVOID lpParam )
{
 int i, j, n, NbByteRead;
 char Buffer[10];
 AnsiString S,S1,S2,S3,St;
 
 while(1)
 {
   fSuccess = ReadFile( Form1->hSerial, &Ibuffer, Form1->buf_rez, &nBytesRead, NULL );
   if(fSuccess && nBytesRead)
   {
     fSuccess = 0;
     for (i = 0; i < nBytesRead; i++)
     {
       if(Ibuffer[i] == 0xa1 && Ibuffer[i + 1] == 0xa2)
       {
         NbByteRead = (Ibuffer[i + 2] << 8) | Ibuffer[i + 3];
         //
         sprintf(Buffer," Lenght \n %x", NbByteRead);
         Form1->Label6->Caption = Buffer;
 
         for (j = 0; j < nBytesRead; j++)
         {
           Data[j] = Ibuffer[j];
           Ibuffer[j] = 0;
         }
         hexdumpstrRead(Data, nBytesRead);
       }
     }
 
     Form1->BufferSendCom[0] = 0x55;
     Form1->BufferSendCom[1] = 0x80;
     Form1->BufferSendCom[2] = 0x00;
 
     for(i = 0; i < sizeof(BufferSendCom); i++)
     {
       TransmitCommChar(Form1->hSerial, Form1->BufferSendCom[i]);
     }
     hexdumpstrWrite(Form1->BufferSendCom,128);
   // Задержка
   ::Sleep(1);
   }
 }
 return 0;
 
}
 
Для того, чтобы в Memo2 нормально отобразилась принятая информация по каждому запуску программы нужно устанавливать переменную  Form1->buf_rez через
Код
void __fastcall TForm1::Edit1KeyDown(TObject *Sender, WORD &Key,
     TShiftState Shift)
{
  if(Key == 13) { buf_rez = Edit1->Text.ToInt();}
}
 

Подскажите пожалуйста как отобразить в Memo принятую информацию с любым количеством байт.
Записан
serg76
Участник
**

Сказали спасибо: +38/-2
Offline Offline

Сообщений: 575


« Ответ #1 : 07 октября 2016, 09:46:35 »

не понял
Цитировать
не получается правильно отображать в окне Memo принятые байты

Код:
  AnsiString asd; 

    int count=recvfrom(udp,&bufw[0],1400,0,(sockaddr *) &dest_addr, &fromlen);

    asd="recv=";
    for(i=0;i<count;i++)
    {
      asd+=" 0x"+IntToHex((int)bufw[i],2);
    }
    Form1->Memo1->Lines->Add(asd);
на любую длину принятого пакета
Записан
brdm
Участник
**

Сказали спасибо: +0/-0
Offline Offline

Сообщений: 59


« Ответ #2 : 10 октября 2016, 14:36:55 »

Речь идет про Com порт.
Функция приема данных
Код
fSuccess = ReadFile( Form1->hSerial, &Ibuffer, Form1->buf_rez, &nBytesRead, NULL );
предполагает указать количество байт после достижения которых fSuccess установится в 1. Но все дело в том, что каждый раз это количество байт разное. Как правильно принять и отобразить данные пришедшие на Com порт?
Записан
serg76
Участник
**

Сказали спасибо: +38/-2
Offline Offline

Сообщений: 575


« Ответ #3 : 11 октября 2016, 10:06:37 »

в нормальном пакете ответа, 1й байт адрес, 2й(и 3й) - размер пакета

if(nBytesRead>3) считываешь размер и
Form1->buf_rez=размер;(начальное Form1->buf_rez=844;)

или

в Form1->buf_rez=максимально возможный размер пакета;(4801 байт)
и fSuccess должен установиться в 1 в конце пакета, размер nBytesRead
Записан
brdm
Участник
**

Сказали спасибо: +0/-0
Offline Offline

Сообщений: 59


« Ответ #4 : 14 октября 2016, 18:09:06 »

Ошибка втом, что когда неизвестно количество принимаемых байт прием нужно вести асинхронно. Это выглядит так
Код
#include <vcl.h>
#pragma hdrstop
 
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
 
#define BUFSIZE 1000     //ёмкость буфера
 
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
 : TForm(Owner)
{
}
//---------------------------------------------------------------------------
// Глобальные переменные
 
//---------------------------------------------------------------------------
AnsiString portname;
HANDLE COMport;
DWORD WINAPI ReadThread(LPVOID);
DWORD WINAPI WriteThread(LPVOID);
 
//дескриптор потока чтения из порта
HANDLE reader;
//дескриптор потока записи в порт
HANDLE writer;
//приёмный и передающий буферы
unsigned char bufrd[BUFSIZE], bufwr[BUFSIZE];
 
//структура OVERLAPPED необходима для асинхронных операций, при этом для операции чтения и записи нужно объявить разные структуры
//эти структуры необходимо объявить глобально, иначе программа не будет работать правильно
OVERLAPPED overlapped;      //будем использовать для операций чтения (см. поток ReadThread)
OVERLAPPED overlappedwr;    //будем использовать для операций записи (см. поток WriteThread)
int handle;                 //дескриптор для работы с файлом с помощью библиотеки <io.h>
//  bool fl=0;  //флаг, указывающий на успешность операций записи (1 - успешно, 0 - не успешно)
DWORD bc;
DCB *dcb;
// счетчик приемов
unsigned long counterRX;
 
//---------------------------------------------------------------------------
 
// Функции
 
//---------------------------------------------------------------------------
// Вывод принятых байтов на экран
 
void ReadPrinting(DWORD btr)
{
 String stroka, temp_str;
 for(UINT32 i=0; i<btr ; i++)
   stroka = stroka + IntToHex(bufrd[i], 2) + ' '; // Перенос буфера в строку.
 
 Form1->Memo1->Lines->Add(stroka); // Вывод.
 Form1->Label1->Caption = "Всего принято: " + IntToStr(counterRX);
 memset(bufrd, 0, BUFSIZE); // Очистить буфер (чтобы данные не накладывались друг на друга).
}
 
//---------------------------------------------------------------------------
// Поток чтения ReadThead
 
//главная функция потока, реализует приём байтов из COM-порта
DWORD WINAPI ReadThread(LPVOID)
{
 COMSTAT comstat;        // структура текущего состояния порта, в данной программе используется для определения количества принятых в порт байтов
 DWORD btr, temp, mask, signal;  // переменная temp используется в качестве заглушки
 
 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)                       //если событие прихода байта произошло
   {
     if(GetOverlappedResult(COMport, &overlapped, &temp, true)) //проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent
       if((mask & EV_RXCHAR)!=0)                 //если произошло именно событие прихода байта
       {
         ClearCommError(COMport, &temp, &comstat); // нужно заполнить структуру COMSTAT
         btr = comstat.cbInQue;                    // и получить из неё количество принятых байтов
         if(btr)                                   // если действительно есть байты для чтения
         {
           ReadFile(COMport, bufrd, btr, &temp, &overlapped); // прочитать байты из порта в буфер программы
           counterRX+=btr;                                      // увеличиваем счётчик байтов
           ReadPrinting(btr);                                    // вызываем функцию для вывода данных на экран и в файл
         }
       }
   }
 }
//CloseHandle(overlapped.hEvent);
//flag2=1;
}
 
//---------------------------------------------------------------------------
// Поток записи WriteThread
 
//главная функция потока, выполняет передачу байтов из буфера в COM-порт
DWORD WINAPI WriteThread(LPVOID)
{
 DWORD temp, signal;   //temp - переменная-заглушка
 
 overlappedwr.hEvent = CreateEvent(NULL, true, true, NULL);      //создать событие
 while(1)
 {
   //записать байты в порт (перекрываемая операция!)
   WriteFile(COMport, bufwr, strlen(bufwr), &temp, &overlappedwr);
 
   //приостановить поток, пока не завершится перекрываемая операция WriteFile
   signal = WaitForSingleObject(overlappedwr.hEvent, INFINITE);
 
   //если операция завершилась успешно
   if((signal == WAIT_OBJECT_0) && (GetOverlappedResult(COMport, &overlappedwr, &temp, true)))
   {
     //вывести сообщение об этом в строке состояния
     Form1->Panel2->Caption = "Передача прошла успешно";
   }
   //иначе вывести в строке состояния сообщение об ошибке
   else {Form1->Panel2->Caption = "Ошибка передачи";}
 
   SuspendThread(writer);
 }
}
 
//---------------------------------------------------------------------------
// установки Com порта
 
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 Form1->Panel1->Caption = "";
 DCB dcb;
 COMMTIMEOUTS timeouts;
 
 // Открытие порта
//  portname += "\\\\.";  // com_port.c_str()
//  portname += ComboBox1->Text;
 //portname = ChoiceCOM->Text;
 portname = Form1->ComboBox1->Text;
 COMport=CreateFile(portname.c_str(), GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
 //здесь:
 // - portname.c_str() - имя порта в качестве имени файла,
 //   c_str() преобразует строку типа String в строку в виде массива типа char, иначе функция не примет
 // - GENERIC_READ | GENERIC_WRITE - доступ к порту на чтение/записть
 // - 0 - порт не может быть общедоступным (shared)
 // - NULL - дескриптор порта не наследуется, используется дескриптор безопасности по умолчанию
 // - OPEN_EXISTING - порт должен открываться как уже существующий файл
 // - FILE_FLAG_OVERLAPPED - этот флаг указывает на использование асинхронных операций
 // - NULL - указатель на файл шаблона не используется при работе с портами
 
 if(COMport==INVALID_HANDLE_VALUE)
 {
   Panel1->Font->Color = clRed;
   Panel1->Caption = " Не удалось открыть последовательный порт " + AnsiString(portname.c_str()) + ".";
   return;
 }
 else
 {
   Panel1->Font->Color = clGreen;
   Panel1->Caption = " Открыт порт " + AnsiString(portname.c_str());
   return;
 }
 
 // Инициализация порта
 //dcb=(DCB*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(DCB)); // Выделение области памяти для DCB из кучи и заполнение этой области нулями.
 dcb.DCBlength=sizeof(DCB);
 
 if(!GetCommState(COMport, &dcb)) // Эта функция заполняет DCB информацией о текущем состоянии устройства, точнее о его настройках.
 {
 // Если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния.
//  HeapFree(GetProcessHeap(),0,dcb); // Освобождение памяти из кучи.
   CloseHandle(COMport);
   Panel1->Font->Color = clRed;
   Panel1->Caption = "Не удалось считать DCB";
   return;
 }
 
 // Инициализация структуры DCB.
 dcb.BaudRate = StrToInt(Form1->ComboBox2->Text); //задаём скорость передачи 115200 бод
 dcb.fBinary = TRUE;                              //включаем двоичный режим обмена
 dcb.fOutxCtsFlow = FALSE;                        //выключаем режим слежения за сигналом CTS
 dcb.fOutxDsrFlow = FALSE;                        //выключаем режим слежения за сигналом DSR
 dcb.fDtrControl = DTR_CONTROL_DISABLE;           //отключаем использование линии DTR
 dcb.fDsrSensitivity = FALSE;                     //отключаем восприимчивость драйвера к состоянию линии DSR
 dcb.fNull = FALSE;                               //разрешить приём нулевых байтов
 dcb.fRtsControl = RTS_CONTROL_DISABLE;           //отключаем использование линии RTS
 dcb.fAbortOnError = FALSE;                       //отключаем остановку всех операций чтения/записи при ошибке
 dcb.ByteSize = 8;                                //задаём 8 бит в байте
 dcb.Parity = 0;                                  //отключаем проверку чётности
 dcb.StopBits = 0;                                //задаём один стоп-бит
 
 // Загрузить структуру DCB в порт.
 if(!SetCommState(COMport, &dcb))
 {// Если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния.
//  HeapFree(GetProcessHeap(),0,dcb); // Освобождение памяти из кучи.
   CloseHandle(COMport);
   Panel1->Font->Color = clRed;
   Panel1->Caption = "Не удалось установить DCB.";
   return;
 }
 
 // Установка таймаутов.
 timeouts.ReadIntervalTimeout = 0;     // таймаут между двумя символами
 timeouts.ReadTotalTimeoutMultiplier = 0;  // общий таймаут операции чтения
 timeouts.ReadTotalTimeoutConstant = 0;    // константа для общего таймаута операции чтения
 timeouts.WriteTotalTimeoutMultiplier = 0; // общий таймаут операции записи
 timeouts.WriteTotalTimeoutConstant = 0;   // константа для общего таймаута операции записи
 
 // Загрузить структуру таймаутов в порт.
 if(!SetCommTimeouts(COMport, &timeouts))
 {// Если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния.
   CloseHandle(COMport);
   Panel1->Font->Color = clRed;
   Panel1->Caption = "Не удалось установить тайм-ауты.";
   return;
 }
 
 // Установить размеры очередей приёма и передачи
 SetupComm(COMport,2000,2000);
 
 PurgeComm(COMport,PURGE_RXCLEAR); // Очистка буферов порта.
 
 Button1->Enabled = false;
 Button2->Enabled = true;
 
 // Создание потоков на WINAPI:
 
 // Cоздаём поток чтения, который сразу начнёт выполняться (предпоследний параметр = 0).
 reader = CreateThread(NULL, 0, ReadThread, NULL, 0, NULL);
 
 // Cоздаём поток записи в остановленном состоянии (предпоследний параметр = CREATE_SUSPENDED).
 writer = CreateThread(NULL, 0, WriteThread, NULL, CREATE_SUSPENDED, NULL);
 
 counterRX = 0;
}
//---------------------------------------------------------------------------
// завершение по закрытию формы
 
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
 if(writer)
 {
   TerminateThread(writer,0);
   CloseHandle(overlappedwr.hEvent);   //нужно закрыть объект-событие
   CloseHandle(writer);
 }
 //завершить поток чтения из порта, проверка if(reader) обязательна, иначе возникают ошибки
 //if(reader) reader->Terminate();
 //завершить поток записи в порт, проверка if(writer) обязательна, иначе возникают ошибки
//  if(writer)writer->Terminate();
 if(COMport) CloseHandle(COMport);  //закрыть порт
//  if(handle) close(handle);      //закрыть файл, в который велась запись принимаемых данных
 
 if(reader)
 {
   TerminateThread(reader,0);
   CloseHandle(overlappedwr.hEvent);   //нужно закрыть объект-событие
   CloseHandle(reader);
 }
 
}
//---------------------------------------------------------------------------
// передача данных
 
void __fastcall TForm1::Button2Click(TObject *Sender)
{
 memset(bufwr,0,BUFSIZE); //очистить программный передающий буфер,
   //чтобы данные не накладывались друг на друга
 PurgeComm(COMport, PURGE_TXCLEAR);            //очистить передающий буфер порта
 strcpy(bufwr,Form1->Edit1->Text.c_str());     //занести в программный передающий буфер строку из Edit1
 
 ResumeThread(writer);               //активировать поток записи данных в порт
}
//---------------------------------------------------------------------------
Но проект предполагает сразу после приема пакета из заранее неизвестного количества байт отправить обратно ответ из 128 байт. Проблема в том, как определить момент когда пакет из переменного количества байт полностью принят, чтобы начать передачу ответа???. Ведь прием ведется побайтно. 
 
Записан
serg76
Участник
**

Сказали спасибо: +38/-2
Offline Offline

Сообщений: 575


« Ответ #5 : 17 октября 2016, 11:25:50 »

Код:
  while(1)
  //while(!flag)                       // пока поток не будет прерван, выполняем цикл
  {
    WaitCommEvent(COMport, &mask, &overlapped);                   //ожидать события приёма байта (это и есть перекрываемая операция)
    signal = WaitForSingleObject(overlapped.hEvent, INFINITE);    //приостановить поток до прихода байта
    if(signal == WAIT_OBJECT_0)                       //если событие прихода байта произошло
    {
      if(GetOverlappedResult(COMport, &overlapped, &temp, true)) //проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent
        if((mask & EV_RXCHAR)!=0)                 //если произошло именно событие прихода байта
        {
          ClearCommError(COMport, &temp, &comstat); // нужно заполнить структуру COMSTAT
          btr = comstat.cbInQue;                    // и получить из неё количество принятых байтов
          if(btr)                                   // если действительно есть байты для чтения
          {
            ReadFile(COMport, bufrd, btr, &temp, &overlapped); // прочитать байты из порта в буфер программы
            counterRX+=btr;                                      // увеличиваем счётчик байтов
            ReadPrinting(btr);                                    // вызываем функцию для вывода данных на экран и в файл
          }
        }
    }
  }

если известна длина пакета 844 байта, как завершается прием пакета в бесконечном цикле?

повторю
Цитировать
в нормальном пакете ответа, 1й байт адрес, 2й(и 3й) - размер пакета

if(counterRX>3) считываешь размер пакета и
Form1->buf_rez=размер;(начальное Form1->buf_rez=844;)
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в: