Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Общие вопросы Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 20.10.2023, 03:59   #1
Fox78
Пользователь
 
Регистрация: 17.03.2017
Сообщений: 28
По умолчанию COM порт. Асинхронное чтение данных

Всем привет!
Работаю с COM-портом.
Мне нужно отправлять данные и постоянно "слушать" порт, принимать данные.

С простой отправкой проблем нет вообще, но вот когда нужно реализовать прием, начинаются непонятки.
Создаю поток и в нем жду и читаю приходящие данные.

Компилирую, запускаю приложение, все работает.
Возвращаюсь к работе через несколько часов, запускаю приложение, а оно виснет и на прием и на отправку и просто на закрытие.

Пробовал запуститься с правами администратора (возможно, что-то с правами доступа), помогло, но не надолго. Следующий запуск опять вис
Поотключал брэндмауэр, дефендер, толку ноль.

Закинул exe-шник на другую машину, все запустилось и заработало, прошел какой-то промежуток времени - та же фигня! Висит! Вторая машина не подключена к интернету, поэтому варианты предательских обновлений от винды отпадают.

Помогите разобраться, пплиииз, уже голову свернул

ОС Windows 10 x64

P.S. Порты выбираю правильно, настройки совпадают. Проверяю порты на чтение и запись сторонним приложением - все работает.

Код оставлю ниже:

Последний раз редактировалось Fox78; 20.10.2023 в 04:05.
Fox78 вне форума Ответить с цитированием
Старый 20.10.2023, 04:00   #2
Fox78
Пользователь
 
Регистрация: 17.03.2017
Сообщений: 28
По умолчанию

Unit Main

Код:
unit main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  Vcl.StdCtrls, ComPortReaderThread, Vcl.ComCtrls, Logger;

type
  TForm1 = class(TForm)
    btnConnect: TButton;
    MemoData: TMemo;
    SendMsg: TButton;
    LblCom: TLabel;
    ComboBoxPorts: TComboBox;
    LblSpeed: TLabel;
    ComboBoxBaudRate: TComboBox;
    btnRefresh: TButton;
    MemoReceive: TMemo;
    StatusBar: TStatusBar;
    procedure btnConnectClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure SendMsgClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure btnRefreshClick(Sender: TObject);

  private
    { Private declarations }
    FComHandle: THandle;
    FReadEvent: THandle;

    FComReaderThread: TComPortReaderThread;
    procedure HandleReceivedData(const Data: string);

    procedure SendMessage(const AMessage: string);
    procedure PopulateComPorts;


  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  function HexToBytes(const Hex: string): TBytes;


implementation

{$R *.dfm}

function HexToBytes(const Hex: string): TBytes;
var
  I: Integer;
begin
  SetLength(Result, Length(Hex) div 2);
  for I := 1 to Length(Hex) div 2 do
    Result[I - 1] := StrToInt('$' + Copy(Hex, (I - 1) * 2 + 1, 2));
end;

procedure GetAvailableComPorts(PortList: TStrings);
var
  PortNames: array[0..16383] of Char;
  ComPort: PChar;
  i: Integer;
begin
  PortList.Clear;
  if QueryDosDevice(nil, PortNames, SizeOf(PortNames) div SizeOf(Char)) <> 0 then
  begin
    ComPort := PortNames;
    while ComPort^ <> #0 do
    begin
      if StrLIComp(ComPort, 'COM', 3) = 0 then
        PortList.Add(ComPort);
      Inc(ComPort, StrLen(ComPort) + 1);
    end;
  end;
end;


procedure TForm1.SendMessage(const AMessage: string);
var
  HexValues: TStringList;
  HexBytes: TBytes;
  BytesWritten: DWORD;
  i: Integer;
begin
 // Создаем список для хранения введенных шестнадцатеричных значений
  HexValues := TStringList.Create;
  try
    // Разбиваем введенные значения на отдельные шестнадцатеричные числа
    HexValues.Delimiter := ' ';
    HexValues.DelimitedText := AMessage;

    // Преобразуем шестнадцатеричные значения в байты и отправляем их в COM-порт
    SetLength(HexBytes, HexValues.Count);
    for i := 0 to HexValues.Count - 1 do
    begin
      HexBytes[i] := StrToInt('$' + HexValues[i]);
    end;

    if FComHandle <> INVALID_HANDLE_VALUE then
    begin
      BytesWritten := 0;
      // Отправляем байты в COM-порт
      WriteFile(FComHandle, HexBytes[0], Length(HexBytes), BytesWritten, nil);

      // Проверяем успешность записи
      if BytesWritten <> Length(HexBytes) then
        StatusBar.Panels[1].Text := 'Ошибка отправки данных в COM-порт.'
      else
        StatusBar.Panels[1].Text := 'Данные успешно отправлены в COM-порт.';
    end
    else
      StatusBar.Panels[1].Text := 'COM-порт не подключен.';

  finally
    HexValues.Free;
  end;
end;


procedure TForm1.PopulateComPorts;
var
  PortNames: array[0..4095] of Char; // Выделяем буфер для хранения имен портов
  PortCount, i, StartPos, EndPos: Integer;
  PortName: string;
begin
 // Получаем список доступных COM-портов
  PortCount := QueryDosDevice(nil, PortNames, SizeOf(PortNames) div SizeOf(Char));

  // Очищаем ComboBox перед добавлением новых портов
  ComboBoxPorts.Items.Clear;

  // Добавляем имена COM-портов в ComboBox
  StartPos := 0;
  for i := 0 to PortCount - 1 do
  begin
    if (PortNames[i] = #0) or (i = PortCount - 1) then
    begin
      EndPos := i;
      PortName := '';
      SetString(PortName, PortNames + StartPos, EndPos - StartPos);
      if Pos('COM', PortName) = 1 then
        ComboBoxPorts.Items.Add(PortName);
      StartPos := EndPos + 1;
    end;
  end;

  // Если есть доступные порты, выбираем первый порт в списке
  if ComboBoxPorts.Items.Count > 0 then
    ComboBoxPorts.ItemIndex := 0;

end;


procedure FillBaudRateComboBox(ComboBox: TComboBox);
begin
  ComboBox.Items.Add('75');
  ComboBox.Items.Add('110');
  ComboBox.Items.Add('300');
  ComboBox.Items.Add('600');
  ComboBox.Items.Add('1200');
  ComboBox.Items.Add('2400');
  ComboBox.Items.Add('4800');
  ComboBox.Items.Add('9600');
  ComboBox.Items.Add('14400');
  ComboBox.Items.Add('19200');
  ComboBox.Items.Add('38400');
  ComboBox.Items.Add('57600');
  ComboBox.Items.Add('115200');

  // Значение по умолчанию
  ComboBox.ItemIndex := ComboBox.Items.IndexOf('9600');
end;


procedure TForm1.btnConnectClick(Sender: TObject);
var
  dcb: TDCB;
  ComPort: string;
  BaudRate: integer;
  ReceiveCallback: TProc<string>;
begin
  TLogger.Log('Нажали кнопку Соединить ');
  // Получаем выбранный COM-порт из ComboBox
  ComPort := ComboBoxPorts.Text;

  // Получаем выбранную скорость из ComboBox
  BaudRate := StrToInt(ComboBoxBaudRate.Text);

  FCOMHandle := CreateFile(PChar(ComPort), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0);

  if FComHandle <> INVALID_HANDLE_VALUE then
  begin
    // Создаем анонимный метод для обработки полученных данных
    ReceiveCallback := procedure(Data: string)
                       begin
                         HandleReceivedData(Data);
                       end;

    try
      // Создаем поток для асинхронного чтения данных из COM-порта
      FComReaderThread := TComPortReaderThread.Create(FComHandle, ReceiveCallback);
    except
         on E: Exception do
      begin
        TLogger.Log('Ошибка при создании потока: ' + E.Message);
      end;

    end;

    FillChar(dcb, SizeOf(dcb), 0);
    dcb.DCBlength := SizeOf(dcb);

    if GetCommState(FComHandle, dcb) then
    begin
      dcb.BaudRate := BaudRate;
      dcb.ByteSize := 8;
      dcb.Parity := NOPARITY;
      dcb.StopBits := ONESTOPBIT;

      if SetCommState(FComHandle, dcb) then
      begin
        StatusBar.Panels[0].Text := ComboBoxPorts.Text + ' подключен!';
        TLogger.Log(ComboBoxPorts.Text + ' подключен!');
      end
      else
        StatusBar.Panels[0].Text := ComboBoxPorts.Text + ' - Ошибка установки парамтеров COM-порта';
    end
    else
      StatusBar.Panels[0].Text := ComboBoxPorts.Text + ' - Ошибка получения параметров COM-порта';
  end
  else
    StatusBar.Panels[0].Text := ComboBoxPorts.Text + ' - Не удалось подключиться к COM-порту';

    TLogger.Log('Процедура отработала ');
end;

// Обработчик, который будет вызываться при получении данных из COM-порта
procedure TForm1.HandleReceivedData(const Data: string);
begin
  MemoReceive.Lines.Add(Data);
end;


procedure TForm1.btnRefreshClick(Sender: TObject);
begin
  GetAvailableComPorts(ComboBoxPorts.Items);

  // Если есть доступные порты, выбираем первый порт в списке
  if ComboBoxPorts.Items.Count > 0 then
    ComboBoxPorts.ItemIndex := 0;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  try
  if FComHandle <> INVALID_HANDLE_VALUE then
    begin
      if CloseHandle(FComHandle) then
        begin
          TLogger.Log('COM-порт успешно закрыт.');
        end
      else
        begin
          TLogger.Log('Ошибка при закрытии COM-порта: ' + SysErrorMessage(GetLastError));
        end;
    end;
  except
      on E: Exception do
    begin
      TLogger.Log('Ошибка при зхакрытии формы: ' + E.Message);
    end;

  end;

end;

procedure TForm1.FormCreate(Sender: TObject);
begin

  GetAvailableComPorts(ComboBoxPorts.Items);
  FillBaudRateComboBox(ComboBoxBaudRate);

  // Если есть доступные порты, выбираем первый порт в списке
  if ComboBoxPorts.Items.Count > 0 then
    ComboBoxPorts.ItemIndex := 0;

end;

procedure TForm1.SendMsgClick(Sender: TObject);
begin
  SendMessage(MemoData.Text);
end;

end.

Последний раз редактировалось Fox78; 20.10.2023 в 04:03.
Fox78 вне форума Ответить с цитированием
Старый 20.10.2023, 04:00   #3
Fox78
Пользователь
 
Регистрация: 17.03.2017
Сообщений: 28
По умолчанию

Unit ComPortReaderThread
Код:
unit ComPortReaderThread;

interface

uses
  System.Classes, Winapi.Windows, System.SysUtils, Logger;

type
  TComPortReaderThread = class(TThread)
  private
    FComHandle: THandle;
    FReceiveCallback: TProc<string>;
  protected
    procedure Execute; override;
  public
    constructor Create(ComHandle: THandle; ReceiveCallback: TProc<string>);
  end;

implementation


{ TComPortReaderThread }

constructor TComPortReaderThread.Create(ComHandle: THandle; ReceiveCallback: TProc<string>);
begin
  inherited Create(False); // Создаем поток в режиме "свободен"
  FreeOnTerminate := True; // Поток освобождается автоматически после завершения

  FComHandle := ComHandle;
  FReceiveCallback := ReceiveCallback;
end;

procedure TComPortReaderThread.Execute;
const
  BufferSize = 256;
var
  Buffer: array[0..BufferSize - 1] of Byte;
  BytesRead: DWORD;
  DataString: string;
begin
  while not Terminated do
  begin
    // Читаем данные из COM-порта в буфер
    if ReadFile(FComHandle, Buffer, BufferSize, BytesRead, nil) then
    begin
      if BytesRead > 0 then
      begin
        TLogger.Log(Format('Read %d bytes from COM port', [BytesRead]));

         // Преобразуем байты в строку
        SetString(DataString, PAnsiChar(@Buffer[0]), BytesRead);
        Synchronize(procedure
                    begin
                      FReceiveCallback(DataString);
                    end);
      end;
    end
    else
    begin
       TLogger.Log('Ошибка при чтении данных из COM-порта: ' + SysErrorMessage(GetLastError));
    end;

    Sleep(10); // Ждем некоторое время перед следующей попыткой чтения
  end;
end;

end.
Fox78 вне форума Ответить с цитированием
Старый 20.10.2023, 04:11   #4
Fox78
Пользователь
 
Регистрация: 17.03.2017
Сообщений: 28
По умолчанию

В просмотре событий Windows сейчас отловилась такая ошибка.
Может кто-то сталкивался?
Изображения
Тип файла: jpg 1.jpg (86.8 Кб, 13 просмотров)
Fox78 вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Сетевое асинхронное приложение. C# I_Enjoy_C# Помощь студентам 10 13.03.2016 17:02
Чтение и Запись в com порт Грендайзер Win Api 19 16.12.2013 22:39
передача данных в COM порт stepka Общие вопросы Delphi 9 06.06.2011 11:10
Асинхронное принятие команд с клавиатуры. xaknik Общие вопросы C/C++ 2 25.05.2011 14:46
Загрузка данных в Com-порт Pitbull Общие вопросы Delphi 0 22.01.2009 22:20