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

    classdef TPTransmitter < handle
% TPTransmitter Implements a J1939 transport protocol transmission.
%
%   This class implements a complete J1939 transport protocol
%   implementation for transmitting multiframe parameter groups. It can
%   handle both broadcast and peer-to-peer messaging with appropriate flow
%   control and timeouts.

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

properties (SetAccess = 'private')
    % State - The current state of the multiframe tranfer.
    State
    % PGN - The parameter group number being transferred.
    PGN
    % SourceAddress - The node address of the transmitter of a
    % peer-to-peer multiframe transfer.
    SourceAddress
    % DestinationAddress - The node address of the receiver of a peer-to-peer
    % multiframe transfer.
    DestinationAddress
    % TotalPackets - The total number of data transfer packets required for
    % this multiframe sequence.
    TotalPackets
    % TotalSentPackets - The total number of unique packets sent for this
    % multiframe sequence.
    TotalSentPackets
    % PacketsToSend - The number of packets to be sent before any additional
    % flow control is required.
    PacketsToSend
    % NextPacket - The sequence number of the next packet to send.
    NextPacket
    % CMMessage - The Connection Management message to use to start the
    % multiframe sequence.
    CMMessage
    % DTMessages -  An array of Data Transfer messages that comprise the
    % data transfer for the multiframe sequence.
    DTMessages
    % WaitTimeTic - A tic/toc timer used to manage timeout monitoring
    % within the multiframe sequence.
    WaitTimeTic
    % CANChannel - A CAN channel through which to transmit the multiframe
    % messages for this sequence.
    CANChannel
end
    
methods
    
    function obj = TPTransmitter(channel, pg)
    % TPTransmitter Class constructor.
        
        % Store the CAN channel to use for message transmit.
        obj.CANChannel = channel;
        
        % Store information from the parameter group to send.
        obj.PGN = pg.PGN;
        obj.DestinationAddress = pg.DestinationAddress;
        obj.SourceAddress = pg.SourceAddress;
        
        % Initialize the sequence numbering and transmission counter.
        obj.NextPacket = 1;
        obj.TotalSentPackets = 0;
        
        % Create multiframe CAN messages out of the parameter group.
        [obj.CMMessage, obj.DTMessages] = j1939.TPTransmitter.tpMakeMultiframeMessages(pg);
        % Store information about the quantity if data transfer messages.
        obj.PacketsToSend = numel(obj.DTMessages);
        obj.TotalPackets = numel(obj.DTMessages);
        
        % Set the initial state.
        obj.State = j1939.TPTxState.Inactive;
    end

    function transmitBAM(obj)
    % transmitBAM Transmit a multiframe broadcast parameter group.
    %
    %   This method is used to send a multiframe sequence to broadcast
    %   a parameter group to the entire network. It runs in blocking mode
    %   until the transmission is complete with appropriate timing between
    %   messages.
        
        % Send the CM message to start the TP sequence.
        obj.CANChannel.transmitRaw(obj.CMMessage);
        
        % Wait before starting the DT transmits.
        pause(j1939.Utility.TPDTTransmitDelay);
        
        % Send each of the TP DT message(s) individually.
        for ii = 1:obj.TotalPackets
            % Per protocol specification, we send the DT messages one
            % at a time with fixed delay between each message.
            obj.CANChannel.transmitRaw(obj.DTMessages(ii));
            pause(j1939.Utility.TPDTTransmitDelay);
        end
    end
    
    function transmitP2P(obj)
    % transmitP2P Transmit a multiframe peer-to-peer parameter group.
    %
    %   This method is used to send a multiframe sequence to direct a
    %   parameter group to specific receiver. It runs in blocking mode
    %   until the transmission is complete with appropriate timing between
    %   messages.
        
        % Send the CM message to start the TP sequence.
        obj.CANChannel.transmitRaw(obj.CMMessage);
        
        % Prepare for a new TP peer-to-peer transmit sequence.
        obj.State = j1939.TPTxState.WaitingForCTS;
        obj.WaitTimeTic = tic;

        % Run the TP activity until the transfer is either complete,
        % aborted, or timed out.
        while obj.State ~= j1939.TPTxState.Completed && ...
                obj.State ~= j1939.TPTxState.Aborted && ...
                obj.State ~= j1939.TPTxState.TimedOut
            
            % Take action based on the current state.
            switch obj.State
                case j1939.TPTxState.SendingDT
                    % Send a packet.
                    obj.CANChannel.transmitRaw(obj.DTMessages(obj.NextPacket));
                    
                    % Check if the last sent packet is a new transmission
                    % or a retransmission. Receivers can force
                    % retransmission via CTS messages.
                    if obj.NextPacket > obj.TotalSentPackets
                        % Increment the total sent count for a new packet.
                        obj.TotalSentPackets = obj.TotalSentPackets + 1;
                    end
                    
                    % Adjust details for next transmit.
                    obj.NextPacket = obj.NextPacket + 1;
                    obj.PacketsToSend = obj.PacketsToSend - 1;                    
                    
                    % Check if we are completely done sending.
                    if obj.TotalPackets == obj.TotalSentPackets
                        % If all packets are sent, wait for acknowledgement.
                        obj.State = j1939.TPTxState.WaitingForAck;
                        obj.WaitTimeTic = tic;
                        
                    % Check if we are done sending the last specified
                    % sequence.
                    elseif obj.PacketsToSend == 0
                        % If all specified packets since last CTS are sent,
                        % then wait for continuation.
                        obj.State = j1939.TPTxState.WaitingForCTS;
                        obj.WaitTimeTic = tic;
                        
                    % There is more to send.
                    else
                        % Wait for a short period.
                        pause(j1939.Utility.TPDTTransmitDelay);
                    end
                    
                case j1939.TPTxState.WaitingForCTS
                    % Check for a T3 timeout.
                    if toc(obj.WaitTimeTic) > j1939.Utility.TPTimeoutT3
                        obj.State = j1939.TPTxState.TimedOut;
                        warning(message('vnt:J1939:TPTxTimeoutWaitingForCTS'));
                    end
                    
                case j1939.TPTxState.WaitingForAck
                    % Check for a T3 timeout.
                    if toc(obj.WaitTimeTic) > j1939.Utility.TPTimeoutT3
                        obj.State = j1939.TPTxState.TimedOut;
                        warning(message('vnt:J1939:TPTxTimeoutWaitingForAck'));
                    end
                    
                case j1939.TPTxState.DTDelayed
                    % Check for a T4 timeout.
                    if toc(obj.WaitTimeTic) > j1939.Utility.TPTimeoutT4
                        obj.State = j1939.TPTxState.TimedOut;
                        warning(message('vnt:J1939:TPTxTimeoutWaitingForDataTransferResume'));
                    end
            end
            
            % Pause to allow other processing.
            pause(j1939.Utility.TPWaitTime);
        end
    end
    
    function processCTS(obj, message, sourceAddress, destinationAddress)
    % processCTS Process a Clear To Send TP CM message.
    %
    %   This method is used to manage a Clear To Send message from the
    %   destination of a peer-to-peer multiframe transfer. The Clear To
    %   Send is used for flow control from the receiver, allowing it to
    %   control the data output.
        
        % Check if the source and destination of this message
        % match the currently engaged TP sequence.
        if ~obj.isMatch(sourceAddress, destinationAddress)
            return;
        end
        
        % Get the needed information from the message.
        packetsToSend = message.Data(2);
        nextPacket = message.Data(3);
        
        % Take action based on the message details.
        if packetsToSend == 0
            obj.PacketsToSend = packetsToSend;
            obj.State = j1939.TPTxState.DTDelayed;
            obj.WaitTimeTic = tic;
        else
            obj.PacketsToSend = packetsToSend;
            obj.NextPacket = nextPacket;
            obj.State = j1939.TPTxState.SendingDT;
        end
    end
    
    function processAck(obj, ~, sourceAddress, destinationAddress)
    % processAck Process an Acknowledgment TP CM message.
    %
    %   This method is used to manage an Acknowledgment from the
    %   destination of a peer-to-peer multiframe transfer. The 
    %   Acknowledgment message comes upon complete of reception of a data
    %   by the receiver.
        
        % Check if the source and destination of this message
        % match the currently engaged TP sequence.
        if ~obj.isMatch(sourceAddress,  destinationAddress)
            return;
        end
        
        % Log this transmission as complete.
        obj.State = j1939.TPTxState.Completed;
    end
    
    function processAbort(obj, msgRx, sourceAddress, destinationAddress)
    % processAbort Process an Abort TP CM message.
    %
    %   This message is used to manage an Abort from the destination of a
    %   peer-to-peer multiframe transfer. The Abort is used by the receiver
    %   to cancel a multiframe transfer sequence.
        
        % Check if the source and destination of this message
        % match the currently engaged TP sequence.
        if ~obj.isMatch(sourceAddress, destinationAddress)
            return;
        end
        
        % Log this transmission as aborted.
        obj.State = j1939.TPTxState.Aborted;
        warning(message('vnt:J1939:TPTxAborted', msgRx.Data(2)));
    end
    
    function result = isMatch(obj, sourceAddress, destinationAddress)
    % isMatch Check if the provided address are for this TP sequence.
    %
    %   This sequence is used to validate the destination and source
    %   addresses provided as input to determine if they correspond to this
    %   existing multiframe transfer. It returns a boolean state result.
        
        % Check if the source and destination of this CTS message
        % match the currently engaged TP sequence. Note that we have to
        % cross compare the input source against our intended destination
        % and the input destination against us being the source. We are
        % matching addresses of a message received by us as part of a
        % transport protocol flow control coming from our destination to us
        % as the source.
        if (destinationAddress ~= obj.SourceAddress) || (sourceAddress ~= obj.DestinationAddress)
            result = false;
        else
            result = true;
        end
    end
    
    function result = isBroadcast(obj)
    % isBroadcast Check if this TP sequence is broadcast or peer-to-peer.
    %
    %   This method is used to check if the multiframe sequence represented
    %   by this object is a broadcast or a peer-to-peer transfer. It
    %   returns a boolean state result.
        
        % Return the status of this transmitter if it is a broadcast or
        % not.
        if obj.DestinationAddress == j1939.Utility.DestinationAll
            result = true;
        else
            result = false;
        end
    end
        
end

methods (Static)

    function [tpCMMessage, tpDTMessages] = tpMakeMultiframeMessages(pg)
    % tpMakeMultiframeMessages Create CAN messages for a multiframe transmit.
    %
    %   This method is used to take a multiframe parameter group and create
    %   CAN message structures that would be used to sent that parameter
    %   group according to the J1939 transport protocol. It returns an
    %   array of those CAN message structures.
    
        % Build the first message structure for the TP CM message.
        id = uint32(0);
        id = j1939.Utility.setPDUField('Priority', pg.Priority, id);
        id = j1939.Utility.setPDUField('PGN', j1939.Utility.TPConnectionManagementPGN, id);
        id = j1939.Utility.setPDUField('PDUSpecific', pg.DestinationAddress, id);
        id = j1939.Utility.setPDUField('SourceAddress', pg.SourceAddress, id);
        tpCMMessage.ID = id;      
        tpCMMessage.Extended = true;
        tpCMMessage.Error = false;
        tpCMMessage.Remote = false;
        tpCMMessage.Timestamp = 0;

        % Set the special data for the TP CM message.
        if pg.DestinationAddress == j1939.Utility.DestinationAll
            tpCMMessage.Data(1) = j1939.Utility.TPBroadcastAnnounceControlByte;
        else
            tpCMMessage.Data(1) = j1939.Utility.TPRequestToSendControlByte;
        end
        length = uint16(numel(pg.Data));
        bytes = typecast(length, 'uint8');
        tpCMMessage.Data(2) = bytes(1);
        tpCMMessage.Data(3) = bytes(2);
        packets = uint8(ceil(numel(pg.Data)/7));
        tpCMMessage.Data(4) = packets;
        tpCMMessage.Data(5) = j1939.Utility.FillByte;
        bytes = typecast(pg.PGN, 'uint8');
        tpCMMessage.Data(6) = bytes(1);
        tpCMMessage.Data(7) = bytes(2);
        tpCMMessage.Data(8) = bytes(3);
        
        % Build the TP data transfer messages. 
        tpDTMessages = tpCMMessage;
        id = j1939.Utility.setPDUField('PGN', j1939.Utility.TPDataTransferPGN, id);
        id = j1939.Utility.setPDUField('PDUSpecific', pg.DestinationAddress, id);
        
        % Load the large quantity data into the TP data transfer messages.
        nextData = 1;
        for ii = 1:packets
            tpDTMessages(ii).ID = id;
            tpDTMessages(ii).Extended = true;
            tpDTMessages(ii).Error = false;
            tpDTMessages(ii).Remote = false;
            tpDTMessages(ii).Timestamp = 0;

            % First byte is the sequence number. The rest are set to fill
            % bytes and overwritten with actual data.
            tpDTMessages(ii).Data = uint8([ii repmat(j1939.Utility.FillByte, 1, 7)]);
            
            % Write the actual data into the data transfer messages.
            for jj = 1:7
                tpDTMessages(end).Data(jj+1) = pg.Data(nextData);
                nextData = nextData + 1;
                % Stop loading in data when all has been processed.
                if (ii == packets) && (nextData > length)
                    break;
                end
            end
        end
    end
    
end

end