www.gusucode.com > robotsimulink 工具箱 matlab源码程序 > robotsimulink/robotslros/+robotics/+slros/+internal/+block/Subscriber.m
classdef Subscriber < matlab.System & matlab.system.mixin.Propagates %This class is for internal use only. It may be removed in the future. %Subscribe to a topic on a ROS network % % H = robotics.slros.internal.block.Subscriber creates a system % object, H, that subscribes to a topic on a ROS network and % receives messages on that topic. % % This system object is intended for use with the MATLAB System % block. In order to access the ROS functionality from MATLAB, see % ROSSUBSCRIBER. % % See also robotics.slros.internal.block.Publisher % Copyright 2014-2016 The MathWorks, Inc. %#codegen properties (Nontunable) %ROSTopic Topic to subscribe to % This sytem object will use ROSTopic as specified in both % simulation and code generation. In particular, it will not add a % "/" in front of topic, as that forces the topic to be in the % absolute namespace. ROSTopic = '/my_topic' %ROSMessageType Message type of topic % e.g., 'std_msgs/Int32' ROSMessageType = 'std_msgs/Int32' %MessageQueueLen Length of subscribe callback queue % Only used in generated code MessageQueueLen = 1 end % The following should ideally not show up in the MATLAB System block % dialog. However, setting them as 'Hidden' will prevent them from % being accessible via set_param & get_param. % % ModelName is needed for managing the node instance % BlockId is needed to generate a unique identifier in codegen properties(Nontunable) %SLBusName Simulink Bus Name for message type SLBusName = 'SL_Bus' %ModelName Name of Simulink model % Used for managing node instance ModelName = 'untitled' %BlockId Simulink Block Identifier % Used to generate unique identifier for the block during code % generation. This should be obtained using Simulink.ID.getSID() % on the library block (*not* the MATLAB system block). The % SID has the format '<modelName>:<blocknum>' BlockId = 'sub1' %InputStreamWSVar Receive message from variable in base worksace % Used to redirect Subscriber to receive messages from a workspace % variable instead of from ROS network (used for testing only). % The workspace variable should be created like this: % % workspaceVar = robotics.slros.internal.sim.MsgListInputStream(rostopic, msgtype); % workspaceVar.setMessageList( {msg1, msg2, msg3} ); % % This capability of using workspace variables is only used in % internal testing, and is not documented for Simulink users. InputStreamWsVar = '' end properties(Constant,Access=private) % Name of header file with declarations for variables and types % referred to in code emitted by setupImpl and stepImpl. HeaderFile = robotics.slros.internal.cgen.Constants.InitCode.HeaderFile end properties (Access=private, Transient) % Converter - Handle to object that encapsulats converting a MATLAB ROS % message to a Simulink bus struct. It is initialized to indicate % the class of the object Converter = robotics.slros.internal.sim.ROSMsgToBusStructConverter.empty % InputStream - Handle to object that implements robotics.slros.internal.sim.MsgInputStream % This can be a sim.MsgNetInputStream (for getting messages off % the ROS network) or sim.MsgListInputStream (for getting messages % from an object in the base workspace (used in conjunction with % InputStreamWsVar property) InputStream % LastConvertedMsg - Handle to last received "new" message LastConvertedMsg = [] % ROSMaster - Handle to an object that encapsulates interaction % with the ROS master. It is initialized to indicate the class of % the object. ROSMaster = robotics.slros.internal.sim.ROSMaster.empty end methods function obj = Subscriber(varargin) % Enable code to be generated even this file is p-coded coder.allowpcode('plain'); % Support name-value pair arguments when constructing the object. setProperties(obj,nargin,varargin{:}); end function set.ROSTopic(obj, val) validateattributes(val, {'char'}, {'nonempty'}, '', 'ROSTopic'); if coder.target('MATLAB') robotics.ros.internal.Namespace.canonicalizeName(val); % throws error end obj.ROSTopic = val; end function set.ROSMessageType(obj, val) validateattributes(val, {'char'}, {'nonempty'}, '', 'ROSMessageType'); if coder.target('MATLAB') robotics.ros.internal.Namespace.canonicalizeName(val); % throws error end obj.ROSMessageType = val; end function set.MessageQueueLen(obj, val) validateattributes(val, ... {'numeric'}, {'positive', 'integer', 'scalar'}, '', 'MessageQueueLen'); obj.MessageQueueLen = int32(val); end function set.SLBusName(obj, val) validateattributes(val, {'char'}, {'nonempty'}, '', 'SLBusName'); obj.SLBusName = val; end function set.ModelName(obj, val) validateattributes(val, {'char'}, {'nonempty'}, '', 'ModelName'); obj.ModelName = val; end function set.BlockId(obj, val) validateattributes(val, {'char'}, {'nonempty'}, '', 'BlockId'); obj.BlockId = val; end function set.InputStreamWsVar(obj, val) validateattributes(val, {'char'}, {}, '', 'InputStreamWsVar'); obj.InputStreamWsVar = val; end end methods (Access = protected) function num = getNumInputsImpl(~) num = 1; end function num = getNumOutputsImpl(~) num = 2; end function varargout = getOutputSizeImpl(~) varargout = {[1 1], [1 1]}; end function varargout = isOutputFixedSizeImpl(~) varargout = {true, true}; end function varargout = getOutputDataTypeImpl(obj) varargout = {'logical', obj.SLBusName}; end function varargout = isOutputComplexImpl(~) varargout = {false, false}; end end methods (Access = protected) %% Common functions function out = constantSampleTimePropagationImpl(~) % Enable this system object to inherit constant ('inf') sample times out = 'Allow'; end % We don't save SimState, since there is no way save & restore % the Subscriber object. However, saveObjectImpl and x`Impl % are required since we have private properties. function s = saveObjectImpl(obj) % The errorIf() below will ensure that FastRestart cannot be used % in a model with ROS blocks coder.internal.errorIf(true, 'robotics:robotslros:sysobj:SubscriberSimStateNotSupported'); s = saveObjectImpl@matlab.System(obj); end function loadObjectImpl(obj,s,wasLocked) loadObjectImpl@matlab.System(obj,s,wasLocked); end %% function setupImpl(obj) % setupImpl is called when model is being initialized at the % start of a simulation if coder.target('MATLAB') % Executing in MATLAB interpreted mode modelHasPriorState = robotics.slros.internal.sim.ModelStateManager.hasState(obj.ModelName); nodeRefCountIncremented = false; try %#ok<EMTC> modelState = robotics.slros.internal.sim.ModelStateManager.getState(obj.ModelName, 'create'); % The following could be a separate method, but system % object infrastructure doesn't appear to allow it %-- if ~isempty(obj.InputStreamWsVar) % Use a variable in MATLAB Workspace as source of messages. obj.InputStream = evalin('base', obj.InputStreamWsVar); validateattributes(obj.InputStream, {'robotics.slros.internal.sim.MsgListInputStream'}, {'scalar'}); else % Use ROS network as a source of messages. if isempty(modelState.ROSNode) || ~isvalid(modelState.ROSNode) obj.ROSMaster = robotics.slros.internal.sim.ROSMaster(); % verifyReachable() errors if ROS master is unreachable obj.ROSMaster.verifyReachable(); % createNode() errors if unable to create node % (e.g., if node with same name already exists) uniqueName = obj.ROSMaster.makeUniqueName(obj.ModelName); modelState.ROSNode = obj.ROSMaster.createNode(uniqueName); end obj.InputStream = robotics.slros.internal.sim.MsgNetInputStream(... obj.ROSTopic, obj.ROSMessageType, modelState.ROSNode); end %-- modelState.incrNodeRefCount(); nodeRefCountIncremented = true; obj.Converter = robotics.slros.internal.sim.ROSMsgToBusStructConverter(... obj.ROSMessageType, obj.ModelName); catch ME if nodeRefCountIncremented modelState.decrNodeRefCount(); end obj.InputStream = []; if ~modelHasPriorState || ~modelState.nodeHasReferrers() robotics.slros.internal.sim.ModelStateManager.clearState(obj.ModelName); end % RETHROW will generate a hard-to-read stack trace, so % use THROW instead. throw(ME); end elseif coder.target('RtwForRapid') % Rapid Accelerator. In this mode, coder.target('Rtw') % returns true as well, so it is important to check for % 'RtwForRapid' before checking for 'Rtw' coder.internal.errorIf(true, 'robotics:robotslros:sysobj:SubscriberRapidAccelNotSupported'); elseif coder.target('Rtw') coder.cinclude(obj.HeaderFile); % Append 0 to obj.ROSTopic, since MATLAB doesn't % automatically zero-terminate strings in generated code zeroDelimTopic = [obj.ROSTopic 0]; coder.ceval([obj.BlockId '.createSubscriber'], ... zeroDelimTopic, obj.MessageQueueLen); elseif coder.target('Sfun') % 'Sfun' - SimThruCodeGen target % Do nothing. MATLAB System block first does a pre-codegen % compile with 'Sfun' target, & then does the "proper" % codegen compile with Rtw or RtwForRapid, as appropriate. else % 'RtwForSim' - ModelReference SIM target % 'MEX', 'HDL', 'Custom' - Not applicable to MATLAB System block coder.internal.errorIf(true, 'robotics:robotslros:sysobj:UnsupportedCodegenMode'); end end %% function [isNewData, msg] = stepImpl(obj, busstruct) % <busstruct> is a blank (empty) bus structure. It is necessary % since there is no convenient way to create the (arbitrarily % complex and nested) bus structure. isNewData = false; % If isNewData is true, msg holds the new message (SL bus layout). % % If isNewData is false, msg is unmodified from busstruct % (i.e., it is an empty bus). The rationale is that we want % to avoid overhead of converting a message to bus. % In this case, MATLAB system block output needs to be % latched, so that the user sees the most-recent-valid % message. (This latching could have been done inside % get_latest_msg, but it is better to let Simulink do it, as % that allows it to generate more optimized code). if coder.target('MATLAB') [isNewData, rosmsg] = obj.InputStream.getLatestMessage(); if isNewData || isempty(obj.LastConvertedMsg) msg = obj.Converter.convert(rosmsg); obj.LastConvertedMsg = msg; else % There is no performance saving to assigning busstruct % instead of obj.LastConvertedMsg, but we do so for % compatibility with codegen behavior. msg = busstruct; end else % coder.target('Rtw') msg = coder.nullcopy(busstruct); isNewData = coder.ceval([obj.BlockId '.getLatestMessage'], coder.wref(msg)); end end %% function releaseImpl(obj) if coder.target('MATLAB') % We need to release all the InputStreams (which hold the % Subscriber) before releasing the node. If the order is % reversed, the Subscriber tries to connect to the master % to unregister & waits for this operation to succeed (w/ a % time out), which causes a delay during model termination. st = robotics.slros.internal.sim.ModelStateManager.getState(obj.ModelName); st.decrNodeRefCount(); % This following command takes ~0.5s, due to a blocking % wait in the destructor for ros.Subscriber obj.InputStream = []; % release publisher if ~st.nodeHasReferrers() robotics.slros.internal.sim.ModelStateManager.clearState(obj.ModelName); end else % coder.target('Rtw') % Nothing to do. The system object will never be released % in the generated node. end end end end