/*******************************************************************************
* Copyright (c) 2010-2013, Embraer S.A., Budapest University of Technology and Economics
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Rodrigo Rizzi Starr, Lincoln Nascimento - initial API and implementation
*******************************************************************************/
package br.com.embraer.massif.commandevaluation.server;
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import br.com.embraer.massif.commandevaluation.command.InteractionCommand;
import br.com.embraer.massif.commandevaluation.exception.MatlabError;
import br.com.embraer.massif.commandevaluation.exception.MatlabOutputException;
import br.com.embraer.massif.commandevaluation.exception.MatlabRMIException;
import com.mathworks.jmi.Matlab;
/**
* Class responsible for executing the matlab evals and fevals
* in background. Its main code is run in a thread, then the matlab
* command line is not locked
*
* @author ldnascim
*/
public class MatlabServerController implements Runnable {
/** Flag to indicate if the thread is running**/
private boolean running;
private boolean debug;
/** Contains the commands to be executed in matlab **/
private BlockingQueue<InteractionCommand> commands;
/** Contains the outputs for the commands executed in matlab **/
private ConcurrentMap<UUID, InteractionCommand> commandsResponses;
/**
* Semaphore used to control that commands were received
* This is used in order to "notify" the server controller thread
* that a command was added, in order to avoid using a Thread-poll
* solution, which consumes CPU resource
*/
private Semaphore commandsSemaphore;
/**
* Default constructor. A thread is started here
* in order to enable the execution of the commands
* in background
*/
public MatlabServerController() {
commands = new LinkedBlockingQueue<InteractionCommand>();
commandsResponses = new ConcurrentHashMap<UUID, InteractionCommand>();
commandsSemaphore = new Semaphore(0);
// starts thread
new Thread(this).start();
}
/**
* This method will execute the eval/feval commands in the matlab
* thread by using the method "whenAtPrompt"
* @param command Command to be executed
*/
private void executeCommand(final InteractionCommand command) {
if (debug) {
System.out.println("Executing command: " + command.toString());
}
// executes command in the matlab thread
Matlab.whenAtPrompt(new Runnable() {
@Override
public void run() {
command.executeCommand();
}
});
}
/**
* Adds a command in the queue to be executed
* @param command Command to be executed
*/
public void addCommand(InteractionCommand command) {
boolean wasEmpty = commands.isEmpty();
commands.add(command);
// checks if command expects output objects
if (command.getNumberOfOutputsArguments() > 0)
{
commandsResponses.put(command.getIdNumber(), command);
}
// if the list was empty then notify the thread
// that a new command to be executed was added
if (wasEmpty) {
// notifies that a command was added
commandsSemaphore.release();
}
}
/**
* Returns the thread is running or not
* @return true if the thread is running,
* false otherwise
*/
public boolean isRunning() {
return running;
}
public void setDebug(boolean debug) {
this.debug = debug;
}
/**
* Retrieves the output from an executed command
* using the UUID provided in the execution.
* @param idCommand Identifier for the execution
* @return Output for the command
* @throws MatlabOutputException
*/
public Object[] getCommandOutput(UUID idCommand) throws MatlabOutputException {
InteractionCommand command = commandsResponses.remove(idCommand);
// waits until the command finish its execution
if (!command.isFinished()) {
command.waitToFinish();
}
return command.getOutput();
}
@Override
public void run() {
running = true;
while (running) {
if (commands.isEmpty()) {
// waits until receive at least one new command
try {
commandsSemaphore.acquire();
} catch (InterruptedException e) {
MatlabRMIException exception =
new MatlabRMIException(MatlabError.WAITING_NOTIFY_ERROR, e);
// Since we can't throw the exception, print
// the error message
exception.printStackTrace();
}
}
// if the execution reaches here, then we have at least
// one command to execute
if (!commands.isEmpty()) {
InteractionCommand command = commands.poll();
if (command != null) {
executeCommand(command);
}
}
}
}
}