Dúvidas com Thread [ RESOLVIDO]

Bom dia pessoal,

Novas dúvidas…srsr

Seguinte:

Tenho um programa que vai ler a porta serial e fiz exatamente como está no post aqui do guj mesmo:

Porém utilizando a API da RxTx.org e não a JavaComm

A primeira pergunta é baseada na seguinte situação:

Meu programa vai ler dados enviados de um equipamento eletronico.

Quando eu chamar a leitura da porta serial, clicando em um botão em minha tela eu gostaria de ficar em um estado modal na aplicação, pois o programa não pode continuar antes de ler os dados da porta serial.

Então eu fiz assim:

Chamo a leitura da porta

Entro em um while e fico nele até que seja recebido os dados na porta serial, então ai sim, sai do while.

O problema é que se ocorre alguma coisa de errada com o equipamento eletronico, eu não consigo mais sair do while, ou seja, tenho que dar control+alt+del para terminar o programa, pois ele está em um while sem volta.

A segunda pergunta é baseada na seguinte situação:

O método run da classe de exemplo do post acima está assim:


//método RUN da thread de leitura
public void run(){
try {
Thread.sleep(5000);
} catch (Exception e) {
System.out.println("Erro. Status = " + e );
}
}

Ai mesmo que o equipamento mande informações pela porta serial após 10 segundos ( por exemplo ) a thread mesmo assim faz a leitura, mas não era para ela ter morrido depois de 5 segundos, analisando o código do run, ou seja, ela fica dormindo 5 segundos, quando acorda já está na hora de morrer. Não é isso?

Se puderem me ajudar agradeço!

Obrigado!

Você pode aproveitar que Thread.sleep lança uma exceção InterruptedException se você chamar o método Thread.interrupt da thread que você quer interromper, e tratar essa exceção (talvez saindo do loop, por exemplo).

 public  void run(){  
 try {  
 Thread.sleep(5000);  
 } catch (InterruptedException e) {
     // alguém quis que saísse do loop.
    // tratar adequadamente
    ......
 } catch (Exception e) {  
 ...
 }  
 } 

[quote=entanglement]Você pode aproveitar que Thread.sleep lança uma exceção InterruptedException se você chamar o método Thread.interrupt da thread que você quer interromper, e tratar essa exceção (talvez saindo do loop, por exemplo).

 public  void run(){  
 try {  
 Thread.sleep(5000);  
 } catch (InterruptedException e) {
     // alguém quis que saísse do loop.
    // tratar adequadamente
    ......
 } catch (Exception e) {  
 ...
 }  
 } 

[/quote]

Ai que está o problema, na minha aplicação estou em um loop dentro do whlie:

While(mesagemRecebidaDaPortaSerial==null);

Não consigo dar um inrerrupt na thread! E o equipamento eletronico não pode mandar nada pela serial, por exemplo…sei lá, supondo que o equipamento eletronico travou…

Entendeu?

Quem tem de dar um interrupt na thread que está esperando ocorrer as coisas é outra thread (por exemplo, em um botão do seu programa).

Então, mas meu programa está congelado, está em um loop, não dá para clicar em nenhum botão!

Esse que é o problema…

Uma forma de não deixar seu programa congelado é iniciar uma thread que não seja a do botão. O processamento demorado, que pode travar, não pode ser feito no listener do botão, por exemplo, de forma alguma, senão seu programa trava mesmo.

Ele não sai do método da serial nem quando o cabo é removido, ou quando o hardware é resetado?

Ele não sai do método da serial nem quando o cabo é removido, ou quando o hardware é resetado?
[/quote]

Olá ViniGodoy,

Vou tentar explicar melhor o problema real:

O meu programa vai ter um botão com a seguinte função:

“Importar Projeto”

Esse importar projeto importa dados de um equipamento eletronico pela porta serial.

Pois bem, nesse botao, eu chamo um método do meu Controller:


    /** */
    public String Projeto importar(){
    

    	String mensagemRecebida;
	
               //Abre comunicação com a porta serial 

    	portaSerialCOM.abrirPorta("COM1");

    	portaSerialCOM.lerMensagem();

    	while(
    	       portaSerialCOM.bytesRecebidos==false
    	);

    	portaSerialCOM.fecharPorta();

    	mensagemRecebida=portaSerialCOM.mensagemRecebida;

               return mensagemRecebida;

}

A classe portaSerialCOM implementa Runnable, como está no post:

public class portaSerialCOM implements Runnable, SerialPortEventListener {
...
}

Potanto, o botão chama um método do meu Controller e ele é quem está “delegando” o serviço para uma Thread.

O problema é que eu queria colocar um dialogo do tipo:

aceitar e cancelar importação

De forma que, assim que eu clicasse no botão “Importar projeto” aparecesse na tela do usuário uma caixa de dialogo com o botão aceitar desabilitado, pois a thread estaria fazendo a importação, e outro botão “cancelar”, caso mesmo a importação em andamento o usuário tivesse a liberdade de parar a importação, por motivo qualquer, no caso, se o equipamento eletronico vira a dar algum problema. E claro, sem ter que tirar o cabo serial ou resetar o equipamento.

Não sei se consegui passar melhor meu problema, mas se precisarem tento explicar melhor

obrigado!

[quote=entanglement]Uma forma de não deixar seu programa congelado é iniciar uma thread que não seja a do botão. O processamento demorado, que pode travar, não pode ser feito no listener do botão, por exemplo, de forma alguma, senão seu programa trava mesmo.
[/quote]

Olá entanglement / ViniGodoy,

Eu estou sim colocando em uma thread, mas veja o problema:

Vou tentar explicar melhor o problema real:

O meu programa vai ter um botão com a seguinte função:

“Importar Projeto”

Esse importar projeto importa dados de um equipamento eletronico pela porta serial.

Pois bem, nesse botao, eu chamo um método do meu Controller:


    /** */
    public String Projeto importar(){
    

    	String mensagemRecebida;
	
               //Abre comunicação com a porta serial 

    	portaSerialCOM.abrirPorta("COM1");

    	portaSerialCOM.lerMensagem();

    	while(
    	       portaSerialCOM.bytesRecebidos==false
    	);

    	portaSerialCOM.fecharPorta();

    	mensagemRecebida=portaSerialCOM.mensagemRecebida;

               return mensagemRecebida;

}

A classe portaSerialCOM implementa Runnable, como está no post:

public class portaSerialCOM implements Runnable, SerialPortEventListener {
...
}

Potanto, o botão chama um método do meu Controller e ele é quem está “delegando” o serviço para uma Thread.

O problema é que eu queria colocar um dialogo do tipo:

aceitar e cancelar importação

De forma que, assim que eu clicasse no botão “Importar projeto” aparecesse na tela do usuário uma caixa de dialogo com o botão aceitar desabilitado, pois a thread estaria fazendo a importação, e outro botão “cancelar”, caso mesmo a importação em andamento o usuário tivesse a liberdade de parar a importação, por motivo qualquer, no caso, se o equipamento eletronico vira a dar algum problema. E claro, sem ter que tirar o cabo serial ou resetar o equipamento.

Não sei se consegui passar melhor meu problema, mas se precisarem tento explicar melhor

obrigado!

[quote=Rodrigo Ribaldo][quote=entanglement]Uma forma de não deixar seu programa congelado é iniciar uma thread que não seja a do botão. O processamento demorado, que pode travar, não pode ser feito no listener do botão, por exemplo, de forma alguma, senão seu programa trava mesmo.
[/quote]

Olá entanglement / ViniGodoy,

Eu estou sim colocando em uma thread, mas veja o problema:

Vou tentar explicar melhor o problema real:

O meu programa vai ter um botão com a seguinte função:

“Importar Projeto”

Esse importar projeto importa dados de um equipamento eletronico pela porta serial.

Pois bem, nesse botao, eu chamo um método do meu Controller:


    /** */
    public String Projeto importar(){
    

    	String mensagemRecebida;
	
               //Abre comunicação com a porta serial 

    	portaSerialCOM.abrirPorta("COM1");

    	portaSerialCOM.lerMensagem();

    	while(
    	       portaSerialCOM.bytesRecebidos==false
    	);

    	portaSerialCOM.fecharPorta();

    	mensagemRecebida=portaSerialCOM.mensagemRecebida;

               return mensagemRecebida;

}

A classe portaSerialCOM implementa Runnable, como está no post:

public class portaSerialCOM implements Runnable, SerialPortEventListener {
...
}

Potanto, o botão chama um método do meu Controller e ele é quem está “delegando” o serviço para uma Thread.

O problema é que eu queria colocar um dialogo do tipo:

aceitar e cancelar importação

De forma que, assim que eu clicasse no botão “Importar projeto” aparecesse na tela do usuário uma caixa de dialogo com o botão aceitar desabilitado, pois a thread estaria fazendo a importação, e outro botão “cancelar”, caso mesmo a importação em andamento o usuário tivesse a liberdade de parar a importação, por motivo qualquer, no caso, se o equipamento eletronico vira a dar algum problema. E claro, sem ter que tirar o cabo serial ou resetar o equipamento.

Não sei se consegui passar melhor meu problema, mas se precisarem tento explicar melhor

obrigado!
[/quote]

Gente, esqueci de dizer uma coisa…

Assim que a importação terminar o botão da caixa de dialogo “Aceitar” tem que ficar habilitado…

Pois no andamento da importação ele deve estra desabilitado e so o cancelar habilitado…

Se o RXTX não tiver nenhum método de read com timer, então, o usuário será obrigado a resetar o hardware ou desconectar o cabo.

Quanto aos botões, faça sua thread disparar eventos.
Geralmente é o jeito mais fácil de controlar esse tipo de coisa.

[quote=ViniGodoy]Se o RXTX não tiver nenhum método de read com timer, então, o usuário será obrigado a resetar o hardware ou desconectar o cabo.

Quanto aos botões, faça sua thread disparar eventos.
Geralmente é o jeito mais fácil de controlar esse tipo de coisa.[/quote]

Vini, sou leigo em java ainda…

Este é meu primeiro projeto…

Poderia me dizer o que quer dizer com :

“Se o RXTX não tiver nenhum método de read com timer”

e

"Quanto aos botões, faça sua thread disparar eventos. "

Mesmo assim, já lhe agradeço as ajudas.

Obrigado

e

Algumas APIs para a leitura de periféricos, tem métodos de read que bloqueiam por um tempo máximo, em milisegundos. Assim, se o periférico não responder, o read retorna um código de erro e não fica bloqueado.

Não sei se a RXTX tem isso, pois eu usava a javax.comm (que por sinal não tinha).

Sem isso, é impossível sair do método read sem que haja intervenção do usuário (resetando o hardware ou desconectando o cabo).

No caso do evento, veja o link:
http://www.guj.com.br/posts/list/209516.java#1065181

[quote=ViniGodoy]Algumas APIs para a leitura de periféricos, tem métodos de read que bloqueiam por um tempo máximo, em milisegundos. Assim, se o periférico não responder, o read retorna um código de erro e não fica bloqueado.

Não sei se a RXTX tem isso, pois eu usava a javax.comm (que por sinal não tinha).

Sem isso, é impossível sair do método read sem que haja intervenção do usuário (resetando o hardware ou desconectando o cabo).

No caso do evento, veja o link:
http://www.guj.com.br/posts/list/209516.java#1065181[/quote]

Olá vini / Pessoal,

Consegui implementar parte das coisas, veja o que eu fiz:

Declarei uma interface…


public interface ThreadListener {

	public void endRun();
	
	public void endBytes();
}

Na classe que faz leitura da porta serial ( seguindo o exemplo daquele post do Javacomm, porém, utilizando rxtx ) que implementa Runnable fiz o seguinte:


public class PortaSerialCOM implements SerialPortEventListener, Runnable {

     private ThreadListener threadListener;

     @Override
      public void serialEvent(SerialPortEvent serialPortEvent) {  

     //código que lê a serial
      ...

          mensagemRecebida=Dadoslidos;
          
          // Avisa ouvintes que o método de recebimento de bytes da porta serial terminou
          this.threadListener.endBytes();
 

    ...

     }


     @Override
      public void run() {
		// TODO Auto-generated method stub
		try {
			Thread.sleep(5000);

                                             // Avisa ouvintes que o método run() terminou
			this.threadListener.endRun();

		}catch(Exception e) {
			System.out.println("Erro. Status = " + e );
		}
      }


...

}

Criei um botão especial que implementa a interface
ThreadListener



public class BotaoAceitar extends JButton implements ThreadListener{
	
	
	public BotaoAceitar(){
		super();
		this.setEnabled(false);
		
	}

	@Override
	public void endBytes() {
		this.setEnabled(true);
		
	}

	@Override
	public void endRun() {
		// TODO Auto-generated method stub
		
	}

}

Na classe que tem o método responsável pela Importação de dados seriais fiz assim:


    public Projeto importar()
    {
    	
    	Object[] botoesDialogo = new Object[2];

    	String mensagemRecebida;
		
               //Abre comunicação com a porta serial 

    	try{
	    	portaSerialCOM.abrirPorta(portaSerial);
	    	portaSerialCOM.lerMensagem();
	    	
	    	
	    	JButton botaoCancelar = new JButton();
	    	botaoCancelar.setText("Cancelar");


                              BotaoAceitar botaoAceitar = new BotaoAceitar();
	    	botaoAceitar.setText("Aceitar");
	    	
	    	//Aqui registro o botao para ouvir a ThreadListener
	    	portaSerialCOM.addThreadListener(botaoAceitar);
	
	    	
	    	
	    	botoesDialogo[0] = botaoAceitar;
	    	botoesDialogo[1] = botaoCancelar;
	    	
	    	
	    	
	    	int resposta = JOptionPane.showOptionDialog(null, "Deseja Cancelar Importação?", "Importando        Projeto",0,JOptionPane.QUESTION_MESSAGE,null,botoesDialogo,botoesDialogo[1]);
	    	
	    	System.out.println(resposta);


	    	if(resposta==0){
	    		
	    	}
	    	
    	
    	}catch(Exception e){
    		
    	}finally{
    		portaSerialCOM.fecharPorta();
    	}
    	

    	mensagemRecebida=portaSerialCOM.mensagemRecebida;

Agora o problema está no JOptionPane.showOptionDialog, como se pode ver, construi dois botões, ou seja, não quero usar os default do JOptionPane.

Quando inicio a importação, o botão “aceitar” vem desabilitado e o de “cancelar” habilitado, como é o esperado, porém, nenhum dos dois respondem ao evento retornando um inteiro para a variável resposta.

Se eu implementar listeners para eles funciona, eles respondem, mas eu queria que eles funcionacem como botoes normais do JOptionPane, não tem como?

Abraço e obrigado a todos pela ajuda!

Como o funcionamento é bem personalizado, crie um JDialog modal próprio, não use o JOptionPane.

Olá Vini, acabei criando a classe abaixo, funcionou tb:



public class DialogoImportar{
	
	public final int BOTAO_ACEITAR = 0;
	public final int BOTAO_CANCELAR = 1;
	
	private static int indiceBotao = -1;
	
	private JButton botaoCancelar;
	private BotaoAceitar botaoAceitar;
	
	private JOptionPane optionPane = new JOptionPane();
	private JDialog dialogo = new JDialog();
	
	
	
	public DialogoImportar(Component arg0,Object arg1,String arg2,int arg3,int arg4,Icon arg5,Object[] arg6,Object arg7){

		setBotaoAceitar((BotaoAceitar) arg6[0]);
		setBotaoCancelar((JButton) arg6[1]);
		
		setOptionPane(new JOptionPane(   
        		arg1, 
                arg4, 
                arg3, 
                arg5, 
                arg6,  
                arg7  
        )); 
       
        
        setDialogo(getOptionPane().createDialog(arg2)); 
 
        getBotaoAceitar().addActionListener(new ActionListener() {   
            @Override  
            public void actionPerformed(ActionEvent e) {   
                dialogo.dispose();  
                setIndiceBotao(0);
              
            }   
        });   
           
        getBotaoCancelar().addActionListener(new ActionListener() {   
            @Override  
            public void actionPerformed(ActionEvent e) {   
                dialogo.dispose();   
                setIndiceBotao(1);
                
            }   
        }); 
       
        
    
	}

                 // getters and setters
	...
}