www.gusucode.com > mbctools 工具箱 matlab 源码程序 > mbctools/+xregdatagui/MessageService.m

    classdef (CaseInsensitiveProperties) MessageService < mbcgui.multiview.AbstractMessageService
    %xregdatagui.MessageService class
    %    xregdatagui.MessageService properties:
    %       DataObject - Property is of type 'MATLAB array' (read only)
    %       DefaultProperties - Property is of type 'MATLAB array' (read only)
    %       DataObject - Property is of type 'MATLAB array' (read only)
    %       Sweepset - Property is of type 'MATLAB array' (read only)
    %       SweepsetWithBadData - Property is of type 'MATLAB array' (read only)
    %       SweepsetFilter - Property is of type 'MATLAB array' (read only)
    %       TestplanSweepsetFilter - Property is of type 'MATLAB array' (read only)
    %       Design - Property is of type 'MATLAB array' (read only)
    %       Model - Property is of type 'MATLAB array' (read only)
    %       ObjectName - Property is of type 'MATLAB array' (read only)
    %       GoodToBadIndexMap - Property is of type 'MATLAB array' (read only)
    %       isaSS - Property is of type 'bool' (read only)
    %       isaSSF - Property is of type 'bool' (read only)
    %       isaTSSF - Property is of type 'bool' (read only)
    %       isBadDataUpdated - Property is of type 'bool' (read only)
    %       CurrentClusterIndex - Property is of type 'double' (read only)
    %       SelectedTests - Property is of type 'MATLAB array' (read only)
    %       IsReadOnly - Property is of type 'bool'
    %
    %    xregdatagui.MessageService methods:
    %       addlistener - A short description of the function
    %       clearEventQueue - cancel the events in the event queue
    %       findListeners - find certain listeners from a listener array
    %       flushEventQueue - flushes all pending events in the MessageService queue
    %       getdesign - A short description of the function
    %       isEventPending - Check whether an event is queued for execution
    %       pFlushEventQueue - Flush all pending events in the MessageService queue
    %       queueEvent - a function to add events to the event queue
    %       setCurrentCluster - Set the current cluster index
    %       setDataObject - Set the data object in the data message service
    %       setSelectedTests - Set the selected tests
    
    %  Copyright 2015-2016 The MathWorks, Inc. and Ford Global Technologies, Inc.
    
    
    properties (AbortSet, SetObservable)
        %ISREADONLY Property is of type 'bool'
        IsReadOnly = false;
    end
    
    properties (Access=private)
        %Listeners store for listeners
        Listeners = [];
    end
    
    properties (SetAccess=protected, SetObservable)
        %EVENTQUEUE Property is of type 'MATLAB array' (read only)
        EventQueue = cell( 1, 0 );
        %DEFAULTPROPERTIES Property is of type 'MATLAB array' (read only)
        DefaultProperties = [];
        %DATAOBJECT Property is of type 'MATLAB array' (read only)
        DataObject = [];
        %SWEEPSET Property is of type 'MATLAB array' (read only)
        Sweepset = [];
        %CURRENTCLUSTERINDEX Property is of type 'double' (read only)
        CurrentClusterIndex = 0;
        %SELECTEDTESTS Property is of type 'MATLAB array' (read only)
        SelectedTests = [];
        %Actions data editor actions
        Actions
        %OutlierLine outlier line (with GUIDs) for data editor
        OutlierLine = [];
    end
    
    properties (SetAccess=protected, SetObservable)
        %SWEEPSETWITHBADDATA Property is of type 'MATLAB array' (read only)
        SweepsetWithBadData = [];
        %SWEEPSETFILTER Property is of type 'MATLAB array' (read only)
        SweepsetFilter = [];
        %TESTPLANSWEEPSETFILTER Property is of type 'MATLAB array' (read only)
        TestplanSweepsetFilter = [];
        %DESIGN Property is of type 'MATLAB array' (read only)
        Design = [];
        %MODEL Property is of type 'MATLAB array' (read only)
        Model = [];
        %OBJECTNAME Property is of type 'MATLAB array' (read only)
        ObjectName = [];
        %GOODTOBADINDEXMAP Property is of type 'MATLAB array' (read only)
        GoodToBadIndexMap = [];
        %ISASS Property is of type 'bool' (read only)
        isaSS
        %ISASSF Property is of type 'bool' (read only)
        isaSSF
        %ISATSSF Property is of type 'bool' (read only)
        isaTSSF
        %ISBADDATAUPDATED Property is of type 'bool' (read only)
        isBadDataUpdated = false;
    end
    
    properties (Dependent,SetAccess=private)
        %HasExtras extras are installed
        HasExtras
        %isOneStage data is defined as one-stage data 
        isOneStage
    end
    
    
    events
        dmsDataTypeChanged
        ssDataChanged
        ssRecordsChanged
        ssNamesChanged
        ssUnitsChanged
        ssTestsChanged
        ssFilenameChanged
        ssfModifyDataChanged
        ssfVariablesChanged
        ssfFiltersChanged
        ssfTestDefinitionChanged
        ssfSweepVariablesChanged
        ssfSweepFiltersChanged
        ssfSweepOrderChanged
        ssfSweepNotesChanged
        ssfNameChanged
        ssfResamplingChanged
        ssfBadDataChanged
        tssfExcludedDataChanged
        tssfActualDesignChanged
        tssfClustersCreated
        tssfClustersChanged
        SelectedTestsChanged
        CurrentClusterChanged
        %Busy notify busy status to app so it can change the figure pointer
        Busy
        %Idle notify idle status to app so it can change the figure pointer
        Idle
    end  % events
    
    methods  % constructor block
        function obj = MessageService(varargin)
        %MessageService constructor for the MessageService object
        %  OBJ = MessageService(VARARGIN)
        
        % Initialize DefaultProperties.  This was a Factory default value but it is
        % a little too heavyweight a structure for this, plus we only ever
        % actually create one of these objects at a time anyway.
        obj.DefaultProperties = struct('Sweepset', sweepset,...
            'SweepsetWithBadData', [],...
            'SweepsetFilter', sweepsetfilter,...
            'TestplanSweepsetFilter', testplansweepsetfilter,...
            'Design', xregdesign,...
            'Model', [],...
            'ObjectName', '',...
            'GoodToBadIndexMap', []);
        
        % Need to ensure that the outputs are set
        obj.pSetDataObject(0);
        
        % Listener that ensures the selected tests vector is valid
        obj.Listeners = [event.listener(obj, 'ssTestsChanged', @iUpdateSelectedTests);
            event.listener(obj, 'tssfClustersCreated', @iUpdateCurrentCluster)
            event.listener(obj, 'ssfTestDefinitionChanged', @iUpdateTestDefinition)];
        
        % create outlier line
        obj.OutlierLine = xregdatagui.OutlierLine;
        % create actions
        obj.Actions = xregdatagui.Actions(obj);
        
        end  % MessageService
        
    end  % constructor block
    
    methods
        function set.DataObject(obj,value)
        obj.DataObject = pSetDataObject(obj,value);
        end
        
        function value = get.SweepsetWithBadData(obj)
        value = i_getSweepsetWithBadData(obj,obj.sweepsetwithbaddata);
        end
        
        function value = get.GoodToBadIndexMap(obj)
        value = i_getGoodToBadIndexMap(obj,obj.GoodToBadIndexMap);
        end
        
        function ok = get.HasExtras(obj) %#ok<MANU>
        %get.HasExtras (actually Ford Extras)
        ext = mbcextensions.Extensions.Model;
        ok = any( strncmpi(ext.AddOnNames,'Ford',4) );
        end
        
        function ok = get.isOneStage(obj)
        if obj.hasData 
            if obj.isaSSF
                % uses one test per record setting in sweepsetfilter as this
                % is more reliable in the case of empty data
                ok = isOneStage(obj.SweepsetFilter); %#ok<CPROP>
            else
                ok = isOneStage(obj.Sweepset); %#ok<CPROP>
            end
        else
            ok = false;
        end
        
        end
        
    end   % set and get functions
    
    methods  % public methods
        
        %----------------------------------------
        function ret = hasData(obj) %#ok<MANU>
        %HASDATA Check whether the MessageService contains viewable data
        %  RET = HASDATA(OBJ) returns true if the MessagerService object contains
        %  a data object that can provide any viewing data.
        
        ret = true;
        
        end  % hasData
        
        function busy(ms,msg)
        %busy send event that MessageService is busy
        
        if nargin<2
            msg = '';
        end
        data.Message = msg;
        evt = xregEventData(data);
        notify(ms,'Busy',evt);
        end
        
        function idle(ms)
        %idle send event that MessageService is idle
        notify(ms,'Idle');
        end
        
        %----------------------------------------
        function clearEventQueue(obj)
        %CLEAREVENTQUEUE cancel the events in the event queue
        %  CLEAREVENTQUEUE(OBJ)
        
        obj.EventQueue = cell(1,0);
        end  % clearEventQueue
        
        %----------------------------------------
        function [lArrayOut] = findListeners(obj, lArrayIn, type, varargin)
        %FINDLISTENERS find certain listeners from a listener array
        %  LARRAYOUT = FINDLISTENERS(OBJ, LARRAYIN, TYPE, VARARGIN)
        
        % Which listeners have been requested
        switch lower(type)
            case 'sweepset'
                % Find all listeners which have and event of the form 'ss' followed by
                % an uppercase character and the SourceObject as this DMS
                lArrayOut = lArrayIn.findobj('-depth', 0,...
                    {'-regexp', 'EventName', '^ss[A-Z]'},...
                    '-and', 'Source', {obj},...
                    '-and', varargin{:});
            case 'sweepsetfilter'
                % Find all listeners which have and event of the form 'ssf' followed by
                % an uppercase character and the SourceObject as this DMS
                lArrayOut = lArrayIn.findobj('-depth', 0, {'-regexp', 'EventName', '^ssf[A-Z]'}, '-and', 'Source', {obj});
            case 'testplansweepsetfilter'
                % Find all listeners which have and event of the form 'tssf' followed by
                % an uppercase character and the SourceObject as this DMS
                lArrayOut = lArrayIn.findobj('-depth', 0, {'-regexp', 'EventName', '^tssf[A-Z]'}, '-and', 'Source', {obj});
            otherwise
                % Return nothing by default
                lArrayOut = [];
        end
        
        end  % findListeners
        
        %----------------------------------------
        function flushEventQueue(obj, DataObject)
        %FLUSHEVENTQUEUE flushes all pending events in the MessageService queue
        %  FLUSHEVENTQUEUE(OBJ, DATAOBJECT)
        
        if nargin<2
            DataObject = obj.DataObject;
        end
        % Check that the object type hasn't been changed
        if isequal(class(obj.DataObject), class(DataObject))
            % Is the message service Read-Only
            if obj.IsReadOnly
                warning(message('mbc:xregtools:datamessageservice:InvalidState'));
                obj.clearEventQueue;
            else
                obj.pFlushEventQueue(DataObject);
            end
        else
            error(message('mbc:xregtools:datamessageservice:InvalidArgument', class( obj.DataObject )));
        end
        
        end  % flushEventQueue
        
        %----------------------------------------
        function [out] = getdesign(obj)
        %GETDESIGN get test plan design
        %  OUT = GETDESIGN(IN)
        
        % This method exists to mimic the functionallity in xregdesgui.designpackage
        % which provides a method for retreiving the current design
        out = obj.Design;
        end  % getdesign
        
        %----------------------------------------
        function ret = isEventPending(obj, eventname)
        %ISEVENTPENDING Check whether an event is queued for execution
        %  RET = ISEVENTPENDING(OBJ, EVENTNAME) where EVENTNAME is either a string
        %  or a cell array of strings returns a boolean vector indicating whether
        %  each event specified is currently queued ready for execution.
        
        ret = ismember(eventname, obj.EventQueue);
        
        end  % isEventPending
        
        %----------------------------------------
        function pFlushEventQueue(obj, dataObject)
        %PFLUSHEVENTQUEUE Flush all pending events in the MessageService queue
        %  PFLUSHEVENTQUEUE(OBJ, DATAOBJECT)
        
        % Set the current data properties and then flush all events
        if nargin>1
            obj.DataObject = dataObject;
        end
        if obj.isOneStage && ~isequal(1:size(dataObject,3),obj.SelectedTests)
            % make sure all tests are selected for ones-stage data
            obj.SelectedTests = 1:size(dataObject,3);
            obj.queueEvent('SelectedTestsChanged');
        end        
        
        % reorder the events in the queue
        % the desired order for events is:
        eventOrder = {'dmsDataTypeChanged',...
            'tssfExcludedDataChanged',...
            'tssfActualDesignChanged',...
            'tssfClustersCreated',...
            'tssfClustersChanged',...
            'ssfModifyDataChanged',...
            'ssfVariablesChanged',...
            'ssfFiltersChanged',...
            'ssfTestDefinitionChanged',...
            'ssfSweepVariablesChanged', ...
            'ssfSweepFiltersChanged',...
            'ssfSweepOrderChanged',...
            'ssfSweepNotesChanged',...
            'ssfResamplingChanged', ...
            'ssfNameChanged',...
            'ssFilenameChanged',...
            'ssRecordsChanged',...
            'ssNamesChanged',...
            'ssUnitsChanged',...
            'ssTestsChanged',...
            'ssfBadDataChanged',...
            'ssDataChanged', ...
            'SelectedTestsChanged', ...
            'CurrentClusterChanged'};
        
        % NOTE - there could be some debate over the positioning of the ssfBadDataChanged
        % event. Since it relates to changes in something which mimics ssDataChanged it
        % currently sits just before ssDataChanged. I don't think this will have any long
        % term repercussions, but I could be proved wrong. JLM 28/07/03
        
        ind = ismember(eventOrder, obj.EventQueue);
        obj.EventQueue = eventOrder(ind);
        
        % Drain the event queue if there are any events in it
        while ~isempty(obj.EventQueue)
            % Get the first event
            eventName = obj.EventQueue{1};
            % Remove it from the queue
            obj.EventQueue(1) = [];
            % Generate the event data to send with the event
            eventData = event.EventData;
            % Send the event
            notify(obj, eventName, eventData);
        end
        enable(obj.Actions);

        end  % pFlushEventQueue
        
        %----------------------------------------
        function queueEvent(obj, varargin)
        %QUEUEEVENT a function to add events to the event queue
        %  QUEUEEVENT(OBJ, EVENT)
        
        % Setup the valid events array
        validEvents = true(length(varargin), 1);
        evtList = events(obj);
        for i = 1:length(varargin)
            thisEvent = varargin{i};
            % Check that the event is valid for the MessageService
            if ~any(strcmp(thisEvent,evtList));
                warning(message('mbc:datamessageservice:InvalidArgument', thisEvent));
                % Remove the offending event
                validEvents(i) = false;
            end
        end
        % Gte the valid events
        queuedEvents = varargin(validEvents);
        % Append those not already in the event queue
        obj.EventQueue = [obj.EventQueue queuedEvents(~ismember(queuedEvents, obj.EventQueue))];
        
        end  % queueEvent
        
        %----------------------------------------
        function setCurrentCluster(obj, val)
        %SETCURRENTCLUSTER Set the current cluster index
        %  SETCURRENTCLUSTER(OBJ, VAL)
        
        if ~isequal(obj.CurrentClusterIndex,val)
            obj.CurrentClusterIndex = val;
            obj.queueEvent('CurrentClusterChanged');
            obj.pFlushEventQueue(obj.DataObject);
        end
        end  % setCurrentCluster
        
        %----------------------------------------
        function setDataObject(obj, val)
        %SETDATAOBJECT Set the data object in the data message service
        %  SETDATAOBJECT(OBJ, VAL)
        
        % Is it any different to the current data object
        if builtin('isequaln', val, obj.DataObject)
            return
        end
        
        % Check if the new object and the old object are of the same type and
        % queue a type changed event if the classes are different. This will give
        % view's an opertunity to respond to a changed in type
        if ~isequal(class(obj.DataObject), class(val))
            obj.queueEvent('dmsDataTypeChanged');
        end
        
        % Queue all events
        obj.queueEvent('ssDataChanged',...
            'ssRecordsChanged',...
            'ssNamesChanged',...
            'ssUnitsChanged',...
            'ssTestsChanged',...
            'ssFilenameChanged',...
            'ssfModifyDataChanged',...
            'ssfVariablesChanged',...
            'ssfFiltersChanged',...
            'ssfTestDefinitionChanged',...
            'ssfSweepVariablesChanged', ...
            'ssfSweepFiltersChanged',...
            'ssfSweepOrderChanged',...
            'ssfSweepNotesChanged',...
            'ssfNameChanged',...
            'ssfResamplingChanged', ...
            'tssfExcludedDataChanged',...
            'tssfActualDesignChanged',...
            'tssfClustersCreated',...
            'tssfClustersChanged');
        
        % Flush the event queue
        obj.pFlushEventQueue(val);
        
        end  % setDataObject
        
        %----------------------------------------
        function setSelectedTests(obj, val)
        %SETSELECTEDTESTS Set the selected tests
        %  SETSELECTEDTESTS(OBJ, VAL)
        
        if ~isequal(obj.SelectedTests,val)
            obj.SelectedTests = val;
            obj.queueEvent('SelectedTestsChanged');
            obj.pFlushEventQueue(obj.DataObject);
        end
        end  % setSelectedTests
        
        %----------------------------------------
        function s = getDataName(obj)
        
        s = obj.ObjectName;
        end
        
        %----------------------------------------
        function updateViews(obj,SelectedTests)
        %updateViews force update all views
        ss = obj.Sweepset;
        if obj.isOneStage
            SelectedTests = 1:size(ss,3);
        else
            SelectedTests(SelectedTests>size(ss,3)) = [];
            if isempty(SelectedTests)
                SelectedTests = min(1,size(ss,3));
            end
        end
        
        obj.SelectedTests = SelectedTests;
        obj.queueEvent('SelectedTestsChanged');
        obj.queueEvent('ssDataChanged');
        obj.pFlushEventQueue(obj.DataObject);
        
        end
        
    end  % public methods
    
    
    methods (Hidden) % possibly private or hidden
        %----------------------------------------
        function val = pSetDataObject(obj, val)
        %PSETDATAOBJECT
        %  VAL = PSETDATAOBJECT(OBJ, VAL)
        
        % By default all typecase fields are double empty
        s = obj.DefaultProperties;
        % What to do with an input?
        if isa(val, 'sweepset')
            s.Sweepset = val;
            s.ObjectName = get(val, 'FileName');
            if size(s.ObjectName, 1) > 1
                s.ObjectName = s.ObjectName(1,:);
            end
        end
        if isa(val, 'sweepsetfilter')
            s.Sweepset = sweepset(val);
            s.SweepsetFilter = val;
            s.ObjectName = get(val, 'Label');
        end
        if isa(val, 'testplansweepsetfilter')
            s.TestplanSweepsetFilter = val;
        end
        
        % Copy everything across - Note that we EXPLICITLY set the flag to
        % indicate that the bad data needs updateing
        set(obj,'Sweepset', s.Sweepset,...
            'SweepsetWithBadData', s.SweepsetWithBadData,...
            'SweepsetFilter', s.SweepsetFilter,...
            'TestplanSweepsetFilter', s.TestplanSweepsetFilter,...
            'Design', s.Design,...
            'Model', s.Model,...
            'ObjectName', s.ObjectName,...
            'GoodToBadIndexMap', s.GoodToBadIndexMap,...
            'isBadDataUpdated', false,...
            'isaSS', isa(val, 'sweepset'),...
            'isaSSF', isa(val, 'sweepsetfilter'),...
            'isaTSSF', isa(val, 'testplansweepsetfilter'));
        
        end  % pSetDataObject
        
        %----------------------------------------
        function output = pSetSweepsetWithBadData(obj, returnArgument)
        %PSETSWEEPSETWITHBADDATA
        
        dataObject = obj.DataObject;
        % Proceed differently if the dataObject is a sweepsetfilter or sweepset
        if isa(dataObject, 'sweepset')
            ss = dataObject;
            indexMap = 1:size(ss, 3);
        elseif isa(dataObject, 'sweepsetfilter')
            [ss, indexMap] = sweepsetWithBadData(dataObject);
        else
            ss = sweepset;
            indexMap = [];
        end
        % Set the fields for next time
        set(obj,'SweepsetWithBadData', ss,...
            'GoodToBadIndexMap', indexMap,...
            'isBadDataUpdated', true);
        
        % Which argument to return
        if returnArgument == 1
            output = ss;
        else
            output = indexMap;
        end
        end  % pSetSweepsetWithBadData
        
    end  % possibly private or hidden
    
end  % classdef

% ------------------------------------------------------------------------------
% Note - get functions can become highly recursive if you try and set the
% value inside so make sure they NEVER recurse
% ------------------------------------------------------------------------------
function val = i_getGoodToBadIndexMap(obj, val)
if ~obj.isBadDataUpdated
    RETURN_INDEX = 2;
    val = pSetSweepsetWithBadData(obj, RETURN_INDEX);
end
end  % i_getGoodToBadIndexMap

% ------------------------------------------------------------------------------
function val = i_getSweepsetWithBadData(obj, val)
if ~obj.isBadDataUpdated
    RETURN_SWEEPSET = 1;
    val = pSetSweepsetWithBadData(obj, RETURN_SWEEPSET);
end
end  % i_getSweepsetWithBadData

function iUpdateSelectedTests(dms, ~)

% Get sweepset
ss = dms.Sweepset;
tn = testnum(ss);

% Find all values that are smaller than the new tests
sel = dms.SelectedTests;
valuesToKeep = (sel <= length(tn));
% Are any of the values outside the new range
if ~all(valuesToKeep)
    % Only keep valid indices
    dms.setSelectedTests(sel(valuesToKeep));
end
end  % iUpdateSelectedTests

function iUpdateCurrentCluster(dms, ~)
% Quietly reset the current cluster - we don't want to generate more events
% while in the ClustersCreated event.
dms.CurrentClusterIndex = 0;
end  % iUpdateCurrentCluster

function iUpdateTestDefinition(dms,~)
%iUpdateTestDefinition test definition has changed so need to change
%selected tests
if dms.isOneStage
    % select all tests if one-stage
    dms.SelectedTests = 1:size(dms.Sweepset,1);
elseif ~isscalar(dms.SelectedTests)
    % select first test for two-stage
    dms.SelectedTests = 1;
end
end