GARANTIR DESCONTO

Fórum Identificar mensagens de erro dentro de uma thread, usando tpapro. #442872

14/05/2013

0

Olá pessoal, estou quebrando a cabeça e não consegui resolver.
É o seguinte, fiz um programinha no delphi conforme indicado no manual Apro_DevGuide.pdf, para enviar mensagens via sms e funcionou perfeitamente, inclusive a parte de mensagens do componente, me informando quando ocorreu tudo certo ou quando deu algum erro.
O problema é que preciso usar esse programa como uma thread e tentei de várias formas, mas as mensagens não são geradas dentro do thread.
Vou colocar as units e solicito que me ajudem, por favor.

Primeira unit, do formulário principal.
unit uSms;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, adgsm, OoMisc, AdPort;

type
  TForm1 = class(TForm)

    Edit1: TEdit;
    Edit2: TEdit;
    Button1: TButton;
    ListBox1: TListBox;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
    {linhas comentadas para teste com a thread}
//    procedure ApdGSMPhone1GSMComplete(Pager: TApdCustomGSMPhone;
//      State: TGSMStates; ErrorCode: Integer);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses uThreadSMS;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
Var
  envia : TThreadSMS;
begin
    {linhas comentadas para teste com a thread}
//  ApdGSMPhone1.SMSAddress := Edit1.Text;
//  ApdGSMPhone1.SMSMessage := Edit2.Text;
//  ApdGSMPhone1.SendMessage;
  envia := TThreadSMS.Create;
  envia.Resume;
end;
    {linhas comentadas para teste com a thread}
    {esse procedimento funciona perfeitamente aqui, mas não na thread}
//procedure TForm1.ApdGSMPhone1GSMComplete(Pager: TApdCustomGSMPhone;
//  State: TGSMStates; ErrorCode: Integer);
//begin
//  ListBox1.Items.Add('Erro :' + IntToStr(ErrorCode) + '-' + ErrorMsg(ErrorCode));
//  ListBox1.Items.Add('Error number ' + IntToStr(ErrorCode));
//  case State of
//    gsNone:    ListBox1.Items.Add('idle');
//    gsConfig:  ListBox1.Items.Add('Configuration finished');
//    gsSendAll: ListBox1.Items.Add('Enviando todas as mensagens armazenadas');
//    gsSend:    ListBox1.Items.Add('Message was sent');
//    gsListAll: ListBox1.Items.Add('List all messages finished');
//    gsWrite:   ListBox1.Items.Add('Escrevendo uma mensagem no armazenamento');
//  end;
//end;

end.


e agora a unit da thread.

unit uThreadSMS;

interface

uses
  Classes {$IFDEF MSWINDOWS} , Windows {$ENDIF}, adgsm, OoMisc, AdPort, AdExcept, Forms;

type
  TThreadSMS = class(TThread)

    ApdGSMPhone2: TApdGSMPhone;
    ApdComPort2: TApdComPort;
    procedure ApdGSMPhone2GSMComplete(Pager: TApdCustomGSMPhone;
      State: TGSMStates; ErrorCode: Integer);
    procedure SmsgsNone;
    Procedure SmsgsConfig;
    procedure SmsgsSendAll;
    procedure SmsgsWrite;
    procedure SmsgsListAll;
    procedure SmsgsSend;
    procedure PegaTelefone;
    procedure PegaMensagem;
    procedure PegaPorta;
    Procedure PegaVelocidade;
    Procedure AtualizaForm1;
    Constructor Create;


  private
    procedure SetName;
  protected
    procedure Execute; override;
  end;

implementation

uses uSms;

{ Important: Methods and properties of objects in visual components can only be
  used in a method called using Synchronize, for example,

      Synchronize(UpdateCaption);

  and UpdateCaption could look like,

    procedure TThreadSMS.UpdateCaption;
    begin
      Form1.Caption := 'Updated in a thread';
    end; }

{$IFDEF MSWINDOWS}
type
  TThreadNameInfo = record
    FType: LongWord;     // must be 0x1000
    FName: PChar;        // pointer to name (in user address space)
    FThreadID: LongWord; // thread ID (-1 indicates caller thread)
    FFlags: LongWord;    // reserved for future use, must be zero
  end;
{$ENDIF}

{ TThreadSMS }

procedure TThreadSMS.SetName;
{$IFDEF MSWINDOWS}
var
  ThreadNameInfo: TThreadNameInfo;
{$ENDIF}
begin
{$IFDEF MSWINDOWS}
  ThreadNameInfo.FType := $1000;
  ThreadNameInfo.FName := 'SMS';
  ThreadNameInfo.FThreadID := $FFFFFFFF;
  ThreadNameInfo.FFlags := 0;

  try
    RaiseException( $406D1388, 0, sizeof(ThreadNameInfo) div sizeof(LongWord), @ThreadNameInfo );
  except
  end;
{$ENDIF}
end;

procedure TThreadSMS.Execute;
begin
  SetName;
  { Place thread code here }
   ApdComPort2 := TApdComPort.Create(nil);
   ApdGSMPhone2 := TApdGSMPhone.Create(nil);

  ApdComPort2.ComNumber := 5;
  ApdComPort2.Baud := 115000;
  ApdComPort2.Parity := pNone;
  ApdComPort2.DataBits := 8;
  ApdComPort2.StopBits := 1;
  ApdComPort2.Open := True;
  ApdGSMPhone2.ComPort := ApdComPort2;
  ApdGSMPhone2.GSMMode := gmDetect;
  Synchronize(PegaTelefone);
  Synchronize(PegaMensagem);
  ApdGSMPhone2.QuickConnect := True;
  ApdGSMPhone2.SendMessage;
end;

    {é essa parte que quero que informe as mensagens, mas não funciona dentro da thread}
procedure TThreadSMS.ApdGSMPhone2GSMComplete(Pager: TApdCustomGSMPhone;
  State: TGSMStates; ErrorCode: Integer);
begin
  Synchronize(AtualizaForm1);
//  ListBox1.Items.Add('Erro :' + IntToStr(ErrorCode) + '-' + ErrorMsg(ErrorCode));
//  ListBox1.Items.Add('Error number ' + IntToStr(ErrorCode));
  case State of
    gsNone:    Synchronize(SmsgsNone);
    gsConfig:  Synchronize(SmsgsConfig);
    gsSendAll: Synchronize(SmsgsSendAll);
    gsSend:    Synchronize(SmsgsSend);
    gsListAll: Synchronize(SmsgsListAll);
    gsWrite:   Synchronize(SmsgsWrite);
  end;
end;

procedure TThreadSMS.SmsgsConfig;
begin
    Form1.ListBox1.Items.Add('Configuration finished');
end;

procedure TThreadSMS.SmsgsListAll;
begin
    Form1.ListBox1.Items.Add('List all messages finished');

end;

procedure TThreadSMS.SmsgsNone;
begin
    Form1.ListBox1.Items.Add('Idle');

end;

procedure TThreadSMS.SmsgsSend;
begin
    Form1.ListBox1.Items.Add('Message was sent');

end;

procedure TThreadSMS.SmsgsSendAll;
begin
    Form1.ListBox1.Items.Add('Enviando todas as mensagens armazenadas');

end;

procedure TThreadSMS.SmsgsWrite;
begin
    Form1.ListBox1.Items.Add('Escrevendo uma mensagem no armazenamento');

end;

procedure TThreadSMS.PegaMensagem;
begin

  ApdGSMPhone2.SMSMessage := Form1.Edit2.Text;
end;

procedure TThreadSMS.PegaTelefone;
begin
  ApdGSMPhone2.SMSAddress := Form1.Edit1.Text;
end;

constructor TThreadSMS.Create;
begin
  inherited Create(True); { Chama o contrutor herdado. Ele irá temporariamente colocar o thread em estado de espera para depois executá-lo. }
  FreeOnTerminate := True;
end;

procedure TThreadSMS.PegaPorta;
begin
//  ApdComPort2.ComNumber := Form1.ApdComPort1.ComNumber;

end;

procedure TThreadSMS.PegaVelocidade;
begin
//  ApdComPort2.Baud := Form1.ApdComPort1.Baud;
end;

procedure TThreadSMS.AtualizaForm1;
begin
  Form1.ListBox1.Items.Add('Chegou no GSMComplete');
  Form1.Label1.Caption := 'chegou no GSMComplete';
  Application.ProcessMessages;
end;

end.


Obrigado pela atenção.
Marcos Saffran

Marcos Saffran

Responder

Posts

15/05/2013

Alex Constâncio

Olá

Por favor, seja mais específico quando diz "não funciona". Você que dizer que recebe uma mensagem de erro (e nesse caso seria bom colocá-la aqui) ou que simplesmente nada acontece?

Independentemente do caso, vejo no teu código uma prática a ser evitada: o acesso á variável Form1. Mais interessante e seguro é você criar uma propriedade na classe da thread que receba o Form e passar a usar esta propriedade. Quando o Teu form mudar de nome, o código da thread não vai sofrer.

Alex
Responder

Gostei + 0

15/05/2013

Marcos Saffran

Olá

Por favor, seja mais específico quando diz "não funciona". Você que dizer que recebe uma mensagem de erro (e nesse caso seria bom colocá-la aqui) ou que simplesmente nada acontece?

Independentemente do caso, vejo no teu código uma prática a ser evitada: o acesso á variável Form1. Mais interessante e seguro é você criar uma propriedade na classe da thread que receba o Form e passar a usar esta propriedade. Quando o Teu form mudar de nome, o código da thread não vai sofrer.

Alex


Olá Alex,
simplesmente nada acontece, o evento GSMComplete não ocorre, ou não sei como capturá-lo, dentro da thread.

Obrigado pela dica da prática a ser evitada, farei a sua recomendação.
Responder

Gostei + 0

15/05/2013

Marcos Saffran

Tentando explicar um pouco melhor, uso o componente TApdGSMPhone, mas dentro da thread não consigo usar o evento GSMComplete do componente para identificar as mensagens geradas pelo processo.
Responder

Gostei + 0

17/05/2013

Alex Constâncio

Olá

Este componente é assíncrono? Quando acontece o evento Complete? Percebi que o Execute da tua Thread não fica em loop, ela inicia, faz várias coisas e termina. Quando o Execute termina, a thread pára de executar, eventualmente sendo destruída. Para operar o thread, a tua lógica tem que mudar um pouco para levar este comportamento em conta.

Alex
Responder

Gostei + 0

23/05/2013

Marcos Saffran

Olá Alex, me desculpe a demora.
Eu não sei se o componente é assíncrono.
O evento complete ocorre no término do envio da mensagem para o modem.
A thread não está em loop, pois ela é executada a cada clique do botão de envio, enviando uma mensagem por vez de forma direta, ou seja, envia a mensagem diretamente através do modem, sem usar a memória do mesmo.
Obrigado.
Responder

Gostei + 0

28/05/2013

Guilherme Wiethaus

Bom dia,


Algumas coisas você precisa rever das suas ideias. Vi alguns problemas, mas
são muitas as coisas a serem discutidas. Vou colocar algumas delas aqui para
te esclarecer um mais de como melhor será sua estratégia.

A forma que estas declarado o código de Execute() não surtirá muito efeito
pois irá executar em primeira instância sua Thread, passará pelo Synchronize()
e encerrará a execução da Thread. Para que a execução não termine necessita
dentro da Thread um laço while. a propriedade Terminated() indica que a sua
thread foi terminada e ela sairá do laço. Mas não se baseie apenas neste, pode
e deve ser usado com outros sinalizadores declarados por você que vão dizer
outros momentos de se encerrar a execução do laço na thread e consequentemente
a finalziar thread.

procedure TWMinhaThread.Execute;
begin
  while (not Terminated) begin
        Synchronize (ExecutaUmaTarefa_SMS);
  end;
end;


Outro ponto é evitar de criar criar objetos dentro de Execute() a não ser
que eles sejam destruídos logo abaixo. Se obviamente inserir a criação de
objetos dentro do laço irá criar infinitos objetos em memória até estourar
sua pilha e travamentos do sistema operacional (não deve, mas acontece em
alguns casos).

O ideal é criar fora da thread e configurar suas propriedades. A parte de
aquisição de dados você deve por dentro da Thread, mas tem um porém ai.

Se a transmissão/recepção de dados for mais lenta em relação a execução
de cada linha na thread (isto é o mais comum) irá executar o comando sem
ter transmitido/recebido completamente o pacote anterior, ocasionará assim,
dependendo da tecnologia do dispositivo, falhas na transmissão/recepção.
Geralmente o fabricante disponibiliza funções que avisa no modo assíncrono,
quando o dispositivo terminou de processar um comando. O Status do dispositivo
deve ser sempre lido.

Portanto, para contornar isto precisas usar sinalizadores que indicam
que a transmissão foi feita ou recebida, para que novamente a linha seja
executada. Assim deverá esperar por esta conclusão.

Existe outra questão ai no meio. Vai que seu dispositivo falhe e não
retorne o que espera....O thread ficará bloqueado intermitentemente esperando
em algum laço ou procedimento Wait() por alguma coisa que nunca irá receber.
Para tanto, se utiliza uma técnica de contagem de tempo de inicio e de fim da
execução. Se demorar mais doque o fabricante menciona necessário na execução de
um comando, este então deverá sair do laço de espera por timeout.

Outros recursos que precisa usar geralmente é o controle de seção crítica,
através de Semáforos e Mutex.

Vou colocar algumas referências aqui que pode ajudar a esclarecer mais
os conceitos de Thread:

Trabalhando com Threads:
[url]http://balaiotecnologico.blogspot.com.br/2009/08/trabalhando-com-threads.html[/url]

Trabalhando com Threads em Delphi - exemplo básico:
[url]http://balaiotecnologico.blogspot.com.br/2009/08/trabalhando-com-threads-em-delphi.html[/url]

TThread - Melhores práticas:
[url]http://eugostododelphi.blogspot.com.br/2009/11/tthread-melhores-praticas.html[/url]

Threads no Delphi, por onde começar ? – Parte I:
[url]http://drgarcia1986.wordpress.com/2013/01/31/threads-no-delphi-por-onde-comecar-parte-i/[/url]

Threads no Delphi, por onde começar ? – Parte II:
[url]http://drgarcia1986.wordpress.com/2013/02/08/threads-no-delphi-por-onde-comecar-parte-ii/[/url]

Threads no Delphi, por onde começar ? – Parte III:
[url]http://drgarcia1986.wordpress.com/2013/02/11/threads-no-delphi-por-onde-comecar-parte-iii/[/url]

Threads no Delphi, por onde começar ? – Parte IV:
[url]http://drgarcia1986.wordpress.com/2013/02/13/threads-no-delphi-por-onde-comecar-parte-iv/[/url]

Abraços
Responder

Gostei + 0

28/05/2013

Marcos Saffran

Obrigado pelo retorno Guilherme,
agradeço pelo ensinamento e estudarei os links, depois alterarei a thread e postarei os resultados.
Até mais,
Responder

Gostei + 0

28/05/2013

Tiago Nunes

Usar Thread é muito bom

type
TMinhaThread = class(TThread)
protected
procedure Execute; override;
end;

...

procedure TMinhaThread.Execute;
begin
while True do
begin
Sleep(100);
//Codigo
end;
end;

procedure TForm1.ExecuteMinhaThread;
var oThread: TMinhaThread;
begin
Screen.Cursor := crHourGlass;
try
Application.ProcessMessages;
oThread := TMinhaThread(True);
oThread.FreeOnTerminate := True;
oThread.Resume;
finally
Screen.Cursor := crDefault;
end;
end;

end.
Responder

Gostei + 0

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.

Aceitar