www.gusucode.com > robotsimulink 工具箱 matlab源码程序 > robotsimulink/robotslros/+robotics/+slros/+internal/+bus/VarlenArraySizeStore.m

    classdef VarlenArraySizeStore < handle
    %This class is for internal use only. It may be removed in the future.
    
    %  VarlenArraySizeStore is an interface for managing variable-length
    %  array information for a single Simulink model (this information is
    %  stored with the model in the Model Workspace).
    %
    %  Methods:
    %    updateModel      - Update model workspace with modified array-size
    %                       info (dirties the model but does not save it)
    %
    %    getMsgTypeInfo   - Get array-size info for a ROS message type
    %    setMsgTypeInfo   - Set array-size info for a ROS message type
    %    resetMsgTypeInfo - Clear array-size info for a ROS message type
    %    
    %    getTruncateAction - Get truncate action for the model
    %    setTruncateAction - Set truncate action for the model
    %
    %    applyMaxLengths  -  Apply stored array-size limits for a message 
    %                        (if they exist), else apply defaults
    %   
    %  Static Methods:   
    %    getDefaultTruncateAction - Get default truncate action
    %    applyDefaultMaxLengths  - Apply default array-size limits to a
    %                              message
    %
    %  See also: bus.VarLenArrayInfo
    
    %   Copyright 2014 The MathWorks, Inc.       

    properties(Constant)
        DefaultPrimitiveArrayMaxLen = 128
        DefaultMessageArrayMaxLen = 16
    end

    properties(Constant, Access=private)
        WorkspaceVarName = 'SL_ROS_VariableLengthArrays_MaxSizes'
    end
    
    
    properties(SetAccess=private)
        ModelWorkspace
        DataIsModified = false
        ModelWSListener 
        
        ModelName
        MsgTypeMap
        CommonInfo
    end
    
    
    methods
        function obj = VarlenArraySizeStore(model)
            % No point in constructing without a model
            % we would have to keep checking for validity of Map
            
            validateattributes(model, {'char'}, {'nonempty'});
            
            % bdIsLoaded() can error if model is not a properly formatted name
            % The error message (Simulink:utility:InvalidBlockDiagramName)
            % is informative, so no need to wrap it
            
            if ~bdIsLoaded(model)
                % We could use load_system, but it's not clear when we
                % would unload (and there would be no indication to use
                % that the model is loaded). Cleaner to let the user
                % manage the load/unload
                error(message('robotics:robotslros:arraysizestore:UnableToLoadModel', model));
            end
            
            try
                obj.ModelWorkspace = get_param(model,'ModelWorkspace');
            catch 
                % Model does not have a ModelWorkspace param (most likely 
                % old-style .mdl file).
                error(message('robotics:robotslros:arraysizestore:SLXFormatRequired', model));
            end
            
            if isempty(obj.ModelWorkspace)
               % Most likely, the model is a library.                
               if strcmpi(get_param(bdroot(model), 'BlockDiagramType'), 'library')
                   error(message('robotics:robotslros:arraysizestore:LibraryNotAllowed', model));
               else
                   error(message('robotics:robotslros:arraysizestore:UnableToLoadWorkspace', model));
               end
            end
            
            obj.ModelName = bdroot(model);
            obj.ModelWSListener = handle.listener(obj.ModelWorkspace, 'ObjectBeingDestroyed', @(~,~) delete(obj));            
            obj.loadDataFromWorkspace();
        end        
        
        
        function updateModel(obj)
            if ~ishandle(obj.ModelWorkspace)
                error(message('robotics:robotslros:arraysizestore:ModelNoLongerLoaded', obj.ModelName));
            end
            
            if obj.DataIsModified
                obj.saveDataToWorkspace();            
                % ModelWorkspace.saveToModel() cannot be called if source is
                % 'Model File'. Just set the dirty bit and let the user decide
                % whether to save the model or not
                owningModel = obj.ModelWorkspace.ownerName;
                set_param(owningModel,'Dirty','on');
                
                % Clear the workspace bus variables (so that
                % subsequent simulation runs get updated sizes)
                robotics.slros.internal.bus.Util.clearSLBusesInBaseWorkspace();
            end
        end
        

        function arrayinfo = getMsgTypeInfo(obj, msgType)
            if obj.MsgTypeMap.isKey(msgType)
                msgtypeInfo = obj.MsgTypeMap(msgType);
                % resuscitate the VarLenArrayInfo object
                arrayinfo = robotics.slros.internal.bus.VarLenArrayInfo(msgType);
                arrayinfo.setPropsStruct(msgtypeInfo);
            else
                arrayinfo = [];
            end
        end
        
        
        function setMsgTypeInfo(obj, msgType, arrayinfo)
            
            validateattributes(msgType, {'char'}, {'nonempty'});
            validateattributes(arrayinfo, {'robotics.slros.internal.bus.VarLenArrayInfo'}, {'scalar'});

            % Don't store as handle array (this is an internal class,
            % whereas this information will be saved with model, and can
            % persist across releases)            
            obj.MsgTypeMap(msgType) = arrayinfo.getPropsStruct();
            obj.DataIsModified = true;            
        end

        
        function resetMsgTypeInfo(obj, msgType)
           if obj.MsgTypeMap.isKey(msgType)
              obj.MsgTypeMap.remove(msgType);
              obj.DataIsModified = true; 
           end
        end
        
                
        function out = getTruncateAction(obj)
            out = obj.CommonInfo.ArrayTruncateAction;
        end
        
        
        function setTruncateAction(obj, action)
            validateattributes(action, ...
                {'robotics.slros.internal.bus.VarLenArrayTruncationAction'}, {'scalar'});

            if obj.CommonInfo.ArrayTruncateAction ~= action
                obj.CommonInfo.ArrayTruncateAction = action;
                obj.DataIsModified = true;
            end            
        end

        
        function applyMaxLengths(obj, arrayinfo)
            % applyMaxLengths(ARRAYINFO) applies stored array-size limits
            % for a message (if they exist) to ARRAYINFO. If there are no stored 
            % limits, it applies default array-size limits.
            % 
            validateattributes(arrayinfo, {'robotics.slros.internal.bus.VarLenArrayInfo'}, {'scalar'});
                
            msgtype = arrayinfo.MessageType;
            if obj.MsgTypeMap.isKey(msgtype)
                % msg is in database, return existing values
                
                % doApplyDefaults will be flipped to false only if all
                % conditions are met
                doApplyDefaults = true;                 
                
                storedInfo = obj.MsgTypeMap(msgtype);
                validStoredInfo = isstruct(storedInfo) && ...
                    all(isfield(storedInfo, {'PropertyName', 'DataType', 'MaxLength'}));
                
                if validStoredInfo
                    storedNames = {storedInfo.PropertyName};
                    curNames = {arrayinfo.ArrayProps.PropertyName};
                    if numel(storedNames) == numel(curNames) && all(strcmpi(storedNames, curNames))
                        arrayinfo.setFromCellArray('MaxLength', {storedInfo.MaxLength});
                        doApplyDefaults = false;                        
                        storedInfoMismatch = false;
                    else % stored property names don't match
                        storedInfoMismatch = true;
                    end
                else % stored info doesn't look right
                    storedInfoMismatch = true;
                end
                
            else % msg is not in database
                doApplyDefaults = true;
                storedInfoMismatch = false;                
            end
        
            if storedInfoMismatch
                obj.resetMsgTypeInfo(msgtype); % remove from store                
                warning(message('robotics:robotslros:arraysizestore:MaxArraySizesReset', ...
                    arrayinfo.MessageType, obj.ModelName));
            end
                
            if doApplyDefaults || storedInfoMismatch
                obj.applyDefaultMaxLengths(arrayinfo);
            end
        end
                       
    end
    
    %%
    methods(Access=private)
        
        function loadDataFromWorkspace(obj)                        
            if ~obj.ModelWorkspace.hasVariable(obj.WorkspaceVarName)
                applyDefaults = true;
            else
                try
                    data =  obj.ModelWorkspace.getVariable(obj.WorkspaceVarName);
                    obj.CommonInfo = data.CommonInfo;
                    % convert from string to enum type
                    obj.CommonInfo.ArrayTruncateAction = ...
                        robotics.slros.internal.bus.VarLenArrayTruncationAction.fromChar(data.CommonInfo.ArrayTruncateAction);
                    obj.MsgTypeMap = data.MsgTypeMap;
                    applyDefaults = false;
                catch
                    warning(message('robotics:robotslros:arraysizestore:CorruptedModelWorkspace', ...
                        obj.ModelName));
                    applyDefaults = true;
                    obj.DataIsModified = true;                    
                    % Clear workspace bus variables to flush out existing information 
                    robotics.slros.internal.bus.Util.clearSLBusesInBaseWorkspace();
                end
            end
            
            if applyDefaults
                obj.CommonInfo = struct();
                obj.CommonInfo.ArrayTruncateAction = obj.getDefaultTruncateAction();
                obj.MsgTypeMap = containers.Map;                
            end            
        end
        
        
        function saveDataToWorkspace(obj)
            commonInfo = obj.CommonInfo;
            commonInfo.ArrayTruncateAction = char(commonInfo.ArrayTruncateAction);
            data = struct('CommonInfo', commonInfo, 'MsgTypeMap', obj.MsgTypeMap);
            obj.ModelWorkspace.assignin(obj.WorkspaceVarName, data);            
        end        
        
    end
    
    %%
    methods(Static)
        function out = getDefaultTruncateAction()
            out = robotics.slros.internal.bus.VarLenArrayTruncationAction.EmitWarning;
        end
        
        function applyDefaultMaxLengths(arrayinfo)
            import robotics.slros.internal.bus.VarlenArraySizeStore
             
            validateattributes(arrayinfo, {'robotics.slros.internal.bus.VarLenArrayInfo'}, {'scalar'});     
            maxLengths = cell(1,numel(arrayinfo.ArrayProps));
            for i=1:numel(arrayinfo.ArrayProps)
                % datatype is either a ROS message type (e.g.,
                % 'geometry_msgs/Point') or a primitive Simulink datatype
                % (e.g, 'uint8', 'single').                
                datatype = arrayinfo.ArrayProps(i).DataType;                                
                if strfind(datatype,'/')
                    % Array of ROS messages
                    maxLength = VarlenArraySizeStore.DefaultMessageArrayMaxLen;
                else
                    % Array of primitive type
                    % Note: strings are mapped to arrays of uint8
                    maxLength = VarlenArraySizeStore.DefaultPrimitiveArrayMaxLen;
                end                
                maxLengths{i} = maxLength;
            end        
            arrayinfo.setFromCellArray('MaxLength', maxLengths);
        end
        
    end
    
end