/*
* Copyright (C) 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.ros.osgi.common;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.logging.Log;
import org.ros.concurrent.DefaultScheduledExecutorService;
import org.ros.exception.RosRuntimeException;
import org.ros.master.uri.MasterUriProvider;
import org.ros.namespace.GraphName;
import org.ros.namespace.NameResolver;
import org.ros.node.ConnectedNode;
import org.ros.node.DefaultNodeFactory;
import org.ros.node.DefaultNodeMainExecutor;
import org.ros.node.Node;
import org.ros.node.NodeConfiguration;
import org.ros.node.NodeFactory;
import org.ros.node.NodeListener;
import org.ros.node.NodeMain;
import org.ros.node.NodeMainExecutor;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* A very basic ROS environment.
*
* @author Keith M. Hughes
*/
public class SimpleRosEnvironment implements RosEnvironment {
/**
* Node runner for this environment.
*/
private NodeMainExecutor nodeRunner;
/**
* Executor service used for running the ROS threads.
*/
private ScheduledExecutorService executorService;
/**
* {@code true} is this instance owns the executor service.
*/
private boolean ownExecutorService;
/**
* Host that this environment is running on.
*/
private String host = "localhost";
/**
* Host the master is running on.
*/
private URI masterUri = NodeConfiguration.DEFAULT_MASTER_URI;
/**
* Overall name for the node.
*/
private String nodeName;
/**
* The network type for the ROS graph.
*
* <p>
* This allows distinguishing between ROS Masters, e.g. localdev, prod,
* fredbot.
*/
private String networkType;
/**
* The container logger.
*/
private Log log;
/**
* The properties associated with this environment.
*/
private final Map<String, String> properties = Maps.newHashMap();
/**
* {@code true} if this is an environment for masters, false otherwise.
*/
private boolean master;
/**
* A provider for ROS Master URIs.
*
* <p>
* This can be null.
*/
private MasterUriProvider masterUriProvider;
/**
* The node factory for creating ROS nodes.
*/
private NodeFactory nodeFactory;
/**
* Start up the ROS environment.
*/
public void startup() {
// Get the URI of the master.
String masterUri = getProperty(CONFIGURATION_ROS_MASTER_URI);
if (masterUri != null) {
try {
setMasterUri(new URI(masterUri));
} catch (URISyntaxException e) {
throw new RosRuntimeException("Cannot start ros environment. Illegal master URI: "
+ masterUri, e);
}
}
// Set the host running the node.
String host = getProperty(CONFIGURATION_ROS_HOST);
if (host != null)
setHost(host);
// Set the name of the node.
String nodeName = getProperty(CONFIGURATION_ROS_NODE_NAME);
if (nodeName != null)
setNodeName(nodeName);
String networkType = getProperty(CONFIGURATION_ROS_NETWORK_TYPE);
if (networkType != null)
setNetworkType(networkType);
if (executorService == null) {
executorService = new DefaultScheduledExecutorService();
ownExecutorService = true;
}
nodeRunner = DefaultNodeMainExecutor.newDefault(executorService);
nodeFactory = new DefaultNodeFactory(executorService);
}
/**
* Shut the ROS environment down.
*/
public void shutdown() {
nodeRunner.shutdown();
if (ownExecutorService) {
executorService.shutdown();
}
}
@Override
public ConnectedNode newNode(NodeConfiguration configuration) {
// TODO(ROS): Part of ROS update
// return nodeFactory.newNode(configuration, true);
return newNode(configuration, null);
}
@Override
public ConnectedNode newNode(NodeConfiguration configuration, Collection<NodeListener> listeners) {
// TODO(ROS): Part of ROS update
// return nodeFactory.newNode(configuration, listeners, true);
final AtomicReference<ConnectedNode> node = new AtomicReference<ConnectedNode>();
final CountDownLatch registeredLatch = new CountDownLatch(1);
NodeListener registrationListener = new NodeListener() {
@Override
public void onStart(ConnectedNode connectedNode) {
node.set(connectedNode);
registeredLatch.countDown();
}
@Override
public void onShutdownComplete(Node node) {
log.info(String.format("Shut down complete for node %s", node.getName()));
}
@Override
public void onShutdown(Node node) {
// Nothing to do
}
@Override
public void onError(Node node, Throwable throwable) {
log.error(String.format("Error for node %s", node.getName()), throwable);
}
};
List<NodeListener> fullListeners = Lists.newArrayList(registrationListener);
if (listeners != null) {
fullListeners.addAll(listeners);
}
Node bareNode = nodeFactory.newNode(configuration, fullListeners);
try {
if (registeredLatch.await(10000, TimeUnit.MILLISECONDS)) {
return node.get();
} else {
throw new RuntimeException(String.format("No registration connection made for ROS node %s",
bareNode.getName()));
}
} catch (InterruptedException e) {
return null;
}
}
@Override
public void executeNodeMain(NodeMain nodeMain, NodeConfiguration configuration) {
try {
nodeRunner.execute(nodeMain, configuration);
} catch (Exception e) {
log.error("Could not run node main", e);
}
}
@Override
public NodeConfiguration getPublicNodeConfiguration() {
if (masterUriProvider != null) {
NodeConfiguration conf = NodeConfiguration.newPublic(host, masterUriProvider.getMasterUri());
conf.setLog(log);
return conf;
} else if (masterUri != null) {
NodeConfiguration conf = NodeConfiguration.newPublic(host, masterUri);
conf.setLog(log);
return conf;
} else {
throw new RosRuntimeException("No ROS Master URI available");
}
}
@Override
public NodeConfiguration getPublicNodeConfigurationWithNodeName() {
NodeConfiguration configuration = getPublicNodeConfiguration();
configuration.setParentResolver(new NameResolver(GraphName.of(getNodeName()),
new HashMap<GraphName, GraphName>()));
return configuration;
}
@Override
public NodeConfiguration getPublicNodeConfigurationWithNodeName(String subname) {
NodeConfiguration configuration = getPublicNodeConfiguration();
configuration.setParentResolver(new NameResolver(GraphName.of(getNodeName() + "/" + subname),
new HashMap<GraphName, GraphName>()));
return configuration;
}
@Override
public NodeConfiguration getPrivateNodeConfiguration() {
NodeConfiguration configuration = NodeConfiguration.newPrivate(masterUri);
configuration.setLog(log);
return configuration;
}
@Override
public String getNodeName() {
return nodeName;
}
@Override
public String getHost() {
return host;
}
@Override
public URI getMasterUri() {
if (masterUriProvider != null) {
return masterUriProvider.getMasterUri();
} else {
return masterUri;
}
}
@Override
public String getNetworkType() {
return networkType;
}
@Override
public ScheduledExecutorService getExecutorService() {
return executorService;
}
@Override
public Log getLog() {
return log;
}
/**
* Set the root name of the node.
*
* @param nodeName
* The root name of the node.
*/
public void setNodeName(String nodeName) {
this.nodeName = nodeName;
}
/**
* Set the URI of the ROS Master.
*
* @param masterUri
* The URI of the ROS Master.
*/
public void setMasterUri(URI masterUri) {
this.masterUri = masterUri;
}
/**
* Set the host the environment is running on. This determines what network
* interface is bound to.
*
* @param host
* The host.
*/
public void setHost(String host) {
this.host = host;
}
/**
* The network type for the ROS graph.
*
* <p>
* This allows distinguishing between ROS Masters, e.g. localdev, prod,
* fredbot.
*
* @param networkType
*/
public void setNetworkType(String networkType) {
this.networkType = networkType;
}
/**
* @param executorService
* the executorService to set
*/
public void setExecutorService(ScheduledExecutorService executorService) {
this.executorService = executorService;
this.ownExecutorService = false;
}
/**
* @param log
* the log to set
*/
public void setLog(Log log) {
this.log = log;
}
@Override
public String getProperty(String property) {
return properties.get(property);
}
@Override
public void setProperty(String property, String value) {
properties.put(property, value);
}
@Override
public void setProperties(Map<String, String> properties) {
properties.putAll(properties);
}
@Override
public void setMasterUriProvider(MasterUriProvider masterUriProvider) {
this.masterUriProvider = masterUriProvider;
}
@Override
public boolean isMaster() {
return master;
}
/**
* Set whether this is a master environment or not.
*
* @param master
* {@code true} if it is a master environment.
*/
public void setMaster(boolean master) {
this.master = master;
}
}