www.gusucode.com > mbctools 工具箱 matlab 源码程序 > mbctools/@mdevtestplan/pAssessDataChanges.m

    function [out,questMsg] = pAssessDataChanges(T, newData)
%PASSESSDATACHANGES work out what has changed in the test plan data
%
%  PASSESSDATACHANGES(T, NEWTSSF, OLDTSSF)
%  

%  Copyright 2000-2015 The MathWorks, Inc. and Ford Global Technologies, Inc.

% Create the output structure to indicate the changes
out = struct(...
    'INPUT_SIGNAL_REMOVED', false,...
    'RESPONSE_SIGNAL_REMOVED', false,...
    'MONITOR_SIGNAL_REMOVED', false,...
    'ACTUAL_DESIGN_CHANGED', false,...
    'INPUT_DATA_CHANGED', false,...
    'RESPONSE_DATA_CHANGED', false,...
    'DATA_CHANGED', false,...
    'KeepValidationData',false,...
    'InputSignalsFound', [],...
    'InputSignalsMissing', {cell(0,1)},...
    'InputSignalsChanged', {cell(0,1)},...
    'InputSignalsUnchanged', {cell(0,1)},...
    'ResponseSignalsFound', [],...
    'ResponseHasValidDatum', [],...
    'ResponseSignalsMissing', {cell(1,0)},...
    'ResponseDatumMissing', {cell(1,0)},...
    'ResponseSignalsChanged', {cell(1,0)},...
    'ResponseSignalsUnchanged', {cell(1,0)},...
    'MonitorSignalsMissing', {{}},...
    'OldData', [],...
    'ResponsesToKeep',false);
% Some definitions ...
% nI is the number of inputs to the testplan
% nR is the number of current or pending responses in the testplan
% INPUT_SIGNAL_REMOVED     - logical : An input signal is missing or of zero length
% RESPONSE_SIGNAL_REMOVED  - logical : A response signal is missing or of zero length
% MONITOR_SIGNAL_REMOVED   - logical : A monitor signal is missing or of zero length
% ACTUAL_DESIGN_CHANGED    - logical : The ActualDesign has changed
% INPUT_DATA_CHANGED       - logical : An input signal is different (value or test)
% RESPONSE_DATA_CHANGED    - logical : A response signal is different (value or test)
% DATA_CHANGED             - logical : The data is different
% KeepValidationData       - logical : keep validation data (input signals
%                                      must be in validation data set).
% InputSignalsFound        - Logical array (1xnI) : Input signals that exist.
% InputSignalsMissing      - Cell array of string : Signals missing
% InputSignalsChanged      - Cell array of string : Signals changed
% InputSignalsUnchanged    - Cell array of string : Signals that are the same
% ResponseSignalsFound     - Logical array (1xnR) : Response signals that exist.
% ResponseHasValidDatum    - Logical array (1xnR) : Response signals that have a valid datum
% ResponseSignalsMissing   - Cell array of string : Signals missing
% ResponseDatumMissing     - Cell array of string : Datum missing
% ResponseSignalsChanged   - Cell array of string : Signals changed
% ResponseSignalsUnchanged - Cell array of string : Signals that are the same
% MonitorSignalsMissing    - Cell array of string : Signals missing
% ResponsesToKeep          - Logical array of responses to keep in tree

% Get the old and new data - need to deal carefully with the case where the
% testplan is unmatched as oldData isn't what was previously in the testplan
if IsMatched(T)
    out.OldData = T.DataLink.info;
else
    out.OldData = testplansweepsetfilter;
end

% Convert to sweepsets as we aren't actually interesed in much from
% the tssf itself - will look at the ActualDesign in a minute
newSS = sweepset(newData);
oldSS = sweepset(out.OldData);

% Get the new siganl names
newNames = get(newSS, 'Name');

% Get the important signals for this testplan
inputSignalNames = factorNames(designdev(T));
[responseSignalNames,responsesToKeep] = iGetResponseNames(T);
% Remember monitor isn't always a structure and T.Monitor.values might be
% empty
if ~isempty(T.Monitor) && ~isempty(T.Monitor.values)
    monitorSignalNames = unique([T.Monitor.values ;  T.Monitor.Xdata]);
else
    monitorSignalNames = {};
end

SIGNALS_FOUND = size(newSS, 1) > 0;
% Which signals still exist - no data in an input signal is as bad as the
% signal itself being missing
inputSignalsFound    = ismember(inputSignalNames, newNames) & SIGNALS_FOUND;
responseSignalsFound = ismember(responseSignalNames, newNames) & SIGNALS_FOUND;
monitorSignalsFound  = ismember(monitorSignalNames, newNames) & SIGNALS_FOUND;

out.INPUT_SIGNAL_REMOVED    = ~all(inputSignalsFound);
out.RESPONSE_SIGNAL_REMOVED = ~all(responseSignalsFound);
out.MONITOR_SIGNAL_REMOVED  = ~all(monitorSignalsFound);

out.InputSignalsMissing     = inputSignalNames(~inputSignalsFound,1);
out.ResponseSignalsMissing  = responseSignalNames(1,~responseSignalsFound);
out.MonitorSignalsMissing   = monitorSignalNames(~monitorSignalsFound);

% all input signals must be in validation data set
valssf = valdata(T);
if ~isempty(valssf)
    ValSignals = get(valssf,'Name');
    
    out.KeepValidationData = all( ismember(inputSignalNames,ValSignals) );
end

responseHasValidDatum = responseSignalsFound;
% Which responses will need to be removed because a datum response is being
% deleted - NOTE that the datum response is ALWAYS the first response
if ~isempty(responseSignalsFound) && ~responseSignalsFound(1) 
    % Which other responses need to be removed
    linkedIndex = getDatumLinkedResponseIndex(T);    
    responseHasValidDatum(linkedIndex) = false;
    out.ResponseDatumMissing = responseSignalNames(1,linkedIndex);
end

out.InputSignalsFound = inputSignalsFound;
out.ResponseSignalsFound = responseSignalsFound;
out.ResponseHasValidDatum = responseHasValidDatum;
% find responses to Keep - these might be changed by the responses to
% remove
responsesToRemove = ~(out.ResponseSignalsFound & out.ResponseHasValidDatum);
for i= find(responsesToKeep)
    responsesToKeep(i) = ~responsesToRemove(i);
end
out.ResponsesToKeep = responsesToKeep;
% Check for tests changing - this constitutes a change to the data that
% will not be picked up by an isequal(double, double) test and which does
% represent a very real modification 
TESTS_CHANGED = ~isequal(getGuids(oldSS), getGuids(newSS)) || ...
    ~isequal(getSweepGuids(oldSS), getSweepGuids(newSS));

out.DATA_CHANGED = TESTS_CHANGED || ...
    ~isequaln(double(oldSS), double(newSS)) ;

% Has the actual design changed
newDesign = get(newData, 'codeddesign');
oldDesign = ActualDesign(T);
out.ACTUAL_DESIGN_CHANGED = ~isequal(factorsettings(oldDesign), factorsettings(newDesign));

% A base assumption for the rest of this code is that all named signals
% exist in the oldSS - this is not the case when oldSS is empty, as in the
% initialisation of the test plan. If this is the case then we need to fill
% in the extra fields appropriately. NOTE the initialisation will occur
% when T is not matched
if ~IsMatched(T)
    out.INPUT_DATA_CHANGED = true;
    out.RESPONSE_DATA_CHANGED = true;
    out.InputSignalsChanged = inputSignalNames(inputSignalsFound);
    out.ResponseSignalsChanged = responseSignalNames(responseSignalsFound & responseHasValidDatum);
else
    
    % Check the input data for consistency
    dblInOld = double(oldSS(:, inputSignalNames(inputSignalsFound)));
    dblInNew = double(newSS(:, inputSignalNames(inputSignalsFound)));
    out.INPUT_DATA_CHANGED = out.INPUT_SIGNAL_REMOVED || ...
        TESTS_CHANGED || ...
        ~isequaln(dblInOld, dblInNew);

    if  ~out.INPUT_DATA_CHANGED && numChildren(T) > 0



        out.RESPONSE_DATA_CHANGED = ~all(out.ResponsesToKeep) || ...
            length(T.Responses)>length(out.ResponsesToKeep);
        % check whether input signal definition (range, symbols) has changed
        
        models = children(T, @model);
        baseModel = HSModel(T.DesignDev);
        if isa(models{1},'localmulti')
            % Point-by-point model
            baseModel = get(baseModel,'Local');
        end
        out.INPUT_DATA_CHANGED = ~hasSameInputs(baseModel,models{1});
        out.DATA_CHANGED= out.DATA_CHANGED || out.INPUT_DATA_CHANGED || ...
            out.RESPONSE_DATA_CHANGED;
    else
        out.RESPONSE_DATA_CHANGED = false;
    end

    if out.INPUT_DATA_CHANGED
        % Which inputs have been changed
        out = iInputSignals(out,inputSignalNames,TESTS_CHANGED,dblInOld,dblInNew);
    else
        % No changes to the input signals mean that they must all be unchanged
        out.InputSignalsUnchanged = inputSignalNames;
    end

    % Implicit change to a response if the input data has changed
    dblRespOld = double(oldSS(:, responseSignalNames(responseSignalsFound)));
    dblRespNew = double(newSS(:, responseSignalNames(responseSignalsFound)));
    out.RESPONSE_DATA_CHANGED = out.RESPONSE_DATA_CHANGED || ...
        out.RESPONSE_SIGNAL_REMOVED || ...
        out.INPUT_DATA_CHANGED || ...
        ~isequaln(dblRespOld, dblRespNew);

    if out.RESPONSE_DATA_CHANGED
        % Which responses have been changed
        out = iResponseSignals(out,responseSignalNames,dblRespOld,dblRespNew);

    else
        % No changes to the response signals mean that they must all be unchanged
        out.ResponseSignalsUnchanged = responseSignalNames;
    end
end

if nargout>1
    questMsg = iCreateMessages(T, out);
end


% iGetResponseNames
function [responseNames, responsesToKeep] = iGetResponseNames(T)
% Default is no responses

% Responses are either the children of a testplan or they have yet to be
% built and reside in the Response field of the testplan
if numChildren(T) > 0
    responseNames = children(T, @varname);
    models = children(T, @model);
    responsesToKeep = false(1,length(models));
    for i=1:min(length(models),length(T.Responses))
        % check that model is the same 
        % cleanup is called to remove any non persistent data such as
        % xreglinear.Store from the object before comparison
        responsesToKeep(i) = isequal(cleanup(models{i}),cleanup(T.Responses{i}));
        if ~responsesToKeep(i)
            % once a response which cannot be kept is found, all subsequent
            % responses will be rebuilt. This is so the order of the
            % responses remains as it was in the wizard and the Model
            % Browser tree.
            break
        end
    end
    for j = i:length(T.Responses)
        % new responses 
        responseNames{j} = varname(T.Responses{j});
    end
    responseNames = responseNames(1,1:length(T.Responses));
else
    responseNames = cell(1,length(T.Responses));
    for i = 1:length(T.Responses)
        responseNames{i}= varname(T.Responses{i});
    end
    responsesToKeep = false(0,1);
end



% iInputSignals
function out = iInputSignals(out,inputSignalNames,TESTS_CHANGED,dblInOld,dblInNew)

% Which inputs have been changed
for i = 1:length(inputSignalNames)
    % Only check inputs that have been found
    if out.InputSignalsFound(i)
        % Calculate index into the available input data
        thisIndex = sum(out.InputSignalsFound(1:i));
        % Are the values different
        if TESTS_CHANGED || ...
                ~isequaln(dblInOld(:, thisIndex), dblInNew(:, thisIndex))
            out.InputSignalsChanged(end+1,1) = inputSignalNames(i);
        else
            out.InputSignalsUnchanged(end+1,1) = inputSignalNames(i);
        end
    end
end

% iResponseSignals
function out = iResponseSignals(out,responseSignalNames,dblRespOld,dblRespNew)

for i = 1:length(responseSignalNames)
    % Only check responses that have been found and have valid DATUM's
    if out.ResponseSignalsFound(i) && out.ResponseHasValidDatum(i)
        % Calculate index into the available input data
        thisIndex = sum(out.ResponseSignalsFound(1:i));
        % Is their data different - note implicit change if INPUT_DATA_CHANGED
        if out.INPUT_DATA_CHANGED || ...
                ~isequaln(dblRespOld(:, thisIndex), dblRespNew(:, thisIndex))
            out.ResponseSignalsChanged{end+1} = responseSignalNames{i};
        elseif i<=length(out.ResponsesToKeep)
            if out.ResponsesToKeep(i)
                out.ResponseSignalsUnchanged{end+1} = responseSignalNames{i};
            else
                out.ResponseSignalsChanged{end+1} = responseSignalNames{i};
            end
        else
            out.ResponseSignalsChanged{end+1} = responseSignalNames{i};
        end
    end
end



% iCreateMessages
function questMsg = iCreateMessages(T, changes)
% We have 2 messages to set up - one that relates to the actual re-fit
% process and what is happening and one that is the question we ask the
% user to tell them what is going to happen


% OK - What has happened to the data and how do we form the correct
% messages to the user? Need to take account of the current state of the
% testplan - it might not have any models yet

if changes.INPUT_SIGNAL_REMOVED
    % Worst case - An input signal has been removed and the user must
    % revert to the original testplan
    questMsg{1} = i_selectMessagePlurality( ...
        ['The model input signal %s is missing from the new data.  ', ...
        'This signal cannot be removed while it is being used in a model.'], ...
        ['The model input signals %s are missing from the new data.  ', ...
        'These signals cannot be removed while they are being used in a model.'], ...
        changes.InputSignalsMissing);
    questMsg{2} = '';
    questMsg{3} = 'Your changes will be discarded and the testplan will revert to using its previous data.';
    questMsg{4} = '';
elseif changes.RESPONSE_SIGNAL_REMOVED
    questMsg{1} = i_selectMessagePlurality( ...
        ['Missing response model %s: the response signal is missing.', ...
        'This signal cannot be removed while it is being used in a model.'],...
        ['Missing response models %s: the response signals are missing.', ...
        'These signals cannot be removed while it is being used in a model.'],...
        changes.ResponseSignalsMissing);
    questMsg{2} = '';
    questMsg{3} = 'Your changes will be discarded and the testplan will revert to using its previous data.';
    questMsg{4} = '';

else
    questMsg = {};
    if IsMatched(T) || numChildren(T)>0
        % Do any responses have missing datum's
        if ~isempty(changes.ResponseDatumMissing)
            questMsg{end+1} = i_selectMessagePlurality( ...
                'Missing response model %s: the datum model has been deleted.', ...
                'Missing response models %s: the datum model has been deleted.', ...
                changes.ResponseDatumMissing);
        end
        % We know that there are some responses left - what will the data
        % changes do to them
        if ~isempty(changes.ResponseSignalsChanged)
            if changes.INPUT_DATA_CHANGED
                questMsg{end+1} = 'Refit all response models: the input data has changed.';
            else
                RespNames= children(T,@varname);
                if ~isempty(T.Responses)
                    NewRespNames= cellfun(@varname,T.Responses,'UniformOutput',false);
                else
                    NewRespNames = {};
                end
                
                
                % responses to delete
                OKdelete = ~ismember(RespNames,NewRespNames);
                RespDelete = RespNames(OKdelete);
                if ~isempty(RespDelete)
                    questMsg{end+1} = i_selectMessagePlurality( ...
                        'Delete response model %s.', ...
                        'Delete response models %s.', ...
                        RespDelete);
                end
                
                % Responses to refit
                OKrefit = ismember(changes.ResponseSignalsChanged,RespNames(changes.ResponsesToKeep));
                RespToRefit = changes.ResponseSignalsChanged(OKrefit);
                if ~isempty(RespToRefit)
                    questMsg{end+1} = i_selectMessagePlurality( ...
                        'Refit response model %s: the response data has changed.', ...
                        'Refit response models %s: the response data has changed.', ...
                        RespToRefit);
                end   
                % models to rebuild
                OKrebuild = ismember(changes.ResponseSignalsChanged,RespNames(~changes.ResponsesToKeep));
                RespToRebuild = setdiff(changes.ResponseSignalsChanged(OKrebuild),RespDelete);
                if ~isempty(RespToRebuild)
                    questMsg{end+1} = i_selectMessagePlurality( ...
                        'Rebuild response model %s: the model has changed.', ...
                        'Rebuild response models %s: the models have changed.', ...
                        RespToRebuild);
                end
                
                % new responses
                AllRespProcessed = [RespToRebuild,RespToRefit RespDelete];
                NewResponses = setdiff(changes.ResponseSignalsChanged,AllRespProcessed);
                if ~isempty(NewResponses)
                    questMsg{end+1} = i_selectMessagePlurality( ...
                        'Build new response model for %s.', ...
                        'Build new response models for %s.', ...
                        NewResponses);
                end
            end
        elseif ~all(changes.ResponsesToKeep)
            RespDelete = children(T,~changes.ResponsesToKeep,@varname);
            questMsg{end+1} = i_selectMessagePlurality( ...
                'Delete response model %s.', ...
                'Delete response models %s.', ...
                RespDelete);
        end
    else
        % Do any responses have missing datum's
        if ~isempty(changes.ResponseDatumMissing)
            questMsg{end+1} = i_selectMessagePlurality( ...
                'Missing response model %s: the datum model has been deleted.', ...
                'Missing response models %s: the datum model has been deleted.', ...
                changes.ResponseDatumMissing);
        end
        if ~isempty([changes.ResponseSignalsChanged changes.ResponseSignalsUnchanged]);
            
            questMsg{end+1} = i_selectMessagePlurality( ...
                'Build response model %s.', ...
                'Build response models %s.', ...
                [changes.ResponseSignalsChanged changes.ResponseSignalsUnchanged]);
        end
    end
    
    % Are we going to update the actual design
    if changes.ACTUAL_DESIGN_CHANGED
        questMsg{end+1} = 'Update the experimental design "Actual Design".';
    end

    if changes.INPUT_DATA_CHANGED && ~isnull(T.ConstraintData)
        if isequal(factors(T),get(T.ConstraintData.getdata,'Name'))
            questMsg{end+1} = 'Refit all boundary models.';
        else
            questMsg{end+1} = 'Delete all boundary models.';
        end

    end
    valssf = valdata(T);
    if ~isempty(valssf) && ~changes.KeepValidationData
        questMsg{end+1} = 'Remove validation data.';
    end
    
    % If anything has changed with the data or design then ask the user if they
    % want to accept the changes
    if ~isempty(questMsg)
        % Add a bullet point to all messages
        for n = 1:length(questMsg)
            questMsg{n} = ['\bullet ', questMsg{n}];
        end
        % Tag on a prefix intro and postfix question
        questMsg = [{'The following changes need to be made to the project:'}, ...
            {''}, ...
            questMsg, ...
            {''}, ...
            {'Do you want to make these changes?'}, ...
            {''}];
    end
end

% i_commaDelimit
function str = i_commaDelimit(strings)
% Escape any tex characters
strings = detex(strings);
if isscalar(strings)
    str = strings{1};
else
    str = sprintf('%s, ', strings{1:end-1});  
    str = sprintf('%s and %s', str(1:end-2), strings{end});
end


% i_selectMessagePlurality
function str = i_selectMessagePlurality(singularMsg, pluralMsg, strings)
if isscalar(strings)
    str = sprintf(singularMsg, i_commaDelimit(strings));
else
    str = sprintf(pluralMsg, i_commaDelimit(strings));
end