gusucode.com > vnt工具箱matlab源码程序 > vnt/vnt/+j1939/Channel.m
classdef Channel < hgsetget & matlab.mixin.CustomDisplay % Channel Connection to a J1939 network. % % This class provides connectivity to a J1939 protocol network. It % contains properties and methods to configure and use the % channel on a network. % % See also VNT. % Authors: Jaremy Pyle % Copyright 2015 The MathWorks, Inc. properties (SetAccess = 'private') % DeviceVendor - The vendor of the device used to connect to the % network. DeviceVendor % Device - The name of the device used to connect to the network. Device % DeviceChannelIndex - The index of the channel on the device used to % connect to the network. DeviceChannelIndex % DeviceSerialNumber - The vendor serial number of the device used to % connect to the network. DeviceSerialNumber % BusSpeed - The bit rate (bps) of the network. BusSpeed % SJW - The synchronization jump width for the bit timing configuration. SJW % TSEG1 - The first time segement for the bit timing configuration. TSEG1 % TSEG2 - The second time segement for the bit timing configuration. TSEG2 % NumOfSamples - The number of samples taken for the bit timing % configuration. NumOfSamples % TransceiverName - The name of the transceiver used by the device used % to connect to the network. TransceiverName % InitializationAccess - A boolean value indicating if this channel % holds initialization rights to the device channel in use. InitializationAccess % InitialTimestamp - Indicates the time at which the channel was online. InitialTimestamp % BusStatus - The state of the controller of the device. BusStatus % Running - A boolean value indicating if the channel is started or % stopped. Running % ParameterGroupsReceived - The total number of parameter groups % received since the channel was started. ParameterGroupsReceived = 0; % ParameterGroupsTransmitted - The total number of parameter groups % transmitted since the channel was started. ParameterGroupsTransmitted = 0; % FilterPassList - A cell array of paramater group names and numbers % configured to pass through the channel. All other items are blocked. FilterPassList % FilterBlockList - A cell array of parameter group names and numbers % configured to be blocked by the channel. All other items are passed. FilterBlockList end properties (Dependent, SetAccess = 'private') % ParameterGroupsAvailable - The number of parameter groups available % to receive from the channel. ParameterGroupsAvailable end properties % TransceiverState - The current operational state of the transceiver % used by the device to connect to the network. TransceiverState % SilentMode - A boolean value indicating if the channel is configured % for active communication or listen-only mode. SilentMode % UserData - A location to store user-specific information in the % channel. UserData = []; end properties (SetAccess = 'private', GetAccess = 'private') % CANChannel - A CAN channel object used by the J1939 channel to % provide access to a CAN bus. CANChannel % Database - A CANdb database object that describes the network and % parameter groups used on the network. Database % DataBuffer - A buffer for storing parameter groups held in the % channel and receivable by the user. DataBuffer % TPReceiver - A map object for storing in-progress received transport % protocol sequences so they can be rebuilt into singular parameter % groups upon completion. TPReceiver % TPTransmitter - A j1949.TPTransmitter object that operates multiframe % transmit operations. TPTransmitter % FilterInternalPassList - An array of parameter group numbers allowed % to pass through the channel. All other items are blocked. FilterInternalPassList % FilterInternalBlockList - An array of parameter group numbers that % are blocked by the channel. All other items are passed. FilterInternalBlockList % TimerRate - The rate of the timer used for internal processing. TimerRate = 0.050; % BufferSize - The size of the data buffer used to store % parameter groups in the channel. BufferSize = 100000; end properties (Transient, SetAccess = 'private', GetAccess = 'private') % ProcessingTimer - A internal.IntervalTimer used to run processs on % the channel for managing parameter groups. ProcessingTimer % ProcessingTimerListener - A listener on the timer used to trigger % periodic processing operations. ProcessingTimerListener end methods function obj = Channel(database, varargin) % Channel Construct a connection to a J1939 network. % Validate the input argument count. narginchk(3, 4); % 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 % Set the database object into the channel. obj.Database = database; % Set the CAN channel construction parameters. obj.DeviceVendor = varargin{1}; obj.Device = varargin{2}; % Adjust for difference in CAN channel constructor for different % vendors. Some do not have a channel index argument. if nargin == 4 obj.DeviceChannelIndex = varargin{3}; else obj.DeviceChannelIndex = []; end % Create a CAN channel. if isempty(obj.DeviceChannelIndex) obj.CANChannel = canChannel(obj.DeviceVendor, obj.Device); else obj.CANChannel = canChannel(obj.DeviceVendor, obj.Device, obj.DeviceChannelIndex); end % Initialize the transport protocol data buffer. obj.TPReceiver = containers.Map(); % Create objects to seed the data buffer using the first % definition found in the database. It does not matter what we use, % it just matters that we fill the buffer with J1939 paramater % group objects to reserve the memory. bufferData = j1939.ParameterGroup(obj.Database, obj.Database.MessageInfo(1).Name); obj.DataBuffer = vnt.internal.DataBuffer(obj.BufferSize, bufferData); end function configBusSpeed(obj, varargin) % configBusSpeed Set the bit timing of a J1939 channel. % % configBusSpeed(CHANNEL, BUSSPEED) sets the speed of the CHANNEL % to BUSSPEED in a direct form that uses default bit timing % calculation factors. % % configBusSpeed(CHANNEL, BUSSPEED, SJW, TSEG1, TSEG2, NUMBEROFSAMPLES) % sets the speed of the CHANNEL to BUSSPEED using specified bit % timing calculation factors in an advanced form. % % Inputs: % BUSSPEED - The speed of a network in bits per second. % SJW - The Synchronization Jump Width which helps define the % length of a bit on a network. % TSEG1 - Time Segment 1 which defines the section before % a bit is sampled on a network. % TSEG2 - Time Segment 2 which defines the section after % a bit is sampled on a network. % NUMBEROFSAMPLES - The count of samples used for determining % the bit state of a network. % % Note that unless you have specific timing requirements provided % for your network, it is recommended to use the direct form of % configBusSpeed. Also note that bit timing can only be set when % the channel is offline and has initialization access to the device. % % Example: % db = canDatabase('MyDatabase.dbc') % ch = j1939Channel(db, 'Vector', 'CANCaseXL 1', 1) % configBusSpeed(ch, 250000) % configBusSpeed(ch, 500000, 1, 4, 3, 1) % % See also VNT. % Check the argument count. if ((nargin ~= 2) && (nargin ~= 6)) error(message('vnt:Channel:IncorrectArgumentCount')); end % Pass through to the CAN channel. if ~isempty(obj.CANChannel) if nargin == 2 obj.CANChannel.configBusSpeed(varargin{1}); else obj.CANChannel.configBusSpeed(varargin{1}, varargin{2}, ... varargin{3}, varargin{4}, varargin{5}); end end end function start(obj) % start Start a connection to a J1939 bus. % % start(CHANNEL) activates the CHANNEL on the bus to send and % receive messages. The CHANNEL will go offline if stop is called % or it is cleared from the memory. % % Example: % db = canDatabase('MyDatabase.dbc') % ch = j1939Channel(db, 'Vector', 'CANCaseXL 1', 1) % start(ch) % % See also VNT. % Validate the input argument count. narginchk(1, 1); % Check if the channel is already online, and return immediately if % so. No need to error on this condition. if obj.Running return; end % Clear the contents of the data buffer. obj.DataBuffer.wipe(); % Initialize the transport protocol data buffer. obj.TPReceiver = containers.Map(); % Start the CAN channel. obj.CANChannel.start(); % Create and start a timer for processing data. obj.ProcessingTimer = internal.IntervalTimer(obj.TimerRate); obj.ProcessingTimerListener = event.listener( ... obj.ProcessingTimer, ... 'Executing', ... @obj.updateProcessing); start(obj.ProcessingTimer); end function stop(obj) % stop Stop a connection to a J1939 bus. % % stop(CHANNEL) deactivates the CHANNEL on the bus. The CHANNEL will % also stop running if it is cleared. % % Example: % db = canDatabase('MyDatabase.dbc') % ch = j1939Channel(db, 'Vector', 'CANCaseXL 1', 1) % start(ch) % stop(ch) % % See also VNT. % Validate the input argument count. narginchk(1, 1); % Check if the channel is already offline, and return immediately if % so. No need to error on this condition. if ~obj.Running return; end % Check the state of the timer. if ~isempty(obj.ProcessingTimer) % Stop the timer. stop(obj.ProcessingTimer); % Clear the timer. obj.ProcessingTimer = []; obj.ProcessingTimerListener = []; end % Clear the CAN channel. obj.CANChannel.stop(); end function out = receive(obj, count) % receive Receive parameter groups from a J1939 bus. % % PG = receive(CHANNEL, COUNT) returns received PG(s) equal to or % less than COUNT from the CHANNEL. % % COUNT specifies the maximum numer of parameter groups to receive. It % must be a nonzero, positive value or Inf to indicate return all % available parameter groups. If less parameter groups are available % to be received than COUNT specifies, the amount currently available will be % returned. If no parameter groups are available to receive, an empty % array is returned. % % Example: % db = canDatabase('MyDatabase.dbc') % ch = j1939Channel(db, 'Vector', 'CANCaseXL 1', 1) % start(ch) % pg = receive(ch, Inf) % % See also VNT. % Validate the input argument count. narginchk(2, 2); % Validate that count is a numeric, integer, positive value or inf. if count ~= inf if ~(isnumeric(count) && (rem(count, 1) == 0) && (count >= 1)) error(message('vnt:J1939:InvalidPGsRequested')); end end % If no parameter groups are available, return empty. if obj.ParameterGroupsAvailable == 0 out = j1939.ParameterGroup.empty(); return; end % Retrieve the requested items from the data buffer. out = obj.DataBuffer.pop(count); % Increment the receive counter. obj.ParameterGroupsReceived = obj.ParameterGroupsReceived + numel(out); end function transmit(obj, parameterGroups) % transmit Send paramter groups onto a J1939 bus. % % transmit(CHANNEL, PG) sends the PG(s) onto the bus via the CHANNEL. % % Example: % db = canDatabase('MyDatabase.dbc') % ch = j1939Channel(db, 'Vector', 'CANCaseXL 1', 1) % start(ch) % pg = j1939ParameterGroup(db, 'MyParameterGroup') % transmit(ch, pg) % % See also VNT. % Validate the input argument count. narginchk(2, 2); % Error if the channel is not online. if ~obj.Running error(message('vnt:J1939:ChannelNotOnline')); end % Transmitting is not allowed in Silent mode if obj.SilentMode error(message('vnt:J1939:ChannelNotInNormalMode')); end % Validate the parameter group input. The argument is validated with % direct checks due to performance gains over validateAttributes. if ~isa(parameterGroups, 'j1939.ParameterGroup') || ~isvector(parameterGroups) error(message('vnt:J1939:InvalidParameterGroup')); end % Protect against interleaving of multiframe PGs and single frame % PGs within the input. if numel(parameterGroups) > 1 for ii = 1:numel(parameterGroups) if numel(parameterGroups(ii).Data) > 8 error(message('vnt:J1939:MultiframePGTransmitNotIndependent')); end end end % Check if this is a parameter group requiring use of the transport % protocol to send. if (numel(parameterGroups) == 1) && (numel(parameterGroups.Data) > 8) % Use the transport protocol methods to send this parameter % group in a multiframe sequence. obj.tpTransmit(parameterGroups); else try % Structurize the entire parameter group array ahead of using it. This % speeds up the transmit processing by negating the many slow % property references required as they are all now made on the % structure fields. pgStruct = [parameterGroups.PrivateRawStruct]; % Send the parameter groups. obj.CANChannel.transmitRaw(pgStruct); catch err error(message('vnt:J1939:ChannelUnableToTransmit')); end end % Increment the transmit counter. obj.ParameterGroupsTransmitted = obj.ParameterGroupsTransmitted + numel(parameterGroups); end function discard(obj) % discard Discard available parameter groups on the J1939 channel. % % discard(CHANNEL) deletes all parameter groups available on the CHANNEL. % % Example: % db = canDatabase('MyDatabase.dbc') % ch = j1939Channel(db, 'Vector', 'CANCaseXL 1', 1) % start(ch) % discard(ch) % % See also VNT. % Validate the input argument count. narginchk(1, 1); % Clear the contents of the data buffer. obj.DataBuffer.wipe(); % Initialize the transport protocol data buffer. obj.TPReceiver = containers.Map(); % Pass through to the CAN channel. if ~isempty(obj.CANChannel) obj.CANChannel.discard(); end end function filterAllowOnly(obj, value) % filterAllowOnly Set the parameter group pass filter for specific groups. % % filterAllowOnly(CHANNEL, PGNAME) configures the parameter group filter % on CHANNEL to pass only the groups indicated by PGNAME. PGNAME may be % a single string or a cell array of strings. % % Example: % db = canDatabase('MyDatabase.dbc') % ch = j1939Channel(db, 'Vector', 'CANCaseXL 1', 1) % filterAllowOnly(ch, 'PG1'); % filterAllowOnly(ch, {'PG1' 'PG2'}); % % See also VNT. % Validate the input argument count. narginchk(2, 2); % Verify that the channel is offline to configure filtering. obj.errorIfOnline(); % Process the input to get both the names and numbers of the % parameter groups for the filter. [pgnNameList, pgnNumericList] = filterProcessValues(obj, value); % Clear the existing filters. obj.filterClear(); % Set the internal filter. obj.FilterInternalPassList = pgnNumericList; % Set the filter list property visibile to the user. for ii = 1:numel(pgnNumericList) % Build the property value. obj.FilterPassList{end+1} = sprintf('%s (%d)', pgnNameList{ii}, pgnNumericList(ii)); end end function filterBlockOnly(obj, value) % filterBlockOnly Set the parameter group block filter for specific groups. % % filterBlockOnly(CHANNEL, PGNAME) configures the parameter group filter % on CHANNEL to block only the groups indicated by PGNAME. PGNAME may be % a single string or a cell array of strings. % % Example: % db = canDatabase('MyDatabase.dbc') % ch = j1939Channel(db, 'Vector', 'CANCaseXL 1', 1) % filterBlockOnly(ch, 'PG1'); % filterBlockOnly(ch, {'PG1' 'PG2'}); % % See also VNT. % Validate the input argument count. narginchk(2, 2); % Verify that the channel is offline to configure filtering. obj.errorIfOnline(); % Process the input to get both the names and numbers of the % parameter groups for the filter. [pgnNameList, pgnNumericList] = filterProcessValues(obj, value); % Clear the existing filters. obj.filterClear(); % Set the internal filter. obj.FilterInternalBlockList = pgnNumericList; % Set the filter list property visibile to the user. for ii = 1:numel(pgnNumericList) % Build the property value. obj.FilterBlockList{end+1} = sprintf('%s (%d)', pgnNameList{ii}, pgnNumericList(ii)); end end function filterAllowAll(obj) % filterAllowAll Open the parameter group filters on the channel. % % filterAllowAll(CHANNEL) opens all parameter group filters on the % CHANNEL. All parameter groups will thus be receivable. % % Example: % db = canDatabase('MyDatabase.dbc') % ch = j1939Channel(db, 'Vector', 'CANCaseXL 1', 1) % filterAllowAll(ch); % % See also VNT. % Validate the input argument count. narginchk(1, 1); % Verify that the channel is offline to configure filtering. obj.errorIfOnline(); % Clear the existing filters. obj.filterClear(); end function value = get.DeviceSerialNumber(obj) % Read the property value from the CAN channel. value = obj.readPropertyFromCANChannel('DeviceSerialNumber'); end function value = get.BusSpeed(obj) % Read the property value from the CAN channel. value = obj.readPropertyFromCANChannel('BusSpeed'); end function value = get.SJW(obj) % Read the property value from the CAN channel. value = obj.readPropertyFromCANChannel('SJW'); end function value = get.TSEG1(obj) % Read the property value from the CAN channel. value = obj.readPropertyFromCANChannel('TSEG1'); end function value = get.TSEG2(obj) % Read the property value from the CAN channel. value = obj.readPropertyFromCANChannel('TSEG2'); end function value = get.NumOfSamples(obj) % Read the property value from the CAN channel. value = obj.readPropertyFromCANChannel('NumOfSamples'); end function value = get.TransceiverName(obj) % Read the property value from the CAN channel. value = obj.readPropertyFromCANChannel('TransceiverName'); end function value = get.TransceiverState(obj) % Read the property value from the CAN channel. value = obj.readPropertyFromCANChannel('TransceiverState'); end function set.TransceiverState(obj, value) % Write the property value to the CAN channel. obj.writePropertyToCANChannel('TransceiverState', value); end function value = get.InitializationAccess(obj) % Read the property value from the CAN channel. value = obj.readPropertyFromCANChannel('InitializationAccess'); end function value = get.InitialTimestamp(obj) % Read the property value from the CAN channel. value = obj.readPropertyFromCANChannel('InitialTimestamp'); end function value = get.SilentMode(obj) % Read the property value from the CAN channel. value = obj.readPropertyFromCANChannel('SilentMode'); end function set.SilentMode(obj, value) % Write the property value to the CAN channel. obj.writePropertyToCANChannel('SilentMode', value); end function value = get.BusStatus(obj) % Read the property value from the CAN channel. value = obj.readPropertyFromCANChannel('BusStatus'); end function value = get.Running(obj) % Read the property value from the CAN channel. value = obj.readPropertyFromCANChannel('Running'); end function value = get.ParameterGroupsAvailable(obj) % Get the value of this property from the count of items currently % available on the underlying data buffer. value = obj.DataBuffer.Count; end end methods (Hidden) function delete(obj) % delete Delete the J1939 channel object. % Check the state of the timer. if ~isempty(obj.ProcessingTimer) % Stop the timer. stop(obj.ProcessingTimer); % Clear the timer. obj.ProcessingTimer = []; obj.ProcessingTimerListener = []; end % Check the state of the CAN channel. if ~isempty(obj.CANChannel) % Stop the CAN channel if it is running. if obj.CANChannel.Running obj.CANChannel.stop(); end % Clear the CAN channel. obj.CANChannel = []; end end function updateProcessing(obj, ~, ~) % updateProcessing Timer callback function for internal processing. % % This method is the primary processing function of the channel. It % is called from the main timer at a fixed rate to perform incoming % message and parameter group handling and processing. It runs any % regular required J1939 protocol activity as well. % Get all available messages from the CAN channel. rxData = obj.CANChannel.receiveRaw(); % Return immediately if nothing was received. if isempty(rxData) return; end % Perform all required extra processing. for ii = 1:numel(rxData) % Get the individual parameter elements from the identifier. [~, sourceAddress, destinationAddress, pgn] = ... mexJ1939Utility('getSubsetForConstruction', rxData(ii).ID); % Add the PGN to the structure for filtering purposes. rxData(ii).PGN = pgn; % Perform processing on any protocol-specific PGNs. switch pgn case j1939.Utility.TPConnectionManagementPGN obj.tpProcessCM(rxData(ii), sourceAddress, destinationAddress); case j1939.Utility.TPDataTransferPGN obj.tpProcessDT(rxData(ii), sourceAddress, destinationAddress); end end % Handle any complete multiframe parameter groups. multiframePGs = obj.tpCheckForCompletions(); % Combine the regular and multiframe PGs and sort them according to % the timestamps. When we convert to PG objects, all the entries % will be in proper chronological order. if ~isempty(multiframePGs) rxData = [rxData multiframePGs]; timestamps = [rxData.Timestamp]; [~, sortedIndex] = sort(timestamps); rxData = rxData(sortedIndex); end % Apply the message filtering capability. rxData = obj.filterApply(rxData); % Before converting to parameter group objects and setting into the % data buffer, make sure that we still have values to handle. It is % possible that the filter may have blocked all of the received input. if ~isempty(rxData) % Convert the received data into J1939 parameter group objects. newPGs = j1939.ParameterGroup(obj.Database, rxData); % Store them in the data buffer. obj.DataBuffer.push(newPGs); end end end methods (Access = protected) function group = getPropertyGroups(obj) %#ok<MANU> % getPropertyGroups Build custom display for the class. % % This method is used from the matlab.mixin.CustomDisplay class to % perform custom command line display configuration. It presents % properties as logical groups for a more usable display. % Build the property sections for display. title1 = sprintf('Device Information:\n -------------------'); plist1 = {'DeviceVendor', 'Device', 'DeviceChannelIndex', 'DeviceSerialNumber'}; title2 = sprintf('Data Details:\n -------------'); plist2 = {'ParameterGroupsAvailable', 'ParameterGroupsReceived', ... 'ParameterGroupsTransmitted', 'FilterPassList', 'FilterBlockList'}; title3 = sprintf('Channel Information:\n --------------------'); plist3 = {'Running', 'BusStatus', 'InitializationAccess', 'InitialTimestamp', ... 'SilentMode', 'TransceiverName', 'TransceiverState', 'BusSpeed', ... 'SJW', 'TSEG1', 'TSEG2', 'NumOfSamples'}; title4 = sprintf('Other Information:\n ------------------'); plist4 = {'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); group(4) = matlab.mixin.util.PropertyGroup(plist4, title4); end end methods (Access = 'private') function [pgnNameList, pgnNumericList] = filterProcessValues(obj, value) % filterProcessValues Build filter setting input to usable values. % % This method is used to process filter set input (for both pass and % block filters) and parse it fully into parameter groups names and % numbers. These resulting arrays of names and numbers are what we % need to actually configure the filter activity for the channel. % Do processing for names. if ischar(value) || iscell(value) % For a single message convert to cell array as further % processing requires cell array input. if ~iscell(value) value = {value}; end % Loop through the cell array of names and process them. pgnNameList = value; pgnNumericList = []; for ii = 1:numel(pgnNameList) % Look up the parameter group in the database. pgInfo = privateMessageInfoByName(obj.Database, pgnNameList{ii}); % Verify the parameter group definition exists in the database. if isempty(pgInfo) error(message('vnt:J1939:ParameterGroupNotFound', pgnNameList{ii})); end % Add this PGN to the filter list. pgnNumericList = [pgnNumericList pgInfo.J1939.PGN]; %#ok<AGROW> end else error(message('vnt:J1939:InvalidFilterSetting')); end end function itemsToFilter = filterApply(obj, itemsToFilter) % filterApply Run the parameter group filtering check. % % This method is used to apply the filtering as configured on the % channel to incoming parameter group messages. It takes an array of % messages as input and returns a filtered array as output, % discarding any items that did not get through the filter. % Create a logical index array for filtering parameter groups. filterIndex = false(1, numel(itemsToFilter)); % Action if the pass filter is set. if ~isempty(obj.FilterInternalPassList) % Apply each entry in the pass filter. for ii = 1:numel(obj.FilterInternalPassList) % Generate a logical index for each item in the pass list % so we know which entries should be marked for removal. % Combine the logical index arrays for each entry in the % pass list filter to determine the final filterable array. filterIndex = filterIndex | ([itemsToFilter.PGN] == obj.FilterInternalPassList(ii)); end % Reverse the filter index list because this is a pass filter. % The index tells which items to keep, so we invert it to % determine which items to discard. filterIndex = ~filterIndex; end % Action if the block filter is set. if ~isempty(obj.FilterInternalBlockList) % Apply each entry in the block filter. for ii = 1:numel(obj.FilterInternalBlockList) % Generate a logical index for each item in the block list % so we know which entries should be marked for removal. % Combine the logical index arrays for each entry in the % block list filter to determine the final filterable array. filterIndex = filterIndex | ([itemsToFilter.PGN] == obj.FilterInternalBlockList(ii)); end end % Commit the filter by removing any parameter groups that should % not pass. If no filter was set, this action will have no effect. itemsToFilter(filterIndex) = []; end function filterClear(obj) % filterClear Resets all filter settings. % % This method is used to fully open and reset all of the filter % setting properties of the channel. % Clear the existing filters. obj.FilterPassList = cell(0); obj.FilterBlockList = cell(0); obj.FilterInternalPassList = []; obj.FilterInternalBlockList = []; end function tpProcessCM(obj, msgRx, sourceAddress, destinationAddress) % tpProcessCM Handle a TP connection management message. % % This method is used to process transport protocol connection % management messages. These messages are used in J1939 to start % multiframe transfer sequences as well as flow control them. % Switch on the first data byte, which is the % connection management control byte. switch msgRx.Data(1) case {j1939.Utility.TPBroadcastAnnounceControlByte, j1939.Utility.TPRequestToSendControlByte} % Get a map key for this CM packet. tpKey = j1939.Utility.tpCreateReceiverKey(sourceAddress, destinationAddress); % Create a new TP PG object for this connection. tpPG = j1939.TPReceiver(obj.Database, msgRx); % Store the object in the TP data buffer map. obj.TPReceiver(tpKey) = tpPG; case j1939.Utility.TPClearToSendControlByte % Check if the channel is engaged in any multiframe % peer-to-peer transmission. if ~isempty(obj.TPTransmitter) obj.TPTransmitter.processCTS(msgRx, sourceAddress, destinationAddress); end % Check for an active TP receiver for this message. tpReceiver = obj.tpGetReceiver(destinationAddress, sourceAddress); % Dispatch to the receiver if it exists. if ~isempty(tpReceiver) tpReceiver.processCTS(msgRx); end case j1939.Utility.TPAcknowledgmentControlByte % Check if the channel is engaged in any multiframe % peer-to-peer transmission. if ~isempty(obj.TPTransmitter) obj.TPTransmitter.processAck(msgRx, sourceAddress, destinationAddress); end % Check for an active TP receiver for this message. tpReceiver = obj.tpGetReceiver(destinationAddress, sourceAddress); % Dispatch to the receiver if it exists. if ~isempty(tpReceiver) tpReceiver.completeParameterGroup(msgRx.Timestamp); end case j1939.Utility.TPConnectionAbortControlByte % Check if the channel is engaged in any multiframe % peer-to-peer transmission. if ~isempty(obj.TPTransmitter) obj.TPTransmitter.processAbort(msgRx, sourceAddress, destinationAddress); end % Get a TP receiver key for this message. tpKey = j1939.Utility.tpCreateReceiverKey(destinationAddress, sourceAddress); % Check if this message is part of a transport protocol % sequence that we are receiving. if obj.TPReceiver.isKey(tpKey) % If so, remove it. The sequence has been aborted. obj.TPReceiver.remove(tpKey); end end end function tpProcessDT(obj, msgRx, sourceAddress, destinationAddress) % tpProcessDT Handle a TP data transfer message. % % This method is used to process transport protocol data transfer % messages. It directs them to the appropriate open TP sequence % handler based on the source and destination of the message. % Check for an active TP receiver for this message. tpReceiver = obj.tpGetReceiver(sourceAddress, destinationAddress); % Dispatch to the receiver if it exists. if ~isempty(tpReceiver) tpReceiver.processDT(msgRx); end end function multiframePGs = tpCheckForCompletions(obj) % tpCheckForCompletions Check and process completed TP receptions % % This method is used to look for and finish processing of completed % transport protocol receptions. Once all of the messages comprising % a multiframe sequence are complete, they need to be consolidated % together into a final parameter group. % Default to empty output. multiframePGs = []; % Get all of the current processing multiframe sequences and % iterate through them to check status. tpPGsAll = obj.TPReceiver.values(); for jj = 1:numel(tpPGsAll) % Inspect each entry for completion. if tpPGsAll{jj}.isComplete() % Extract the completed parameter group. multiframePGs = [multiframePGs tpPGsAll{jj}.CompletePG]; %#ok<AGROW> % Get a map key for this transport protocol % data buffer entry. tpKey = j1939.Utility.tpCreateReceiverKey( ... tpPGsAll{jj}.SourceAddress, ... tpPGsAll{jj}.DestinationAddress); % Remove this entry from the data buffer now % that it is fully processed. obj.TPReceiver.remove(tpKey); end end end function out = tpGetReceiver(obj, sourceAddress, destinationAddress) % tpGetReceiver Retrieves a specific TP receiver object. % % This method is used to check the TP receiver map for a specific key % based on the source and destination addresses provided. If an entry % for this key exists, that item is returned. If not, then empty is % returned. % Get a TP receiver key for the provided input. tpKey = j1939.Utility.tpCreateReceiverKey(sourceAddress, destinationAddress); % Check if this key is part of a transport protocol sequence that % we are receiving. if obj.TPReceiver.isKey(tpKey) % Get the transport protocol receiver entry to which % this packet belongs. out = obj.TPReceiver(tpKey); else % If the key is void, then return empty. out = []; end end function tpTransmit(obj, parameterGroup) % tpTransmit Transmit a parameter group via the transport protocol. % % This method is used to send a parameter group onto the network % using the J1939 transport protocol. This means the data is larger % than 8 bytes and requires multiple CAN messages to complete the % data transfer. % Create a transmitter object. obj.TPTransmitter = j1939.TPTransmitter(obj.CANChannel, parameterGroup); % Check if this is a broadcast transmission. if obj.TPTransmitter.isBroadcast() % Send the broadcast. obj.TPTransmitter.transmitBAM(); else % Send as a peer-to-peer transmission. obj.TPTransmitter.transmitP2P(); end % Clear the object. obj.TPTransmitter = []; end function value = readPropertyFromCANChannel(obj, name) % readPropertyFromCANChannel Get property value from the CAN channel. % % This method returns the value of the property in the "name" % argument as read from the underlying CAN channel object. if isempty(obj.CANChannel) value = []; else value = obj.CANChannel.(name); end end function writePropertyToCANChannel(obj, name, value) % writePropertyToCANChannel Set property value to the CAN channel. % % This method sets the value of the property in the "name" % argument into the underlying CAN channel object. if ~isempty(obj.CANChannel) obj.CANChannel.(name) = value; end end function errorIfOnline(obj) % errorIfOnline Throws an error if the channel is online. % % This method is used to trigger an error if the channel is online. % It is used by user visible methods to ensure proper channel state % for certain operations. % Check if the channel is online, and error if so. if obj.Running error(message('vnt:J1939:ChannelNotOffline')); end end end methods (Static, Hidden) function obj = loadobj(obj) % loadobj Load a J1939 channel object from memory. end end end