gusucode.com > vnt工具箱matlab源码程序 > vnt/vnt/+j1939/ParameterGroup.m

    classdef ParameterGroup < hgsetget & ...
                          matlab.mixin.CustomDisplay & ...
                          vnt.internal.mixin.message.Extractions & ...
                          vnt.internal.mixin.message.SignalAccess
% ParameterGroup Handle to a J1939 parameter group.
%
%   This class represents a J1939 parameter group. It contains properties to
%   act as a parameter group using CAN database files providing a signals 
%   based interface. The methods provided by the class allow for 
%   manipulation of parameter group data as well as various analysis 
%   assisting functionality.
%
%   See also VNT.

% Authors: Jaremy Pyle
% Copyright 2015 The MathWorks, Inc.

properties (SetAccess = 'private')
    % PGN - The J1939 parameter group number address of this parameter group.
    PGN
    % PDUFormatType - The J1939 protocol data unit format of this parameter group.
    PDUFormatType
end

properties (Dependent)
    % Priority - The J1939 priority of this parameter group.
    Priority
    % SourceAddress - The J1939 source address of this parameter group.
    SourceAddress
    % DestinationAddress - The J1939 destintation address of this parameter group.
    DestinationAddress
    % Data - Parameter group data array.
    Data
    % Signals - A structure of the signals contained within the
    %   parameter group with a named field for each signal.
    Signals
end

properties (Dependent, SetAccess = 'private')
    % ID - The numeric CAN identifier of the parameter group.
    ID
    % Name - The symbolic name of the parameter group.
    Name
    % Database - A handle to a CAN database file containing parameter group and
    %   signal information for this parameter group.
    Database
    % Timestamp - The hardware logged time of reception for the parameter group.
    Timestamp
end

properties
    % UserData - Storage for custom information.
    UserData
end

properties (SetAccess = 'private', GetAccess = 'private')
    % DatabaseLength - The length in bytes of the message as defined in the
    %   database file.
    DatabaseLength
    % SignalEndBit - An array of values containing the ending bit
    %   number of each signal in the parameter group.
    SignalEndBit
    % Version - Indicates the version number of the class for
    %   determining the difference between current and out of date
    %   versions of parameter group objects.
    Version
end

% Internal property private storage. Used because some properties are
% related in their set/get operations and to bypass the overhead of
% validation when referencing properties internally.
properties (Hidden, SetAccess = protected)
    PrivatePriority
    PrivateSourceAddress
    PrivateDestinationAddress
    PrivateID
    PrivateExtended = true;
    PrivateError = false;
    PrivateRemote = false;
    PrivateTimestamp
    PrivateData
    PrivateDatabase
    PrivateName
end

properties (Hidden, Dependent, SetAccess = protected)
    PrivateRawStruct
end

methods
    
    function obj = ParameterGroup(varargin)
    % Message Construct a J1939 parameter group.
        
        % Validate the input arguments.
        narginchk(2, 2);

        % Copy the input to local variables.
        database = varargin{1};

        % Build the parameter group directly from a structure.
        % PG = ParameterGroup(DATABASE, STRUCT)
        if isstruct(varargin{2})
            % Copy the input to local variables.
            msgStruct = varargin{2};
            
            % Create parameter group objects equal in count to the number of
            % structures received which will be configured and
            % returned from the constructor.
            obj = j1939.ParameterGroup.newarray(1, numel(msgStruct));
            
            % Build a logical array for removing non-J1939 entries.
            removeIndexes(numel(msgStruct)) = false;
            
            % Curate the structures for J1939 details.
            for ii = 1:numel(msgStruct)
                % If the entry is not an extended identifier or is an error
                % or remote frame then flag to remove this entry.
                if ~msgStruct(ii).Extended || msgStruct(ii).Error || msgStruct(ii).Remote
                    removeIndexes(ii) = true;
                    continue;
                end
                
                % Parse out the PGN from the ID, along with other
                % parameters that may be required later.
                msgStruct(ii).ID = uint32(msgStruct(ii).ID);
                [priority, sourceAddress, destinationAddress, pgn] = ...
                    mexJ1939Utility('getSubsetForConstruction', msgStruct(ii).ID);
                % Check for this PGN in the database.
                msgInfo = privateMessageInfoByPGN(database, pgn);
                % If the PGN was not found, flag to remove this entry.
                if isempty(msgInfo)
                    removeIndexes(ii) = true;
                    continue;
                end
                    
                % Load the pertinent J1939 identifier parameters.
                obj(ii).PrivatePriority = priority;
                obj(ii).PrivateSourceAddress = sourceAddress;
                obj(ii).PGN = msgInfo.J1939.PGN;
                
                % Set some values depending on the protocol data unit type.
                if msgInfo.J1939.PDUFormatType == 1
                    % For PDU type 1, the destination address is used.
                    obj(ii).PrivateDestinationAddress = destinationAddress;
                    obj(ii).PDUFormatType = j1939.Utility.PDUFormatType1;
                else
                    % For PDU type 2, destination address is not used.
                    obj(ii).PrivateDestinationAddress = j1939.Utility.DestinationAll;
                    obj(ii).PDUFormatType = j1939.Utility.PDUFormatType2;
                end
                
                % Load the general parameters.
                obj(ii).PrivateID = msgStruct(ii).ID;
                obj(ii).PrivateTimestamp = msgStruct(ii).Timestamp;
                obj(ii).PrivateData = msgStruct(ii).Data;
                obj(ii).PrivateDatabase = database;
                obj(ii).PrivateName = msgInfo.Name;
                obj(ii).Version = 1.0;

                % Load the signal integrity details.
                obj(ii).DatabaseLength = msgInfo.Length;
                obj(ii).calculateSignalDetails(msgInfo);
            end
            
            % Remove the non-J1939 entries from the output array.
            obj(removeIndexes) = [];
            
        % Create a single J1939 parameter group object from database information.
        % PG = ParameterGroup(DATABASE, NAME)
        else
            % Copy the input to local variables.
            msgName = varargin{2};
            
            % Validate the database argument.
             if ~isa(database, 'can.Database') || ~database.Usable
                error(message('vnt:J1939:InvalidDatabase'));
             end
            
             % Ensure that the supplied database is for a J1939 network.
             if ~database.DatabaseTypeJ1939
                error(message('vnt:J1939:DatabaseNotJ1939'));
             end
             
            % Validate the name argument.
            validateattributes(msgName, {'char'}, ...
                {'nonempty', 'row'}, ...
                'Message', 'NAME');
            
            % Check for the parameter group in the database.
            msgInfo = privateMessageInfoByName(database, msgName);
            % Error on an unfound parameter group.
            if isempty(msgInfo)
                error(message('vnt:J1939:ParameterGroupNotFoundInDatabase'));
            end
            
            % Load the pertinent J1939 identifier parameters.
            obj.PrivatePriority = msgInfo.J1939.Priority;
            obj.PrivateSourceAddress = msgInfo.J1939.SourceAddress;
            obj.PGN = msgInfo.J1939.PGN;

            % Set some values depending on the protocol data unit type.
            if msgInfo.J1939.PDUFormatType == 1
                % For PDU type 1, the destination address is used.
                obj.PrivateDestinationAddress = msgInfo.J1939.PDUSpecific;
                obj.PDUFormatType = j1939.Utility.PDUFormatType1;
            else
                % For PDU type 2, destination address is not used.
                obj.PrivateDestinationAddress = j1939.Utility.DestinationAll;
                obj.PDUFormatType = j1939.Utility.PDUFormatType2;
            end
    
            % Load the general parameters.
            obj.PrivateID = uint32(msgInfo.ID);
            obj.PrivateTimestamp = 0;
            obj.PrivateData = repmat(j1939.Utility.FillByte, 1, msgInfo.Length);
            obj.PrivateDatabase = database;
            obj.PrivateName = msgInfo.Name;
            obj.Version = 1.0;
            
            % Load the signal integrity details.
            obj.DatabaseLength = msgInfo.Length;
            obj.calculateSignalDetails(msgInfo);
        end
    end
    
    function [extracted, remainder] = extractAll(obj, value)
    % extractAll Returns all occurrences of the specified parameter group(s).
    %
    %   [EXTRACTED, REMAINDER] = extractAll(PG, PG_NAMES) parses the given
    %   array PG and returns all parameter groups found with the matched PG_NAMES.
    %
    %   Inputs:
    %       PG - The parameter groups to parse.
    %       PG_NAMES - The string name(s) of the parameter groups to extract.
    %
    %   Outputs:
    %       EXTRACTED - A parameter group array containing all found instances of
    %           the given group(s). If no matches were found, then EXTRACTED
    %           will return as empty.
    %       REMAINDER - A parameter group array containing all groups from the
    %           original PG array not matching the specified input.
    %
    %   Note that PG_NAMES can be given as a cell array. For example, if PG_NAMES 
    %   is passed as {'PG_NAME1' 'PG_NAME2'}, extractAll returns every instance of 
    %   both parmaeter groups as found in the PG array.
    %
    %   Also note that REMAINDER is an optional output. If a variable for
    %   REMAINDER is not specified, only the extracted parameter groups are returned.
    %
    %   Examples:
    %       [extractedPGs, remainder] = extractAll(parameterGroups, 'PG1')
    %       extractedPGs = extractAll(parameterGroups, 'PG1')
    %       [extractedPGs, remainder] = extractAll(parameterGroups, {'PG1' 'PG2'})
    %
    %   See also VNT.
        
        % Check the argument count.
        narginchk(2, 2);

        % Validate the input and convert from numbers to names.
        desiredNames = obj.validatePGInput(value);

        % Run the extraction using the mixin method.
        [extracted, remainder] = obj.doExtractAllByName(desiredNames);
    end
    
    function extracted = extractRecent(obj, varargin)
    % extractRecent Returns the most recent occurrence of the specified parameter group(s).
    %
    %   EXTRACTED = extractRecent(PG) parses the array PG and returns 
    %   the most recent instance of each unique parameter group found
    %   in the array.
    %
    %   EXTRACTED = extractRecent(PG, PG_NAMES) parses the given
    %   array PG and returns the most recent instance of parameter groups 
    %   found with the matched PG_NAMES.
    %
    %   Inputs:
    %       PG - The parameter groups to parse.
    %       PG_NAMES - The string name(s) of the parameter groups to extract.
    %
    %   Outputs:
    %       EXTRACTED - A parameter group array which are the most recent occurrence of
    %           the requested group(s) based on the Timestamp. If no matches
    %           were found, then EXTRACTED will return as empty.
    %
    %   Note that PG_NAMES can be given as a cell array. For example, if PG_NAMES 
    %   is passed as {'PG_NAME1' 'PG_NAME2'}, extractAll returns every instance of 
    %   both parmaeter groups as found in the PG array.
    %
    %   Examples:
    %       extractedPGs = extractRecent(parameterGroups)
    %       extractedPGs = extractRecent(parameterGroups, 'PG1')
    %       extractedPGs = extractRecent(parameterGroups, {'PG1' 'PG2'})
    %
    %   See also VNT.

        % Check the argument count.
        narginchk(1, 2);
        
        if nargin == 1
            % Get all of the unique names from the input objects.
            desiredNames = unique({obj.Name});
        else
            % Validate the input and convert from numbers to names.
            desiredNames = obj.validatePGInput(varargin{1});
        end

        % Run the extraction using the mixin method.
        extracted = obj.doExtractRecentByName(desiredNames);
    end
    
    function extracted = extractTime(obj, startTime, endTime)
    % extractTime Returns all parameter groups having occurred within a time period.
    %
    %   EXTRACTED = extractTime(PG, STARTTIME, ENDTIME) parses the
    %   array PG and returns all parameter groups found to be within
    %   the time period bounded inclusively by STARTTIME and ENDTIME.
    %
    %   Inputs:
    %       PG - The parameter groups to parse.
    %       STARTTIME - The beginning of the time range specified in seconds.
    %       ENDTIME - The end of the time range specified in seconds.
    %
    %   Outputs:
    %       EXTRACTED - A parameter group array containing all parameter 
    %           groups having timestamps within the specified time range. If
    %           no groups are found within the time range, EXTRACTED returns
    %           as empty.
    %
    %   Note that the time range specified must be given in increasing order
    %   from STARTTIME to ENDTIME. Inf is also an accepted value for ENDTIME
    %   to specify the largest available time. 0 is the earliest accepted
    %   STARTTIME.
    %
    %   Examples:
    %       extractedPGs = extractTime(parameterGroups, 5, 10.5)
    %       extractedPGs = extractTime(parameterGroups, 0, 60)
    %       extractedPGs = extractTime(parameterGroups, 150, Inf)
    %
    %   See also VNT.
        
        % Check the argument count.
        narginchk(3, 3);
        
        % Validate startTime and endTime.
        validateattributes(startTime, {'numeric'}, ...
            {'finite', 'nonempty', 'nonnan', 'nonnegative', ...
             'nonsparse', 'real', 'scalar'}, ...
            'extractTime', 'STARTTIME');
        validateattributes(endTime, {'numeric'}, ...
            {'nonempty', 'nonnan', 'nonnegative', 'nonsparse', ...
             'real', 'scalar'}, ...
            'extractTime', 'ENDTIME');
        
        % Validate that the start time is earlier than the end time.
        if (startTime > endTime)
            error(message('vnt:J1939:InvalidTimeRange'));
        end
        
        % Run the extraction using the mixin method.
        extracted = obj.doExtractByTime(startTime, endTime);
    end
    
    function out = get.Priority(obj)
        out = obj.PrivatePriority;
    end
    
    function set.Priority(obj, value)
        % Validate the new value.
        validateattributes(value, {'numeric'}, ...
            {'finite', 'nonempty', 'nonnan', 'nonnegative', ...
             'nonsparse', 'real', 'scalar', '>=', 0, '<=', 7}, ...
            'set.Priority', 'VALUE');

        % Store the property value in its internal property.
        obj.PrivatePriority = uint32(value);
        % Set the value into the J1939 parameter group identifier.
        obj.PrivateID = j1939.Utility.setPDUField('Priority', ...
            obj.PrivatePriority, ...
            obj.PrivateID);
    end
    
    function out = get.DestinationAddress(obj)
        out = obj.PrivateDestinationAddress;
    end
    
    function set.DestinationAddress(obj, value)
        % Validate the new value.
        validateattributes(value, {'numeric'}, ...
            {'finite', 'nonempty', 'nonnan', 'nonnegative', ...
             'nonsparse', 'real', 'scalar', '>=', 0, '<=', 255}, ...
            'set.DestinationAddress', 'VALUE');

        % Only allow setting of the destination address when the parameter
        % group is of PDU format type 2.
        if strcmpi(obj.PDUFormatType, j1939.Utility.PDUFormatType2)
            error(message('vnt:J1939:DestinationAddressNotApplicable'));
        end
        
        % Store the property value in its internal property.
        obj.PrivateDestinationAddress = uint32(value);
        % Set the value into the J1939 parameter group identifier.
        obj.PrivateID = j1939.Utility.setPDUField('PDUSpecific', ...
            obj.PrivateDestinationAddress, ...
            obj.PrivateID);
    end
    
    function out = get.SourceAddress(obj)
        out = obj.PrivateSourceAddress;
    end
    
    function set.SourceAddress(obj, value)
        % Validate the new value.
        validateattributes(value, {'numeric'}, ...
            {'finite', 'nonempty', 'nonnan', 'nonnegative', ...
             'nonsparse', 'real', 'scalar', '>=', 0, '<=', 255}, ...
            'set.SourceAddress', 'VALUE');

        % Store the property value in its internal property.
        obj.PrivateSourceAddress = uint32(value);
        % Set the value into the J1939 parameter group identifier.
        obj.PrivateID = j1939.Utility.setPDUField('SourceAddress', ...
            obj.PrivateSourceAddress, ...
            obj.PrivateID);
    end
    
    function out = get.Name(obj)
        out = obj.PrivateName;
    end
    
    function out = get.Database(obj)
        out = obj.PrivateDatabase;
    end
    
    function out = get.ID(obj)
        out = obj.PrivateID;
    end
    
    function out = get.Timestamp(obj)
        out = obj.PrivateTimestamp;
    end
    
    function out = get.Data(obj)
        out = obj.PrivateData;
    end
    
    function set.Data(obj, value)
        % Validate the new value for proper type.
        validateattributes(value, {'numeric'}, ...
            {'finite', 'integer', 'nonempty', 'nonnan', 'nonnegative', ...
             'nonsparse', 'real', 'row'}, ...
            'set.Data', 'VALUE');
        
        % Some parameter groups in J1939 allow for variable length data, 
        % so we allow changing of the data length by the user. We do warn
        % though if the length is changed to something different than what
        % is defined in the database just in case the change was being made
        % in error.
        if numel(value) ~= obj.DatabaseLength
            warning(message('vnt:J1939:DataLengthChanged'));
        end
        
        % Set the property while casting to uint8 type.
        obj.PrivateData = uint8(value);
    end
    
    function out = get.Signals(obj)
        % Use the mixin function to get the signal values.
        out = obj.getSignalValues();
    end
    
    function set.Signals(obj, value)
        % Use the mixin function to set the signal values.
        obj.setSignalValues(value);
    end
    
    function out = get.PrivateRawStruct(obj)
        % Return a structure of the base fields required to minimally
        % represent this parameter group as a CAN message.
        out.ID = obj.PrivateID;
        out.Extended = obj.PrivateExtended;
        out.Error = obj.PrivateError;
        out.Remote = obj.PrivateRemote;
        out.Timestamp = obj.PrivateTimestamp;
        out.Data = obj.PrivateData;
    end
    
end


methods (Access = protected)
    
    function group = getPropertyGroups(obj) %#ok<MANU>
    % getPropertyGroups Build custom display for the class.

        % Build the property sections for display.
        title1 = sprintf('Protocol Data Unit Details:\n   ---------------------------');
        plist1 = {'Name', 'PGN', 'Priority', 'PDUFormatType', 'SourceAddress', 'DestinationAddress'};
        title2 = sprintf('Data Details:\n   -------------');
        plist2 = {'Timestamp', 'Data', 'Signals'};
        title3 = sprintf('Other Information:\n   ------------------');
        plist3 = {'UserData'};
        
        % Set the property groups.
        group(1) = matlab.mixin.util.PropertyGroup(plist1, title1);
        group(2) = matlab.mixin.util.PropertyGroup(plist2, title2);
        group(3) = matlab.mixin.util.PropertyGroup(plist3, title3);
    end
    
    function validity = isSignalValid(obj, signalIndex)
    % isSignalValid Verifies signal integrity.
    %
    %   This method is used to perform any required signal integrity
    %   checking needed when reading or writing signals values. In the
    %   case of a J1939 parameter group, this method is used to invalidate
    %   any signals for which there is not enough to actually determine
    %   their value in the case of variable length data.

        % Check signal integrity. Due to some parameter groups having
        % variable length data, not all defined signals may be validaly
        % unpacked. The Vector CANdbLib will unpack every signal though
        % regardless of length. We want to check for any signals with
        % locations outside the data array and invalidate them.
        % Calculate the number of bits in this parameter group data array.
        bitLength = numel(obj.PrivateData) * 8;

        % Check the end bit location of the specified signal against 
        % the bit length of the data.
        if obj.SignalEndBit(signalIndex) > bitLength
            % It falls outside, so invalidate the signal.
            validity = false;
        else
            % It falls inside, so the signal is valid.
            validity = true;
        end
    end
    
end


methods (Access = private)

    function pgNameList = validatePGInput(obj, pgInput) %#ok<INUSL>
    % validatePGInput Check parameter group input for methods.
    %   
    %   This method is used to validate parameter group input for class
    %   methods that take parameter groups as names or numbers. It
    %   validates and errors is applicable. It returns a cell array of the
    %   parameter group names, converting from numbers if required.
        
        % Do processing for parameter group names.
        if ischar(pgInput) || iscell(pgInput)
            % For a single name, convert to cell array as further
            % processing requires cell array input.
            if ~iscell(pgInput)
                pgNameList = {pgInput};
            else
                pgNameList = pgInput;
            end
            
            % Validate that each entry in the cell is a string.
            for ii = 1:numel(pgNameList)
                validateattributes(pgNameList{ii}, {'char'}, ...
                    {'nonempty', 'row'}, 'extractAll', 'NAME');
            end
        else
            % Error for invalid input.
            error(message('vnt:J1939:InvalidParameterGroupInput'));
        end
    end
    
    function calculateSignalDetails(obj, msgInfo)
    % calculateSignalDetails Sets signal end bit.
    %
    %   This method is used to calculate and store the end bits for
    %   signals in the object. These values are used for signal integrity
    %   purposes when signals are accessed.
        
        % Check each signal in the parameter group.
        for ii = 1:numel(msgInfo.Signals)
            % Get the end bit for the signal. Note we have to adjust for 
            % bit logistics and endian type. Also adjust for 0 versus 1
            % indexing from CANdb to MATLAB.
            switch msgInfo.SignalInfo(ii).ByteOrder
                case 'LittleEndian'
                    obj.SignalEndBit(ii) = msgInfo.SignalInfo(ii).StartBit ...
                        + msgInfo.SignalInfo(ii).SignalSize;
                case 'BigEndian'
                    obj.SignalEndBit(ii) = msgInfo.SignalInfo(ii).StartBit + 1;
            end
        end
    end
    
end


methods (Hidden)
    
    function delete(obj) %#ok<INUSD>
    % delete Delete the J1939 parameter group object.
    end
    
end


methods (Static, Hidden)
    
    function obj = loadobj(obj)
    % loadobj Load a J1939 parameter group object.
    
        % Reconstruct the object.
        try
            obj = j1939.ParameterGroup(obj.Database, obj.Name);
        catch 
            obj = j1939.ParameterGroup.empty();
        end        
    end
    
end
    
end