www.gusucode.com > ros 工具箱 matlab源码程序 > ros/+robotics/+ros/+internal/+action/ActionIntrospection.m
classdef ActionIntrospection < ... robotics.ros.internal.mixin.Unsaveable & ... robotics.ros.internal.mixin.NodeDependent & ... robotics.ros.internal.action.IActionIntrospection %This class is for internal use only. It may be removed in the future. %ActionIntrospection Class retrieves information about actions in the ROS network % % ActionIntrospection properties: % MasterURI - The ROS master that is used to query actions % % ActionIntrospection methods: % actionList - Get list of actions in ROS network % actionType - Get type of action in ROS namespace % actionInfo - Get detailed information about action in ROS namespace % infoStructToString - Convert the action information structure to a string % Copyright 2016 The MathWorks, Inc. properties (SetAccess = private) %MasterURI - URI of ROS Master to which node is connected MasterURI = '' end properties (Access = ?matlab.unittest.TestCase) %NetworkIntrospection - Object used for getting information about ROS network NetworkIntrospection = robotics.ros.internal.NetworkIntrospection %Namespace - Object used for manipulating ROS namespaces Namespace = robotics.ros.internal.Namespace end properties (Transient, Access = ?matlab.unittest.TestCase) %PublishedTopicNames - Storage for published topic names PublishedTopicNames %PublishedTopicTypes - Storage for message types of published topic names % The cell array will have the same size as PublishedTopicNames. PublishedTopicTypes end methods function obj = ActionIntrospection(node) %ActionIntrospection Standard Constructor % The object will use the provided ROS node to connect to the % ROS network and get information about actions. % If NODE is empty, the global node will be used. if isempty(node) node = robotics.ros.internal.Global.getNodeHandle(false); end % Construct superclass obj = obj@robotics.ros.internal.mixin.NodeDependent(node); obj.MasterURI = node.MasterURI; end function namespaceList = actionList(obj) %actionList Get list of actions in ROS network % NAMESPACELIST = actionList(OBJ) returns a string array of % namespaces NAMESPACELIST that correspond to actions in the % ROS network. % Store names and message types of all published topics obj.storeTopicNamesTypes; % Extract unique namespaces from topic names allTopicNamespaces = cellfun(@(topicName) obj.Namespace.parentNamespace(topicName), ... obj.PublishedTopicNames, 'UniformOutput', false); uniqueNamespaces = unique(allTopicNamespaces); % Find the namespaces that are associated with actions isActNs = cellfun(@(ns) obj.isActionNamespace(ns), uniqueNamespaces); % Return list of action namespaces. The list will be sorted, % since unique sorts automatically. namespaceList = uniqueNamespaces(isActNs); end function actType = actionType(obj, nameSpace) %actionType Get type of action in ROS namespace % ACTTYPE = actionType(OBJ, NAMESPACE) retrieves the action % type, ACTTYPE, for an action in the ROS NAMESPACE. % If NAMESPACE does not correspond to an action, and error will % be displayed. % Store names and message types of all published topics obj.storeTopicNamesTypes; % Verify that this is an action namespace and retrieve action type [isAction, actType] = isActionNamespace(obj, nameSpace); % Display an error if the namespace does not refer to an action assert(isAction, message('robotics:ros:action:NotActionNamespace', nameSpace)); end function infoStruct = actionInfo(obj, nameSpace) %actionInfo Get detailed information about action in ROS namespace % INFOSTRUCT = actionInfo(OBJ, NAMESPACE) retrieves a % structure containing detailed information about the action. % INFOSTRUCT is a structure containing the following fields: % - ActionType: Type of the action % - GoalMessageType: Message type of goal messages sent % by the action client % - FeedbackMessageType: Message type of feedback messages % sent by the action server. % - ResultMessageType: Message type of result messages % sent by the action server. % - ActionServer: Structure with information about the % action server. % - ActionClients: Structure array with information about % all registered action clients. % % The structure for 'ActionServer' and 'ActionClients' has % the following fields: % - NodeName: Name of the node containing the action server or client % - URI: The URI of the node containing the action server or client % Store names and message types of all published topics obj.storeTopicNamesTypes; % Pre-allocate return structure infoStruct = struct('ActionType', '', ... 'GoalMessageType', '', ... 'FeedbackMessageType', '', ... 'ResultMessageType', '', ... 'ActionServer', struct.empty(0,1), ... 'ActionClients', struct.empty(0,1) ... ); % Verify that this is an action namespace and retrieve action type [isAction, infoStruct.ActionType] = isActionNamespace(obj, nameSpace); % Display an error if the namespace does not refer to an action assert(isAction, message('robotics:ros:action:NotActionNamespace', nameSpace)); % Construct expected message types for goal, feedback, and result infoStruct.GoalMessageType = [infoStruct.ActionType 'Goal']; infoStruct.FeedbackMessageType = [infoStruct.ActionType 'Feedback']; infoStruct.ResultMessageType = [infoStruct.ActionType 'Result']; % Get detailed information about action topics. We are % interested in active publishers and subscribers to % determine the list of action clients and servers. % We verified above that this is a valid action namespace, so % all topics should exist nameSpace = obj.JavaNode.resolveName(nameSpace); goalInfo = obj.NetworkIntrospection.getPublishedTopicInfo(... char(nameSpace.join('goal')), false, obj.MasterURI); cancelInfo = obj.NetworkIntrospection.getPublishedTopicInfo(... char(nameSpace.join('cancel')), false, obj.MasterURI); statusInfo = obj.NetworkIntrospection.getPublishedTopicInfo(... char(nameSpace.join('status')), false, obj.MasterURI); resultInfo = obj.NetworkIntrospection.getPublishedTopicInfo(... char(nameSpace.join('result')), false, obj.MasterURI); feedbackInfo = obj.NetworkIntrospection.getPublishedTopicInfo(... char(nameSpace.join('feedback')), false, obj.MasterURI); % Action server subscribes to goal/cancel, and publishes status/result/feedback if isempty(goalInfo.Subscribers) || isempty(cancelInfo.Subscribers) || ... isempty(statusInfo.Publishers) || isempty(resultInfo.Publishers) || isempty(feedbackInfo.Publishers) actServerNodeNames = {}; else actServerNodeNames = obj.intersectAll({goalInfo.Subscribers.NodeName}, {cancelInfo.Subscribers.NodeName}, ... {statusInfo.Publishers.NodeName}, {resultInfo.Publishers.NodeName}, {feedbackInfo.Publishers.NodeName}); end % Actionlib was designed to only have a single action server per namespace, % but this code can handle the degenerate condition of multiple % action servers. infoStruct.ActionServer = obj.populateNodeInfo(actServerNodeNames); % Action client subscribes to status/result/feedback, and publishes goal/cancel if isempty(goalInfo.Publishers) || isempty(cancelInfo.Publishers) || ... isempty(statusInfo.Subscribers) || isempty(resultInfo.Subscribers) || isempty(feedbackInfo.Subscribers) actClientNodeNames = {}; else actClientNodeNames = obj.intersectAll({goalInfo.Publishers.NodeName}, {cancelInfo.Publishers.NodeName}, ... {statusInfo.Subscribers.NodeName}, {resultInfo.Subscribers.NodeName}, {feedbackInfo.Subscribers.NodeName}); end % Populate information structure with information for each action client infoStruct.ActionClients = obj.populateNodeInfo(actClientNodeNames); end function infoString = infoStructToString(~, infoStruct) %infoStructToString Convert the action information structure to a string % This string can be used for displaying the information. % % Example string output for one action server and two action % clients: % % >> rosaction info /fibonacci % ActionType: actionlib_tutorials/Fibonacci % % GoalMessageType: actionlib_tutorials/FibonacciGoal % FeedbackMessageType: actionlib_tutorials/FibonacciFeedback % ResultMessageType: actionlib_tutorials/FibonacciResult % % Action Server: % * /fibonacci (http://172.28.194.77:38647/) % % Action Clients: None % * /node_1 (http://AH-RPILLAT:50986/) % * /some_other_node_name (http://AH-RPILLAT:50994/) infoWriter = StringWriter; % Print action type infoWriter.addcr([message('robotics:ros:action:InfoType').getString ': ' infoStruct.ActionType]); infoWriter.addcr; % Print message types infoWriter.addcr([message('robotics:ros:action:InfoGoalType').getString ': ' infoStruct.GoalMessageType]); infoWriter.addcr([message('robotics:ros:action:InfoFeedbackType').getString ': ' infoStruct.FeedbackMessageType]); infoWriter.addcr([message('robotics:ros:action:InfoResultType').getString ': ' infoStruct.ResultMessageType]); infoWriter.addcr; % Print information about action server(s) infoWriter.add([message('robotics:ros:action:InfoServer').getString ':']); if isempty(infoStruct.ActionServer) infoWriter.add([' ' message('robotics:ros:action:InfoNone').getString]); end infoWriter.addcr; for i = 1:numel(infoStruct.ActionServer) infoWriter.addcr(['* ' infoStruct.ActionServer(i).NodeName ... ' (' infoStruct.ActionServer(i).URI ')']); end infoWriter.addcr; % Print information about action clients infoWriter.add([message('robotics:ros:action:InfoClients').getString ':']); if isempty(infoStruct.ActionClients) infoWriter.add([' ' message('robotics:ros:action:InfoNone').getString]); end infoWriter.addcr; for i = 1:numel(infoStruct.ActionClients) infoWriter.addcr(['* ' infoStruct.ActionClients(i).NodeName ... ' (' infoStruct.ActionClients(i).URI ')']); end % Return complete string infoString = infoWriter.string; end end methods (Access = ?matlab.unittest.TestCase) function [isAction, actionType] = isActionNamespace(obj, nameSpace) %isActionNamespace Determine if given namespace contains action topics % ISACTION = isActionNamespace(OBJ, NAMESPACE) returns true % if the given ROS NAMESPACE contains the topic associated % with an action. % [ISACTION, ACTIONTYPE} = ____ also returns the type of % action in this NAMESPACE. ACTIONTYPE is empty if ISACTION % is false. % Resolve absolute name through node. This is a Java GraphName % object. nameSpace = obj.JavaNode.resolveName(nameSpace); isAction = false; actionType = ''; % If any exception occurs, this is not an action namespace try % For the goal, feedback, and result topics, we can extract the % common action type actionTypes = cell(3,1); % Verify that the goal, feedback, and result topics exists % and that they have the right message type actionTypes{1} = obj.getTypeWithoutSuffix(char(nameSpace.join('goal')), 'ActionGoal'); actionTypes{2} = obj.getTypeWithoutSuffix(char(nameSpace.join('feedback')), 'ActionFeedback'); actionTypes{3} = obj.getTypeWithoutSuffix(char(nameSpace.join('result')), 'ActionResult'); % The action type (without suffix) should be the same % across the three topics. If not, this is not an action. uniqueTypes = unique(actionTypes); if length(uniqueTypes) ~= 1 return; end % Also check the cancel and status topics, We can ignore % the output, since we are only interested in verifying the % message type. obj.getTypeWithoutSuffix(char(nameSpace.join('cancel')), 'actionlib_msgs/GoalID'); obj.getTypeWithoutSuffix(char(nameSpace.join('status')), 'actionlib_msgs/GoalStatusArray'); catch % If any exception occurs, return false return; end % All checks passed, so this is an action namespace. isAction = true; actionType = uniqueTypes{1}; end function actionType = getTypeWithoutSuffix(obj, topicName, expectedSuffix) %getTypeWithoutSuffix Remove suffix from topic type % ACTIONTYPE = getTypeWithoutSuffix(OBJ, TOPICNAME, SUFFIX) % retrieves the message type of the topic with TOPICNAME. The % the type ends in SUFFIX, remove the suffix and return the % resulting string in ACTIONTYPE. % % An error is displayed if the topic with TOPICNAME does not % exist, or if it does not end in SUFFIX. % Get message type for topic. This will throw an error if % the topic with the given name does not exist. [topicExists, topicIdx] = ismember(topicName, obj.PublishedTopicNames); assert(topicExists, message('robotics:ros:topic:TopicNameNotFound', topicName)); msgType = obj.PublishedTopicTypes{topicIdx}; % Make sure that message type ends with the expected suffix. % Throw an error otherwise. assert(robotics.ros.internal.Parsing.endsWith(msgType, expectedSuffix), ... message('robotics:ros:action:UnexpectedSuffix', msgType, expectedSuffix)); actionType = msgType(1:end-length(expectedSuffix)); if isempty(actionType) % If complete string is removed, make sure that return data % type is a string. actionType = ''; end end function storeTopicNamesTypes(obj) %storeTopicNamesTypes Store published topic names and associated message types % This object data will be used by other internal functions. [obj.PublishedTopicNames, obj.PublishedTopicTypes] = obj.NetworkIntrospection.getPublishedTopicNamesTypes(obj.MasterURI); end end methods (Access = private) function commonElements = intersectAll(~, varargin) %intersectAll Calculate set intersection of multiple entities % COMMONELEMENTS = intersectAll(OBJ, VARARGIN) calculates the % set intersection of all entities passed with VARARGIN. It % returns the common entity elements in COMMONELEMENTS. entities = varargin; if length(entities) < 2 commonElements = entities; return; else commonElements = entities{1}; end for i = 2:length(entities) commonElements = intersect(commonElements, entities{i}); end end function nodeInfoStruct = populateNodeInfo(obj, nodeNames) %populateNodeInfo Create info structure for ROS nodes % NODEINFOSTRUCT = populateNodeInfo(OBJ, NODENAMES) creates a % structure array of node information. Each node in NODENAMES % creates a separate structure in the array. % Each structure has the following fields: % - NodeName - Name of the node % - URI - The URI of the node nodeInfoStruct = struct.empty(0,1); % Return empty structure if no node names specified if isempty(nodeNames) return; end % Create structure array for all node names. Retrieve node URI % for each node name. numNodes = length(nodeNames); nodeInfoStruct = repmat(struct('NodeName', '', 'URI', ''), numNodes, 1); for i = 1:numNodes nodeInfoStruct(i).NodeName = nodeNames{i}; nodeInfoStruct(i).URI = obj.NetworkIntrospection.getNodeURI(nodeNames{i}, obj.MasterURI); end end end methods (Access = protected) function onNodeShutdownComplete(obj, ~, ~) %onNodeShutdownComplete Called when the Java node finishes shut down % This function overrides the function definition in % robotics.ros.internal.mixin.NodeDependent % This callback will be triggered when the node that this % object is attached to shuts down. In this case, the % handle object should be deleted, so it cannot be % used in the workspace and is clearly marked as % non-functional. obj.delete; end end end