Relatório TP2 - Fundamentos de Sistemas Operativos PDF
Document Details
Uploaded by FrugalGyrolite9375
LEIM
2024
Pedro Grilo, Fernando Prates, Ana Silva
Tags
Summary
Este relatório apresenta o trabalho prático 2 sobre fundamentos de sistemas operativos, focando na implementação de uma interface gráfica intuitiva com a biblioteca Swing, em Java. Descreve o desenvolvimento de uma aplicação multiprocesso para gestão e comunicação entre dois processos JAVA. O trabalho envolve a criação de GUIs para dois processos, um aproveitando o trabalho prático anterior (TP1), e um novo processo (IG) para tarefas de imitação e gravação.
Full Transcript
LEIM – Licenciatura em Engenharia Informática e Multimédia Fundamentos de Sistemas Operativos Relatório TP2 - 32D - G05 Pedro Grilo | A51319 Fernando Prates | A51635 Ana Silva | A50247 16-12-2024...
LEIM – Licenciatura em Engenharia Informática e Multimédia Fundamentos de Sistemas Operativos Relatório TP2 - 32D - G05 Pedro Grilo | A51319 Fernando Prates | A51635 Ana Silva | A50247 16-12-2024 1 Índice Introdução...................................................................................................... 5 Desenvolvimento............................................................................................. 6 Aula prática 1...................................................................................................... 6 Aula prática 2.................................................................................................... 10 Aula prática 3...................................................................................................... 6 Aula prática 4...................................................................................................... 9 Aula prática 5.................................................................................................... 12 Aula prática 6.................................................................................................... 15 Código........................................................................................................... 17 Comunicação...................................................................................................... 17 CanalComunicacao.java................................................................................... 17 CanalComunicacaoConsistente.java.................................................................. 19 Mensagem.java................................................................................................. 22 Gravador.java................................................................................................... 23 IG........................................................................................................................ 25 App..................................................................................................................... 25 Application.java................................................................................................ 25 BD.java............................................................................................................ 26 GUI.java........................................................................................................... 31 Tarefas................................................................................................................ 44 Gravar.java....................................................................................................... 44 Imitar.java........................................................................................................ 47 ITarefas.java..................................................................................................... 49 Reproduzir.java................................................................................................. 50 Tarefas.java...................................................................................................... 50 TP1...................................................................................................................... 53 App..................................................................................................................... 53 Application.java................................................................................................ 53 BD.java............................................................................................................ 54 GUI.java........................................................................................................... 58 2 Tarefas................................................................................................................ 72 Evitar.java........................................................................................................ 72 Fugir.java......................................................................................................... 73 ITarefas.java..................................................................................................... 75 Tarefa.java........................................................................................................ 76 Vaguear.java..................................................................................................... 77 Bibliografia..................................................................................................... 80 Índice Imagens Fig.1 - Aula prática 1 | GUI TP1.................................................................................................................6 Fig.2 - Aula prática 1 | GUI IG....................................................................................................................8 Fig.4 – Aula Prática 2|UML TP2 – TP1 e Comunicação..........................................................................5 Fig.5 – Aula Prática 2|UML TP2 – IG e Comunicação.............................................................................4 Fig.6 – Diagrama de estados da classe Gravar do projeto IG................................................................ 12 Fig.7– Excerto de código da classe Gravar – código que engloba os estados DORMIR e GRAVAR. 13 Fig.8 – Excerto de código da classe Gravar – código que engloba o estado REPRODUZIR e regresso a DORMIR.................................................................................................................................................. 14 Fig.9 – GUI TP1 – Finalizada.................................................................................................................... 16 3 Introdução Este trabalho tem como objetivo a implementação de uma interface gráfica intuitiva, utilizando a biblioteca Swing, e o desenvolvimento de uma aplicação multiprocesso que irá permitir a gestão e comunicação entre dois processos JAVA com memória partilhada suportada pela classe JAVA MappedByteBuffer. Um dos processos é aproveitado do primeiro trabalho prático (TP1), que já tem a comunicação com o robot sólida e estabelecida, o segundo processo será chamado IG em concordância com os comportamentos que irá adotar que serão o Imitar e o Gravar. Esta introdução prática à criação de GUIs e ao desenvolvimento de múltiplas aplicações multitarefa e a comunicação entre processos estabelece a base para a criação de sistemas mais complexos e eficientes, explorando aspetos fundamentais da comunicação e sincronização entre tarefas e processos em Java. O estabelecimento do modelo Produtor-Consumidor (TP1-IG) será dado pelo processo Comunicacao que proverá um desafio interessante para melhor compreensão de gestão entre processos e indispensável para entender e criar um sistema ainda mais intrincado e como os processos podem comunicar diretamente entre si e, neste caso, comandar os robôs a comportarem-se de forma síncrona. 4 Desenvolvimento Aula prática 1 Nesta aula estivemos a desenvolver a GUI para os nossos processos. Aproveitando a GUI do primeiro trabalho TP1 tivemos de realizar algumas alterações. Estas alterações consistiram: Na criação de um botão “Lançar IG” que lançará a GUI do processo de IG. Um campo e um botão para procurar um ficheiro onde aparecerá no campo do texto o nome do ficheiro escolhido Um botão radial para realizar o Debug, caso algo corra mal Um espaço para colocar o Nº de mensagens permitido sendo que o seu máximo será de 16 E claro, o botão radial de Abrir/Fechar Canal de comunicação entre este processo e o de IG que estará disponível após o carregar do botão de lançar IG. 5 Fig.1 - Aula prática 1 | GUI TP1 6 Seguidamente, preparámos a GUI do processo IG. Muito semelhante à GUI do TP1, sendo que as diferenças são: Campo Imitar: o Botão radial para Abrir o canal de comunicação o Botão checkbox para Ativar ou Desativar Campo Gravar: o Um campo e um botão para procurar um ficheiro onde aparecerá no campo do texto o nome do ficheiro escolhido o Um botão para Gravar o Um botão para Reproduzir o Um campo para escrever o Um botão para Limpar o Um botão checkbox para Imprimir o que foi escrito 7 Fig.2 - Aula prática 1 | GUI IG 8 Aula prática 2 A aula de hoje consistiu no mapeamento do nosso, até agora, código em UML com recurso à ferramenta MagicDraw. UML - Unified Modeling Language - trata-se de uma linguagem gráfica standard, estruturada e útil para uma melhor compreensão dos requisitos, arquitectura e design de software criado. Funciona quase que como um mapa com o objectivo de guiar os programadores, ou neste caso, nós, os alunos, a olhar para o nosso código e descobrir potenciais problemas e soluções. 9 Fig.3 – Aula Prática 2|UML TP2 Completo 10 Fig.4 – Aula Prática 2|UML TP2 – TP1 e Comunicação 11 TP1 Tarefas 1. Interface ITarefas Define os métodos que as classes Fugir, Vaguear e Evitar implementam, como executar() e bloquear(). Esta interface garante que todas as tarefas do robot seguem uma estrutura comum, facilitando a sua implementação. 2. Classes de Comportamento: Fugir, Vaguear, Evitar Estas classes implementam a interface ITarefas e contêm a lógica específica para cada comportamento do robot. Atributos: robot: Referência ao objeto do tipo RobotLegoEV3, que representa o robot controlado. gui: Um objeto GUI associado. ESTADO, distancia, angulo, etc., que ajudam a controlar o estado e o comportamento específico do robot. Operações: Cada classe tem métodos como run() e executar(), onde a lógica do comportamento específico é implementada. 12 O método bloquear(boolean bloquear) está relacionado com a sincronização ou gestão de bloqueios durante a execução das tarefas. App 1. Classe BD A classe BD (Base de Dados) armazena informações sobre o robot, como o seu estado (onOff, terminar, vaguear, evitar, fugir), bem como métodos para definir e obter esses valores. Esta classe também inclui métodos para manipular o estado e informações do robot, como setDistancia(), setAngulo(), setRobot(), entre outros. 2. Classe GUI Esta classe representa a interface gráfica do utilizador. Contém diversos componentes gráficos (ex.: JLabel, JTextField, JButton, JCheckBox), como elementos de interface que permitem a interação com o utilizador. Operações: Métodos como initGUI(), getEngineBD(), enableButtons(), e outros, indicam a configuração e manipulação da interface gráfica. 3. Classe Application 1 Esta é a classe principal que inicia o sistema. Ela configura a interface (gui) e tem o método main() que executa o programa. Relações entre as Classes As classes Fugir, Vaguear, e Evitar implementam a interface ITarefas, o que é mostrado pelas setas de herança. Estas classes encontram-se ligadas à classe Tarefa onde se encontram os métodos e atributos em comum dessas classes. A classe GUI usa a classe BD e mantém referências a instâncias das classes Fugir, Vaguear, e Evitar. A classe Application usa a classe GUI, demonstrando que esta é responsável por configurar e executar a interface gráfica. Comunicação 1.Classe Mensagem Será a classe responsável pela mensagem passar entre os processos. 2.Classe CanalDeComunicacao Classe que irá criar um canal de comunicação entre processos. 3.Classe CanalDeComunicacaoConsistente Classe de comunicação entre processos estável. 2 Relações entre as classes A classe Mensagem encontra-se ligada à classe Application tanto do processo TP1 como do processo IG. A classe de CanalDeComunicacaoConsistente herdará da classe CanalDeComunicacao e ligar-se-á à classe BD do processo TP1 e à classe Application do processo IG. 3 Fig.5 – Aula Prática 2|UML TP2 – IG e Comunicação IG O processo IG é uma cópia das Classes dos mesmos nomes de TP1 mas apenas com algumas alterações na GUI. Assim: 4 1. Classe BD A classe BD (Base de Dados) armazena informações sobre o robot, como o seu estado (onOff, terminar, vaguear, evitar, fugir), bem como métodos para definir e obter esses valores. Esta classe também inclui métodos para manipular o estado e informações do robot, como setDistancia(), setAngulo(), setRobot(), entre outros. 2. Classe GUI Esta classe representa a interface gráfica do utilizador. Contém diversos componentes gráficos (ex.: JLabel, JTextField, JButton, JCheckBox), como elementos de interface que permitem a interação com o utilizador. A diferença entre este processo e o TP1, é que são adicionados campos para Imitar e Gravar com tudo o que os involve. Operações: Métodos como initGUI(), getEngineBD(), enableButtons(), e outros, indicam a configuração e manipulação da interface gráfica. 3. Classe Application Esta é a classe principal que inicia o sistema. Ela configura a interface (gui) e tem o método main() que executa o programa. 5 Aula prática 3 A aula de hoje consistiu na elaboração das classes CanalComunicacao e CanalComunicacaoConsistente. Estas classes têm de existir para permitir a comunicação entre os processos TP1 e o IG de forma a possibilitar futuramente o caminhar sincronizado entre os robôs. CanalComunicacao Esta classe vai ser responsável por possuir métodos que permitem não só a abertura e fecho do canal de comunicação como também os métodos de receber e enviar mensagem. Começamos por criar o construtor, CanalComunicacao(), que inicializa a classe, definindo a variável indiceMensagens como 0, e deixando as variáveis de ficheiro, canal e buffer como null, preparando o objeto para ser usado. Para tal, o método de abertura do canal, abrirCanal(), vai criar um ficheiro com o nome “comunicacao.dat”. Seguidamente cria um canal de comunicação de leitura e escrita e finalmente mapeia o conteúdo do ficheiro para memória. O método receberMensagem(), como o nome indica, recebe uma mensagem e vai buscar quatro campos int (index, comando, argumento1 e argumento2) para depois convertere-la em objecto do tipo Mensagem(index, comando, argumento1, argumento2). O método enviarMensagem(Mensagem msg) que vai enviar um objecto Mensagem como uma mensagem recorrendo ao método putInt() de MappedByteBuffer para colocar a informação nos campos index, comando, argumento1 e argumento2 no buffer de forma sequencial. Depois de a 6 informação estar armazenada, o índice de mensagens é incrementado para não haver sobreposição. O método de fecho do canal, fecharCanal(), de forma aos recursos serem liberados após a finalização da sua utilização. O método getIndex() que, como o nome indica, vai buscar o indíce da mensagem. CanalComunicacaoConsistente Esta classe vai herdar da classe anterior e expandir sobre ela para implementar novos métodos: O método construtor CanalComunicacaoConsistente() que vai buscar o construtor da classe anterior pela implementação de super(). CanalLock(FileChannel canal) que recorrendo à utilização de FileLock, bloqueia o canal de maneira que apenas um processo (TP1 ou IG) esteja a utilizá-lo de cada vez sem interferências. CanalRelease(FileLock fl) liberta o canal assim que a sua utilização já não for necessária pelo processo que possui o seu acesso de momento. O método enviarMensagem(Mensagem msg) aproveitado da classe CanalComunicacao mas implementando os métodos de FileLock descritos anteriormente para garantir o acesso exclusivo do processo. 7 receberMensagem(), tal como anterior é aproveitado de CanalComunicacao mas estabilizado com o uso de FileLock para garantir o acesso exclusivo do canal. getAndSetConsumidor(Mensagem m), booleano, em que recebe a mensagem do Consumidor (IG), e se o comando da mensagem for equivalente a 0, o método envia a mensagem recebida “m” de volta para o canal, retornando true o que indica que foi bem sucedida. Senão retornará false o que indica que a operação não foi bem sucedida. E finalmente o booleano getAndSetProdutor(Mensagem m) que é idêntico ao método anterior, mas agora da parte do Produtor (TP1). 8 Aula prática 4 A aula de hoje consistiu na elaboração do código e implementação da sincronização de movimento dos dois robôs. 1. Para isso, para além dos canais de CanalComunicacao e CanalComunicacaoConsistente, foi criada a classe Mensagem, responsável por obter e tornar um objecto Mensagem (Mensagem (index, comando, argumento1, argumento2)) numa mensagem em formato String através de gets para cada campo da mensagem e pela aplicação do método toString(): public String toString() { return "indice: " + this.index +"\n" + "comando: " + this.comando +"\n" + "arg1: " + this.arg1 +"\n" + "arg2: " + this.arg2 +"\n"+ "FIM MENSAGEM"+"\n"; } 2. Foi alterada a classe BD do processo IG de forma a acomodar o canal de comunicação, nomeadamente de métodos get e setFile() de forma a obter e processar a informação do ficheiro, get e setCanalCom() para obter e definir informação do canal de comunicação e getTamanhoMensagem() para obter a informação do tamanho da mensagem. 3. Para além disto, foi adicionada na classe Application do processo IG métodos para a tarefa Imitar que permitirá o segundo robô “copiar” as acções do primeiro. 9 4. Criação da classe Imitar, responsável pela imitação de acções/comandos do primeiro robô pelo segundo robô. De maneira a conseguir fazer isto, a classe vai herdar da classe Tarefa e implementar os métodos de ITarefas. Assim ela vai buscar o canal, cria um ArrayList para a mensagem. O seu construtor vai buscar informação de GUI, adicionar mensagem se null e vai buscar informação sobre o canal. O processo run() consiste num while, que quando true, vai ter dois estados: O de BLOQUEADO onde liberta a Thread através do Thread.yield(), e uma condição onde se o estado for diferente de BLOQUEADO é lançado um print na consola da GUI "Tarefa vaguear iniciada" mudando seguidamente para o estado de EXECUTAR. O de EXECUTAR onde terá duas condições. Uma é que se o getAndSetConsumidor encontrar um objecto mensagem com todos os seus parâmetros a 0, aparecerá na consola da GUI um print da última mensagem em String existente no canal e irá adicionar uma mensagem. A segunda condição é que caso o estado seja o de BLOQUEADO, o estado actual torna-se BLOQUEADO. Na interface ITarefas foi adicionada a variável Random r que invocará o método Random() que randomizará uma acção dentro dos limites estipulados. As tarefas de TP1 Vaguear e Fugir vão receber r para randomizar os parâmetros de andamento do primeiro robô. No caso de Vaguear, r estará em distância onde, até ao limite de 90 escolherá 10 um número inteiro ao acaso (this.distancia = r.nextInt(90); ), seguidamente será o ângulo onde também escolherá um número inteiro ao acaso no mesmo limite (this.angulo = r.nextInt(90); ) e finalmente a acção que será escolhida aleatoriamente até limite de 3 (this.acao = r.nextInt(3); ). No Fugir, r irá escolher aleatoriamente na condição o próximo booleano que irá desencadear uma série de acções. 11 Aula prática 5 A aula de hoje consistiu na elaboração de UML, um diagrama de estados de forma a orientar a construção de código seguinte. Fig.6 – Diagrama de estados da classe Gravar do projeto IG 12 Explicando sucintamente o diagrama acima: Iniciando, o sistema encontra-se no estado DORMIR. O sistema sairá do estado DORMIR pela intervenção do utilizador em que poderá decidir entre os estados GRAVAR e REPRODUZIR. Supondo que o GRAVAR é ativado, ativará a parte do código gui.getBD().getGravar() que iniciará a gravação. Após a gravação de todas as mensagens do robô, ativa-se a parte do código gui.getBD().getMensagensGravar().isEmpty() e o estado do robô volta para o DORMIR. Fig.7– Excerto de código da classe Gravar – código que engloba os estados DORMIR e GRAVAR Para iniciar o estado de REPRODUZIR, será ativado a sequência de código de gui.getBD().getReproduzir(). Quando a sequência está terminada, ou por ter reproduzido todas as 13 mensagens ou por ordem do utilizador, é ativado pela negação do código anterior !gui.getBD().getReproduzir() como é denotado pelo ponto de exclamação “!” e o sistema volta ao estado DORMIR. Fig.8 – Excerto de código da classe Gravar – código que engloba o estado REPRODUZIR e regresso a DORMIR Quando o sistema está no estado DORMIR poder-se-á terminar o sistema assim que existir ordem para tal por parte do utilizador. 14 Aula prática 6 Nesta aula, foi necessário “limar algumas arestas” para que tudo corresse bem a nível de programa, GUI e de sincronização dos robôs. Foi adicionado um campo na GUI do TP1 para procura de ficheiros nomeadamente Ficheiro IG onde poderemos colocar o ficheiro adquirido após gravação para depois poder reproduzir o mesmo. Efectivamente o que irá acontecer é que o segundo robô irá reproduzir os comandos que foram gravados na execução do primeiro robô. Fig.9 – GUI TP1 – Finalizada 15 Quando experimentámos o robô ao lado do professor reparámos em alguns problemas nomeadamente: o segundo robô não voltava a imitar o primeiro quando parado por alguns minutos e voltar a ser activado. O primeiro robô (projecto TP1) apagava e reescrevia os campos do buffer sem intervenção da limpeza de mensagens por parte do processo IG do segundo robô. O botão do processo IG não tinha o comando associado. Todos estes pontos foram corrigidos de forma a conseguirmos validar positivamente o trabalho. Contudo, para melhorar a experiência do trabalho, os docentes acrescentaram um requisito a mais. Assim foi alterado o código para acomodar a nova exigência: No canal de comunicação foram adicionadas as variáveis protected int OFFSET_IDXPUT e OFFSET_IDXGET na classe CanalComunicacao de forma a controlar os índices de escrita e de leitura no buffer, optimizando a manipulação de mensagens, evitando colisões. No método de abrirCanal presente no CanalComunicacao: OFFSET_IDXPUT representa a posição onde o índice de escrita será armazenado garantindo que o programa saiba onde começar a escrever a próxima mensagem no buffer. OFFSET_IDXGET representa a posição no buffer onde o índice de leitura será armazenado. Assim o programa saberá onde deve procurar para começar a ler a próxima mensagem. @SuppressWarnings("resource") public boolean abrirCanal(String ficheiro, int n_mensagens) { try { 16 canal = new RandomAccessFile(new File(ficheiro), "rw").getChannel(); } catch (FileNotFoundException e) { System.out.println("Ficheiro não encontrado");return false;} try { buffer = canal.map(FileChannel.MapMode.READ_WRITE, 0, n_mensagens * TAMANHO_MENSAGEM + 8); this.n_mensagens = n_mensagens; index = this.loadFromBuffer(OFFSET_IDXPUT); this.storeInBuffer(OFFSET_IDXGET, index); this.OFFSET_IDXPUT = n_mensagens * TAMANHO_MENSAGEM; this.OFFSET_IDXGET = n_mensagens * TAMANHO_MENSAGEM + 4; this.limparCanal(n_mensagens); } catch (IOException e) { return false;} return true; } Temos a carga e armazenamento dos índices em: index = this.loadFromBuffer(OFFSET_IDXPUT); this.storeInBuffer(OFFSET_IDXGET, index); onde loadFromBuffer recupera o índice de escrita armazenado previamente no buffer para continuar as operações de escrita do ponto onde pararam através do método: protected int loadFromBuffer(int offset) { FileLock fl = canalLock(); int posInit = buffer.position(); buffer.position(offset); int value = buffer.getInt(); buffer.position(posInit); canalRelease(fl); 17 return value; } E storeInBuffer vai armazenar o índice de leitura inicial no buffer sincronizando o estado de leitura através do seu método: protected void storeInBuffer(int offset, int value) { FileLock fl = canalLock(); int posInit = buffer.position(); buffer.position(offset); buffer.putInt(value); buffer.position(posInit); canalRelease(fl); Thread.yield(); } Os Offsets são iniciados em: this.OFFSET_IDXPUT = n_mensagens * TAMANHO_MENSAGEM; this.OFFSET_IDXGET = n_mensagens * TAMANHO_MENSAGEM + 4; sendo que OFFSET_IDXPUT aqui é definido para ocupar 4 bytes (igual a um inteiro ((int))) logo após a última mensagem. E OFFSET_IDXGET ocupa os 4 bytes seguintes. Depois dos offsets serem definidos, o canal é limpo com: this.limparCanal(n_mensagens); garantindo assim que o buffer fique pronto para uso sendo que as mensagens antigas são eliminadas e os índices voltam a sofrer sincronização. 18 19 Código Comunicação CanalComunicacao.java package Comunicacao; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; public class CanalComunicacao { protected FileChannel canal; protected MappedByteBuffer buffer; //número de bytes numa mensagem protected int TAMANHO_MENSAGEM = 16; public CanalComunicacao(){ canal= null; buffer= null; } @SuppressWarnings("resource") public boolean abrirCanal(String ficheiro, int n_mensagens) { try { canal = new RandomAccessFile(new File(ficheiro), "rw").getChannel(); } catch (FileNotFoundException e) { System.out.println("Ficheiro não encontrado");return false;} try { buffer = canal.map(FileChannel.MapMode.READ_WRITE, 0, n_mensagens * TAMANHO_MENSAGEM); } catch (IOException e) { return false;} return true; } public void fecharCanal() { if (canal!=null) 20 try { canal.close(); } catch (IOException e) { canal= null; } } //recebe 4 ints seguidos e interpreta-os como uma mensagem public Mensagem receberMensagem() { int[] params = new int; for (int i = 0; i < 4; i++){ params[i] = buffer.getInt(); } //se chegar ao fim do buffer, volta ao inicio if(!buffer.hasRemaining()) { buffer.position(0); } return new Mensagem(params, params, params, params); } //percorre o canal de comunicacao e limpa-o, enviando mensagens vazias public void limparCanal(int n_mensagens) { for (int i = 0; i < n_mensagens; i++) { enviarMensagem(new Mensagem(0,0,0,0)); } } //envia uma mensagem para o canal no formato de 4 ints seguidos public void enviarMensagem(Mensagem msg) { int[] params = {msg.getIndex(), msg.getComando(), msg.getArg1(), msg.getArg2()}; for (int i = 0; i < 4; i++){ buffer.putInt(params[i]); } //se chegar ao fim do buffer, volta ao inicio if(!buffer.hasRemaining()) { buffer.position(0); } } public int getTamanhoMensage() { return this.TAMANHO_MENSAGEM; }} 21 CanalComunicacaoConsistente.java package Comunicacao; import java.io.IOException; import java.nio.channels.FileLock; public class CanalComunicacaoConsistente extends CanalComunicacao { private int index; private Mensagem ultimaMensagemLida; public CanalComunicacaoConsistente() { this.index = 0; } public FileLock canalLock() { try { return super.canal.lock(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } public void canalRelease(FileLock fl) { try { fl.release(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); 22 } } @Override public void enviarMensagem(Mensagem msg) { FileLock fl = canalLock(); super.enviarMensagem(msg); canalRelease(fl); } @Override public Mensagem receberMensagem() { FileLock fl = canalLock(); Mensagem msg = super.receberMensagem(); canalRelease(fl); return msg; } public boolean getAndSetProdutor(Mensagem m) { Mensagem msg = receberMensagem(); //envia mensagem apenas houver um espaço vazio if(msg.getComando() == 0) { //reposiciona o buffer para escrever no slot vazio if(buffer.position() != 0) { buffer.position(buffer.position() - TAMANHO_MENSAGEM); } else buffer.position(buffer.capacity() - TAMANHO_MENSAGEM); this.enviarMensagem(m); index++; 23 return true; } return false; } public boolean getAndSetConsumidor() { Mensagem msg = receberMensagem(); //lê as mensagens e substitui por mensagens vazias, ignora mensagens vazias if(msg.getComando() == 0) { return false; } else { //reposiciona o buffer para limpar a mensagem lida if(buffer.position() != 0) { buffer.position(buffer.position() - TAMANHO_MENSAGEM); } else buffer.position(buffer.capacity() - TAMANHO_MENSAGEM); this.enviarMensagem(new Mensagem(0,0,0,0)); this.ultimaMensagemLida = msg; index++; return true; } } public int getIndex() { return this.index; } public Mensagem getUltimaMensagemLida() { return this.ultimaMensagemLida; } } 24 Mensagem.java package Comunicacao; import robot.RobotLegoEV3; public class Mensagem { private int index; private int comando; private int arg1; private int arg2; public Mensagem(int index, int comando, int arg1, int arg2){ this.index = index; this.comando = comando; this.arg1 = arg1; this.arg2 = arg2; } public int getIndex() { return this.index; } public int getComando() { return this.comando; } public int getArg1() { return this.arg1; } public int getArg2() { return this.arg2; } public void setTimestamp(long t) { this.index = (int)t; } public long getTimestamp() { return this.index; } public void executarComandoMensagem(RobotLegoEV3 robot) { switch (comando){ case 1: robot.Reta(arg1); break; case 2: robot.CurvarDireita(arg1, arg2); break; 25 case 3: robot.CurvarEsquerda(arg1, arg2); break; case 4: robot.Parar(true); break; default: break; } } public String toString() { return "indice/tempo: " + this.index +" | " + "comando: " + this.comando +" | " + "arg1: " + this.arg1 +" | " + "arg2: " + this.arg2; } } Gravador.java package Comunicacao; import java.io.IOException; import java.nio.channels.FileLock; import java.util.ArrayList; public class Gravador extends CanalComunicacao{ public Gravador() { super(); super.TAMANHO_MENSAGEM = 16; } @Override public void enviarMensagem(Mensagem msg) { FileLock fl = canalLock(); int[] params = {msg.getIndex(), msg.getComando(), msg.getArg1(), msg.getArg2()}; for (int i = 0; i < 4; i++){ buffer.putInt(params[i]); } canalRelease(fl); } @Override public Mensagem receberMensagem() { 26 FileLock fl = canalLock(); int[] params = new int; for (int i = 0; i < 4; i++){ params[i] = buffer.getInt(); } canalRelease(fl); return new Mensagem(params, params, params, params); } public ArrayList lerMensagens(int n_mensagens) { ArrayList mensagens = new ArrayList(); buffer.position(0); for (int i = 0; i < n_mensagens; i++) { if (buffer.remaining() >= TAMANHO_MENSAGEM) { mensagens.add(receberMensagem()); } else { break; } } return mensagens; } public FileLock canalLock() { try { return super.canal.lock(); } catch (IOException e) { e.printStackTrace(); } return null; } public void canalRelease(FileLock fl) { try { fl.release(); } catch (IOException e) { e.printStackTrace(); } } } 27 IG App Application.java package App; import java.io.File; import Tarefas.Gravar; import Tarefas.Imitar; public class Application { private GUI gui; private Gravar gravar; private Imitar imitar; public Application() { gui = new GUI(this); imitar = new Imitar(gui); gravar = new Gravar(gui); } public void run(String[] args) { if(args.length != 0) { //receber dados do processo TP1 gui.getBd().setFileCom(new File(args)); gui.getBd().setTamanhoMensagem(Integer.parseInt(args)); //substituir por path do ficheiro de comunicação para poder dar debug ao lançar no IDE } else gui.getBd().setFileCom(new File("C:\\Users\\kingofthevoi\\Documents\\FSO\\comunicacao.txt")) ; imitar.start(); imitar.pausar(); 28 gravar.start(); gravar.pausar(); while(!gui.getBd().isTerminar()) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } gui.getBd().getCanal().fecharCanal(); gui.fecharRobot(); } public static void main(String[] args) { Application app = new Application(); System.out.println("A aplicação começou!"); app.run(args); System.out.println("A aplicação terminou!"); } public Gravar getGravar() { return this.gravar; } public Imitar getImitar() { return this.imitar; } } BD.java package App; import java.io.File; import java.util.ArrayList; import Comunicacao.CanalComunicacaoConsistente; import Comunicacao.Mensagem; import robot.RobotLegoEV3; public class BD { 29 private RobotLegoEV3 robot; private String nomeRobot; private File fileCom; private File fileGravar; private int distancia; private int angulo; private int tamanhoMensagem; private boolean Debug; private boolean onOff; private boolean terminar; private boolean imitar; private boolean canalCom; private boolean gravar; private boolean reproduzir; private boolean imprimir; private boolean lerCanal; private CanalComunicacaoConsistente canal; private ArrayList mensagensGravar; public BD() { robot = new RobotLegoEV3(); canal = new CanalComunicacaoConsistente(); distancia = 20; angulo = 45; Debug = false; onOff = false; terminar = false; imitar = false; canalCom = false; gravar = false; reproduzir = false; imprimir = false; lerCanal = false; mensagensGravar = new ArrayList(); this.tamanhoMensagem = 8; } public RobotLegoEV3 getRobot() { return robot; } 30 public void setRobot(RobotLegoEV3 robot) { this.robot = robot; } public String getNomeRobot() { return nomeRobot; } public void setNomeRobot(String nomeRobot) { this.nomeRobot = nomeRobot; } public void setAngulo(int angulo) { this.angulo = angulo; } public int getAngulo() { return this.angulo; } public int getDistancia() { return distancia; } public void setDistancia(int distancia) { this.distancia = distancia; } public boolean isOnOff() { return onOff; 31 } public void setOnOff(boolean onOff) { this.onOff = onOff; } public boolean isDebug() { return this.Debug; } public void setDebug(boolean debug) { this.Debug = debug; } public boolean isTerminar() { return terminar; } public void setTerminar(boolean terminar) { this.terminar = terminar; } public void setFileCom(File fileCom) { this.fileCom = fileCom; } public File getFileCom() { return this.fileCom; } public boolean getImitar() { return this.imitar; } public void setImitar(boolean imitar) { this.imitar = imitar; } 32 public boolean getCanalCom() { return this.canalCom; } public void setCanalCom(boolean canalCom) { this.canalCom = canalCom; } public int getTamanhoMensagem() { return this.tamanhoMensagem; } public void setTamanhoMensagem(int tamanhoMensagem) { this.tamanhoMensagem = tamanhoMensagem; } public void setGravar(boolean gravar) { this.gravar = gravar; } public boolean getGravar() { return this.gravar; } public void setReproduzir(boolean reproduzir) { this.reproduzir = reproduzir; } public boolean getReproduzir() { return this.reproduzir; } public File getFileGravar() { return this.fileGravar; } public void setFileGravar(File fileGravar) { this.fileGravar = fileGravar; } public void setImprimir(boolean imprimir) { this.imprimir = imprimir; } public boolean getImprimir() { return this.imprimir; } 33 public boolean getLerCanal() { return this.lerCanal; } public CanalComunicacaoConsistente getCanal() { return this.canal; } public void setLerCanal(boolean lerCanal) { this.lerCanal = lerCanal; } public ArrayList getMensagensGravar() { return this.mensagensGravar; } } GUI.java package App; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.border.EmptyBorder; import com.formdev.flatlaf.themes.FlatMacLightLaf; import java.awt.Font; import javax.swing.JCheckBox; import java.awt.Color; 34 import javax.swing.SwingConstants; import javax.swing.UIManager; import javax.swing.JFileChooser; import javax.swing.ScrollPaneConstants; public class GUI extends JFrame { private static final long serialVersionUID = 1L; private JPanel contentPane; private JLabel lblRobotNome; //Variables private BD bd; private Application app; private JRadioButton rdbtnOnOff; private JTextArea textAreaConsola; private JTextField textFieldNome; private JTextField textFieldConsola; private JButton btnRight; private JButton btnForward; private JButton btnLeft; private JButton btnStop; private JButton btnBackwards; private JTextField textFieldFicheiro; final JFileChooser fc = new JFileChooser(); private JButton btnGravar; private JButton btnReproduzir; private JButton btnLimpar; private long logCode; private long startTime; public void myPrint(String S) { EventQueue.invokeLater(new Runnable() { public void run() { try { textAreaConsola.append('\n' + getCurrentTimeFormated() + S); textFieldConsola.setText(S); }catch (Exception e) { e.printStackTrace(); 35 } //se imprimir for selecionado, passa a imprimir todas as mensagens da consola para um ficheiro txt para facilitar debugging if(bd.getImprimir()) log('\n' + getCurrentTimeFormated() +S, "IG_log_"+logCode+".txt"); } }); } //retorna timestamps usadas na consola private String getCurrentTimeFormated () { long t = System.currentTimeMillis() - startTime; long minutes = (t / 1000) / 60; long seconds = (t / 1000) % 60; return "["+minutes+":"+seconds+"] "; } //imprime strings para um ficheiro, usado para dar log dos conteudos da consola private void log(String S, String filepath) { try { PrintWriter out = new PrintWriter(new FileWriter(filepath, true), true); out.write(S); out.close(); } catch (IOException e) { e.printStackTrace(); } } private void initGUI() { EventQueue.invokeLater(new Runnable() { public void run() { try { UIManager.setLookAndFeel(new FlatMacLightLaf()); setDefaultCloseOperation(JFrame. EXIT_ON_CLOSE); setBounds(100, 100, 479, 691); contentPane = new JPanel(); 36 contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); getContentPane().setLayout(null); lblRobotNome = new JLabel("Nome:"); lblRobotNome.setHorizontalAlignment(SwingConstants. CENTER); lblRobotNome.setBounds(20, 39, 46, 14); getContentPane().add(lblRobotNome); rdbtnOnOff = new JRadioButton("On/Off"); rdbtnOnOff.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { bd.setNomeRobot(textFieldNome.getText()); bd.setOnOff(!bd.isOnOff()); if(bd.isOnOff()) { if(!initRobot()) { myPrint("Erro ao iniciar o robot"); rdbtnOnOff.setSelected(false); bd.setOnOff(false); } else { enableButtons(); myPrint("Sucesso ao iniciar o robot "+ bd.getNomeRobot()); } } else { disableButtons(); fecharRobot(); } } }); rdbtnOnOff.setBounds(306, 35, 68, 23); getContentPane().add(rdbtnOnOff); JRadioButton rdbtnDebug = new JRadioButton("Debug"); rdbtnDebug.addActionListener(new ActionListener() { 37 public void actionPerformed(ActionEvent e) { bd.setDebug(!bd.isDebug()); if(bd.isDebug()) { myPrint("Modo Debug inicializado"); enableButtons(); } else { myPrint("Modo Debug terminado"); disableButtons(); } } }); rdbtnDebug.setBounds(376, 35, 75, 23); contentPane.add(rdbtnDebug); textFieldNome = new JTextField(); textFieldNome.setBounds(76, 36, 224, 20); getContentPane().add(textFieldNome); textFieldNome.setColumns(10); JScrollPane scrollPane = new JScrollPane(); scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants. HORIZONTAL_SCROLLBAR_NEVER); scrollPane.setBounds(9, 365, 442, 188); contentPane.add(scrollPane); textAreaConsola = new JTextArea(); scrollPane.setViewportView(textAreaConsola); textAreaConsola.setFont(new Font("Monospaced", Font.BOLD, 13)); textAreaConsola.setEnabled(false); textAreaConsola.setEditable(false); btnForward = new JButton("Frente"); btnForward.setEnabled(false); btnForward.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { 38 myPrint("Robot a andar para a frente " + bd.getDistancia()+" cm"); bd.getRobot().Parar(false); } }); btnForward.setFont(new Font("Tahoma", Font.PLAIN, 11)); btnForward.setBounds(184, 84, 90, 20); contentPane.add(btnForward); btnRight = new JButton("Direita"); btnRight.setEnabled(false); btnRight.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { myPrint("Robot a virar à direita com raio " + bd.getDistancia()+" cm e ângulo "+ bd.getAngulo()+"º"); bd.getRobot().Parar(false); } }); btnRight.setBounds(284, 117, 90, 20); contentPane.add(btnRight); btnLeft = new JButton("Esquerda"); btnLeft.setEnabled(false); btnLeft.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { myPrint("Robot a virar à esquerda com raio" + bd.getDistancia()+" cm e ângulo"+ bd.getAngulo()+"º"); bd.getRobot().Parar(false); } }); btnLeft.setBounds(87, 116, 90, 20); contentPane.add(btnLeft); btnStop = new JButton("Parar"); btnStop.setEnabled(false); btnStop.setBackground(new Color(255, 255, 255)); btnStop.addActionListener(new ActionListener() { 39 public void actionPerformed(ActionEvent arg0) { myPrint("Robot a parar"); bd.getRobot().Parar(true); } }); btnStop.setBounds(184, 116, 90, 20); contentPane.add(btnStop); btnBackwards = new JButton("Trás"); btnBackwards.setEnabled(false); btnBackwards.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { myPrint("Robot a andar para trás " + bd.getDistancia()+" cm"); bd.getRobot().Parar(false); } }); btnBackwards.setBounds(184, 147, 90, 20); contentPane.add(btnBackwards); JLabel lblRobotNome_1 = new JLabel("Robot"); lblRobotNome_1.setFont(new Font("Tahoma", Font.BOLD, 12)); lblRobotNome_1.setHorizontalAlignment(SwingConstants. LEFT); lblRobotNome_1.setBounds(10, 11, 46, 14); contentPane.add(lblRobotNome_1); textFieldFicheiro = new JTextField(); textFieldFicheiro.setEditable(false); textFieldFicheiro.setText(""); textFieldFicheiro.setBounds(95, 261, 279, 20); contentPane.add(textFieldFicheiro); textFieldFicheiro.setColumns(10); JLabel lblFicheiro = new JLabel("Ficheiro Gravação:"); lblFicheiro.setBounds(0, 264, 90, 14); contentPane.add(lblFicheiro); btnGravar = new JButton("Gravar"); 40 btnGravar.setEnabled(false); btnGravar.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { bd.setGravar(!bd.getGravar()); if(bd.getGravar()) { app.getGravar().retomarGravar(); } updateBtnGravar(); } }); btnGravar.setBounds(128, 292, 89, 23); contentPane.add(btnGravar); btnReproduzir = new JButton("Reproduzir"); btnReproduzir.setEnabled(false); btnReproduzir.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { bd.setReproduzir(!bd.getReproduzir()); if(bd.getReproduzir()) { app.getGravar().retomarReproduzir(); } updateBtnReproduzir(); } }); JButton btnFileChooser = new JButton("Browse"); btnFileChooser.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { if (fc.showOpenDialog(GUI.this) == JFileChooser.APPROVE_OPTION) { bd.setFileGravar(fc.getSelectedFile()); textFieldFicheiro.setText(bd.getFileGravar().getAbsolutePath()); 41 if(bd.getFileGravar() != null) btnReproduzir.setEnabled(true); btnGravar.setEnabled(true); } } }); btnFileChooser.setBounds(376, 260, 89, 23); contentPane.add(btnFileChooser); btnReproduzir.setBounds(223, 292, 106, 23); contentPane.add(btnReproduzir); btnLimpar = new JButton("Limpar"); btnLimpar.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { textAreaConsola.setText(""); textFieldConsola.setText(""); } }); btnLimpar.setFont(new Font("Tahoma", Font.PLAIN, 12)); btnLimpar.setBounds(128, 613, 89, 23); contentPane.add(btnLimpar); JCheckBox chckbxImprimir = new JCheckBox("Imprimir"); chckbxImprimir.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(!bd.getImprimir()) { logCode = System.currentTimeMillis(); } bd.setImprimir(!bd.getImprimir()); } }); chckbxImprimir.setFont(new Font("Tahoma", Font.PLAIN, 12)); chckbxImprimir.setBounds(237, 613, 75, 23); contentPane.add(chckbxImprimir); 42 JLabel lblRobotNome_1_1 = new JLabel("Consola"); lblRobotNome_1_1.setHorizontalAlignment(SwingConstants.LEFT) ; lblRobotNome_1_1.setFont(new Font("Tahoma", Font.BOLD, 12)); lblRobotNome_1_1.setBounds(10, 340, 56, 14); contentPane.add(lblRobotNome_1_1); JLabel lblRobotNome_1_2 = new JLabel("Gravar"); lblRobotNome_1_2.setHorizontalAlignment(SwingConstants.LEFT) ; lblRobotNome_1_2.setFont(new Font("Tahoma", Font.BOLD, 12)); lblRobotNome_1_2.setBounds(10, 239, 46, 14); contentPane.add(lblRobotNome_1_2); JLabel lblRobotNome_1_3 = new JLabel("Imitar"); lblRobotNome_1_3.setHorizontalAlignment(SwingConstants.LEFT) ; lblRobotNome_1_3.setFont(new Font("Tahoma", Font.BOLD, 12)); lblRobotNome_1_3.setBounds(10, 173, 46, 14); contentPane.add(lblRobotNome_1_3); JCheckBox chckbxAtivardesativar = new JCheckBox("Ativar/Desativar"); chckbxAtivardesativar.setEnabled(false); chckbxAtivardesativar.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { bd.setImitar(!bd.getImitar()); if(bd.getImitar() || !bd.getLerCanal()) { bd.setLerCanal(true); app.getImitar().retomar(); } 43 if(!bd.getImitar()) { bd.getRobot().Parar(true); } } }); chckbxAtivardesativar.setFont(new Font("Tahoma", Font.PLAIN, 12)); chckbxAtivardesativar.setBounds(334, 194, 117, 23); contentPane.add(chckbxAtivardesativar); JRadioButton rdbtnAbrirCanalComunio = new JRadioButton("Abrir Canal Comunicação"); rdbtnAbrirCanalComunio.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(!bd.getCanalCom()) { //ABRIR CANAL DE COMUNICAÇÃO myPrint("Canal de Comunicação aberto no ficheiro "+bd.getFileCom().getName()); bd.setCanalCom(bd.getCanal().abrirCanal(bd.getFileCom().getA bsolutePath(), bd.getTamanhoMensagem())); chckbxAtivardesativar.setEnabled(true); if(bd.getImitar()) app.getImitar().retomar(); } else { //FECHAR CANAL DE COMUNICAÇÃO myPrint("Canal de Comunicacao Fechado"); bd.setCanalCom(false); //bd.setLerCanal(false); //bd.setImitar(false); app.getImitar().pausar(); chckbxAtivardesativar.setEnabled(false); //chckbxAtivardesativar.setSelected(false); bd.getCanal().fecharCanal(); } } 44 }); rdbtnAbrirCanalComunio.setFont(new Font("Tahoma", Font.PLAIN, 12)); rdbtnAbrirCanalComunio.setBounds(20, 194, 167, 23); contentPane.add(rdbtnAbrirCanalComunio); textFieldConsola = new JTextField(); textFieldConsola.setBounds(9, 552, 442, 23); contentPane.add(textFieldConsola); textFieldConsola.setEditable(false); textFieldConsola.setColumns(10); setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); } public BD getBd() { return bd; } public GUI(Application app) { setTitle("Imitar & Gravar"); startTime = System.currentTimeMillis(); bd = new BD(); this.app = app; initGUI(); } public void enableButtons() { btnRight.setEnabled(true); btnLeft.setEnabled(true); btnForward.setEnabled(true); btnStop.setEnabled(true); 45 btnBackwards.setEnabled(true); } public void disableButtons() { btnRight.setEnabled(false); btnLeft.setEnabled(false); btnForward.setEnabled(false); btnStop.setEnabled(false); btnBackwards.setEnabled(false); } public void updateBtnGravar() { if(bd.getGravar()) { btnReproduzir.setEnabled(false); btnGravar.setText("Parar Gravação"); } else { btnReproduzir.setEnabled(true); btnGravar.setText("Gravar"); } } public void updateBtnReproduzir() { if(bd.getReproduzir()) { btnGravar.setEnabled(false); btnReproduzir.setText("Parar Reprodução"); } else { btnGravar.setEnabled(true); btnReproduzir.setText("Reproduzir"); } } public boolean initRobot() { myPrint("A inicializar robot..."); return bd.getRobot().OpenEV3(bd.getNomeRobot()); } public void fecharRobot() { bd.getRobot().CloseEV3(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } myPrint("Comunicação com o robot terminada"); } 46 } Tarefas Gravar.java package Tarefas; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import App.GUI; import Comunicacao.Gravador; import Comunicacao.Mensagem; public class Gravar extends Tarefa implements ITarefas{ private int tamanhoGravar; private Gravador gravador; private String file; private long tempoInicio; ArrayList mensagens; long timeStart; public Gravar(GUI gui) { super(gui); this.gravador = new Gravador(); this.bloqueado = false; file = null; } public void run() { while(true) { switch(ESTADO) { case DORMIR: Dormir(); Thread.yield(); break; case GRAVAR: 47 this.file = gui.getBd().getFileGravar().getAbsolutePath(); gui.myPrint("Gravação Iniciada"); //limpar ficheiro de gravação antes de gravar //se forem gravadas menos mensagens que existiam no ficheiro, vão aparecer mensagens vazias no fim dele this.tamanhoGravar = getFileSize(file); if(gravador.abrirCanal(file, tamanhoGravar)) { gravador.limparCanal(tamanhoGravar); gravador.fecharCanal(); } //esperar que o utilizador volte a carregar no botão de gravar para saber quando deixam de ser colocadas mensagens no Arraylist while(gui.getBd().getGravar()) Thread.yield();; //enviar as mensagens que foram colocadas no ArrayList, no ficheiro de gravação //esvazia o ArrayList à medida que mensagens são colocadas //para quando não houver mais mensagens no ArrayList this.tamanhoGravar = gui.getBd().getMensagensGravar().size(); if(gravador.abrirCanal(file, tamanhoGravar)){ while(!gui.getBd().getMensagensGravar().isEmpty()) { gui.myPrint(file); gravador.enviarMensagem(gui.getBd().getMensagensGravar().get (0)); gui.getBd().getMensagensGravar().remove(0); } gravador.fecharCanal(); } else gui.myPrint("Ficheiro Inválido"); gui.myPrint("Gravação Terminada"); this.pausar(); break; case REPRODUZIR: 48 this.file = gui.getBd().getFileGravar().getAbsolutePath(); gui.myPrint("Reprodução Iniciada"); this.tamanhoGravar = getFileSize(file); //abre um canal de tamanho igual ao tamanho do ficheiro if(gravador.abrirCanal(file, tamanhoGravar)) { //lê as mensagens todas no ficheiro de gravação e retorna-as num ArrayList mensagens = gravador.lerMensagens(tamanhoGravar); gravador.fecharCanal(); tempoInicio = System.currentTimeMillis(); for(int i = 0; i= msg.getTimestamp()) { //acaba a reprodução se encontrar uma mensagem vazia //acontece apenas quando foi usada a funcionalidade de gravar num ficheiro maior do que a gravação obtida if(msg.getComando() == 0) break; msg.executarComandoMensagem(robot); gui.myPrint(msg.toString()); } else i--; } //para o robot ao completar a reprodução this.robot.Parar(true); //limpa o ArrayList de reprodução 49 mensagens.clear(); } else gui.myPrint("Ficheiro Inválido"); gui.myPrint("Reprodução Finalizada"); gui.getBd().setReproduzir(false); gui.updateBtnReproduzir(); this.pausar(); break; } } } public void setFile(File file) { this.file = file.getAbsolutePath(); } public void retomarGravar() { pausar.release(); ESTADO = GRAVAR; } public void retomarReproduzir() { pausar.release(); ESTADO = REPRODUZIR; } @SuppressWarnings("null") //retorna o número de mensagens num ficheiro private int getFileSize(String fileName) { Path path = Paths.get(fileName); try { // size of a file (in bytes) return (int) Files.size(path) / gravador.getTamanhoMensage(); } catch (IOException e) { e.printStackTrace(); return (Integer) null; } } } Imitar.java package Tarefas; 50 import java.util.ArrayList; import App.GUI; import Comunicacao.CanalComunicacaoConsistente; import Comunicacao.Mensagem; public class Imitar extends Tarefa implements ITarefas{ CanalComunicacaoConsistente canal; private ArrayList mensagens = new ArrayList(); private long tempoInicio; private boolean tempoGravado; private boolean pararImitar; public Imitar(GUI gui) { super(gui); mensagens.add(null); this.canal = gui.getBd().getCanal(); this.tempoGravado = false; this.pararImitar = false; } public void run() { while(true) { switch(ESTADO) { case DORMIR: Thread.yield(); Dormir(); break; case EXECUTAR: mensagens = new ArrayList(); if(gui.getBd().getCanalCom()) { if(canal.getAndSetConsumidor()) { //adiciona as mensagens recebidas a um ArrayList local Mensagem msg = canal.getUltimaMensagemLida(); mensagens.add(msg); gui.myPrint(msg.toString()); //enquanto gravar estiver ativado, deposita as mensagens recebidas num ArrayList, tendo em conta timeStamps if(gui.getBd().getGravar()) { if(!tempoGravado) { tempoInicio = System.currentTimeMillis(); 51 tempoGravado = true; } msg.setTimestamp(System.currentTimeMillis() - tempoInicio); if(gui.getBd().getImitar()) { gui.getBd().getMensagensGravar().add(msg); this.pararImitar = false; } else if (!pararImitar) { gui.getBd().getMensagensGravar().add(new Mensagem((int)(System.currentTimeMillis() - tempoInicio), 4, 0,0)); pararImitar = true; } } else tempoGravado = false; } } //envia as mensagens no ArrayList local ao robot, esvazia o ArrayList à medida que são enviadas //só envia as mensagens se imitar estiver ativado if(!mensagens.isEmpty()) { if(gui.getBd().getImitar() && !gui.getBd().getReproduzir()) { mensagens.get(0).executarComandoMensagem(robot); } mensagens.remove(0); } break; } } } } ITarefas.java package Tarefas; import java.util.Random; 52 public interface ITarefas{ final int DORMIR = 1; final int EXECUTAR = 2; final int GRAVAR = 3; final int REPRODUZIR = 4; final int VIRAR_ESQUERDA = 1; final int VIRAR_DIREITA = 2; final int ANDAR_FRENTE = 3; static Random r = new Random(); } Reproduzir.java package Tarefas; import App.GUI; public class Reproduzir extends Tarefa implements ITarefas{ public Reproduzir(GUI gui) { super(gui); // TODO Auto-generated constructor stub } public void run() { while(true) { } } } Tarefas.java package Tarefas; 53 import java.util.concurrent.Semaphore; import App.GUI; import robot.RobotLegoEV3; public class Tarefa extends Thread implements ITarefas{ protected GUI gui; protected RobotLegoEV3 robot; protected Semaphore pausar; protected int ESTADO; protected boolean bloqueado; public Tarefa(GUI gui) { this.gui = gui; this.robot = gui.getBd().getRobot(); this.bloqueado = true; this.ESTADO = DORMIR; pausar = new Semaphore(1); } public void bloquear(boolean bloquear) { this.bloqueado = bloquear; } public void pausar() { pausar.drainPermits(); ESTADO = DORMIR; } public void retomar() { pausar.release(); ESTADO = EXECUTAR; } protected void Dormir() { try { pausar.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } } protected void trySleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { 54 e.printStackTrace(); } } } 55 TP1 App Application.java package App; import Tarefas.Evitar; import Tarefas.Fugir; import Tarefas.Vaguear; public class Application { private GUI gui; private Vaguear vaguear; private Evitar evitar; private Fugir fugir; public Application() { gui = new GUI(); vaguear = new Vaguear(gui, gui.getCanal()); evitar = new Evitar(gui); fugir = new Fugir(gui); fugir.setVaguear(vaguear); evitar.setVaguear(vaguear); evitar.setFugir(fugir); } private void run() { vaguear.start(); evitar.start(); fugir.start(); while(!gui.getBd().isTerminar()) { evitar.bloquear(!gui.getBd().getEvitar()); fugir.bloquear(!gui.getBd().getFugir()); vaguear.bloquear(!gui.getBd().getVaguear()); 56 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } gui.fecharRobot(); } public static void main(String[] args) { Application app = new Application(); System.out.println("A aplicação começou!"); app.run(); System.out.println("A aplicação terminou!"); } } BD.java package App; import java.io.File; import java.util.concurrent.Semaphore; import robot.RobotLegoEV3; public class BD { private RobotLegoEV3 robot; private String nomeRobot; private File file; private int distancia; private int angulo; private int tamanhoMensagem; private boolean canalCom; private boolean Debug; private boolean onOff; private boolean terminar; private boolean vaguear; private boolean evitar; private boolean fugir; private Semaphore acessoRobot; private String jarPath; public BD() { 57 robot = new RobotLegoEV3(); acessoRobot = new Semaphore(1); tamanhoMensagem = 8; canalCom = false; Debug = false; onOff = false; terminar = false; vaguear = false; evitar = false; fugir = false; } public RobotLegoEV3 getRobot() { return robot; } public void setRobot(RobotLegoEV3 robot) { this.robot = robot; } public String getNomeRobot() { return nomeRobot; } public void setNomeRobot(String nomeRobot) { this.nomeRobot = nomeRobot; } public void setAngulo(int angulo) { this.angulo = angulo; } public int getAngulo() { return this.angulo; } public int getDistancia() { return distancia; } public void setDistancia(int distancia) { this.distancia = distancia; } public boolean isOnOff() { return onOff; } public void setOnOff(boolean onOff) { this.onOff = onOff; } public boolean isDebug() { return this.Debug; } public void setDebug(boolean debug) { this.Debug = debug; } public boolean isTerminar() { return terminar; } public void setTerminar(boolean terminar) { this.terminar = terminar; } 59 public void setVaguear(boolean vaguear) { this.vaguear = vaguear; } public boolean getVaguear() { return this.vaguear; } public void setEvitar(boolean evitar) { this.evitar = evitar; } public boolean getEvitar() { return this.evitar; } public boolean getFugir() { return fugir; } public void setCanalCom(boolean canalCom) { this.canalCom = canalCom; } public boolean getCanalCom() { return this.canalCom; } public void setFugir(boolean fugir) { this.fugir = fugir; } public void setFile(File file) { this.file = file; } public File getFile() { return this.file; } public Semaphore getAcessoRobot() { return this.acessoRobot; } public void setTamanhoMensagem(int tamanhoMensagem) { this.tamanhoMensagem = tamanhoMensagem; 60 } public int getTamanhoMensagem() { return this.tamanhoMensagem; } public void setJarPath(File jar) { this.jarPath = jar.getAbsolutePath(); } public String getJarPath() { return this.jarPath; } } GUI.java package App; import java.awt.Color; import java.awt.EventQueue; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.text.ParseException; import Comunicacao.CanalComunicacaoConsistente; import Comunicacao.Mensagem; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.SpinnerNumberModel; import javax.swing.SwingConstants; import javax.swing.UIManager; import javax.swing.border.EmptyBorder; import javax.swing.event.ChangeListener; 61 import com.formdev.flatlaf.themes.FlatMacLightLaf; import javax.swing.event.ChangeEvent; public class GUI extends JFrame { private static final long serialVersionUID = 1L; private JPanel contentPane; private JLabel lblRobotNome; //Variables private BD bd; private JRadioButton rdbtnOnOff; private JTextArea textAreaConsola; private JTextField textFieldNome; private JTextField textFieldConsola; private JTextField textFieldRaio; private JTextField textFieldAngulo; private JButton btnRight; private JButton btnForward; private JButton btnLeft; private JButton btnStop; private JButton btnBackwards; private JLabel lblRobotNome_2; private JCheckBox chckbxRoam; private JCheckBox chckbxAvoid; private JCheckBox chckbxEscape; private JRadioButton rdbtnAbrirFecharCanal; private JTextField textFieldFicheiro; private CanalComunicacaoConsistente canal; final JFileChooser fc = new JFileChooser(); private JButton btnIG; private JTextField textFieldJar; public void myPrint(String S) { EventQueue.invokeLater(new Runnable() { public void run() { try { textAreaConsola.append('\n' + S); textFieldConsola.setText(S); }catch (Exception e) { 62 e.printStackTrace(); } } }); } private void initGUI() { EventQueue.invokeLater(new Runnable() { public void run() { try { UIManager.setLookAndFeel(new FlatMacLightLaf()); setDefaultCloseOperation(JFrame. EXIT_ON_CLOSE); setBounds(100, 100, 521, 737); contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); getContentPane().setLayout(null); lblRobotNome = new JLabel("Nome:"); lblRobotNome.setHorizontalAlignment(SwingConstants. CENTER); lblRobotNome.setBounds(32, 39, 46, 14); getContentPane().add(lblRobotNome); rdbtnOnOff = new JRadioButton("On/Off"); rdbtnOnOff.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { bd.setNomeRobot(textFieldNome.getText()); bd.setOnOff(!bd.isOnOff()); if(bd.isOnOff()) { if(!initRobot()) { myPrint("Erro ao iniciar o robot"); rdbtnOnOff.setSelected(false); bd.setOnOff(false); } else { 63 enableButtons(); myPrint("Sucesso ao iniciar o robot "+ bd.getNomeRobot()); } } else { disableButtons(); fecharRobot(); } } }); rdbtnOnOff.setBounds(284, 35, 68, 23); getContentPane().add(rdbtnOnOff); JRadioButton rdbtnDebug = new JRadioButton("Debug"); rdbtnDebug.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { bd.setDebug(!bd.isDebug()); if(bd.isDebug()) { myPrint("Modo Debug inicializado"); enableButtons(); } else { myPrint("Modo Debug terminado"); disableButtons(); } } }); rdbtnDebug.setBounds(354, 35, 75, 23); contentPane.add(rdbtnDebug); textFieldNome = new JTextField(); textFieldNome.setBounds(88, 36, 190, 20); getContentPane().add(textFieldNome); textFieldNome.setColumns(10); textFieldConsola = new JTextField(); textFieldConsola.setEditable(false); textFieldConsola.setBounds(32, 639, 442, 38); getContentPane().add(textFieldConsola); 64 textFieldConsola.setColumns(10); JScrollPane scrollPane = new JScrollPane(); scrollPane.setBounds(32, 440, 442, 188); contentPane.add(scrollPane); textAreaConsola = new JTextArea(); scrollPane.setViewportView(textAreaConsola); textAreaConsola.setFont(new Font("Monospaced", Font.BOLD, 13)); textAreaConsola.setEnabled(false); textAreaConsola.setEditable(false); btnForward = new JButton("Frente"); btnForward.setEnabled(false); btnForward.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { bd.getAcessoRobot().acquire(); } catch (InterruptedException e1) { e1.printStackTrace(); } myPrint("Robot a andar para a frente " + bd.getDistancia()+" cm"); bd.getRobot().Reta(Integer. parseInt(textFieldRaio.getText()) ); bd.getRobot().Parar(false); bd.getAcessoRobot().release(); Mensagem msg = new Mensagem(canal.getIndex(), 1, bd.getDistancia(), bd.getAngulo()); myPrint(msg.toString()); canal.getAndSetProdutor(msg); } }); btnForward.setFont(new Font("Tahoma", Font.PLAIN, 11)); btnForward.setBounds(309, 114, 90, 20); contentPane.add(btnForward); 65 btnRight = new JButton("Direita"); btnRight.setEnabled(false); btnRight.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { bd.getAcessoRobot().acquire(); } catch (InterruptedException e1) { e1.printStackTrace(); } myPrint("Robot a virar à direita com raio " + bd.getDistancia()+" cm e ângulo "+ bd.getAngulo()+"º"); bd.getRobot().CurvarDireita(Integer. parseInt(textFieldRaio.g etText()), Integer.parseInt(textFieldAngulo.getText())); bd.getRobot().Parar(false); bd.getAcessoRobot().release(); Mensagem msg = new Mensagem(canal.getIndex(), 2, bd.getDistancia(), bd.getAngulo()); myPrint(msg.toString()); canal.getAndSetProdutor(msg); } }); btnRight.setBounds(409, 147, 90, 20); contentPane.add(btnRight); btnLeft = new JButton("Esquerda"); btnLeft.setEnabled(false); btnLeft.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { bd.getAcessoRobot().acquire(); } catch (InterruptedException e1) { e1.printStackTrace(); } myPrint("Robot a virar à esquerda com raio" + bd.getDistancia()+" cm e ângulo"+ bd.getAngulo()+"º"); 66 bd.getRobot().CurvarEsquerda(Integer. parseInt(textFieldRaio. getText()), Integer.parseInt(textFieldAngulo.getText())); bd.getRobot().Parar(false); bd.getAcessoRobot().release(); Mensagem msg = new Mensagem(canal.getIndex(), 3, bd.getDistancia(), bd.getAngulo()); myPrint(msg.toString()); canal.getAndSetProdutor(msg); } }); btnLeft.setBounds(212, 146, 90, 20); contentPane.add(btnLeft); btnStop = new JButton("Parar"); btnStop.setEnabled(false); btnStop.setBackground(new Color(255, 255, 255)); btnStop.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { myPrint("Robot a parar"); bd.getRobot().Parar(true); Mensagem msg = new Mensagem(canal.getIndex(), 4, bd.getDistancia(), bd.getAngulo()); myPrint(msg.toString()); canal.getAndSetProdutor(msg); } }); btnStop.setBounds(309, 146, 90, 20); contentPane.add(btnStop); btnBackwards = new JButton("Trás"); btnBackwards.setEnabled(false); btnBackwards.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { bd.getAcessoRobot().acquire(); } catch (InterruptedException e1) { e1.printStackTrace(); 67 } myPrint("Robot a andar para trás " + bd.getDistancia()+" cm"); bd.getRobot().Reta(- Integer.parseInt(textFieldRaio.getText())); bd.getRobot().Parar(false); bd.getAcessoRobot().release(); } }); btnBackwards.setBounds(309, 177, 90, 20); contentPane.add(btnBackwards); chckbxAvoid = new JCheckBox("Evitar"); chckbxAvoid.setEnabled(false); chckbxAvoid.setBounds(309, 204, 57, 23); contentPane.add(chckbxAvoid); chckbxAvoid.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { bd.setEvitar(!bd.getEvitar()); } }); chckbxEscape = new JCheckBox("Fugir"); chckbxEscape.setEnabled(false); chckbxEscape.setBounds(409, 204, 57, 23); contentPane.add(chckbxEscape); chckbxEscape.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { bd.setFugir(!bd.getFugir()); } }); chckbxRoam = new JCheckBox("Vaguear"); chckbxRoam.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { bd.setVaguear(!bd.getVaguear()); } }); chckbxRoam.setEnabled(false); chckbxRoam.setBounds(212, 204, 68, 23); 68 contentPane.add(chckbxRoam); textFieldRaio = new JTextField(); textFieldRaio.setText("0"); textFieldRaio.setBounds(106, 115, 86, 20); contentPane.add(textFieldRaio); textFieldRaio.setColumns(10); textFieldAngulo = new JTextField(); textFieldAngulo.setText("0"); textFieldAngulo.setBounds(106, 146, 86, 20); contentPane.add(textFieldAngulo); textFieldAngulo.setColumns(10); bd.setAngulo(Integer.parseInt(textFieldAngulo.getText())); JLabel lblRaio = new JLabel("Raio/Distancia"); lblRaio.setHorizontalAlignment(SwingConstants. CENTER); lblRaio.setBounds(10, 117, 85, 14); contentPane.add(lblRaio); JLabel lblAngulo = new JLabel("Angulo"); lblAngulo.setHorizontalAlignment(SwingConstants. CENTER); lblAngulo.setBounds(32, 148, 46, 19); contentPane.add(lblAngulo); JButton btnUpdate = new JButton("Confirmar"); btnUpdate.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { getBd().setDistancia(Integer.parseInt(textFieldRaio.getText( ))); getBd().setAngulo(Integer. parseInt(textFieldAngulo.getText() )); myPrint("Distância/raio inseridos a "+ bd.getDistancia()); myPrint("Ângulo inserido a "+ bd.getAngulo()); 69 } }); btnUpdate.setBounds(103, 177, 90, 20); contentPane.add(btnUpdate); JLabel lblRobotNome_1 = new JLabel("Robot"); lblRobotNome_1.setFont(new Font("Tahoma", Font.BOLD, 15)); lblRobotNome_1.setHorizontalAlignment(SwingConstants. LEFT); lblRobotNome_1.setBounds(10, 11, 46, 14); contentPane.add(lblRobotNome_1); lblRobotNome_2 = new JLabel("Controlo"); lblRobotNome_2.setHorizontalAlignment(SwingConstants. LEFT); lblRobotNome_2.setFont(new Font("Tahoma", Font.BOLD, 15)); lblRobotNome_2.setBounds(10, 92, 68, 14); contentPane.add(lblRobotNome_2); rdbtnAbrirFecharCanal = new JRadioButton("Abrir/Fechar Canal"); rdbtnAbrirFecharCanal.setEnabled(false); rdbtnAbrirFecharCanal.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { if(!bd.getCanalCom()) { myPrint(""+bd.getFile().getAbsolutePath()); bd.setCanalCom(canal.abrirCanal(bd.getFile().getAbsolutePath (), bd.getTamanhoMensagem())); } else { //canal.limparCanal(); bd.setCanalCom(false); canal.fecharCanal(); } } }); rdbtnAbrirFecharCanal.setBounds(201, 400, 134, 33); contentPane.add(rdbtnAbrirFecharCanal); 70 JSpinner nMsgs = new JSpinner(); nMsgs.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent arg0) { try { nMsgs.commitEdit(); } catch (ParseException e) { e.printStackTrace(); } bd.setTamanhoMensagem((int) nMsgs.getValue()); System.out.println(""+bd.getTamanhoMensagem()); } }); nMsgs.setModel(new SpinnerNumberModel(8, 8, 16, 1)); nMsgs.setBounds(201, 363, 62, 27); contentPane.add(nMsgs); textFieldFicheiro = new JTextField(); textFieldFicheiro.setEditable(false); textFieldFicheiro.setBounds(136, 289, 248, 20); contentPane.add(textFieldFicheiro); textFieldFicheiro.setColumns(10); JLabel lblFicheiro = new JLabel("Ficheiro Comunicação:"); lblFicheiro.setBounds(10, 292, 125, 17); contentPane.add(lblFicheiro); JLabel lblMsgs = new JLabel("Nº msgs:"); lblMsgs.setBounds(106, 369, 86, 14); contentPane.add(lblMsgs); JButton btnFileChooserCom = new JButton("Browse"); btnFileChooserCom.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { if (fc.showOpenDialog(GUI.this) == JFileChooser.APPROVE_OPTION) { bd.setFile(fc.getSelectedFile()); 71 textFieldFicheiro.setText(bd.getFile().getAbsolutePath()); if(bd.getFile() != null) rdbtnAbrirFecharCanal.setEnabled(true); if(bd.getJarPath() != null && bd.getFile() != null) btnIG.setEnabled(true); } } }); btnFileChooserCom.setBounds(394, 288, 89, 23); contentPane.add(btnFileChooserCom); btnIG = new JButton("Lançar IG"); btnIG.setEnabled(false); btnIG.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { nMsgs.setEnabled(false); btnFileChooserCom.setEnabled(false); if(bd.getCanalCom()) { canal.limparCanal(bd.getTamanhoMensagem()); } String nameProcess = bd.getJarPath();; String filePath = bd.getFile().getAbsolutePath(); String dimensaoCanal = String.valueOf(bd.getTamanhoMensagem()); ProcessBuilder pb = new ProcessBuilder("java", "-jar", nameProcess, filePath, dimensaoCanal); try { pb.start(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } }