multithreading em delphi par uso de portas seriais

Delphi

15/04/2013

Boa noite.

Uso componentes / ActiveX para enviar SMS com modem conectado no USB.

Preciso criar uma multithread para ler várias portas USB ao mesmo tempo, tipo todas portas USB existente no PC ou até mesmo usando um HUB USB.

Obrigado a quem puder ajudar.
Welder.

Welder.

Curtidas 0

Respostas

Welder.

Welder.

15/04/2013

Alguém por favor.
GOSTEI 0
Diego Garcia

Diego Garcia

15/04/2013

amigo sua duvida é com relação a criação de threads ou com relação a leitura da porta serial ?
GOSTEI 0
Welder.

Welder.

15/04/2013

amigo sua duvida é com relação a criação de threads ou com relação a leitura da porta serial ?


Olá drgarcia1986, hoje eu já consigo ler as portas, me conecto na porta COM (USB).
O que eu preciso é criar uma thread que conecte em várias portas COM ao mesmo tempo, usando várias instância do componente que uso.

Ex:
objSMS1.OpenComPort(COM10);
objSMS2.OpenComPort(COM12);
objSMS3.OpenComPort(COM14);

Isso sem saber a quantidade de portas que será necessário, tipo usando um For
GOSTEI 0
Diego Garcia

Diego Garcia

15/04/2013

porque você não faz uma thread que se conecta a uma determinada porta (que você vai informar a ela) e cria um for de acordo com a sua regra de negocio que crie N instancias dessa thread cada uma para uma determinada porta ?
GOSTEI 0
Welder.

Welder.

15/04/2013

porque você não faz uma thread que se conecta a uma determinada porta (que você vai informar a ela) e cria um for de acordo com a sua regra de negocio que crie N instancias dessa thread cada uma para uma determinada porta ?


Exatamente isso que preciso. Mas o problema é que não estou conseguindo criar.
Assim o Objeto ficaria dentro da thread? Não irá instanciar um objeto já instanciado?
GOSTEI 0
Diego Garcia

Diego Garcia

15/04/2013

isso, o esquema é vc criar o seus objeto dentro da Thread, ai cada thread tem a sua instancia desse objeto que monitora as portas..
GOSTEI 0
Welder.

Welder.

15/04/2013

isso, o esquema é vc criar o seus objeto dentro da Thread, ai cada thread tem a sua instancia desse objeto que monitora as portas..


Dá uma olhada como está meu código.

1 - Criação da Thread
TSendReceive = Class(TThread)
protected
procedure Execute; override;

private
FTimer: TTimer;
procedure OverrideOnTerminate(Sender: TObject);
procedure OverrideOnTimer(Sender: TObject);
public
objSMS: TGSMSMS;
iError: Integer;
procedure SetParametros(Port, Parity, StopBits: String; BaudRate: Integer;
CharEncoding, FlowControl, DataBits: SmallInt);
procedure SendReceiveSms(sTexto, sNumero: String; RelatorioEntrega, SmsDeAlerta: Boolean);

end;



2 - Implementação dos métodos
procedure TSendReceive.Execute;
begin
inherited;
FreeOnTerminate := True;
FTimer := TTimer.Create(nil);
FTimer.OnTimer := OverrideOnTerminate;
FTimer.OnTimer := OverrideOnTimer;
FTimer.Interval := 1000;
end;

procedure TSendReceive.SendReceiveSms(sTexto, sNumero: String; RelatorioEntrega, SmsDeAlerta: Boolean);
begin
objSMS.SendMessage(sTexto, sNumero, RelatorioEntrega, SmsDeAlerta);
iError := objSMS.ErrorNo;
if iError = 0 then
frmSmsBrasilDelivery.MotraMensagem(' Mensagem enviada com sucesso!!!.')
else
frmSmsBrasilDelivery.MotraMensagem(' Mensagem não enviada.' + objSMS.ErrorDescription);
end;

procedure TSendReceive.SetParametros(Port, Parity, StopBits: String; BaudRate: Integer;
CharEncoding, FlowControl, DataBits: SmallInt);
begin
objSMS := TGSMSMS.Create(Application);
objSMS.COMPort := Port;
objSMS.BaudRate := BaudRate;
objSMS.DataBits := DataBits;
objSMS.Parity := Parity;
objSMS.StopBits := StopBits;
objSMS.FlowControl := FlowControl;
objSMS.CharEncoding := CharEncoding;
if objSMS.OpenCOMPort then
frmSmsBrasilDelivery.MotraMensagem(Port + ' Aberta com sucesso.')
else
frmSmsBrasilDelivery.MotraMensagem(Port + ' Falha na abertura da porta.');
end;

procedure TSendReceive.OverrideOnTerminate(Sender: TObject);
begin
FTimer.Enabled := false;
FreeAndNil(FTimer);
end;

procedure TSendReceive.OverrideOnTimer(Sender: TObject);
begin
Self.Execute;
end;


3 - Chamada dos métodos
if cboCharacter.ItemIndex = 0 then
Characther := 8
else
if cboCharacter.ItemIndex = 1 then
Characther := 7
else
Characther := 16;

dmPrincipal.ChecaPortasAtiva;
dmPrincipal.cdsPortasAtivas.First;
while not dmPrincipal.cdsPortasAtivas.Eof do
begin
W := TSendReceive.Create(True);
with W do
begin
FreeOnTerminate := True;
SetParametros(dmprincipal.cdsPortasAtivas.FieldByName('PORTA').Value, 'N', '1', 115200, Characther, 0, 8);
Resume;
end;
dmPrincipal.cdsPortasAtivas.Next;
end;



Obs: Preciso que o método SendReceiveSms seja executado várias vezes, tipo a cada segundo, para cada registro existente no cdsPortasAtivas.

GOSTEI 0
Diego Garcia

Diego Garcia

15/04/2013

amigo, acredito que tenha um erro nesse código:

FTimer.OnTimer := OverrideOnTerminate;
FTimer.OnTimer := OverrideOnTimer;


imagino que você quis fazer
Self.OnTerminate := OverrideOnTerminate;
FTimer.OnTimer := OverrideOnTimer;


agora, não seria mais correto no Execute da thread você fazer algo do tipo

FTimer.Enable := true;
while not (self.Terminate) do
begin
  continue;
end;
FTimer.Enable := false;


E no OnTimer do TTimer vc manda as msgs ?
GOSTEI 0
Diego Garcia

Diego Garcia

15/04/2013

só uma correção

while not (Self.Terminated) do
begin
...
GOSTEI 0
Welder.

Welder.

15/04/2013

só uma correção

while not (Self.Terminated) do
begin
...


Ok. Fiz a correção. Agora tenho uma procedure que executa várias instruções, faz consulta no banco e tal.
eu preciso colocar essa procedure no execute da Thread.
GOSTEI 0
Welder.

Welder.

15/04/2013

só uma correção

while not (Self.Terminated) do
begin
...



coloquei minha procedure dentro do executar da Thread, ao executar o programa ocorre:
delphi EOleSysError with message 'CoInitialize não foi chamado

o que Ficou errado?
GOSTEI 0
Diego Garcia

Diego Garcia

15/04/2013

tente colocar o

CoInitialize(nil);


no inicio da thread e o

 CoUninitialize();


no final da thread
GOSTEI 0
Welder.

Welder.

15/04/2013

tente colocar o

CoInitialize(nil);


no inicio da thread e o

 CoUninitialize();


no final da thread


Agora ocorre esse erro:

---------------------------
Debugger Exception Notification
---------------------------
Project SmsBrasilDelivery.exe raised exception class EAccessViolation with message 'Access violation at address 72A249AD in module 'MSVBVM60.DLL'. Write of address 00000098'.
---------------------------
Break Continue Help
---------------------------
GOSTEI 0
Diego Garcia

Diego Garcia

15/04/2013

quando executa qual linha ?
GOSTEI 0
Welder.

Welder.

15/04/2013

quando executa qual linha ?


procedure TSendReceive.Execute;
begin
inherited;
try
FreeOnTerminate := True;
FTimer := TTimer.Create(nil);
FTimer.OnTimer := OverrideOnTerminate;
FTimer.OnTimer := OverrideOnTimer;
FTimer.Interval := 1000;
FTimer.Enable := true;
while not (self.Terminate) do
begin
SendReceiveSms(sTexto, sNumero: String; RelatorioEntrega, SmsDeAlerta: Boolean);
end;
finally
CoUninitialize();
end;
end;


Ocorre quando executo a linha SendReceiveSms(sTexto, sNumero: String; RelatorioEntrega, SmsDeAlerta: Boolean);

Código da procedure abaixo:
procedure TSendReceive.SendReceiveSms(sTexto, sNumero: String; RelatorioEntrega, SmsDeAlerta: Boolean);
begin
objSMS.SendMessage(sTexto, sNumero, RelatorioEntrega, SmsDeAlerta);
iError := objSMS.ErrorNo;
if iError = 0 then
frmSmsBrasilDelivery.MotraMensagem(' Mensagem enviada com sucesso!!!.')
else
frmSmsBrasilDelivery.MotraMensagem(' Mensagem não enviada.' + objSMS.ErrorDescription);
end;


o objSMS, como informei no inicio do posto é um componente ActiveX da http://www.logiccodesoft.com/lcgsmsms.aspx
GOSTEI 0
Welder.

Welder.

15/04/2013

Olá Diego Garcia. Viu ai que te passei a linha que ocorre o erro? Desculpa a insistência é que estou precisando.

Obrigado.
GOSTEI 0
Alex Constâncio

Alex Constâncio

15/04/2013

Olá

Há aqui um questão conceitual grave a ser corrigida. Não se deve usar um timer dentro de uma thread. O objetivo de uma thread é precisamente evitar o pooling por meio de um evento temporizado, que é o que o TTimer faz. Ser for realmente necessário fazer a thread esperar um certo tempo ou evento, então o correto é utilizar um TEvent e um WaitFor para fazer a thread esperar pelo evento ou por um timeout eventual.

Minha recomendação a você é primeiro o de resolver a questão das threads e depois da comunicação serial. Antes de tudo você precisa ter várias threads rodando, por exemplo, lendo de um arquivo e gravando em outro. Qualquer processo simples que não dependa da comunicação serial para ser uma variável a menos no processo. Quando as tuas threads estiverem operando corretamente então será o momento de inserir a parte de comunicação serial.

Alex
GOSTEI 0
Welder.

Welder.

15/04/2013

Olá

Há aqui um questão conceitual grave a ser corrigida. Não se deve usar um timer dentro de uma thread. O objetivo de uma thread é precisamente evitar o pooling por meio de um evento temporizado, que é o que o TTimer faz. Ser for realmente necessário fazer a thread esperar um certo tempo ou evento, então o correto é utilizar um TEvent e um WaitFor para fazer a thread esperar pelo evento ou por um timeout eventual.

Minha recomendação a você é primeiro o de resolver a questão das threads e depois da comunicação serial. Antes de tudo você precisa ter várias threads rodando, por exemplo, lendo de um arquivo e gravando em outro. Qualquer processo simples que não dependa da comunicação serial para ser uma variável a menos no processo. Quando as tuas threads estiverem operando corretamente então será o momento de inserir a parte de comunicação serial.

Alex



Olá Alex.

Obrigado pela resposta.

Está meio confuso pra mim ainda. Vou te explicar porque. Seguinte:
Tenho o sistema já funcionando. Já conecto na porta serial, abro, escrevo leio, de boa. A questão é: preciso fazer esse procedimento em todas portas seriais (portas com) que estiverem um modem 3G conectado, pois hoje faço só em uma por vez usando um for I := 0 componentcount - 1 do
Por isso tentei usar a Thread.
GOSTEI 0
Alex Constâncio

Alex Constâncio

15/04/2013

A idéia da thread é muito boa, não há motivos para fazer uma porta esperar por causa da latência da outra. É uma forma elegante de melhorar o teu processo.

No entanto me parece que o teu problema é especificamente o usa das threads. O código que você mandou está, em princípio, correto. Você entendeu que cada processo teu deverá ser executado em uma thread e cada uma é um objeto derivado de TThread. está correto também fazer o teu processamento no override do Execute da classe descendente.

No entanto é mandatório repensar o teu processo, pois o TTimer terá que sair. Para mim está obscuro como o teu processo funciona. Para te ajudar eu precisaria ter mais alguns detalhes. Se você puder descrever o processo que você hoje executa para uma única porta, creio que poderei te orientar a convertê-lo para o modelo em thread.

Alex
GOSTEI 0
Welder.

Welder.

15/04/2013

A idéia da thread é muito boa, não há motivos para fazer uma porta esperar por causa da latência da outra. É uma forma elegante de melhorar o teu processo.

No entanto me parece que o teu problema é especificamente o usa das threads. O código que você mandou está, em princípio, correto. Você entendeu que cada processo teu deverá ser executado em uma thread e cada uma é um objeto derivado de TThread. está correto também fazer o teu processamento no override do Execute da classe descendente.

No entanto é mandatório repensar o teu processo, pois o TTimer terá que sair. Para mim está obscuro como o teu processo funciona. Para te ajudar eu precisaria ter mais alguns detalhes. Se você puder descrever o processo que você hoje executa para uma única porta, creio que poderei te orientar a convertê-lo para o modelo em thread.

Alex




Olá Alex. Que bom que você pode me ajudar. Vou tentar descreve da melhor forma possível, mas se necessário te mando os .pas

Primeiro eu tenho um ComboBox onde listo todas portas COM;
Tenho uma tabela onde coloco as portas que o usuário seleciona no ComboBox, e nessa tabela guardo o nome do componente utilizado;
tenho um datamodule com 30 componentes, onde faço um for i := 0 componentcount - 1 do;
Se o nome do componente for igual ao nome gravado na tabela então faço a abertura da porta a varedura no banco de dados para verificar se há mensagens a enviar se sim executo a função que envia o SMS;
tenho também uma rotina que verifica se há SMS na caixa de entrada (do SIM chip ou modem) se sim gravo no banco.
GOSTEI 0
Alex Constâncio

Alex Constâncio

15/04/2013

Bom, acho que você terá que sofisticar um pouco a tua implementação de threads. Será necessário você controlar a disponibilidade das threads, pois existe o limite de 16 threads por unidade de processamento autônomo. Então, se você tem 20 portas, e um processador de apenar um core, terá apenas 16 threads para trabalhar. Portanto terá ainda assim algumas portas ociosas esperando por uma thread ficar disponível para tratá-la. Este limite é uma boa prática, não haverá impedimento de usar mais de 16 threads, mas o desempenho cairá drasticamente pois provocará um efeito no processador chamado trashing, onde haverá mais esforço em trocar de threads do que de processá-las.

No teu lugar, ao invés de ter várias instâncias dos teus componentes de comunicação dentro do DataModule, eu teria uma instância dentro de cada thread. As threads seriam criadas e iniciariam dormindo. Seriam colocadas em uma lista de threads livres. No momento em que fosse necessário enviar uma mensagem eu pegaria uma thread livre, configuraria os parâmetros dela por meio de propriedades e a acordaria para trabalhar.

Quando thread acorda ela vai executar o método Execute. No Execute da thread os parâmetros seriam usados para fazer eventuais configurações e então realizaria o processo, qualquer que seja. Quando o processo acabar acabar a thread gera um evento para notificar que ficou ociosa e volta a dormir, mas não termina. O Execute só deve terminar quando a thread for interrompida ou destruída. Sabendo que a thread está livre, um código (possivelmente do teu DataModule) novamente a colocaria em uma lista de threads livres, para ser usada novamente em outra ocasião.

Um cuidado importante é não fazer chamadas que alteram a tela diretamente de dentro da thread. É necessário usar um método intermediário chamado Syncrhonize para isso (leia no help), no entanto, o mais legal mesmo seria a thread gerar eventos durante a sua execução (dentro de um Synchronize) para notificar a respeito do que está acontecendo (um erro, por exemplo) e um código externo a ela se preocupar em eventualmente fazer uma mensagem aparecer na tela.

Seria mais ou menos este o desenho que eu daria para esta solução.

Alex
GOSTEI 0
POSTAR