gusucode.com > vnt工具箱matlab源码程序 > vnt/vnt/+xcp/Channel.m
classdef Channel < hgsetget % Channel Class that implements an XCP protocol user channel. % Authors: JDP % Copyright 2012 The MathWorks, Inc. properties (SetAccess = 'private') % SlaveName - The name of the XCP slave node to which this channel connects. SlaveName % A2LFileName - The name of the A2L file that describes the XCP target node. A2LFileName % TransportLayer - The type of transport layer used for the XCP connection. TransportLayer % TransportLayerDevice - Structure containing details on the XCP transport layer connection. TransportLayerDevice end properties % SeedKeyDLL - A DLL file containing the seed and key access algorithm. SeedKeyDLL end properties (SetAccess = 'private', GetAccess = 'private') % A2LFileObject - An xcp.A2L file object. A2LFileObject % ProtocolObject - An xcp.Protocol object that implements the XCP protocol messaging. ProtocolObject % SlaveInfo - A structure of slave information acquired from the slave. SlaveInfo % ConnectState - Stores the current slave connection state of the channel. ConnectState % MeasurementState - Stores the current state of measurement activities on the channel. MeasurementState % DAQListObject - An array of all currently configured DAQ list objects. DAQListObject % DAQData - A structure with array values of stored DAQ data for measurements. DAQData % DAQTimer - A timer object that runs the periodic DAQ tasks. DAQTimer % DAQTimerListener - Listener for the DAQ timer events. DAQTimerListener % DAQTimerRate - The rate at which to run the DAQ periodic task. DAQTimerRate % STIMListObject - An array of all currently configured STIM list objects. STIMListObject % STIMTimer - A timer object that runs the periodic STIM tasks. STIMTimer % STIMTimerListener - Listener for the STIM timer events. STIMTimerListener % STIMTimerRate - The rate at which to run the STIM periodic task. STIMTimerRate % SeedKeyDLLFullPath - Full path to the seed and key access DLL file. SeedKeyDLLFullPath end properties (Dependent, SetAccess = 'private', GetAccess = 'private') % NumberOfDAQLists - The number of configured DAQ lists. NumberOfDAQLists % NumberOfSTIMLists - The number of configured STIM lists. NumberOfSTIMLists % NumberOfMeasurementLists - The number of configured DAQ and STIM lists. NumberOfMeasurementLists end methods function obj = Channel(a2l, transportLayer, varargin) % Channel Creates and returns an XCP Channel object. % % OBJ = Channel(A2L_OBJECT, 'CAN', DEVICE_VENDOR, DEVICE, % DEVICE_CHANNEL_INDEX) creates an XCP channel OBJ connected to the % slave device described by the A2L file A2L_OBJECT. The % TRANSPORT_LAYER is specified as 'CAN' in order to use a CAN bus to % communicate with the slave. Additional arguments are necessary to % define the CAN device through which to access the network. The % DEVICE_VENDOR must be specified as a string, the DEVICE also as a % string, and the DEVICE_CHANNEL_INDEX as a numeric value. % % Example: % a2lObj = xcpA2L('myFile.a2l'); % channelObj = xcpChannel(a2lObj, 'CAN', 'Vector', 'CANcaseXL 1', 1); % % See also VNT. % Perform an argument count check. narginchk(4, 5); % Validate the A2L argument. validateattributes(a2l, {'xcp.A2L'}, {'scalar', 'nonempty'}, ... 'xcp.Channel', 'A2L file object', 1); % Convert the transport layer argument to all upper case since CAN % is the only supported option, and we always want that name as a % string represented in the object in upper case. transportLayer = upper(transportLayer); % Validate the transport layer argument. validatestring(transportLayer, {'CAN'}, ... 'xcp.Channel', 'transport layer', 2); % Validate the variable arguments. validateattributes(varargin{1}, {'char'}, {'nonempty'}, ... 'xcp.Channel', 'transport layer device vendor', 3); validateattributes(varargin{2}, {'char'}, {'nonempty'}, ... 'xcp.Channel', 'transport layer device', 4); if nargin == 5 validateattributes(varargin{3}, {'numeric'}, {'nonempty', 'integer', 'real', 'finite', 'nonnan', 'positive'}, ... 'xcp.Channel', 'transport layer device channel index', 5); end % Set the base property values from the input arguments. obj.A2LFileObject = a2l; obj.A2LFileName = a2l.FileName; obj.SlaveName = a2l.SlaveName; obj.TransportLayer = transportLayer; obj.TransportLayerDevice.Vendor = varargin{1}; obj.TransportLayerDevice.Device = varargin{2}; if nargin == 5 obj.TransportLayerDevice.ChannelIndex = varargin{3}; else obj.TransportLayerDevice.ChannelIndex = []; end % Initialize internal states. obj.ConnectState = false; obj.MeasurementState = false; % Initialize the measurement list properties. obj.DAQListObject = xcp.MeasurementList.empty(); obj.DAQData = []; obj.DAQTimerRate = 0.025; obj.STIMListObject = xcp.MeasurementList.empty(); obj.STIMTimerRate = 0.010; end function delete(obj) % delete Clean up and remove the object. % % delete(OBJ) deletes the XCP channel object OBJ. Any activity is % stopped and connection to a slave module is disconnected. % % Example: % a2lObj = xcpA2L('myFile.a2l'); % channelObj = xcpChannel(a2lObj, 'CAN', 'Vector', 'CANcaseXL 1', 1); % delete(channelObj); % % See also VNT. % Disconnect from the slave if the connection is active. if obj.isConnected() obj.disconnect(); end end function connect(obj) % connect Starts an active connection to the slave module. % % connect(OBJ) actively connects the XCP channel object OBJ to the slave % module that was specified during creation of the object. The % required XCP commands necessary to establish a connection are sent % to the slave and any applicable responses are received and % processed. % % Example: % a2lObj = xcpA2L('myFile.a2l'); % channelObj = xcpChannel(a2lObj, 'CAN', 'Vector', 'CANcaseXL 1', 1); % connect(channelObj); % % See also VNT. % Perform an argument count check. narginchk(1, 1); % Validate current state conditions. obj.errorWhenConnected(); % Build a protocol object for communicating with the slave. try obj.ProtocolObject = xcp.Protocol( ... obj.A2LFileObject, ... obj.TransportLayer, ... obj.TransportLayerDevice.Vendor, ... obj.TransportLayerDevice.Device, ... obj.TransportLayerDevice.ChannelIndex); catch err error(message('vnt:XCP:UnableToCreateProtocolObject')); end % Connect to the slave. [~, resource, commModeBasic, maxCTO, maxDTO] = ... obj.ProtocolObject.connect(xcp.Utility.NORMAL_MODE); % Store the returned information in this channel. obj.SlaveInfo.Resource = resource; obj.SlaveInfo.CommModeBasic = commModeBasic; obj.SlaveInfo.MaxCTO = maxCTO; obj.SlaveInfo.MaxDTO = maxDTO; % Query for current slave status. [~, currentSessionStatus, currentResourceProtectionStatus, sessionConfigurationID] = ... obj.ProtocolObject.getStatus(); % Store the returned information in this channel. obj.SlaveInfo.CurrentSessionStatus = currentSessionStatus; obj.SlaveInfo.CurrentResourceProtectionStatus = currentResourceProtectionStatus; obj.SlaveInfo.SessionConfigurationID = sessionConfigurationID; % If DAQ is available and locked, unlock it. if obj.SlaveInfo.Resource.DAQ && obj.SlaveInfo.CurrentResourceProtectionStatus.DAQ obj.unlockResource('DAQ'); end % If STIM is available and locked, unlock it. if obj.SlaveInfo.Resource.STIM && obj.SlaveInfo.CurrentResourceProtectionStatus.STIM obj.unlockResource('STIM'); end % If CAL is available and locked, unlock it. if obj.SlaveInfo.Resource.CAL && obj.SlaveInfo.CurrentResourceProtectionStatus.CAL obj.unlockResource('CAL'); end % Modify the internal state. obj.ConnectState = true; end function disconnect(obj) % disconnect Stops an active connection to the slave module. % % disconnect(OBJ) disconnects the XCP channel object OBJ from the slave % module that was specified during creation of the object. The % required XCP commands necessary to close a connection are sent % to the slave and any applicable responses are received and % processed. % % Example: % a2lObj = xcpA2L('myFile.a2l'); % channelObj = xcpChannel(a2lObj, 'CAN', 'Vector', 'CANcaseXL 1', 1); % connect(channelObj); % disconnect(channelObj); % % See also VNT. % Perform an argument count check. narginchk(1, 1); % Validate current state conditions. obj.errorWhenDisconnected(); % If measurements are active, stop them. if obj.isMeasurementRunning() obj.stopMeasurement(); end % If the channel is connected, disconnect it. if obj.isConnected() obj.ProtocolObject.disconnect(); obj.ProtocolObject = []; end % Modify the internal state. obj.ConnectState = false; end function createMeasurementList(obj, resource, eventName, measurementNames) % createMeasurementList Configure a DAQ or STIM list on the XCP channel. % % createMeasurementList(OBJ, RESOURCE, EVENT_NAME, MEASUREMENT_NAMES) % configures a dynamic DAQ or STIM list on the XCP channel object % OBJ. The RESOURCE must be specified as a string to denote this a % 'DAQ' or 'STIM' list. The EVENT_NAME is also a string that is the % name of the event on which to configure this list. Measurements are % specified as MEASUREMENT_NAMES as a string or cell array of strings % to define which measurements are configured for this list. % % Example: % a2lObj = xcpA2L('myFile.a2l'); % channelObj = xcpChannel(a2lObj, 'CAN', 'Vector', 'CANcaseXL 1', 1); % connect(channelObj); % createMeasurementList(channelObj, 'DAQ', 'Event1', 'Measurement1'); % createMeasurementList(channelObj, 'STIM', 'Event2', {'Measurement1', 'Measurement1'}); % % See also VNT. % Perform an argument count check. narginchk(4, 4); % Validate current state conditions. obj.errorWhenDisconnected(); obj.errorWhenMeasurementActive(); % Force the direction argument to be all caps because that is how % we want the values represented inside the channel. resource = upper(resource); % Validate the resource argument. validatestring(resource, {'DAQ', 'STIM'}, ... 'xcp.createMeasurementList', 'direction', 2); % Validate the event argument. validatestring(eventName, obj.A2LFileObject.Events, ... 'xcp.createMeasurementList', 'event name', 3); % Create a cell array out of the measurement names argument. This % argument may be sent as a single string, but we always reference % this argument as a cell array. if ~iscell(measurementNames) measurementNames = {measurementNames}; end % Validate each entry in the measurements argument. for ii = 1:numel(measurementNames) validatestring(measurementNames{ii}, obj.A2LFileObject.Measurements, ... 'xcp.createMeasurementList', 'measurement names', 4); end % Verify that the requested resource is an available resource on % this slave. For example, error if STIM was requested but STIM is % not supported by the slave. if ~obj.SlaveInfo.Resource.(resource) error(message('vnt:XCP:ResourceNotAvailableBySlave', resource)); end % Verify that the specified event is not already in use for the % given resource. We only allow an event to be used once for DAQ % and once for STIM at a time. for ii = 1:obj.(['NumberOf' resource 'Lists']) % Check through each list already configured for the given % resource type to see if the list is using the requested event. if strcmpi(eventName, obj.([resource 'ListObject'])(ii).EventInfo.Name) error(message('vnt:XCP:EventAlreadyUsedForThisResource', eventName, resource)); end end % Verify that the specified measurements are not already in use for % the given resource. We only allow a measurement to be used once % for DAQ and once for STIM at a time. for ii = 1:numel(measurementNames) % Check each resource list. for jj = 1:obj.(['NumberOf' resource 'Lists']) % Use logical indexing to check for the measurement. if any(strcmpi(measurementNames{ii}, obj.([resource 'ListObject'])(jj).MeasurementNames)) error(message('vnt:XCP:MeasurementAlreadyUsedForThisResource', measurementNames{ii}, resource)); end end end % Get the detailed event information from the A2L file. eventInfo = obj.A2LFileObject.getEventInfo(eventName); % Validate that this event supports the specified resource. Some % events can be used for both STIM and DAQ while others only % support one option. if ~strcmpi(resource, eventInfo.Direction) && ~strcmpi(eventInfo.Direction, 'DAQ_STIM') error(message('vnt:XCP:EventDirectionNotSupported', eventInfo.Name, resource)); end % If this is a STIM list, verify that the specified event is a % periodic/time-based event. Only time-based events are supported for % STIM in the current implementation, but the A2L file may have % non-periodic events configured as possible for STIM. if strcmpi(resource, 'STIM') && eventInfo.ChannelTimeCycleInSeconds == 0 error(message('vnt:XCP:EventNotPeriodic', eventInfo.Name, resource)); end % Get the detailed measurement information from the A2L file for % each specified measurement. measurementInfo = struct([]); for ii = 1:numel(measurementNames) measurementInfo = [measurementInfo obj.A2LFileObject.getMeasurementInfo(measurementNames{ii})]; %#ok<AGROW> end % Verify that each of the specified measurements are smaller in % size than the maximum allowed by the slave. for ii = 1:numel(measurementNames) if measurementInfo(ii).SizeInBytes > obj.A2LFileObject.DAQInfo.MaxODTEntrySize error(message('vnt:XCP:MeasurementExceedsMaximumODTEntrySize', ... measurementNames{ii}, ... obj.A2LFileObject.DAQInfo.MaxODTEntrySize)); end end % Create a new measurement list for the specified resource. obj.([resource 'ListObject']) = [obj.([resource 'ListObject']) ... xcp.MeasurementList(obj.A2LFileObject, resource, eventInfo, measurementInfo)]; end function freeMeasurementLists(obj) % freeMeasurementLists Remove all DAQ and STIM lists from the XCP channel. % % freeMeasurementLists(OBJ) deletes all configured DAQ and STIM lists % configured on the XCP channel object OBJ. % % Example: % a2lObj = xcpA2L('myFile.a2l'); % channelObj = xcpChannel(a2lObj, 'CAN', 'Vector', 'CANcaseXL 1', 1); % connect(channelObj); % createMeasurementList(channelObj, 'DAQ', 'Event1', 'Measurement1'); % freeMeasurementLists(channelObj); % % See also VNT. % Perform an argument count check. narginchk(1, 1); % Validate current state conditions. obj.errorWhenMeasurementActive(); obj.errorWhenNoMeasurementListsConfigured(); % Erase the stored measurement lists. obj.DAQListObject = xcp.MeasurementList.empty(); obj.STIMListObject = xcp.MeasurementList.empty(); end function viewMeasurementLists(obj) % viewMeasurementLists View the configured DAQ and STIM lists. % % viewMeasurementLists(OBJ) displays information to the command line % about all of the currently configured DAQ and STIM lists on the XCP % channel object OBJ. % % Example: % a2lObj = xcpA2L('myFile.a2l'); % channelObj = xcpChannel(a2lObj, 'CAN', 'Vector', 'CANcaseXL 1', 1); % connect(channelObj); % createMeasurementList(channelObj, 'DAQ', 'Event1', 'Measurement1'); % viewMeasurementLists(channelObj); % % See also VNT. % Perform an argument count check. narginchk(1, 1); % Validate current state conditions. obj.errorWhenNoMeasurementListsConfigured(); % Put the measurement lists together to configure them. measurementLists = [obj.DAQListObject obj.STIMListObject]; % Display information for all configured measurements lists. fprintf(1, '\n'); for ii = 1:obj.NumberOfMeasurementLists % Display basic list information. fprintf(1, '%s List #%d', measurementLists(ii).Resource, ii); if measurementLists(ii).EventInfo.ChannelTimeCycleInSeconds == 0 fprintf(1, ' using the "%s" event ', measurementLists(ii).EventInfo.Name); else fprintf(1, ' using the "%s" event @ %f seconds ', ... measurementLists(ii).EventInfo.Name, ... measurementLists(ii).EventInfo.ChannelTimeCycleInSeconds); end fprintf(1, 'and the following measurements:\n'); % Construct a disp table to display the measurements. measurementTable = internal.DispTable(); measurementTable.ColumnSeparator = ' | '; measurementTable.ShowHeader = false; measurementTable.Indent = 3; measurementTable.addColumn('Measurements'); for jj = 1:measurementLists(ii).NumberOfMeasurements measurementTable.addRow(measurementLists(ii).MeasurementNames{jj}); end disp(measurementTable); % Display extra white space. fprintf(1, '\n'); end end function startMeasurement(obj) % startMeasurement Start all configured DAQ and STIM lists. % % startMeasurement(OBJ) actively starts all configured DAQ and STIM % lists on the XCP channel object OBJ. DAQ lists begin acquiring % data values from the slave module. STIM lists begin transmitting % data values to the slave module. % % Example: % a2lObj = xcpA2L('myFile.a2l'); % channelObj = xcpChannel(a2lObj, 'CAN', 'Vector', 'CANcaseXL 1', 1); % connect(channelObj); % createMeasurementList(channelObj, 'DAQ', 'Event1', 'Measurement1'); % startMeasurement(channelObj); % % See also VNT. % Perform an argument count check. narginchk(1, 1); % Validate current state conditions. obj.errorWhenDisconnected(); obj.errorWhenMeasurementActive(); obj.errorWhenNoMeasurementListsConfigured(); % Clear existing DAQ data. obj.DAQData = []; % Put the measurement lists together to configure them. measurementLists = [obj.DAQListObject obj.STIMListObject]; % Free all existing measurement lists. obj.ProtocolObject.freeDAQ(); % Configure for the required number of lists. obj.ProtocolObject.allocDAQ(obj.NumberOfMeasurementLists); % Configure the ODTs for each list. All ODTs must be % configured before continuing to add ODT entries and % measurements. for ii = 1:obj.NumberOfMeasurementLists % Configure this list for the required number of ODTs. obj.ProtocolObject.allocODT( ... ii - 1, ... % List number adjusted for zero index. measurementLists(ii).NumberOfODTs); end % Configure the ODT entries for each ODT on each list. All % ODT entries must be configured before measurements are added % to the entries. for ii = 1:obj.NumberOfMeasurementLists % Loop to configure the ODT entries for each ODT. for jj = 1:measurementLists(ii).NumberOfODTs % Configure the ODT entries. obj.ProtocolObject.allocODTEntry( ... ii - 1, ... % List number adjusted for zero index. jj - 1, ... % ODT number adjusted for zero index. measurementLists(ii).ODTObject(jj).NumberOfODTEntries); end end % Add the measurements to the appropriate list, ODT, and % ODT entry. for ii = 1:obj.NumberOfMeasurementLists % Loop through each ODT to add measurements. for jj = 1:measurementLists(ii).NumberOfODTs % Set the DAQ pointer to prepare for linking the % measurement information with the DAQ list, ODT, and % ODT entry. obj.ProtocolObject.setDAQPtr( ... ii - 1, ... % List number adjusted for zero index. jj - 1, ... % ODT number adjusted for zero index. xcp.Utility.FIRST_ODT_ENTRY); % Loop to write each measurement to their ODT entries. for kk = 1:measurementLists(ii).ODTObject(jj).NumberOfODTEntries % Write the measurement information to the ODT. obj.ProtocolObject.writeDAQ( ... xcp.Utility.IGNORE_BIT_OFFSET, ... measurementLists(ii).ODTObject(jj).ODTEntryInfo(kk).SizeInBytes, ... measurementLists(ii).ODTObject(jj).ODTEntryInfo(kk).ECUAddressExtension, ... measurementLists(ii).ODTObject(jj).ODTEntryInfo(kk).ECUAddress); % If this measurement list is a DAQ list, then add % an entry for this measurement to the DAQ data. if strcmpi(measurementLists(ii).Resource, 'DAQ') obj.DAQData.(measurementLists(ii).ODTObject(jj).ODTEntryInfo(kk).Name) = []; end end end end % Configure the lists for starting. for ii = 1:obj.NumberOfMeasurementLists % Set the measurement list mode. obj.ProtocolObject.setDAQListMode( ... measurementLists(ii).ListMode, ... ii - 1, ... % List number adjusted for zero index. measurementLists(ii).EventInfo.ChannelNumber, ... xcp.Utility.NO_TRANSMISSION_RATE_PRESCALER, ... xcp.Utility.LET_SLAVE_MANAGE_PRIORITY); % Prepare the list to start. [~, firstNumber] = obj.ProtocolObject.startStopDAQList( ... xcp.Utility.SET_SYNCHRONIZED_START_STOP, ... ii - 1); % List number adjusted for zero index. % Set the absolute list number for the list. measurementLists(ii).Number = ii - 1; % Set the numbers for the ODTs in the list. measurementLists(ii).setODTNumbers(firstNumber); end % Configure STIM messages for each STIM list. for ii = 1:obj.NumberOfSTIMLists obj.STIMListObject(ii).configureSTIMMessageObjects(); end % Turn on the lists. obj.ProtocolObject.startStopSynch(xcp.Utility.SYNCH_START_SELECTED_LISTS); % Configure and start a timer to process DAQ messages if DAQ is % being used as a resource. if ~isempty(obj.DAQListObject) obj.DAQTimer = internal.IntervalTimer(obj.DAQTimerRate); obj.DAQTimerListener = event.listener( ... obj.DAQTimer, ... 'Executing', ... @(~, ~)(obj.executeDAQTimerCallback)); start(obj.DAQTimer); end % Configure and start a timer to process STIM messages if STIM is % being used as a resource. if ~isempty(obj.STIMListObject) obj.STIMTimer = internal.IntervalTimer(obj.STIMTimerRate); obj.STIMTimerListener = event.listener( ... obj.STIMTimer, ... 'Executing', ... @(~, ~)(obj.executeSTIMTimerCallback)); start(obj.STIMTimer); end % Modify the internal state. obj.MeasurementState = true; end function stopMeasurement(obj) % stopMeasurement Stop all configured DAQ and STIM lists. % % stopMeasurement(OBJ) stops all configured DAQ and STIM % lists on the XCP channel object OBJ. DAQ lists stop acquiring % data values from the slave module. STIM lists stop transmitting % data values to the slave module. % % Example: % a2lObj = xcpA2L('myFile.a2l'); % channelObj = xcpChannel(a2lObj, 'CAN', 'Vector', 'CANcaseXL 1', 1); % connect(channelObj); % createMeasurementList(channelObj, 'DAQ', 'Event1', 'Measurement1'); % startMeasurement(channelObj); % stopMeasurement(channelObj); % % See also VNT. % Perform an argument count check. narginchk(1, 1); % Validate current state conditions. obj.errorWhenDisconnected(); obj.errorWhenMeasurementInactive(); % Stop and remove the STIM timer if in use. if ~isempty(obj.STIMTimer) stop(obj.STIMTimer); obj.STIMTimer = []; end % Turn off all of the lists. obj.ProtocolObject.startStopSynch(xcp.Utility.SYNCH_STOP_ALL_LISTS); % Stop and remove the DAQ timer if in use. if ~isempty(obj.DAQTimer) stop(obj.DAQTimer); obj.DAQTimer = []; end % Modify the internal state. obj.MeasurementState = false; end function data = readDAQListData(obj, measurementName, varargin) % readDAQListData Read samples of the specified measurement from a DAQ list. % % DATA = readDAQListData(OBJ, MEASUREMENT_NAME) returns all % acquired DAQ list data from the XCP channel object OBJ for the % specified MEASUREMENT_NAME. % % DATA = readDAQListData(OBJ, MEASUREMENT_NAME, COUNT) returnes % acquired DAQ list data from the XCP channel object OBJ for the % specified MEASUREMENT_NAME. In this case, the quantity of data % values to read is given as COUNT. That number of values will be % returned or less if fewer samples are available. % % Example: % a2lObj = xcpA2L('myFile.a2l'); % channelObj = xcpChannel(a2lObj, 'CAN', 'Vector', 'CANcaseXL 1', 1); % connect(channelObj); % createMeasurementList(channelObj, 'DAQ', 'Event1', 'Measurement1'); % startMeasurement(channelObj); % data = readDAQListData(channelObj, 'Measurement1', 10); % data = readDAQListData(channelObj, 'Measurement1'); % % See also VNT. % Perform an argument count check. narginchk(2, 3); % Validate the measurement name argument. validatestring(measurementName, obj.A2LFileObject.Measurements, ... 'xcp.readDAQListData', 'measurement name', 2); % Validate the number of samples argument if it exists. if nargin == 3 validateattributes(varargin{1}, {'numeric'}, ... {'nonempty', 'integer', 'real', 'finite', 'nonnan', 'positive'}, ... 'xcp.readDAQListData', 'number of samples', 3); end % If the internal DAQ data is empty, then return empty as no data % is yet available. if isempty(obj.DAQData) data = []; return; end % Read the number of samples currently available. try sampleCount = numel(obj.DAQData.(measurementName)); catch err % If the measurement is not in the DAQ data, then it has not % been added to a DAQ list, so error. error(message('vnt:XCP:MeasurementNotConfiguredForDAQ', measurementName)); end % If no samples are available, then return empty. if sampleCount == 0 data = []; return; end % If a sample count was provided and that value is less than the % number of samples available, then read only that quantity. if nargin == 3 && varargin{1} < sampleCount sampleCount = varargin{1}; end % Return the data. data = obj.DAQData.(measurementName)(1:sampleCount); % Clear the read data from the object. obj.DAQData.(measurementName)(1:sampleCount) = []; end function writeSTIMListData(obj, measurementName, newValue) % writeSTIMListData Write a new value of the specified measurement to a STIM list. % % writeSTIMListData(OBJ, MEASUREMENT_NAME, NEW_VALUE) writes a % NEW_VALUE to the STIM list measurement MEASUREMENT_NAME on the XCP % channel object OBJ. % % Example: % a2lObj = xcpA2L('myFile.a2l'); % channelObj = xcpChannel(a2lObj, 'CAN', 'Vector', 'CANcaseXL 1', 1); % connect(channelObj); % createMeasurementList(channelObj, 'DAQ', 'Event1', 'Measurement1'); % startMeasurement(channelObj); % writeSTIMListData(channelObj, 'Measurement1', newValue); % % See also VNT. % Perform an argument count check. narginchk(3, 3); % Validate the measurement name argument. validatestring(measurementName, obj.A2LFileObject.Measurements, ... 'xcp.writeSTIMListData', 'measurement name', 2); % Validate the value argument. validateattributes(newValue, {'numeric'}, ... {'nonempty', 'real', 'finite', 'nonnan'}, ... 'xcp.writeSTIMListData', 'new value', 3); % Pack the data value. for ii = 1:obj.NumberOfSTIMLists % Try to write the value to each STIM list. result = obj.STIMListObject(ii).writeSTIMData(measurementName, newValue); % Check the result. If the write worked, then return. if result return; end end % If no write was made, error as the measurement was not found to % be configured for STIM. error(message('vnt:XCP:MeasurementNotConfiguredForSTIM', measurementName)); end function value = readSingleValue(obj, measurementName) % readSingleValue Read a sample of the specified measurement from direct memory. % % VALUE = readSingleValue(OBJ, MEASUREMENT_NAME) acquires a single % VALUE for the specified MEASUREMENT_NAME through the XCP channel % object OBJ. This action is performed via direct read from memory on % the slave module. % % Example: % a2lObj = xcpA2L('myFile.a2l'); % channelObj = xcpChannel(a2lObj, 'CAN', 'Vector', 'CANcaseXL 1', 1); % connect(channelObj); % value = readSingleValue(channelObj, 'Measurement1'); % % See also VNT. % Perform an argument count check. narginchk(2, 2); % Validate the measurement name argument. validatestring(measurementName, obj.A2LFileObject.Measurements, ... 'xcp.readSingleValue', 'measurement name', 2); % Validate current state conditions. obj.errorWhenDisconnected(); % Get the measurement information from the A2L file. info = obj.A2LFileObject.getMeasurementInfo(measurementName); % Set the memory transfer address for this measurement. obj.ProtocolObject.setMTA(info.ECUAddressExtension, info.ECUAddress); % Upload the data value from the slave. response = obj.ProtocolObject.upload(info.SizeInBytes); % Decode and return the measurement value. firstByte = 2; lastByte = firstByte + info.SizeInBytes - 1; value = xcp.Utility.unpackMeasurement(info, response(firstByte:lastByte)); end function writeSingleValue(obj, measurementName, newValue) % writeSingleValue Write a new value of the specified measurement to direct memory. % % writeSingleValue(OBJ, MEASUREMENT_NAME, NEW_VALUE) writes a single % NEW_VALUE for the specified MEASUREMENT_NAME through the XCP channel % object OBJ. This action is performed via direct write to memory on % the slave module. % % Example: % a2lObj = xcpA2L('myFile.a2l'); % channelObj = xcpChannel(a2lObj, 'CAN', 'Vector', 'CANcaseXL 1', 1); % connect(channelObj); % writeSingleValue(channelObj, 'Measurement1', newValue); % % See also VNT. % Perform an argument count check. narginchk(3, 3); % Validate the measurement name argument. validatestring(measurementName, obj.A2LFileObject.Measurements, ... 'xcp.writeSingleValue', 'measurement name', 2); % Validate the value argument. validateattributes(newValue, {'numeric'}, ... {'nonempty', 'real', 'finite', 'nonnan'}, ... 'xcp.writeSingleValue', 'new value', 3); % Validate current state conditions. obj.errorWhenDisconnected(); % Get the measurement information from the A2L file. info = obj.A2LFileObject.getMeasurementInfo(measurementName); % Convert the data value into bytes suitable for download. byteValue = xcp.Utility.packMeasurement(info, newValue); % Set the memory transfer address for this measurement. obj.ProtocolObject.setMTA(info.ECUAddressExtension, info.ECUAddress); % Download the bytes to the slave. obj.ProtocolObject.download(info.SizeInBytes, byteValue); end function result = isConnected(obj) % isConnected - Returns a boolean value to indicate active connection to the slave. % % RESULT = isConnected(OBJ) returns a boolean RESULT indicating true % if the XCP channel object OBJ is actively connected to the slave % module, otherwise it returns false. % % Example: % a2lObj = xcpA2L('myFile.a2l'); % channelObj = xcpChannel(a2lObj, 'CAN', 'Vector', 'CANcaseXL 1', 1); % connect(channelObj); % result = isConnected(channelObj); % % See also VNT. % Return an indication based off the internal state property. result = obj.ConnectState; end function result = isMeasurementRunning(obj) % isMeasurementRunning - Returns a boolean value to indicate active measurement activity. % % RESULT = isMeasurementRunning(OBJ) returns a boolean RESULT indicating % true if the XCP channel object OBJ is actively running DAQ and/or STIM % measurement lists with the slave module, otherwise it returns false. % % Example: % a2lObj = xcpA2L('myFile.a2l'); % channelObj = xcpChannel(a2lObj, 'CAN', 'Vector', 'CANcaseXL 1', 1); % connect(channelObj); % createMeasurementList(channelObj, 'DAQ', 'Event1', 'Measurement1'); % startMeasurement(channelObj); % result = isMeasurementRunning(channelObj); % % See also VNT. % Return an indication based off the internal state property. result = obj.MeasurementState; end end methods (Access = 'private') function executeDAQTimerCallback(obj) % executeDAQTimerCallback A timer callback function that runs periodic DAQ tasks. % If there is no logged information at the protocol level, then exit. if numel(obj.ProtocolObject.DAQData) == 0 return; end % Copy the information to a local variable to process it. localData = obj.ProtocolObject.DAQData; % Delete the copied information in the protocol object. obj.ProtocolObject.DAQData(1:numel(localData)) = []; % Loop through and process each received entry. for ii = 1:numel(localData) % Find the ODT in the DAQ lists that matches the ODT of the % received message. switch obj.A2LFileObject.DAQInfo.IdentificationFieldType case xcp.Utility.IDENTIFICATION_FIELD_TYPE_ABSOLUTE % When absolute ODT numbering is used, we not know the % list number at this time, so we need to search % through them all until we find the right ODT. for jj = 1:obj.NumberOfDAQLists % Search the list for the ODT, and break when found. ODTObject = obj.DAQListObject(jj).getODTObject(localData{ii}(1)); if ~isempty(ODTObject) break; end end case xcp.Utility.IDENTIFICATION_FIELD_TYPE_RELATIVE_BYTE % For relative byte identification fields, we know the % list number and the relative ODT number. Both are % byte values. ODTObject = obj.DAQListObject(1 + localData{ii}(2)).getODTObject(localData{ii}(1)); case xcp.Utility.IDENTIFICATION_FIELD_TYPE_RELATIVE_WORD % For relative word identification fields, again we % know both the list number and relative ODT number. % The ODT number is a byte. The list number is a word. listNumber = 1 + xcp.Utility.unpackParameter( ... obj.A2LFileObject.ProtocolLayerInfo.ByteOrder, ... xcp.Utility.BYTES_IN_WORD, ... localData{ii}(2:3)); ODTObject = obj.DAQListObject(listNumber).getODTObject(localData{ii}(1)); case xcp.Utility.IDENTIFICATION_FIELD_TYPE_RELATIVE_WORD_ALIGNED % For relative word aligned identification fields, again we % know both the list number and relative ODT number. % The ODT number is a byte. The list number is a word, % but it is located in different byte positions. listNumber = 1 + xcp.Utility.unpackParameter( ... obj.A2LFileObject.ProtocolLayerInfo.ByteOrder, ... xcp.Utility.BYTES_IN_WORD, ... localData{ii}(3:4)); ODTObject = obj.DAQListObject(listNumber).getODTObject(localData{ii}(1)); end % Set the byte index to begin reading data after the % identification field. currentByte = 1 + xcp.Utility.MAP_IDENTIFICATION_FIELD_TYPE_BYTES_USED( ... obj.A2LFileObject.DAQInfo.IdentificationFieldType); % Loop through and read all of the measurements in this ODT. for jj = 1:ODTObject.NumberOfODTEntries % Use the measurement information to read the value. firstByte = currentByte; lastByte = firstByte + ODTObject.ODTEntryInfo(jj).SizeInBytes - 1; data = xcp.Utility.unpackMeasurement(ODTObject.ODTEntryInfo(jj), localData{ii}(firstByte:lastByte)); % Set this new data into the DAQ data arrays. obj.DAQData.(ODTObject.ODTEntryInfo(jj).Name) = ... [obj.DAQData.(ODTObject.ODTEntryInfo(jj).Name) data]; % Increment the current byte counter to index the next ODT % Entry in the message data. currentByte = currentByte + ODTObject.ODTEntryInfo(jj).SizeInBytes; end end end function executeSTIMTimerCallback(obj) % executeSTIMTimerCallback A timer callback function that runs periodic STIM tasks. % Loop through each STIM list. for ii = 1:obj.NumberOfSTIMLists % Subtract the STIM timer rate from the counter. obj.STIMListObject(ii).STIMCounter = obj.STIMListObject(ii).STIMCounter - obj.STIMTimerRate; % If the counter has gone negative then it is time for this % list to be transmitted. if obj.STIMListObject(ii).STIMCounter <= 0 % Transmit the message for all ODTs in the list. obj.ProtocolObject.TransportLayerObject.transmit([obj.STIMListObject(ii).ODTObject.STIMMessageObject]); % Reset the counter. obj.STIMListObject(ii).STIMCounter = obj.STIMListObject(ii).STIMPeriod; end end end function unlockResource(obj, resource) % unlockResource Unlocks a slave with seed and key security access. % % resource - A string indicating which resource to unlock. % Check that a seed and key DLL file is configured. if isempty(obj.SeedKeyDLLFullPath) error(message('vnt:XCP:NoSeedKeyDLLFileConfigured', resource)); end % Call for the first part of the seed. [~, seedLength, seedValue] = obj.ProtocolObject.getSeed( ... xcp.Utility.FIRST_PART_OF_SEED, ... xcp.Utility.(['RESOURCE_' resource])); % Store the number of seed bytes read so far. seedBytesRead = numel(seedValue); % Get the additional parts of the seed value if necessary. while seedBytesRead ~= seedLength % Call for additional seed bytes. [~, ~, seedValueAdditional] = obj.ProtocolObject.getSeed( ... xcp.Utility.REMAINING_PART_OF_SEED, ... xcp.Utility.(['RESOURCE_' resource])); % Put the seed bytes together. seedValue = [seedValue seedValueAdditional]; %#ok<AGROW> % Add to the number of seed bytes read. seedBytesRead = seedBytesRead + numel(seedValueAdditional); end % Call the seed and key DLL MEX interface to turn the seed into a key. [status, keyLength, keyValue] = mexXCPSeedKey( ... 'skGetKeyFromSeed', ... obj.SeedKeyDLLFullPath, ... xcp.Utility.(['RESOURCE_' resource]), ... seedLength, ... seedValue); % Verify that the key value was obtained. if status ~= xcp.Utility.SEED_KEY_SUCCESS error(message('vnt:XCP:KeyGenerationFailed', resource, xcp.Utility.MAP_SEED_KEY_RESPONSE_TO_NAME(status))); end % Use the key value to unlock the resource. keyBytesRemainingToSend = keyLength; while keyBytesRemainingToSend > 0 % Determine which key value bytes to send. if keyBytesRemainingToSend <= xcp.Utility.MAX_KEY_BYTES_PER_CTO % Send all available bytes. keyBytesToSend = keyValue; % Clear the key value. keyValue = []; else % Send the maximum number of bytes that fit into the CTO. keyBytesToSend = keyValue(1:xcp.Utility.MAX_KEY_BYTES_PER_CTO); % Clear those bytes in the key value. keyValue(1:xcp.Utility.MAX_KEY_BYTES_PER_CTO) = []; end % Issue an unlock command. [~, protectionStatus] = obj.ProtocolObject.unlock(keyBytesRemainingToSend, keyBytesToSend); % Adjust the number of remaining key bytes to send. keyBytesRemainingToSend = keyBytesRemainingToSend - numel(keyBytesToSend); end % Verify that the resource was unlocked. if bitand(protectionStatus, xcp.Utility.(['RESOURCE_' resource])) error(message('vnt:XCP:ResourceDidNotUnlock', resource)); end end function errorWhenConnected(obj) % errorWhenConnected Throws an error if the channel is connected. % Check the state. if obj.isConnected() % Throw an error to prevent operation by the calling method. error(message('vnt:XCP:ChannelConnected')); end end function errorWhenDisconnected(obj) % errorWhenDisconnected Throws an error if the channel is disconnected. % Check the state. if ~obj.isConnected() % Throw an error to prevent operation by the calling method. error(message('vnt:XCP:ChannelDisconnected')); end end function errorWhenMeasurementActive(obj) % errorWhenMeasurementActive Throws an error if measurements are active. % Check the state. if obj.isMeasurementRunning() % Throw an error to prevent operation by the calling method. error(message('vnt:XCP:MeasurementActive')); end end function errorWhenMeasurementInactive(obj) % errorWhenMeasurementInactive Throws an error if measurements are inactive. % Check the state. if ~obj.isMeasurementRunning() % Throw an error to prevent operation by the calling method. error(message('vnt:XCP:MeasurementInactive')); end end function errorWhenNoMeasurementListsConfigured(obj) % errorWhenNoMeasurementListsConfigured Throws an error if no lists are configured. % Check if any DAQ or STIM lists are currently configured. if isempty(obj.DAQListObject) && isempty(obj.STIMListObject) % Throw an error to prevent operation by the calling method. error(message('vnt:XCP:NoMeasurementsListsConfigured')); end end end methods % Custom get/set methods for properties. function value = get.NumberOfDAQLists(obj) value = numel(obj.DAQListObject); end function value = get.NumberOfSTIMLists(obj) value = numel(obj.STIMListObject); end function value = get.NumberOfMeasurementLists(obj) value = numel(obj.DAQListObject) + numel(obj.STIMListObject); end function set.SeedKeyDLL(obj, newValue) % Validate the new value argument. validateattributes(newValue, {'char'}, {'nonempty'}); % Get the full path to the seed and key DLL file. [status, fileInfo, ~] = fileattrib(newValue); % Make sure the file exists. if status == 0 error(message('vnt:XCP:SeedKeyDLLFileNotFound')); end % Set the property value. obj.SeedKeyDLL = newValue; % Also set a reference to the full path to the file. obj.SeedKeyDLLFullPath = fileInfo.Name; %#ok<MCSUP> end end % Custom get/set methods for properties. end