You are here

Atividade 1 - Estudo ambiente de desenvolvimento - integração WorldServer3D

Esta primeira atividade teve o foco de estudo da solução de integração WorldServer3D e SOAR, fornecida como base para o desenvolvimento proposto na segunda atividade.
 
 
Logo de início um primeiro ponto observado foi que a versão do WorldServer3D é diferente da utilizada para a primeira aula do curso; isso foi observado porque o cliente desenvolvido naquela aula deixou de funcionar, uma vez que se baseava no uso dos comando "showworldthings" e "getfullstatus3D" que na versão mais nova não estava disponíveis.
 
 

Análise do código do "DemoSOAR"

 

1. Uso da classe "NativeUtils" para resolver o problema do gerenciamento do código JNI

 
A classe que contém o método "main" é a "SimulationSOAR".  Nesse método, primeiramente NativeUtils.setLibraryPath(".") é utilizado para indicar que as bibliotecas nativas deverão ser obtidas do diretório atual - parâmetro ".":
 
 
            NativeUtils.setLibraryPath(".");
            System.out.println("OS:"+System.getProperties().getProperty("os.name")+
                               " Architecture:"+System.getProperties().getProperty("os.arch"));
 
            String osName = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
            String osArch = System.getProperty("os.arch").toLowerCase(Locale.ENGLISH);
 
 
Na sequência, dependendo do sistema operacional detectado - através dos comandos System.getProperty("os.name") e System.getProperty("os.arch") - o método NativeUtils.loadFileFromJar() é utilizado para carregar as bibliotecas SOAR correspondentes.
 
NativeUtils.loadFileFromJar() extrai o arquivo parâmetro de dentro do jar - a partir da InputStream obtida com NativeUtils.class.getResourceAsStream() - gravando como um arquivo separado dentro do diretório atual:
 
 
              } else if(osName.contains("nix") || osName.contains("nux")){
                  if(osArch.contains("64")) {
                      System.out.println("Linux 64 bits");
                      NativeUtils.loadFileFromJar("/linux64/libSoar.so");
                      NativeUtils.loadFileFromJar("/linux64/libJava_sml_ClientInterface.so");
                  }
 
 
Por fim o conjunto de regras "soar-rules.soar" é também carregado:
 
 
            NativeUtils.loadFileFromJar("/soar-rules.soar");
            String soarRulesPath = "soar-rules.soar";
 
 
 
 

2. Observe que essa solução é estendida ao carregamento de arquivos com código SOAR, para uso do controlador. Como isso é implementado no Demo ?

 
Primeiramente o arquivo "soar-rules.soar" é extraído do .jar, via "NativeUtils.loadFileFromJar()". A referência para esse arquivo (seu nome) é passado no método "simulationTask.initializeCreatureAndSOAR()"; a classe "simulationTask" é a responsável pela execução ta tarefa dessa simulação, comandando a interação tanto com o WorldServer3D quanto com o SOAR.
 
Por fim a referência ao arquivo de regras é passada até a classe "SoarBridge" via seu construtor.
 
A classe "SoarBridge" cria e manipula um Kernel para uso do SML (Soar Markup Language); seu construtor faz as seguintes tarefas:
 
  • Cria um Kernel usando "Kernel.CreateKernelInNewThread()"
  • Cria um agente usando "kernel.CreateAgent()"
  • Carrega no agente as regras de "soar-rules.soar", utilizando "agent.LoadProductions()"
  • Ativa um SoarDebugger associado ao agente, utilizando "agent.SpawnDebugger()"
 
 

3. O loop principal de simulação do DemoSOAR também se encontra no método main. Explique seu funcionamento.

 
No método "SimulationSOAR.main()" a execução da simulação está dentro de um "while(true)"; nesse loop é chamado o método "simulationTask.runSimulation()" a cada ciclo.
 
Como mencionado anteriormente, a classe "SimulationTask()" é quem controla a execução da simulação, mantendo a conexão entre o "WorldServer3D" e o "Soar".
 
Para conectar-se ao WorldServer3D é utilizada a classe "WS3DProxy", que oferece os seguintes métodos:
 
  • createCreature(): solicita a criação de uma nova criatura no WorldServer3D, retornando um objeto da classe "Creature".
  • getCreature(): solicita uma referência para uma criatura já existente no WorldServer3D
  • setWorld(): define qual objeto da classe "World" deve ser utilizado pelo proxy
  • getWorld(): obtém uma objeto da classe "World", o qual permite a interação com o WorldServer3D
 
O restante da interface com "WorldServer3D" é obtido com as classes "Creature" e "World"; através dessas classes é possível manipular tanto as criaturas quando o mundo todo do WorldServer3D.
 
 
Já a conexão com o Soar é feita a partir da classe "SoarBridge" comentada anteriormente, essencialmente através das classes SML "Kernel" e "Agent".
 
O método "SimulationTask.prepareAndSetupCreatureToSimulation()" realiza os seguintes passos:
 
  1. Atualiza o status da criatura e prepara os outputs para o SOAR (os inputs para o agente) a partir das informações obtidas do WorldServer3D - método "SimulationTask.prepareAndSetupCreatureToSimulation()".
  2. Executa o agente via "soarBridge.runSimulation()".
  3. Processa as respostas do agente (as extensões criadas pelo agente no seu  output-link) e envia os comandos correspondentes ao WorldServer3D - método "SimulationTask.processResponseComands()".
 
 

Detalhamento dos passos de "SimulationTask.prepareAndSetupCreatureToSimulation()"

 

a. Atualização do estado da criatura e uso do método "SimulationTask.prepareAndSetupCreatureToSimulation()"

 
O estado da criatura dentro do WorldServer3D é atualizado através do método "Creature.updateState()":
 
    public synchronized Creature updateState() {
 
        try {
 
            logger.info("----------- updateThingsStatus -----------");
 
            CommandUtility.updateStatus();
 
 
        } catch (CommandExecException ex) {
            java.util.logging.Logger.getLogger(WS3DProxy.class.getName()).log(Level.SEVERE, null, ex);
        }
 
        return this;
    }
 
É utilizada a classe "CommandUtility()" a qual implementa a maioria dos comandos oferecidos pela interface do WorldServer3D; é utilizando o comando "getcreaturestate [creature_name] para obter o status da criatura, retornando as seguintes informações:
 
<creature_name> <creature_index> <X> <Y> <size> <pitch> <#-motorSys> <wheel> <speed> <fuel> <color> <camera> [<leaflets-list>] [<things-in-vision>]
 
A lista de objetos vistos - informação opcional <things-in-vision> - é utilizada para compor o atributo "Creature.thingsInVision".
 
 
Assim, dentro do método "SimulationTask.prepareAndSetupCreatureToSimulation()", primeiramente são obtidos os objetos dentro do campo de visão da criatura, através da chamada "Creature.getThingsInVision()". Todas essas informações são armazenadas em um objeto da classe "VisualSensor".
 
Adicionalmente é obtido o nível de combustível atual da criatura, via método "Creature.getFuel()". Essa informação é capturada num objeto da classe "FuelSensor".
 
Ambos objetos de sensores são capturados num objeto da classe "SimulationRobot" que é por fim passado para o "SoarBridge" via método "setupStackHolder()".
 
 
Esse método "SoarBridge.setupStackHolder()" é o método que repassa para o SOAR os dados obtidos do "WorldServer3D", utilizando métodos do pacote "sml" que manipulam elementos na memória de trabalho do agente; são usados os seguintes métodos:
 
  • Agent.CreateIdWME(<elemento-pai>, <"nome-novo-elemento">): cria um novo elemento na memória de trabalho
  • Agent.CreateFloatWME(<elemento-pai>, <"nome-novo-elemento">, <valor-novo-element>): cria um novo elemento FLOAT na memória de trabalho
 
É criada a seguinte estrutura de informação:
 
<s> ^input-link <il>
<il> ^CREATURE <c>
<c> ^MEMORY <cm>
    ^PARAMETERS <cp>
    ^POSITION <cpos>
    ^SENSOR <csens>
<cp> ^MINFUEL 400
     ^TIMESTAMP CURRENT-TIME
<cpos> ^X X
       ^Y Y
<csens> ^FUEL FUEL-LEVEL
        ^VISUAL <csvisual>
<csvisual> ^ENTITY <entity-1> ... <entity-n>
<entity-*> ^DISTANCE GEOMETRIC-DISTANCE
           ^X X1
           ^Y Y1
           ^X2 X2
           ^Y2 Y2
           ^TYPE CATEGORY
           ^NAME NAME
           ^COLOR COLOR
 
 

b. Execução do agente

 
Esta etapa é a mais simples, pois corresponde à chamada do método "Agent.RunSelfTilOutput()" que executa o agente até que ele produza uma saída (extensão em "output-link") ou se passem 15 ciclos Soar.
 
 

c. método "processResponseCommands()"

 
A primeira parte desse método é a execução de "SoarBridge.getReceivedCommands()": este método interfaceia com o agente via alguns métodos do pacote "sml":
 
  • Agent.GetCommand(<number>): Devolve o identificador do comando "number";
  • Identifier.GetCommandName(): Devolve o nome do comando; possíveis são "MOVE", "GET" ou "EAT".
  • Identifier.GetParameterValue(<"parameter-name">): Devolve o valor da extensão parâmetro.
 
 
"SoarBridge.getReceivedCommands()" devolve um array de objetos "SoarCommand".
 
Pelo código os comandos são os seguintes - quando criados no output-link:
 
<s> ^output-link <ol>
<ol> ^MOVE <cmove>
<cmove> ^VelR VEL-RIGHT
        ^VelL VEL-LEFT
        ^Vel  VEL-LINEAR
        ^x    X
        ^y    Y
 
<ol> ^GET <cget>
<cget> ^name THING-NAME-TO-GET
 
<ol> ^EAT <ceat>
<ceat> ^name THING-NAME-TO-EAT
 
 
Depois de processar os comandos criados no "output-link", a próxima etapa é enviá-los para o WorldServer3D, o que é feito com o uso da mesma classe "CommandUtility", já descrita anteriormente
 
No caso dos comando que podem ser recebidos do SOAR, o processamento é o seguinte:
 
  • MOVE - envia um comando "setgoTo", caso estejam disponíveis valores de "x,y"; envia um comando "setTurn", caso não estejam disponíveis valores de "x, y"
  • GET - envia um comando "sackit", passando o nome do objeto a ser apanhado
  • EAT - envia um comando "eatit", passando o nome do objeto a ser comido
 
 
 
 

4. Descrição do funcionamento em "soar-rules.soar"

 
Como não é possível executar o SoarDebugger como o DemoSOAR executado a partir do WebStart,  é necessário executá-lo localmente, indicando onde o SoarDebugger.jar pode ser encontrado:
 
- Colocar como  diretório de trabalho o "/bin" onde está o SoarDebugger.jar
 
 
O funcionamento das regras está descrito a seguir:
 
 

Atualização a partir do input-link

 
A cada ciclo armazena dentro da extensão "^MEMORY", criada dentro do DemoSOAR mas nunca apagada, todas as entidades que receber através de "input-link.CREATURE.SENSOR.VISUAL", nas extensões ^ENTITY.
 
Para tanto, utiliza 2 operadores: "seeEntityWithMemoryCount" e "seeEntityWithoutMemoryCount".
 
É a partir das informações armazenadas em "^MEMORY" que as ações do agente serão defindas.
 
 

Proposições de ações

 
Propõe operador "wander" no caso de não haver alterações no sensor visual; esse operador a criatura ficar em rotação para direita com o objetivo dela OLHAR ao seu redor.
 
Caso existam elementos avistados, pode propor os seguintes operadores:
 
  • "moveFood": propõe o movimento até um alimento cuja posição está armazenada na memória
  • "eatFood": propõe que coma um alimento que estiver a menos de 30 de distância
  • "moveJewel": propõe o movimento até uma jóia cuja posição está armazenada na memória
  • "getJewel": propõe que apanhe uma jóia que estiver a menos de 30 de distância
 

Prorização dos operadores:

 
As prioridades dos operadores são as seguintes:
 
  1. Prefere atualizar a memória com o que está vendo do que se mover;
  2. Prefere desviar de um bloco do que atualizar a memória;
  3. Prefere pegar jóia ou comer alimento do que desviar de bloco;
  4. Prefere comer alimento do que se mover;
  5. Entre mover para comida ou jóia, move para comida caso o combustível esteja abaixo ou igual ao limiar estabelecido (400);
  6. Entre mover para comida ou jóia, move para jóia caso o combustível esteja acima do limiar;
  7. Move para alimento/jóia que estiver mais perto;
  8. Pega jóia que estiver mais perto;
  9. Come alimento que estiver mais perto;
  10. Desvia do bloco que estiver mais perto;
  11. Wander é o menos prioritário;
 
 
 

Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer