InteractionsManager.java
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2013, OSRF.
00003  * Copyright (c) 2013, Yujin Robot.
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
00006  * use this file except in compliance with the License. You may obtain a copy of
00007  * the License at
00008  *
00009  * http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00013  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
00014  * License for the specific language governing permissions and limitations under
00015  * the License.
00016  */
00017 
00018 package com.github.rosjava.android_remocons.common_tools.rocon;
00019 
00020 import android.os.AsyncTask;
00021 import android.util.Log;
00022 
00023 import com.github.rosjava.android_remocons.common_tools.master.MasterId;
00024 
00025 import org.ros.address.InetAddressFactory;
00026 import org.ros.android.NodeMainExecutorService;
00027 import org.ros.exception.RosRuntimeException;
00028 import org.ros.exception.ServiceNotFoundException;
00029 import org.ros.internal.node.client.ParameterClient;
00030 import org.ros.internal.node.server.NodeIdentifier;
00031 import org.ros.namespace.GraphName;
00032 import org.ros.node.AbstractNodeMain;
00033 import org.ros.node.ConnectedNode;
00034 import org.ros.node.Node;
00035 import org.ros.node.NodeConfiguration;
00036 import org.ros.node.service.ServiceClient;
00037 import org.ros.node.service.ServiceResponseListener;
00038 
00039 import java.net.URI;
00040 import java.net.URISyntaxException;
00041 
00042 import rocon_interaction_msgs.GetInteraction;
00043 import rocon_interaction_msgs.GetInteractionRequest;
00044 import rocon_interaction_msgs.GetInteractionResponse;
00045 import rocon_interaction_msgs.GetInteractions;
00046 import rocon_interaction_msgs.GetInteractionsRequest;
00047 import rocon_interaction_msgs.GetInteractionsResponse;
00048 import rocon_interaction_msgs.RequestInteraction;
00049 import rocon_interaction_msgs.RequestInteractionRequest;
00050 import rocon_interaction_msgs.RequestInteractionResponse;
00051 
00052 import static com.github.rosjava.android_remocons.common_tools.rocon.Constants.ANDROID_PLATFORM_INFO;
00053 
00074 public class InteractionsManager extends AbstractNodeMain {
00075     public interface FailureHandler {
00079         void handleFailure(String reason);
00080     }
00081 
00082     public enum Action {
00083         NONE, GET_INTERACTIONS_FOR_ROLE, GET_INTERACTION_INFO, REQUEST_INTERACTION_USE
00084     };
00085 
00086     private String role;
00087     private String interactionsNamespace;
00088     private Action action = Action.NONE;
00089         private rocon_interaction_msgs.Interaction app;
00090     private int app_hash;  // need this separately since headless starts don't have any app info.
00091     private String remocon_name;  // need this separately since headless starts don't have any app info.
00092     private ConnectNodeThread connectThread;
00093     private ConnectedNode connectedNode;
00094     private NodeMainExecutorService nodeMainExecutorService;
00095     private FailureHandler failureCallback;
00096         private ServiceResponseListener<RequestInteractionResponse> requestServiceResponseListener;
00097         private ServiceResponseListener<GetInteractionsResponse>    getAppsServiceResponseListener;
00098     private ServiceResponseListener<GetInteractionResponse>     appInfoServiceResponseListener;
00099 
00100 
00101         public InteractionsManager(FailureHandler failureCallback) {
00102 
00103         this.failureCallback = failureCallback;
00104     }
00105 
00113     public void init(String interactionsNamespace) {
00114         this.interactionsNamespace = interactionsNamespace;
00115     }
00116 
00117     public void setRemoconName(String remocon_name) {
00118         this.remocon_name = remocon_name;
00119     }
00120 
00121 
00122     public void setupRequestService(ServiceResponseListener<RequestInteractionResponse> serviceResponseListener) {
00123         this.requestServiceResponseListener = serviceResponseListener;
00124     }
00125 
00126     public void setupGetInteractionsService(ServiceResponseListener<GetInteractionsResponse> serviceResponseListener) {
00127         this.getAppsServiceResponseListener = serviceResponseListener;
00128     }
00129 
00130     public void setupAppInfoService(ServiceResponseListener<GetInteractionResponse> serviceResponseListener) {
00131         this.appInfoServiceResponseListener = serviceResponseListener;
00132     }
00133 
00134     @Override
00135     protected void finalize() throws Throwable {
00136         super.finalize();
00137         shutdown();  // TODO not warrantied to be called, so user should call shutdown; finalize works at all in Android???  I think no....
00138     }
00139 
00140     public void shutdown() {
00141         if (nodeMainExecutorService != null)
00142             nodeMainExecutorService.shutdownNodeMain(this);
00143         else
00144             Log.i("InteractionsMng", "Shutting down an uninitialized apps manager");
00145     }
00146 
00147     public void getAppsForRole(final MasterId masterId, final String role) {
00148         this.action = Action.GET_INTERACTIONS_FOR_ROLE;
00149         this.role = role;
00150 
00151         // If this is the first action requested, we need a connected node, what must be done in a different thread
00152         // The requested action will be executed once we have a connected node (this object itself) in onStart method
00153         if (this.connectedNode == null) {
00154             Log.i("InteractionsMng", "First action requested (" + this.action + "). Starting node...");
00155             new ConnectNodeThread(masterId).start();
00156         }
00157         else {
00158             // But we don't need all this if we already executed an action, and so have a connected node. Anyway,
00159             // we must execute any action asynchronously to avoid the android.os.NetworkOnMainThreadException
00160             new AsyncTask<Void, Void, Void>() {
00161                 @Override
00162                 protected Void doInBackground(Void... params) {
00163                     getAppsForRole();
00164                     return null;
00165                 }
00166             }.execute();  // TODO: can we use this to incorporate a timeout to service calls?
00167         }
00168     }
00169 
00170     public void requestAppUse(final MasterId masterId, final String role, final rocon_interaction_msgs.Interaction app) {
00171         this.action = Action.REQUEST_INTERACTION_USE;
00172         this.role = role;
00173         this.app  = app;
00174         this.app_hash = app.getHash();
00175 
00176         // If this is the first action requested, we need a connected node, what must be done in a different thread
00177         // The requested action will be executed once we have a connected node (this object itself) in onStart method
00178         if (this.connectedNode == null) {
00179             Log.i("InteractionsMng", "First action requested (" + this.action + "). Starting node...");
00180             new ConnectNodeThread(masterId).start();
00181         }
00182         else {
00183             // But we don't need all this if we already executed an action, and so have a connected node. Anyway,
00184             // we must execute any action asynchronously to avoid the android.os.NetworkOnMainThreadException
00185             new AsyncTask<Void, Void, Void>() {
00186                 @Override
00187                 protected Void doInBackground(Void... params) {
00188                     requestAppUse();
00189                     return null;
00190                 }
00191             }.execute();
00192         }
00193     }
00194 
00203     public void getAppInfo(final MasterId masterId, final int hash) {
00204         this.action = Action.GET_INTERACTION_INFO;
00205         this.app_hash = hash;
00206 
00207         // If this is the first action requested, we need a connected node, what must be done in a different thread
00208         // The requested action will be executed once we have a connected node (this object itself) in onStart method
00209         if (this.connectedNode == null) {
00210             Log.i("InteractionsMng", "First action requested (" + this.action + "). Starting node...");
00211             new ConnectNodeThread(masterId).start();
00212         }
00213         else {
00214             // But we don't need all this if we already executed an action, and so have a connected node. Anyway,
00215             // we must execute any action asynchronously to avoid the android.os.NetworkOnMainThreadException
00216             new AsyncTask<Void, Void, Void>() {
00217                 @Override
00218                 protected Void doInBackground(Void... params) {
00219                     getAppInfo();
00220                     return null;
00221                 }
00222             }.execute();  // TODO: can we use this to incorporate a timeout to service calls?
00223         }
00224     }
00225 
00226     private void getAppsForRole() {
00227         // call get_roles_and_apps concert service
00228         ServiceClient<GetInteractionsRequest, GetInteractionsResponse> srvClient;
00229         String serviceName = this.interactionsNamespace + "/get_interactions";
00230 
00231         try {
00232             Log.i("InteractionsMng", "List interactions service client created [" + serviceName + "]");
00233             srvClient = connectedNode.newServiceClient(serviceName, GetInteractions._TYPE);
00234         } catch (ServiceNotFoundException e) {
00235             Log.i("InteractionsMng", "List interactions service not found [" + serviceName + "]");
00236             throw new RosRuntimeException(e); // TODO we should recover from this calling onFailure on listener
00237         }
00238         final GetInteractionsRequest request = srvClient.newMessage();
00239 
00240         request.getRoles().add(role);
00241         request.setUri(ANDROID_PLATFORM_INFO.getUri());
00242 
00243         srvClient.call(request, getAppsServiceResponseListener);
00244         Log.i("InteractionsMng", "List interactions service call done [" + serviceName + "]");
00245     }
00246 
00247     private void requestAppUse() {
00248         // call request_interaction concert service
00249         ServiceClient<RequestInteractionRequest, RequestInteractionResponse> srvClient;
00250         String serviceName = this.interactionsNamespace + "/request_interaction";
00251         try {
00252             Log.i("InteractionsMng", "Request app service client created [" + serviceName + "]");
00253             srvClient = connectedNode.newServiceClient(serviceName, RequestInteraction._TYPE);
00254         } catch (ServiceNotFoundException e) {
00255             Log.i("InteractionsMng", "Request app service not found [" + serviceName + "]");
00256             throw new RosRuntimeException(e); // TODO we should recover from this calling onFailure on listener
00257         }
00258         final RequestInteractionRequest request = srvClient.newMessage();
00259 
00260         request.setRemocon(this.remocon_name);
00261         request.setHash(this.app.getHash());
00262 
00263         srvClient.call(request, requestServiceResponseListener);
00264         Log.i("InteractionsMng", "Request app service call done [" + serviceName + "]");
00265     }
00266 
00267     private void getAppInfo() {
00268         // call get_app concert service
00269         ServiceClient<GetInteractionRequest, GetInteractionResponse> srvClient;
00270         String serviceName = this.interactionsNamespace + "/get_interaction";
00271         try {
00272             Log.i("InteractionsMng", "Get app info service client created [" + serviceName + "]");
00273             srvClient = connectedNode.newServiceClient(serviceName, GetInteraction._TYPE);
00274         } catch (ServiceNotFoundException e) {
00275             Log.i("InteractionsMng", "Get app info not found [" + serviceName + "]");
00276             throw new RosRuntimeException(e); // TODO we should recover from this calling onFailure on listener
00277         }
00278         final GetInteractionRequest request = srvClient.newMessage();
00279         request.setHash(this.app_hash);
00280 
00281         srvClient.call(request, appInfoServiceResponseListener);
00282         Log.i("InteractionsMng", "Get app info service call done [" + serviceName + "]");
00283     }
00284 
00289     private class ConnectNodeThread extends Thread {
00290         private MasterId masterId;
00291 
00292         public ConnectNodeThread(MasterId masterId) {
00293             this.masterId = masterId;
00294             setDaemon(true);
00295             setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
00296                 @Override
00297                 public void uncaughtException(Thread thread, Throwable ex) {
00298                     failureCallback.handleFailure("exception: " + ex.getMessage());
00299                 }
00300             });
00301         }
00302 
00303         @Override
00304         public void run() {
00305             try {
00306                 URI concertUri = new URI(masterId.getMasterUri());
00307 
00308                 // Check if the master exists by looking for a master parameter
00309                 // getParam throws when it can't find the parameter (DJS: what does it throw?).
00310                 // Could get it to look for a hardcoded rocon parameter for extra guarantees
00311                 // (e.g. /rocon/version) however we'd still have to do some checking below
00312                 // when the info is there but interactions not.
00313                 ParameterClient paramClient = new ParameterClient(
00314                         NodeIdentifier.forNameAndUri("/concert_checker", concertUri.toString()), concertUri);
00315                 String name = (String) paramClient.getParam(GraphName.of("/rosversion")).getResult();
00316                 Log.i("Remocon", "Concert " + name + " found; retrieve additional information");
00317 
00318                 nodeMainExecutorService = new NodeMainExecutorService();
00319                 NodeConfiguration nodeConfiguration = NodeConfiguration.newPublic(
00320                         InetAddressFactory.newNonLoopback().getHostAddress(), concertUri);
00321                 nodeMainExecutorService.execute(InteractionsManager.this, nodeConfiguration.setNodeName("apps_manager_node"));
00322 
00323             } catch (URISyntaxException e) {
00324                 Log.i("InteractionsMng", "invalid concert URI [" + masterId.getMasterUri() + "][" + e.toString() + "]");
00325                 failureCallback.handleFailure("invalid concert URI");
00326             } catch (RuntimeException e) {
00327                 // thrown if concert could not be found in the getParam call (from java.net.ConnectException)
00328                 Log.i("InteractionsMng", "could not find concert [" + masterId.getMasterUri() + "][" + e.toString() + "]");
00329                 failureCallback.handleFailure(e.toString());
00330             } catch (Throwable e) {
00331                 Log.i("InteractionsMng", "exception while creating node in concert checker for URI " + masterId.getMasterUri(), e);
00332                 failureCallback.handleFailure(e.toString());
00333             }
00334         }
00335     }
00336 
00337     @Override
00338         public GraphName getDefaultNodeName() {
00339                 return null;
00340         }
00341 
00347         @Override
00348         public void onStart(final ConnectedNode connectedNode) {
00349         if (this.connectedNode != null) {
00350             Log.e("InteractionsMng", "App manager re-started before previous shutdown; ignoring...");
00351             return;
00352         }
00353 
00354         this.connectedNode = connectedNode;
00355 
00356         Log.i("InteractionsMng", "onStart() - " + action);
00357 
00358         switch (action) {
00359             case NONE:
00360                 Log.i("InteractionsMng", "Node started without specifying an action");
00361                 break;
00362             case REQUEST_INTERACTION_USE:
00363                 requestAppUse();
00364                 break;
00365             case GET_INTERACTIONS_FOR_ROLE:
00366                 getAppsForRole();
00367                 break;
00368             case GET_INTERACTION_INFO:
00369                 getAppInfo();
00370                 break;
00371             default:
00372                 Log.i("InteractionsMng", "Unrecognised action requested: " + action);
00373         }
00374 
00375         Log.i("InteractionsMng", "Done");
00376         }
00377 
00378     @Override
00379     public void onShutdown(Node node) {
00380         Log.i("InteractionsMng", "Shutdown connected node...");
00381         super.onShutdown(node);
00382 
00383         // Required so we get reconnected the next time
00384         this.connectedNode = null;
00385         Log.i("InteractionsMng", "Done; shutdown apps manager node main");
00386     }
00387 
00388     @Override
00389     public void onShutdownComplete(Node node) {
00390         super.onShutdownComplete(node);
00391     }
00392 
00393     @Override
00394     public void onError(Node node, Throwable throwable) {
00395         super.onError(node, throwable);
00396 
00397         Log.e("InteractionsMng", node.getName().toString() + " node error: " + throwable.getMessage());
00398         failureCallback.handleFailure(node.getName().toString() + " node error: " + throwable.toString());
00399     }
00400 }


android_remocons
Author(s): Daniel Stonier, Kazuto Murase
autogenerated on Sat Jun 8 2019 19:32:24