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

    classdef ArraySizeManager < handle
    %This class is for internal use only. It may be removed in the future.
    
    %  ArraySizeManager opens a DDG dialog that lets the user inspect
    %  and modify the maximum sizes of variable-length arrays, for all ROS 
    %  messages used in a Simulink model. Once the user accepts the
    %  changes, the information is written back to the model and the model
    %  is dirtied, *but it is not saved*.
    % 
    %  Sample use:
    %   mgr = robotics.slros.internal.dlg.ArraySizeManager(gcs);
    %   mgr.openDialog
    %
    %  See also: bus.VarlenArraySizeStore
    
    %   Copyright 2014 The MathWorks, Inc.

    properties(SetAccess=private)
       Dialog 
       ModelName
       NumMsgTypesInModel = 0
       NumVarLenMsgTypesInModel = 0
    end
    
    properties(Access=private)
        VarlenSizeStore
        MsgVarLenInfo
        Position = [200 200]  % x,y position in pixels
        SelectedMsgTypeIndex
        MsgTypeList
        
        TruncateAction
    end    
    
    methods
       
         function obj = ArraySizeManager(modelname)
             
             modelname = bdroot(modelname);
             obj.VarlenSizeStore = robotics.slros.internal.bus.VarlenArraySizeStore(modelname);
             obj.ModelName = modelname;
                          
             % Below, we get the maximum length info from
             % Util.getAllMessageInfoMapForModel, which consults the
             % current bus, but use VarlenArraySizeStore to distinguish
             % bewteen customized vs. default values. This can cause a
             % problem if the bus definitions in the workspace are
             % out-of-sync for any reason (e.g., if the user has modified
             % the bus definitions using BUSEDITOR).
             %
             % The cleanest solution is to c;ear the buses in the workspace
             % upon entry; they will be recreated in the call to
             % Util.getAllMessageInfoMapForModel with the stored
             % information. (This clearing only needs to be done for the
             % current model but it is safe to clear buses for all models,
             % and also simpler).
              
             robotics.slros.internal.bus.Util.clearSLBusesInBaseWorkspace();
             
             % Get transitive closure for all messages in model
             allMsgsMap = robotics.slros.internal.bus.Util.getAllMessageInfoMapForModel(modelname);
             
             % Now run over all the messages                          
             allMsgsTypesInModel = keys(allMsgsMap);
             obj.NumMsgTypesInModel = numel(allMsgsTypesInModel);
             obj.MsgVarLenInfo = containers.Map;
             
             for i=1:numel(allMsgsTypesInModel)      
                 msgType = allMsgsTypesInModel{i};                 
                 
                 msgMapInfo = allMsgsMap(msgType);
                 msginfo = robotics.slros.internal.bus.VarLenArrayInfo.extractFromMsgMapInfo(msgMapInfo);
                 if numel(msginfo.ArrayProps) > 0
                     % This message has variable-length arrays
                      msgtypeInfo = obj.VarlenSizeStore.getMsgTypeInfo(msgType);
                      if ~isempty(msgtypeInfo)
                          % preexisting custom lengths for model
                          isUserSpecified = true;
                      else
                          % use default values
                          msgtypeInfo = msginfo;
                          isUserSpecified = false;
                      end
                      obj.MsgVarLenInfo(msgType) = ...
                          struct('MsgTypeInfo', msgtypeInfo, 'UserSpecified', isUserSpecified, 'IsDirty', false);                      
                 end
                 obj.TruncateAction.CurrentState = obj.VarlenSizeStore.getTruncateAction();
                 obj.TruncateAction.IsDirty = false;
             end
             
             obj.MsgTypeList = sort(keys(obj.MsgVarLenInfo));
             obj.NumVarLenMsgTypesInModel = numel(obj.MsgTypeList);
             obj.SelectedMsgTypeIndex = 1;
             obj.setPositionWrtModelWindow();
             % openDialog() will put up error dialog if there are no
             % messages in the model
         end
           
         function setPositionWrtModelWindow(obj)
             % There is no way to set the size of the DDG Window (using MCOS) 
             % without specifying the location as well. So set the location
             % relative to the model window
             pos = get_param(obj.ModelName, 'Location'); % [x y width height] (in pixels)
             % position the dialog 1/10 of the way from top-left corner
             obj.Position = round([pos(1)+(pos(3)/10) pos(2)+(pos(4)/10)]);
         end
                  
         function set.Position(obj, val)
             % the X,Y position for a block can be obtained using get(gcbh,'Position')
            validateattributes(val, {'numeric'}, {'size', [1 2], 'real', 'positive', 'integer'});
            obj.Position = val;
         end
        
        function openDialog(obj)
            if obj.NumMsgTypesInModel <= 0 || obj.NumVarLenMsgTypesInModel <= 0
                 if obj.NumMsgTypesInModel <= 0
                     msgStr = message('robotics:robotslros:arraysizemgr:NoROSMessages', obj.ModelName).getString;
                 else
                     msgStr = message('robotics:robotslros:arraysizemgr:NoVarLenROSMessages', obj.ModelName).getString;
                 end
                 dlgTitle = message('robotics:robotslros:arraysizemgr:DialogTitle').getString;
                 dlgProvider = DAStudio.DialogProvider;
                 % obj.Dialog = dlgProvider.errordlg(msgStr, dlgTitle, true); % non-blocking                 
                 obj.Dialog = dlgProvider.msgbox(msgStr, dlgTitle, true); % non-blocking
            else
                 obj.SelectedMsgTypeIndex = 1;
                 obj.Dialog = DAStudio.Dialog(obj);
                 obj.Dialog.refresh;
             end             
        end
                
        function tableChangedCallback(obj, dlg, row, col, newValue)
            % Invoked on any changes to the the table
            msgdata = obj.MsgVarLenInfo( obj.MsgTypeList{obj.SelectedMsgTypeIndex} );
            msginfo = msgdata.MsgTypeInfo;
                        
            oldValue = msginfo.getMaxLength(row+1);
            
            newnum = max(round(str2double(newValue)), 0);
            % row and col are zero-based
            if isnan(newnum) || ~(newnum > 0) || ~isreal(newnum)                
                strValue = num2str(oldValue); % reset back to old value
            else
                % even if new value is valid, may need to modify it
                % (e.g., due to rounding)
                msginfo.setMaxLength(row+1, newnum);                
                strValue =  num2str(newnum);
               
                msgdata.MsgTypeInfo = msginfo;
                msgdata.IsDirty = true;
                obj.MsgVarLenInfo( obj.MsgTypeList{obj.SelectedMsgTypeIndex} ) = msgdata;
            end
            dlg.setTableItemValue('rostable', row, col, strValue);            
            dlg.refresh;
        end
        
        function msgTypeChangedCallback(obj, dlg, tag, value) %#ok<INUSL>
            % Invoked when user selects another message type from listbox
            % value is the index of the user selection (zero-index).
            try
                if isnumeric(value) && isscalar(value)
                    obj.SelectedMsgTypeIndex = value+1;
                    msgtype = obj.MsgTypeList{obj.SelectedMsgTypeIndex};
                    msgdata = obj.MsgVarLenInfo(msgtype);
                    if msgdata.UserSpecified
                        dlg.setWidgetValue('modifydefaults', 0);
                    else
                        dlg.setWidgetValue('modifydefaults', 1);
                    end
                end
            catch ME
                disp(ME.getReport);
            end
            
            dlg.refresh;                        
        end

        function useDefaultsCheckboxCallback(obj, dlg, tag, value) %#ok<INUSL>
            % Invoked when user changes the "use defaults" dropdown
            % <value> is the index of the selection 
            %   0 = use default, 1 = specify custom
            try
                msgtype = obj.MsgTypeList{obj.SelectedMsgTypeIndex};
                msgdata = obj.MsgVarLenInfo(msgtype);
                wasUserSpecified = msgdata.UserSpecified;
                msgdata.UserSpecified = (value == 0);
                if wasUserSpecified && ~msgdata.UserSpecified
                    % transitioning from user-specified to default
                    obj.VarlenSizeStore.applyDefaultMaxLengths(msgdata.MsgTypeInfo);                                        
                    msgdata.IsDirty = true;
                end
                
                obj.MsgVarLenInfo(msgtype) = msgdata;
            catch ME
                disp(ME.getReport);
            end
            dlg.refresh;
        end
        
        function truncateActionDropdownCallback(obj, dlg, tag, value) %#ok<INUSL>
            % Invoked when user changes the truncate action dropdown.
            % <value> is the index of the selection 
            %   0 = Truncate w/ warning, 1 = Truncate silently
            try
                if value == 0
                    newState = ...
                        robotics.slros.internal.bus.VarLenArrayTruncationAction.EmitWarning;
                else
                    newState = ...
                        robotics.slros.internal.bus.VarLenArrayTruncationAction.DoNothing;
                end
                
                if obj.TruncateAction.CurrentState ~= newState
                    obj.TruncateAction.CurrentState = newState;
                    obj.TruncateAction.IsDirty = true;
                end
                
            catch ME
                disp(ME.getReport);
            end
            dlg.refresh;
        end
        
        
        function dlgClose(obj, closeaction)
            % closeaction is 'ok' if user clicked OK
            % 'cancel' if user clicked cancel or closed window
            if ~isvalid(obj.VarlenSizeStore)
                % if the user closed the model, then obj.VarlenSizeStore
                % would have deleted itself
                % warn the user & exit
                warning(message('robotics:robotslros:arraysizemgr:ModelAlreadyClosed', obj.ModelName));
                return;
            end
            
            if ~strcmpi(closeaction, 'ok')
                % Nothing to do if user clicked cancel or closed window
                return;
            end
            
            try
                modelDirty = false;
                
                msgTypes = keys(obj.MsgVarLenInfo);
                for i=1:numel(msgTypes)
                    msgdata = obj.MsgVarLenInfo(msgTypes{i});
                    if msgdata.IsDirty
                        modelDirty = true;
                        if msgdata.UserSpecified
                            obj.VarlenSizeStore.setMsgTypeInfo(msgTypes{i}, msgdata.MsgTypeInfo);
                        else
                            obj.VarlenSizeStore.resetMsgTypeInfo(msgTypes{i});
                        end
                    end                    
                end
                
                if obj.TruncateAction.IsDirty
                    modelDirty = true;
                    obj.VarlenSizeStore.setTruncateAction(obj.TruncateAction.CurrentState);
                end
                
                if modelDirty
                    obj.VarlenSizeStore.updateModel();
                end
            catch ME
                disp(ME.getReport);
                % Absorb all errors. If they are propagated back to
                % DDG, this causes MATLAB to crash, (Can't convert to
                % warnings are not as they are not displayed either).
            end
        end
        

        
        function dlgstruct = getDialogSchema(obj)            
            
            assert(obj.NumMsgTypesInModel > 0 && obj.NumVarLenMsgTypesInModel > 0);
            if isempty(obj.SelectedMsgTypeIndex)
                obj.SelectedMsgTypeIndex = 1;
            end
            
            helptext.Name = message('robotics:robotslros:arraysizemgr:DialogInfo').getString;
            helptext.Type  = 'text';
            helptext.WordWrap = true;
            helptext.Tag = 'rosmsghelp';
            
            modelname.Name = message('robotics:robotslros:arraysizemgr:ModelName', obj.ModelName).getString();
            modelname.Type  = 'text';
            modelname.WordWrap = false;
            modelname.Bold = true;
            modelname.RowSpan = [1 1];
            modelname.ColSpan = [1 1];
            modelname.Tag = 'modelname';            
            
            modelInfoContainer.Type = 'panel';
            modelInfoContainer.Name = '';
            modelInfoContainer.LayoutGrid = [1 2]; % [numrows numcolumns]
            modelInfoContainer.ColStretch = [1 3]; % [numrows numcolumns]
            modelInfoContainer.Items = {modelname};
            modelInfoContainer.Flat = true;
            modelInfoContainer.Visible = 1;
            
            truncateAction.Name = message('robotics:robotslros:arraysizemgr:TruncateActionPrompt').getString();
            truncateAction.Type  = 'combobox';
            truncateAction.NameLocation = 1;
            truncateAction.Entries = {...
                message('robotics:robotslros:arraysizemgr:TruncateActionWarn').getString()
                message('robotics:robotslros:arraysizemgr:TruncateActionNone').getString()
                };
            if obj.TruncateAction.CurrentState == ...
                    robotics.slros.internal.bus.VarLenArrayTruncationAction.EmitWarning 
                truncateAction.Value = 0;
            else
                truncateAction.Value = 1;
            end
            
            truncateAction.Tag = 'truncateaction';
            truncateAction.ObjectMethod = 'truncateActionDropdownCallback'; % call method on UDD source object
            truncateAction.MethodArgs = {'%dialog', '%tag', '%value'}; % '%handle ' is implicit as first arg
            truncateAction.ArgDataTypes = {'handle', 'string', 'mxArray'};                            
            truncateAction.Alignment = 1; % top-left
            
            availableMsgs.Name = message('robotics:robotslros:arraysizemgr:MessageTypesPrompt').getString();
            availableMsgs.Type  = 'listbox';
            availableMsgs.RowSpan = [1 2];
            availableMsgs.ColSpan = [1 1];
            availableMsgs.MultiSelect = false;
            availableMsgs.Entries = obj.MsgVarLenInfo.keys();
            availableMsgs.Alignment = 2;
            availableMsgs.Value = obj.SelectedMsgTypeIndex-1; % convert to zero-based index
            availableMsgs.ObjectMethod = 'msgTypeChangedCallback'; % call method on UDD source object
            availableMsgs.MethodArgs = {'%dialog', '%tag', '%value'}; % '%handle ' is implicit as first arg
            availableMsgs.ArgDataTypes = {'handle', 'string', 'mxarray'};    
            availableMsgs.Tag = 'availablemsgs';
                                                    
            msgdata = obj.MsgVarLenInfo( obj.MsgTypeList{obj.SelectedMsgTypeIndex} );
            msginfo = msgdata.MsgTypeInfo;
            numVarLenArraysInMsg = numel(msginfo.ArrayProps);
            tabledata = cell(numVarLenArraysInMsg, 3);
            for k=1:numel(msginfo.ArrayProps)
                if msgdata.UserSpecified
                    backgroundcolor = [255 255 255];
                else
                    backgroundcolor = [230 230 230];
                end
                propinfo = msginfo.ArrayProps(k);
                tabledata{k,1}.Type  = 'edit';
                tabledata{k,1}.Value = propinfo.PropertyName;
                tabledata{k,1}.Enabled  = false;
                tabledata{k,1}.BackgroundColor  = backgroundcolor;
                
                tabledata{k,2}.Type  = 'edit';
                tabledata{k,2}.Value =  propinfo.DataType;
                tabledata{k,2}.Enabled  = false;
                tabledata{k,2}.BackgroundColor  =  backgroundcolor;
                
                tabledata{k,3}.Type  = 'edit';                
                tabledata{k,3}.Value = sprintf('%d', propinfo.MaxLength);
                tabledata{k,3}.BackgroundColor  =  backgroundcolor;                                        
                tabledata{k,3}.Enabled  = msgdata.UserSpecified;
            end

            mytable.Type  = 'table';
            mytable.Name = '';
            mytable.Size  = [numVarLenArraysInMsg 3]; % colums: variable name, type, max length (editable)
            mytable.Data = tabledata;
            mytable.Grid  = 1;
            if msgdata.UserSpecified
                mytable.ReadOnlyColumns = [0 1];                
            else
                mytable.ReadOnlyColumns = [0 1 2];
            end
            mytable.HeaderVisibility = [0 1];
            mytable.Editable = 1;
            mytable.ColHeader = {
                message('robotics:robotslros:arraysizemgr:ColumnHdrPropName').getString() ...
                message('robotics:robotslros:arraysizemgr:ColumnHdrPropType').getString() ...
                message('robotics:robotslros:arraysizemgr:ColumnHdrMaxItems').getString()
                };                
            % mytable.ColumnCharacterWidth = [15 9 11 11 10];
            mytable.ValueChangedCallback = @obj.tableChangedCallback;
            mytable.Tag = 'rostable';
            mytable.RowSpan = [2 2];
            mytable.ColSpan = [3 4];
            % enabling this also turns makes column headers boldface
            % mytable.SelectionBehavior = 'row';            
            
            useDefaultsCheckbox.Name = message('robotics:robotslros:arraysizemgr:MsgActionUseDefault').getString();
            useDefaultsCheckbox.Type  = 'checkbox';
            useDefaultsCheckbox.RowSpan = [1 1];
            useDefaultsCheckbox.ColSpan = [3 4];
            if msgdata.UserSpecified
                useDefaultsCheckbox.Value = 0;
            else
                useDefaultsCheckbox.Value = 1;
            end
            useDefaultsCheckbox.Tag = 'modifydefaults';
            useDefaultsCheckbox.ObjectMethod = 'useDefaultsCheckboxCallback'; % call method on UDD source object
            useDefaultsCheckbox.MethodArgs = {'%dialog', '%tag', '%value'}; % '%handle ' is implicit as first arg
            useDefaultsCheckbox.ArgDataTypes = {'handle', 'string', 'mxArray'};                
            
            tablecontainer.Name = '';
            tablecontainer.Type = 'group';            
            tablecontainer.LayoutGrid = [2 4];
            % tablecontainer.ColStretch = [0 1 1 1 1 1 1];
            tablecontainer.Items = {availableMsgs, useDefaultsCheckbox, mytable};
            tablecontainer.Visible = 1;
            tablecontainer.Alignment = 0;
            
            % container
            mycontainer.Type = 'panel';
            mycontainer.Name = '';
            mycontainer.Items = {modelInfoContainer, helptext, truncateAction, tablecontainer};
            mycontainer.Visible = 1;
            
            % Main dialog
            dlgstruct.DialogTitle = message('robotics:robotslros:arraysizemgr:DialogTitle').getString;
            dlgstruct.HelpMethod = 'robotics.slros.internal.helpview';
            dlgstruct.HelpArgs =  {'rosManageSizesDlg'}; % doc topic id
            dlgstruct.CloseMethod = 'dlgClose';            
            dlgstruct.CloseMethodArgs = {'%closeaction'};
            dlgstruct.CloseMethodArgsDT = {'string'};            
            % dlgstruct.Sticky = true; % make this dialog modal wrt to other DDG dialogs (doesn't block MATLAB command line)
            
            % Buttons to show on dialog (these are options to pass to DDG,
            % not the final strings, so there is no need to use message
            % catalog)
            dlgstruct.StandaloneButtonSet =  ...
                {'Ok', 'Cancel', 'Help'}; % also available: 'Revert', 'Apply'
            
            dlgstruct.Items = {mycontainer};
            dlgstruct.Geometry = [obj.Position  750  450]; % xpos ypos width height
            dlgstruct.DialogTag = 'slros_arraysizemgr';
        end
    end
    
    methods(Static)
        
        function launch(modelname)
            % Convenience function for opening the dialog
            mgr = robotics.slros.internal.dlg.ArraySizeManager(modelname);
            mgr.openDialog();
        end
 
    end
end