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

    classdef DeviceParameterSpecifier < handle
    %This class is for internal use only. It may be removed in the future.
    
    %  DeviceParameterSpecifier opens a DDG dialog that lets the user
    %  specify the parameters for connecting to a ROS device, e.g. user
    %  name, password, device address.
    %
    %  Example:
    %    userDlg = robotics.slros.internal.dlg.DeviceParameterSpecifier
    %    userDlg.openDialog
    
    %   Copyright 2016 The MathWorks, Inc.
    
    properties (Constant)
        %DialogTag - Unique tag for dialog
        %   This can be used to find the dialog with the findDDGByTag
        %   function.
        DialogTag = 'DeviceParameterSpecifierDialog'
    end        
    
    properties (Access = private)
        %Parser - The parsing helper object
        Parser 
        
        %CloseFcnHandle - The user-specified function handle called when the dialog closes
        %   This function handle is invoked when the user clicks "OK" or
        %   "Cancel".
        CloseFcnHandle = function_handle.empty
        
        %Hostname - The hostname or IP address of the device
        %   This is part of the 'DeviceAddress' property.
        Hostname
        
        %SSHPort - SSH port used to connect to the device (default is 22)
        %   This is part of the 'DeviceAddress' property.
        SSHPort
        
        %IsSticky - Is dialog sticky with respect to other DDG windows
        %   If this is set to true, the user will not be able to interact 
        %   with any other DDG dialogs or the model explorer until he 
        %   dismisses the sticky dialog.        
        IsSticky = false
        
        %PreventCloseCallback - If TRUE, ignore callbacks received through the dialog CloseMethod
        %   Since we are using custom buttons in this dialog, we are
        %   handling the dialog closing ourselves, so this flag gives us
        %   more control.
        PreventCloseCallback = false        
    end
    
    properties (SetObservable = true)
        %ModelName - The name of the Simulink model associated with this device connection
        ModelName = ''
        
        %DeviceAddress - Contents of the device address edit field
        %   Note that device addresses can also contain the SSH port, e.g.
        %   '192.168.1.1:40763'. The individual elements are stored in the
        %   'Hostname' and 'SSHPort' properties.
        DeviceAddress@asciiString
        
        %Username - Contents of the user name edit field
        Username@asciiString
        
        %Password - Contents of the password edit field
        Password@asciiString
        
        %ROSInstallFolder - Contents of the ROS folder edit field
        ROSInstallFolder@asciiString
        
        %CatkinWorkspace - Contents of the Catkin workspace edit field
        CatkinWorkspace@asciiString
        
        %RememberPassword - State of the "Remember my password" checkbox
        RememberPassword@logical scalar
        
    end
    
    methods (Static)
        function deviceParamDlg = openDialogForModel(modelName, varargin)
            %openDialogForModel Convenience function for opening a dialog for a Simulink model
            
            deviceParamDlg = robotics.slros.internal.dlg.DeviceParameterSpecifier;
            deviceParamDlg.ModelName = modelName;
            deviceParamDlg.openDialog(varargin{:});
        end
    end
    
    methods
        function obj = DeviceParameterSpecifier
            %DeviceParameterSpecifier Standard constructor
            
            obj.Parser = robotics.slros.internal.DeviceParameterParser;
        end
        
        function dlg = openDialog(obj, closeFcnHandle, isSticky)
            %openDialog Open the dialog window
            %   openDialog(OBJ) opens the dialog window
            %
            %   openDialog(OBJ, CLOSEFCNHANDLE) allows you to specify a
            %   function handle that will be called when the dialog is
            %   closed (user clicks OK or Cancel).
            %   CLOSEFCNHANDLE is a function handle that takes 6 arguments,
            %   e.g.
            %
            %   closeFcn(closeAction, deviceAddress, hostname, sshport, username, password, rosfolder, catkinws)
            %      closeAction - Either 'ok' or 'cancel' to indicate what button the user pressed
            %      deviceAddress - Last value for device address (combination of hostname and sshport)
            %      hostname - Hostname or IP address
            %      sshport - SSH port used for connection
            %      username - Last value for username
            %      password - Last value for password
            %      rosFolder - Last value for ROS folder
            %      catkinWs - Last value for Catkin workspace
            %
            %   openDialog(OBJ, CLOSEFCNHANDLE, ISSTICKY) lets you indicate
            %   if the dialog should be sticky (modal) or not. ISSTICKY is
            %   a logical scalar.
            
            if exist('closeFcnHandle', 'var')
                validateattributes(closeFcnHandle, {'function_handle'}, {'scalar'});
                obj.CloseFcnHandle = closeFcnHandle;
            else
                obj.CloseFcnHandle = function_handle.empty;
            end
            
            if exist('isSticky', 'var')
                validateattributes(isSticky, {'logical'}, {'scalar'});
                obj.IsSticky = isSticky;
            end
            
            % Reset close callback handling
            obj.PreventCloseCallback = false;
            
            % Initialize the data from the MATLAB preferences
            obj.Hostname = robotics.codertarget.internal.DeviceParameters.getHostname;
            obj.SSHPort = robotics.codertarget.internal.DeviceParameters.getSSHPort;
            
            % Assign the values of the properties associated with dialog
            % widgets
            obj.DeviceAddress = robotics.codertarget.internal.DeviceParameters.getDeviceAddress;
            obj.Username = robotics.codertarget.internal.DeviceParameters.getUsername;
            obj.Password = robotics.codertarget.internal.DeviceParameters.getPassword;
            obj.RememberPassword = robotics.codertarget.internal.DeviceParameters.getSavePassword;
            obj.CatkinWorkspace = robotics.codertarget.internal.DeviceParameters.getCatkinWorkspace;
            obj.ROSInstallFolder = robotics.codertarget.internal.DeviceParameters.getROSInstallFolder;
            
            % Bring up the dialog window
            dlg = DAStudio.Dialog(obj);
        end
        
        function [isAccepted, hostname, sshPort, username, password, rosFolder, catkinWs] = ...
                openDialogAndWait(obj)
            %openDialogAndWait Open dialog and wait until the user closes it
            %   This function is blocking. This window is not model
            %   (sticky), because we want the user to interact with the
            %   diagnostic window when testing device parameters.
            %
            %   The function will return the final value of all internal
            %   device parameters.
            
            % Bring up the dialog window
            dlg = obj.openDialog(@closeDialog, false);
            
            % Wait for dialog to be closed (closeDialog callback is
            % executed at that point)
            waitfor(dlg);
            
            % All output values have already been assigned in the callback
            % Note that the device parameters have to be stored in this
            % callback. If we wait until the code gets here, the dialog has
            % already been deleted and the obj properties are not in a
            % valid state anymore.
            
            function closeDialog(finalaction, ~, hname, ssh, uname, pw, rosdir, ws)
                
                % Set all output value
                isAccepted = isequal(finalaction, 'ok');                
                hostname = hname;
                sshPort = ssh;
                username = uname;
                password = pw;
                rosFolder = rosdir;
                catkinWs = ws;
            end            
        end
    end
    
    methods (Hidden)
        
        function okayButtonPressed(obj, dlg)
            %okayButtonPressed Callback that is executed when the OK button is pressed
            
            try
                % Always save device address, user name, remember password
                % selection, catkin workspace, and ROS install folder when
                % user clicks "OK
                robotics.codertarget.internal.DeviceParameters.setHostname(obj.Hostname);
                robotics.codertarget.internal.DeviceParameters.setSSHPort(obj.SSHPort);
                robotics.codertarget.internal.DeviceParameters.setUsername(obj.Username);
                robotics.codertarget.internal.DeviceParameters.setSavePassword(obj.RememberPassword);
                robotics.codertarget.internal.DeviceParameters.setCatkinWorkspace(obj.CatkinWorkspace);
                robotics.codertarget.internal.DeviceParameters.setROSInstallFolder(obj.ROSInstallFolder);
                
                % Only save password if user selected "Remember my password"
                if obj.RememberPassword
                    robotics.codertarget.internal.DeviceParameters.setPassword(obj.Password);
                else
                    % Reset any saved password if the user selects to not save it
                    robotics.codertarget.internal.DeviceParameters.setPassword('');
                end
                
                % Evaluate function handle for closing
                obj.evaluateCloseFcn('ok');
                
                % Close dialog. To prevent additional callbacks through
                % dlgClose, set a flag.
                obj.PreventCloseCallback = true;
                dlg.delete
                
            catch ex
                % Convert all errors to warnings. If they are propagated back to
                % DDG, this causes MATLAB to crash
                
                warning(ex.identifier, ex.message)
            end
        end
        
        
        function cancelButtonPressed(obj, dlg)
            %cancelButtonPressed Callback that is executed when the Cancel button is pressed
            
            try
                % Evaluate function handle for closing
                obj.evaluateCloseFcn('cancel');
                
                % Close dialog without applying any changes.
                % To prevent additional callbacks through dlgClose, set a
                % flag.
                obj.PreventCloseCallback = true;
                dlg.delete
                
            catch ex
                % Convert all errors to warnings. If they are propagated back to
                % DDG, this causes MATLAB to crash
                
                warning(ex.identifier, ex.message)
            end
        end
        
        function testButtonPressed(obj, ~)
            %testButtonPressed Callback that is executed when the Test button is pressed
            
            try
                % Run a check on all the user settings
                tester = robotics.slros.internal.diag.DeviceDiagnostics(obj.ModelName);
                tester.runDiagnostics(obj.Hostname, obj.SSHPort, obj.Username, obj.Password, obj.ROSInstallFolder, obj.CatkinWorkspace);
                
            catch ex
                % Convert all errors to warnings. If they are propagated back to
                % DDG, this causes MATLAB to crash
                
                warning(ex.identifier, ex.message)
            end
        end
        
        function dlgClose(obj, closeaction)
            %dlgClose Callback executed when dialog is closed
            %   This callback is executed if the dialog gets deleted or
            %   the user clicks the X to close the dialog.
            %   We are using custom OK and Cancel buttons and in their
            %   callbacks, the dialog is deleted as well. To prevent double
            %   callbacks, use the PreventCloseCallback flag to indicate if
            %   additional actions should be performed in dlgClose.
            %
            %   CLOSEACTION is always 'cancel', since we do not use the
            %   standard OK button.
            
            try
                if obj.PreventCloseCallback
                    % Do not do anything if this callback was triggered by
                    % our custom OK or Cancel buttons. They already handled
                    % all necessary actions.
                    return;
                end
                
                % Evaluate function handle for closing
                obj.evaluateCloseFcn(closeaction);
                
            catch ex
                % Convert all errors to warnings. If they are propagated back to
                % DDG, this causes MATLAB to crash
                
                warning(ex.identifier, ex.message)
            end
        end
        
        function evaluateCloseFcn(obj, closeaction)
            %evaluateCloseFcn Evaluate the CloseFcnHandle callback
            
            if ~isempty(obj.CloseFcnHandle)
                feval(obj.CloseFcnHandle, closeaction, obj.DeviceAddress, ...
                    obj.Hostname, obj.SSHPort, obj.Username, obj.Password, ...
                    obj.ROSInstallFolder, obj.CatkinWorkspace);
            end
        end
        
        function validateDeviceAddress(obj, deviceAddress, argName)
            %validateDeviceAddress Validate the device address string
            %   Also separate the host name / IP address from the SSH port.
            %   Some examples of valid device addresses:
            %      localhost
            %      somehost:225
            %      192.168.2.1
            %      192.168.2.1:40763
            
            [validHost, validSSHPort] = obj.Parser.validateDeviceAddress(deviceAddress, '', argName);
            
            obj.Hostname = validHost;
            obj.SSHPort = validSSHPort;
        end
    end
    
    properties (Constant, Access = ?matlab.unittest.TestCase)
        %% Tags for all widget elements.
        %  Make the tags accessible to unit tests to verify settings.
        
        TextHeaderTag = 'textHeader'
        EditDeviceAddressTag = 'editDeviceAddress'
        EditUsernameTag = 'editUsername'
        EditPasswordTag = 'editPassword'
        CheckboxRememberTag = 'checkboxRememberPw'
        EditROSFolderTag = 'editROSFolder'
        EditCatkinWorkspaceTag = 'editCatkinWs'
        ButtonHelpTag = 'buttonHelp'
        ButtonOKTag = 'buttonOK'
        ButtonCancelTag = 'buttonCancel'
        ButtonTestTag = 'buttonTest'
        ButtonPanelTag = 'panelButtonSet'
    end
    
    methods (Hidden)
        function dlgstruct = getDialogSchema(obj)
            %getDialogSchema Specify the DDG schema of the dialog
            
            % Widget for header text
            header.Name = message('robotics:robotslros:deviceparams:DialogHeader').getString;
            header.Type = 'text';
            header.Tag = obj.TextHeaderTag;
            header.MinimumSize = [0 50];
            header.RowSpan = [1 1];
            header.ColSpan = [2 2];
            header.HideName = true;
            header.WordWrap = true;
            
            % Widget for device address
            deviceAddressLabel = message('robotics:robotslros:deviceparams:DeviceAddress').getString;
            deviceAddress.Name = [deviceAddressLabel ': '];
            deviceAddress.Type = 'edit';
            deviceAddress.Tag = obj.EditDeviceAddressTag;
            deviceAddress.RowSpan = [2 2];
            deviceAddress.ColSpan = [2 2];
            deviceAddress.ObjectProperty = 'DeviceAddress';     % Use an object property to store the value of the edit field
            deviceAddress.Mode = true;                          % Update the property in immediate mode
            deviceAddress.ValidationCallback = @(~,~,deviceAddress,~) ...
                obj.validateDeviceAddress(deviceAddress, deviceAddressLabel);
            deviceAddress.ToolTip = message('robotics:robotslros:deviceparams:DeviceAddressTooltip').getString;
            
            % Widget for username
            usernameLabel = message('robotics:robotslros:deviceparams:Username').getString;
            username.Name = [usernameLabel ': '];
            username.Type = 'edit';
            username.Tag = obj.EditUsernameTag;
            username.RowSpan = [3 3];
            username.ColSpan = [2 2];
            username.ObjectProperty = 'Username';
            username.Mode = true;
            username.ValidationCallback = @(~,~,username,~) obj.Parser.validateUsername(username, '', usernameLabel);
            username.ToolTip = message('robotics:robotslros:deviceparams:UsernameTooltip').getString;
            
            % Widget for password
            passwordLabel = message('robotics:robotslros:deviceparams:Password').getString;
            password.Name = [passwordLabel ': '];
            password.Type = 'edit';
            password.Tag = obj.EditPasswordTag;
            password.EchoMode = 'password';         % Obfuscate password characters
            password.RowSpan = [4 4];
            password.ColSpan = [2 2];
            password.ObjectProperty = 'Password';
            password.Mode = true;
            password.ValidationCallback = @(~,~,pw,~) obj.Parser.validatePassword(pw, '', passwordLabel);
            password.ToolTip = message('robotics:robotslros:deviceparams:PasswordTooltip').getString;
            
            % Widget for remember password checkbox
            rememberPw.Name = message('robotics:robotslros:deviceparams:RememberPassword').getString;
            rememberPw.Type = 'checkbox';
            rememberPw.Tag = obj.CheckboxRememberTag;
            rememberPw.MinimumSize = [0 50];
            rememberPw.RowSpan = [5 5];
            rememberPw.ColSpan = [2 2];
            rememberPw.ObjectProperty = 'RememberPassword';
            rememberPw.Mode = true;
            rememberPw.ToolTip = message('robotics:robotslros:deviceparams:RememberPasswordTooltip').getString;
            
            % Widget for Catkin workspace
            catkinWsLabel = message('robotics:robotslros:deviceparams:CatkinWorkspace').getString;
            catkinWs.Name = [catkinWsLabel ': '];
            catkinWs.Type = 'edit';
            catkinWs.Tag = obj.EditCatkinWorkspaceTag;
            catkinWs.RowSpan = [7 7];
            catkinWs.ColSpan = [2 2];
            catkinWs.ObjectProperty = 'CatkinWorkspace';
            catkinWs.Mode = true;
            catkinWs.ValidationCallback = @(~,~,ws,~) obj.Parser.validateCatkinWorkspace(ws, '', catkinWsLabel);
            catkinWs.ToolTip = message('robotics:robotslros:deviceparams:CatkinWorkspaceTooltip').getString;
            
            % Widget for ROS folder
            rosFolderLabel = message('robotics:robotslros:deviceparams:ROSFolder').getString;
            rosFolder.Name = [rosFolderLabel ': '];
            rosFolder.Type = 'edit';
            rosFolder.Tag = obj.EditROSFolderTag;
            rosFolder.RowSpan = [6 6];
            rosFolder.ColSpan = [2 2];
            rosFolder.ObjectProperty = 'ROSInstallFolder';
            rosFolder.Mode = true;
            rosFolder.ValidationCallback = @(~,~,dir,~) obj.Parser.validateROSFolder(dir, '', rosFolderLabel);
            rosFolder.ToolTip = message('robotics:robotslros:deviceparams:ROSFolderTooltip').getString;
            
            % Custom help button
            helpButton.Name = message('robotics:robotslros:deviceparams:HelpButton').getString;
            helpButton.Type = 'pushbutton';
            helpButton.MatlabMethod = 'robotics.slros.internal.helpview';
            helpButton.MatlabArgs = {'deviceParamsDlg'};  % doc topic id
            helpButton.RowSpan = [1 1];
            helpButton.ColSpan = [4 4];
            helpButton.Tag = obj.ButtonHelpTag;
            
            % Custom test button
            testButton.Name = message('robotics:robotslros:deviceparams:TestButton').getString;
            testButton.Type = 'pushbutton';
            testButton.RowSpan = [1 1];
            testButton.ColSpan = [3 3];
            testButton.Tag = obj.ButtonTestTag;
            testButton.ObjectMethod = 'testButtonPressed';
            testButton.MethodArgs = {'%dialog'}; % object handle is implicit first arg
            testButton.ArgDataTypes = {'handle'}; % 'handle' is type of %dialog
            testButton.ToolTip = message('robotics:robotslros:deviceparams:TestButtonTooltip').getString;
            
            % Custom OK button
            okButton.Name = message('robotics:robotslros:deviceparams:OKButton').getString;
            okButton.Type = 'pushbutton';
            okButton.RowSpan = [1 1];
            okButton.ColSpan = [1 1];
            okButton.Tag = obj.ButtonOKTag;
            okButton.ObjectMethod = 'okayButtonPressed';
            okButton.MethodArgs = {'%dialog'}; % object handle is implicit first arg
            okButton.ArgDataTypes = {'handle'}; % 'handle' is type of %dialog
            
            % Custom Cancel button
            cancelButton.Name = message('robotics:robotslros:deviceparams:CancelButton').getString;
            cancelButton.Type = 'pushbutton';
            cancelButton.RowSpan = [1 1];
            cancelButton.ColSpan = [2 2];
            cancelButton.Tag = obj.ButtonCancelTag;
            cancelButton.ObjectMethod = 'cancelButtonPressed';
            cancelButton.MethodArgs = {'%dialog'}; % object handle is implicit first arg
            cancelButton.ArgDataTypes = {'handle'}; % 'handle' is type of %dialog
            
            % Construct custom button panel
            % We cannot use the standard StandaloneButtonSet, because we want
            % to add the custom "Test" button to the same panel.
            buttonPanel.Type = 'panel';
            buttonPanel.LayoutGrid = [1 4];
            buttonPanel.Items = {okButton, cancelButton, helpButton, testButton};
            buttonPanel.Tag = obj.ButtonPanelTag;
            
            % Main dialog structure
            dlgstruct.DialogTitle = message('robotics:robotslros:deviceparams:DialogTitle').getString;
            dlgstruct.DialogTag = obj.DialogTag;
            dlgstruct.LayoutGrid = [7, 3];
            dlgstruct.ColStretch = [1, 10, 1];
            dlgstruct.StandaloneButtonSet = buttonPanel;        % Use custom button panel
            dlgstruct.Items = { header, deviceAddress, username, password, rememberPw, rosFolder, catkinWs};
            dlgstruct.Sticky = obj.IsSticky;
            dlgstruct.CloseMethod = 'dlgClose';            
            dlgstruct.CloseMethodArgs = {'%closeaction'};
            dlgstruct.CloseMethodArgsDT = {'string'};
        end
    end
end