gusucode.com > vnt工具箱matlab源码程序 > vnt/vnt/+can/+ni/+xnet/Channel.m

    classdef Channel < can.Channel
% Channel Connection to an NI-XNET device channel.
%
%   This class provides connectivity to CAN channels for NI-XNET 
%   devices. It contains properties and methods to configure and use the 
%   channel on a network.
%
%   See also VNT.

% Authors: JDP
% Copyright 2013-2015 The MathWorks, Inc.
    
% Note that all uncommented properties are Abstract and inherit their
% help from the super class.

properties (SetAccess = 'private', GetAccess = 'protected')
    AsyncioChannel
    AsyncioReplayChannel
end

properties (Dependent, SetAccess = 'private')
    BusSpeed
    NumOfSamples
    SJW
    TSEG1
    TSEG2
end

properties (Dependent, SetAccess = 'private')
    BusStatus
end

properties (SetAccess = 'private')
    Device
    DeviceChannelIndex
    DeviceSerialNumber
    DeviceVendor
end

properties (SetAccess = 'private')
    % DeviceType - The series and form factor of the device to which the
    %   channel is connected.
    DeviceType
end

properties (SetAccess = 'protected')
    InitializationAccess
    InitialTimestamp
end

properties (Dependent, SetAccess = 'private')
    ReceiveErrorCount
    TransmitErrorCount
end

properties (SetAccess = 'private', GetAccess = 'protected')
    ReceiveQueueSize
    TransmitQueueSize
end

properties (SetAccess = 'private', GetAccess = 'private')
    % SessionReferenceInput - The reference value used within the vendor 
    %   driver to access the device represented by the channel for input.
    SessionReferenceInput
    % SessionReferenceOutput - The reference value used within the vendor 
    %   driver to access the device represented by the channel for output.
    SessionReferenceOutput
end

properties (Dependent)
    SilentMode
    % OnboardTermination - Configures the onboard termination of the
    %   NI-XNET device which controls bus termination on device.
    OnboardTermination
end

properties (SetAccess = 'private')
    TransceiverName
end

properties (Dependent)
    TransceiverState
end

properties (SetAccess = 'private', GetAccess = 'protected')
    MultipleAccessAllowed = true;
    Usable = false;
    PrivateInitialTimestamp = datetime.empty();
end

properties
    UserData
    % StartTriggerTerminal - Configures a sync/trigger connection
    %   to start the NI-XNET channel on the connected source terminal.
    StartTriggerTerminal
end


methods
    
    function obj = Channel(varargin)
    % Channel Construct a connection to a CAN device.
        
        % Check the argument count.
        narginchk(2,2);
        
        % Put the input arguments into local variables.
        inVendor = varargin{1};
        inDevice = varargin{2};
        
        % Validate the vendor string.
        inVendor = validatestring(inVendor, {'NI'}, 'Channel', 'VENDOR', 1);
        % Validate the device string.
        validateattributes(inDevice, {'char'}, {'nonempty', 'row'}, 'Channel', 'DEVICE', 2);
        
        % Check for the presence of the vendor driver.
        if ~can.ni.xnet.Utility.isDriverAvailable();
            error(message('vnt:Channel:DriverNotFound', 'National Instruments X-NET'));
        end
        
        % Get the channel information for the requested device.
        channelInfo = can.ni.xnet.Utility.getChannelInfoByInterface(inDevice);

        % If channel info is empty, the requested interface is not available.
        if isempty(channelInfo)
            error(message('vnt:Channel:DeviceNotFound'));
        end
        
        % Set the device identification properties.
        obj.DeviceVendor = inVendor;
        obj.Device = inDevice;
        obj.DeviceChannelIndex = channelInfo.Interface.PortNumber;
        obj.DeviceSerialNumber = dec2hex(channelInfo.Device.SerialNumber);
        obj.DeviceType = channelInfo.Device.ProductName;
        
        % Construct connections to the device.
        obj.SessionReferenceInput = can.ni.xnet.NIXNET.nxCreateSession( ...
            can.ni.xnet.Utility.USE_DEFAULT_DATABASE, ...
            can.ni.xnet.Utility.USE_NO_CLUSTER, ...
            can.ni.xnet.Utility.USE_NO_LIST, ...
            obj.Device, ...
            can.ni.xnet.Utility.nxMode_FrameInStream);
        
        obj.SessionReferenceOutput = can.ni.xnet.NIXNET.nxCreateSession( ...
            can.ni.xnet.Utility.USE_DEFAULT_DATABASE, ...
            can.ni.xnet.Utility.USE_NO_CLUSTER, ...
            can.ni.xnet.Utility.USE_NO_LIST, ...
            obj.Device, ...
            can.ni.xnet.Utility.nxMode_FrameOutStream);

        % Configure the read and write message queue sizes.
        obj.ReceiveQueueSize = can.ni.xnet.NIXNET.nxGetProperty( ...
            obj.SessionReferenceInput, ...
            can.ni.xnet.Utility.nxPropSession_QueueSize, ...
            'u32');

        obj.TransmitQueueSize = can.ni.xnet.NIXNET.nxGetProperty( ...
            obj.SessionReferenceOutput, ...
            can.ni.xnet.Utility.nxPropSession_QueueSize, ...
            'u32');
        
        % Configure the device so that it does not automatically go online
        % when the connection is established. We want it to go online on
        % demand only.
        can.ni.xnet.NIXNET.nxSetProperty( ...
            obj.SessionReferenceOutput, ...
            can.ni.xnet.Utility.nxPropSession_AutoStart, ...
            false);
        
        % Enable self reception so that messages transmitted are received
        % on this channel.
        can.ni.xnet.NIXNET.nxSetProperty( ...
            obj.SessionReferenceInput, ...
            can.ni.xnet.Utility.nxPropSession_IntfEchoTx, ...
            true);
        
        % NI-XNET sessions are created without any baud rate set, so
        % default it to a known good value.
        can.ni.xnet.NIXNET.nxSetProperty( ...
            obj.SessionReferenceInput, ... 
            can.ni.xnet.Utility.nxPropSession_IntfBaudRate, ...
            can.ni.xnet.Utility.DefaultBusSpeed);

        % Determine the path for all asyncio plugins.
        arch = computer('arch');
        devicePath = fullfile(toolboxdir('vnt'), 'vnt', 'private', arch, 'nixnetplugin');
        converterPath = fullfile(toolboxdir('vnt'), 'vnt', 'private', arch, 'canmlconverter');
        
        % Create the asyncio object for normal receive and transmit.
        asyncioOptions.SessionReferenceInput = obj.SessionReferenceInput;
        asyncioOptions.SessionReferenceOutput = obj.SessionReferenceOutput;
        asyncioOptions.ReceiveCapable = true;
        asyncioOptions.TransmitCapable = true;
        asyncioOptions.ReplayCapable = false;
        
        obj.AsyncioChannel = asyncio.Channel( ...
            devicePath, converterPath, asyncioOptions, [Inf, 0]);
        
        % Create the asyncio replay object.
        asyncioOptions.ReceiveCapable = false;
        asyncioOptions.TransmitCapable = true;
        asyncioOptions.ReplayCapable = true;

        obj.AsyncioReplayChannel = asyncio.Channel( ...
            devicePath, converterPath, asyncioOptions, [Inf, Inf]);
        
        % Normally this is used to indicate if the channel has 
        % initialization rights for the device to which it is connected. 
        % For NI-XNET devices though, InitializationAccess has no concrete
        % meaning because only one connection may be used at a time. It is
        % provided for general compatibility with code written for other
        % vendor devices. For NI-XNET devices then, InitializationAccess is
        % always true.
        obj.InitializationAccess = true;

        % Set the object usable.
        obj.Usable = true;
    end
    
    function configBusSpeed(obj, varargin)
    % configBusSpeed Set the bit timing of a CAN channel.
    %
    %   configBusSpeed(CHANNEL, BUSSPEED) sets the speed of the CHANNEL
    %   to BUSSPEED in a direct form that uses default bit timing
    %   calculation factors.
    %
    %   Examples:
    %       channel = canChannel('NI', 'CAN0')
    %       configBusSpeed(channel, 250000)
    %
    %   See also VNT.
        
        % The full version of the bus speed configuration function is not
        % available for this hardware vendor. In order to avoid confusion,
        % we throw a special error message to indicate this.
        if nargin == 6
            error(message('vnt:Channel:ConfigBusSpeedAdvancedFormNotAvailable'));
        end
    
        % Check the argument count.
        narginchk(2,2);
        
        % Validate the channel before using its functionality.
        assertValidity(obj);
        
        % Verify that the channel is offline.
        if obj.Running
            error(message('vnt:Channel:ChannelNotOffline'));
        end
        
        % Validate the busSpeed.
        validateattributes(varargin{1}, {'numeric'}, ...
            {'integer', 'nonempty', 'nonnan', 'nonsparse', 'real', ...
             'scalar', '>=', 5000, '<=', 1000000}, ...
            'configBusSpeed', 'BUSSPEED');
        
        % Call the vendor driver to set the bit timing in direct form.
        can.ni.xnet.NIXNET.nxSetProperty( ...
            obj.SessionReferenceInput, ...
            can.ni.xnet.Utility.nxPropSession_IntfBaudRate, ...
            varargin{1});
    end
    
    function disp(obj)
    % disp Display array.
        
        % Validate the channel before using its functionality.
        assertValidity(obj);
        
        % Display a top level summary.
        fprintf(1, 'Summary of CAN Channel using ''%s'' ''%s''.\n\n',...
            obj.DeviceVendor,...
            obj.Device);
        
        % Display basic configuration parameters.
        fprintf(1, '  Channel Parameters:  Bus Speed is %d.\n', obj.BusSpeed);
        fprintf(1, '%23sDevice type is ''%s''.\n', '', obj.DeviceType);
        fprintf(1, '%23sSerial Number of this device is ''%s''.\n', '', obj.DeviceSerialNumber);
        fprintf(1, '%23sTransceiver name is ''%s''.\n', '', obj.TransceiverName);
        
        % Display the currently attached database.
        if isempty(obj.Database)
            fprintf(1, '%23sNo database is attached.\n\n', '');
        else
            fprintf(1, '%23s''%s.dbc'' database is attached.\n\n', '', obj.Database.Name);
        end
        
        % Display the current channel status.
        if obj.Running
            fprintf(1, '%14sStatus:  Online.\n', '');
        else
            fprintf(1, '%14sStatus:  Offline - Waiting for start.\n', '');
        end
        
        % Display message transmit and receive information.
        fprintf(1, '%23s%d messages available to receive.\n', '', obj.MessagesAvailable);
        fprintf(1, '%23s%d messages transmitted since last start.\n', '', obj.MessagesTransmitted);
        fprintf(1, '%23s%d messages received since last start.\n\n', '', obj.MessagesReceived);

        % Display a filter history section with the first entry in the log.
        fprintf(1, '      Filter History:  %s\n', obj.FilterHistory);
        
        % Add a final line for spacing.
        fprintf(1, '\n');
    end
    
    function getdisp(obj)
    % getdisp Overrides the standard getdisp to provide a formatting listing.
        
        % Validate the channel before using its functionality.
        assertValidity(obj);
        
        % Display general information.
        fprintf(1, '\n  General Settings:\n');
        fprintf(1, '\tBusStatus = ''%s''\n', obj.BusStatus);
        
        if isempty(obj.Database)
            fprintf(1, '\tDatabase = []\n');
        else
            fprintf(1, '\tDatabase = ''%s.dbc''\n', obj.Database.Name);
        end
        
        fprintf(1, '\tFilterHistory = ''%s''\n', obj.FilterHistory);
        fprintf(1, '\tInitializationAccess = %d\n', obj.InitializationAccess);
        fprintf(1, '\tMessageReceivedFcn = %s\n', can.Utility.callbackFcnToString(obj.MessageReceivedFcn));
        fprintf(1, '\tMessageReceivedFcnCount = %d\n', obj.MessageReceivedFcnCount);
        fprintf(1, '\tMessagesAvailable = %d\n', obj.MessagesAvailable);
        fprintf(1, '\tMessagesReceived = %d\n', obj.MessagesReceived);
        fprintf(1, '\tMessagesTransmitted = %d\n', obj.MessagesTransmitted);
        fprintf(1, '\tReceiveErrorCount = %d\n', obj.ReceiveErrorCount);
        fprintf(1, '\tRunning = %d\n', obj.Running);
        fprintf(1, '\tSilentMode = %d\n', obj.SilentMode);
        fprintf(1, '\tTransmitErrorCount = %d\n', obj.TransmitErrorCount);
        
        % Display device specific information.
        fprintf(1, '\n  Device Settings:\n');
        fprintf(1, '\tDevice = ''%s''\n', obj.Device);
        fprintf(1, '\tDeviceChannelIndex = %d\n', obj.DeviceChannelIndex);
        fprintf(1, '\tDeviceSerialNumber = ''%s''\n', obj.DeviceSerialNumber);
        fprintf(1, '\tDeviceVendor = ''%s''\n', obj.DeviceVendor);
        fprintf(1, '\tDeviceType = ''%s''\n', obj.DeviceType);
        
        % Display transceiver information.
        fprintf(1, '\n  Transceiver Settings:\n');
        fprintf(1, '\tTransceiverName = ''%s''\n', obj.TransceiverName);
        fprintf(1, '\tTransceiverState = %d\n', obj.TransceiverState);
        
        % Display bit timing information.
        fprintf(1, '\n  Bit Timing Settings:\n');
        fprintf(1, '\tBusSpeed = %d\n', obj.BusSpeed);
        fprintf(1, '\tSJW = %d\n', obj.SJW);
        fprintf(1, '\tTSEG1 = %d\n', obj.TSEG1);
        fprintf(1, '\tTSEG2 = %d\n', obj.TSEG2);
        fprintf(1, '\tNumOfSamples = %d\n\n', obj.NumOfSamples);
    end
    
    function setdisp(obj)
    % setdisp Overrides the standard setdisp to provide a formatting listing.
        
        % Validate the channel before using its functionality.
        assertValidity(obj);
        
        fprintf(1, '\n  Database = can.Database handle -or- empty\n');
        fprintf(1, '  MessageReceivedFcn = string -or- function handle -or- cell array\n');
        fprintf(1, '  MessageReceivedFcnCount\n');
        fprintf(1, '  SilentMode = [ {false} | true ]\n');
        fprintf(1, '  TransceiverState\n\n');
    end
    
    
    function out = get.BusStatus(obj)
        % Validate the channel before using its functionality.
        if ~assertValidity(obj)
            % Return empty for any invalid object state.
            out = [];
            return;
        end
        
        % Get the value from the vendor driver.
        [state, ~] = can.ni.xnet.NIXNET.nxReadState( ...
            obj.SessionReferenceInput, ...
            can.ni.xnet.Utility.nxState_CANComm);
        out = state.CommunicationState;
    end
    
    function out = get.ReceiveErrorCount(obj)
        % Validate the channel before using its functionality.
        if ~assertValidity(obj)
            % Return empty for any invalid object state.
            out = [];
            return;
        end
        
        % Get the value from the vendor driver.
        [state, ~] = can.ni.xnet.NIXNET.nxReadState( ...
            obj.SessionReferenceInput, ...
            can.ni.xnet.Utility.nxState_CANComm);
        out = state.ReceiveErrorCounter;
    end
    
    function out = get.TransmitErrorCount(obj)
        % Validate the channel before using its functionality.
        if ~assertValidity(obj)
            % Return empty for any invalid object state.
            out = [];
            return;
        end
        
        % Get the value from the vendor driver.
        [state, ~] = can.ni.xnet.NIXNET.nxReadState( ...
            obj.SessionReferenceInput, ...
            can.ni.xnet.Utility.nxState_CANComm);
        out = state.TransmitErrorCounter;
    end
    
    function out = get.SilentMode(obj)
        % Validate the channel before using its functionality.
        if ~assertValidity(obj)
            % Return empty for any invalid object state.
            out = [];
            return;
        end
        
        % Query the driver for the property value.
        out = logical(can.ni.xnet.NIXNET.nxGetProperty( ...
            obj.SessionReferenceInput, ...
            can.ni.xnet.Utility.nxPropSession_IntfCANLstnOnly, ...
            'boolean'));
    end
    
    function set.SilentMode(obj, newValue)
        % Validate the channel before using its functionality.
        assertValidity(obj);
        
        % Verify that the channel is offline.
        if obj.Running
            error(message('vnt:Channel:ChannelNotOffline'));
        end
        
        % Validate the new value.
        validateattributes(newValue, {'logical'}, {'nonempty', 'scalar'}, 'set.SilentMode', 'VALUE');
        
        % Set the property value according to the input.
        can.ni.xnet.NIXNET.nxSetProperty( ...
            obj.SessionReferenceInput, ...
            can.ni.xnet.Utility.nxPropSession_IntfCANLstnOnly, ...
            newValue);
    end
    
    function out = get.OnboardTermination(obj)
        % Validate the channel before using its functionality.
        if ~assertValidity(obj)
            % Return empty for any invalid object state.
            out = [];
            return;
        end
        
        % Query the driver for the property value.
        out = logical(can.ni.xnet.NIXNET.nxGetProperty( ...
            obj.SessionReferenceInput, ...
            can.ni.xnet.Utility.nxPropSession_IntfCANTerm, ...
            'u32'));
    end
    
    function set.OnboardTermination(obj, newValue)
        % Validate the channel before using its functionality.
        assertValidity(obj);
        
        % Verify that the channel is offline.
        if obj.Running
            error(message('vnt:Channel:ChannelNotOffline'));
        end
        
        % Validate the new value.
        validateattributes(newValue, {'logical'}, {'nonempty', 'scalar'}, 'set.OnboardTermination', 'VALUE');
        
        % Set the property value according to the input.
        can.ni.xnet.NIXNET.nxSetProperty( ...
            obj.SessionReferenceInput, ...
            can.ni.xnet.Utility.nxPropSession_IntfCANTerm, ...
            newValue);
    end
    
    function set.StartTriggerTerminal(obj, newValue)
        % Validate the channel before using its functionality.
        assertValidity(obj);

        % Validate inputs.
        validateattributes(newValue, {'char'}, ...
            {'nonempty'}, 'set.StartTriggerTerminal', 'TERMINAL');

        % Verify that the channel is offline.
        if obj.Running
            error(message('vnt:Channel:ChannelNotOffline'));
        end
        
        % Verify that the property is not being set more than once.
        if ~isempty(obj.StartTriggerTerminal)
            error(message('vnt:Channel:StartTriggerTerminalWriteOnce'));
        end
        
        % Insert a forward slash into the trigger terminal string if one is
        % not present. This is done to mask a bug in the XNET API that will
        % crash MATLAB if a slash is not included. Should NI fix this bug,
        % this protection can be removed.
        if newValue(1) ~= '/'
            newValue = ['/' newValue];
        end
        
        % Get the channel information to determine the form factor.
        channelInfo = can.ni.xnet.Utility.getChannelInfoByInterface(obj.Device); %#ok<MCSUP>
        
        try
            % Set the start trigger in the driver depending on the form factor.
            switch channelInfo.Device.FormFactor
                % C series devices use the nxPropSession_IntfSrcTermStartTrigger
                % property.
                case can.ni.xnet.Utility.nxPropDev_FormFac_Values(2)
                    % Set the XNET property.
                    can.ni.xnet.NIXNET.nxSetProperty( ...
                        obj.SessionReferenceInput, ... 
                        can.ni.xnet.Utility.nxPropSession_IntfSrcTermStartTrigger, ...
                        newValue); %#ok<MCSUP>
                % Other devices use the connectTerminals function.
                otherwise
                    % Call the XNET function.
                    can.ni.xnet.NIXNET.nxConnectTerminals( ...
                        obj.SessionReferenceInput, ...
                        newValue, ...
                        can.ni.xnet.Utility.nxTerm_StartTrigger); %#ok<MCSUP>
            end
        catch err
            % Invalidate the property value.
            obj.StartTriggerTerminal = 'N/A';
            % Rethrow the original error, likely a direct NI-XNET error.
            rethrow(err);
        end
        
        % Set the object property.
        obj.StartTriggerTerminal = newValue;
    end
    
    function out = get.TransceiverName(obj)
        % Validate the channel before using its functionality.
        if ~assertValidity(obj)
            % Return empty for any invalid object state.
            out = [];
            return;
        end
        
        % Get the value of the property.
        out = can.ni.xnet.NIXNET.nxGetProperty( ...
            obj.SessionReferenceInput, ...
            can.ni.xnet.Utility.nxPropSession_IntfCANTcvrType, ...
            'u32');
        out = can.ni.xnet.Utility.nxPropSession_IntfCANTcvrType_Values(out);
    end
    
    function out = get.TransceiverState(obj)
        % Validate the channel before using its functionality.
        if ~assertValidity(obj)
            % Return empty for any invalid object state.
            out = [];
            return;
        end
        
        % Query the driver for the property value.
        out = can.ni.xnet.NIXNET.nxGetProperty( ...
            obj.SessionReferenceInput, ...
            can.ni.xnet.Utility.nxPropSession_IntfCANTcvrState, ...
            'u32');
    end
    
    function set.TransceiverState(obj, newValue)
        % Validate the channel before using its functionality.
        assertValidity(obj);
        
        % Validate the input argument newValue.
        validateattributes(newValue, {'numeric'}, ...
            {'finite', 'integer', 'nonempty', 'nonnan', ...
             'nonnegative', 'nonsparse', 'real', 'scalar'}, ...
            'set.TransceiverState', 'VALUE');
        
        % Query the driver for the property value.
        can.ni.xnet.NIXNET.nxSetProperty( ...
            obj.SessionReferenceInput, ...
            can.ni.xnet.Utility.nxPropSession_IntfCANTcvrState, ...
            newValue);
    end
    
    function out = get.BusSpeed(obj)
        % Validate the channel before using its functionality.
        if ~assertValidity(obj)
            % Return empty for any invalid object state.
            out = [];
            return;
        end
        
        % Query the driver for the property value.
        out = can.ni.xnet.NIXNET.nxGetProperty( ...
            obj.SessionReferenceInput, ...
            can.ni.xnet.Utility.nxPropSession_IntfBaudRate, ...
            'u32');
    end
    
    function out = get.SJW(obj)
        % Validate the channel before using its functionality.
        if ~assertValidity(obj)
            % Return empty for any invalid object state.
            out = [];
            return;
        end
        
        % Return this value as invalid as access to reading it for NI
        % hardware it not provided in their interface.
        out = NaN;
    end
    
    function out = get.TSEG1(obj)
        % Validate the channel before using its functionality.
        if ~assertValidity(obj)
            % Return empty for any invalid object state.
            out = [];
            return;
        end
        
        % Return this value as invalid as access to reading it for NI
        % hardware it not provided in their interface.
        out = NaN;
    end
    
    function out = get.TSEG2(obj)
        % Validate the channel before using its functionality.
        if ~assertValidity(obj)
            % Return empty for any invalid object state.
            out = [];
            return;
        end
        
        % Return this value as invalid as access to reading it for NI
        % hardware it not provided in their interface.
        out = NaN;
    end
    
    function out = get.InitialTimestamp(obj)
        % Validate the channel before using its functionality.
        if ~assertValidity(obj)
            % Return empty for any invalid object state.
            out = [];
            return;
        end
        
        % Check the value of the internal private storage.
        if isempty(obj.PrivateInitialTimestamp)
            % Reread the value from the driver to see if the value has
            % changed or the channel has been triggered after start.
            [out, ~] = can.ni.xnet.NIXNET.nxReadState( ...
                obj.SessionReferenceInput, ...
                can.ni.xnet.Utility.nxState_TimeCommunicating);
            % Set the value into the internal private storage.
            obj.PrivateInitialTimestamp = out;
        else
            % Otherwise return the existing stored value.
            out = obj.PrivateInitialTimestamp;
        end
    end
    
    function out = get.NumOfSamples(obj)
        % Validate the channel before using its functionality.
        if ~assertValidity(obj)
            % Return empty for any invalid object state.
            out = [];
            return;
        end
        
        % Return this value as invalid as access to reading it for NI
        % hardware it not provided in their interface.
        out = NaN;
    end
    
    function obj = saveobj(obj)
    % saveobj Saves the channel information to file.
    %
    %   OBJ = saveobj(OBJ) saves the channel for future loading. The
    %   channel information is saved as a structure to due to the
    %   nature of the channel's underlying connection to third party
    %   drivers and devices.
        
        % Set object properties into a structure for saving. Turn the
        % warning off to so that it does not show to the user.
        warnState = warning('OFF', 'MATLAB:structOnObject');
        saveInfo = struct(obj);
        warning(warnState);
        
        % Remove fields that should not be saved.
        saveInfo = rmfield(saveInfo, 'AsyncioChannel');
        saveInfo = rmfield(saveInfo, 'AsyncioReplayChannel');
        saveInfo = rmfield(saveInfo, 'CustomEventListener');
        saveInfo = rmfield(saveInfo, 'ReceiveCallbackListener');
        
        % Set the output to save.
        obj = saveInfo;
    end
    
end


methods (Access = 'protected')
    
    function onAsyncioCustomEvent(obj, source, data) %#ok<INUSD>
    % onAsyncioCustomEvent Callback function for event occurrence.
    %
    %   onAsyncioCustomEvent(CHANNEL, SOURCE, DATA) is registered
    %   with the Asyncio layer and is called upon custom event.
        
    end
    
    function filterSetInternal(obj, code, mask, idType) %#ok<INUSD>
    % filterSetInternal Sets hardware filters.
    %
    %   This method is used internally by the hardware agnostic filtering
    %   code to employ use of the hardware filters. There is a performance
    %   benefit to block as many messages as possible via hardware filters
    %   before applying software filtering on the channel.
    
        % NI-XNET has no implementation for hardware filtering use.
    end
    
    function setInitialTimestamp(obj)
    % setInitialTimestamp Set the initial timestamp property value.
    
        % Get the value direct from the driver. Set the value into the 
        % internal private storage.
        [obj.PrivateInitialTimestamp, ~] = can.ni.xnet.NIXNET.nxReadState( ...
            obj.SessionReferenceInput, ...
            can.ni.xnet.Utility.nxState_TimeCommunicating);
    end

end


methods (Hidden)
    
    function delete(obj)
        % Stop the channel to ensure it is offline when removed. The
        % channel is stopped in the asyncio layer directly.
        if ~isempty(obj.AsyncioReplayChannel)
            obj.AsyncioReplayChannel.close();
        end
        
        if ~isempty(obj.AsyncioChannel)
            obj.AsyncioChannel.close();
        end
        
        % Clean up any vendor driver connections only if they were
        % initialized. This protects delete from generating errors on
        % partially failed construction.
        if ~isempty(obj.SessionReferenceInput)
            can.ni.xnet.NIXNET.nxClear(obj.SessionReferenceInput);
        end

        if ~isempty(obj.SessionReferenceOutput)
            can.ni.xnet.NIXNET.nxClear(obj.SessionReferenceOutput);
        end
    end
    
    function connectTerminals(obj, sourceTerminal, destinationTerminal)
    % connectTerminals Connects triggering terminals.
    %
    %   connectTerminals(ch, sourceTerminal, destinationTerminal) connects
    %   a source terminal to a destination terminal on the device. This
    %   represents an external or internal hardware connection point.
    %   Terminal inputs use NI Terminal I/O names.
    %
    %   Examples:
    %       channel = canChannel('NI', 'CAN0')
    %       connectTerminals(channel, 'StartTrigger', 'FrontPanel0')
    %
    %   See also VNT.

        % Check the argument count.
        narginchk(3, 3);
        
        % Validate the channel before using its functionality.
        assertValidity(obj);

        % Validate inputs.
        validateattributes(sourceTerminal, {'char'}, ...
            {'nonempty'}, 'connectTerminals', 'SourceTerminal');
        validateattributes(destinationTerminal, {'char','cell'}, ...
            {'nonempty'}, 'connectTerminals', 'DestinationTerminal');

        % Verify that the channel is offline.
        if obj.Running
            error(message('vnt:Channel:ChannelNotOffline'));
        end
        
        % Call direct to the NI-XNET API.
        can.ni.xnet.NIXNET.nxConnectTerminals( ...
            obj.SessionReferenceInput, ...
            sourceTerminal, ...
            destinationTerminal);
    end
    
    function disconnectTerminals(obj, sourceTerminal, destinationTerminal)
    % disconnectTerminals Disconnects triggering terminals.
    %
    %   disconnectTerminals(ch, sourceTerminal, destinationTerminal) disconnects
    %   a source terminal from a destination terminal on the device. This
    %   represents an external or internal hardware connection point.
    %   Terminal inputs use NI Terminal I/O names.
    %
    %   Examples:
    %       channel = canChannel('NI', 'CAN0')
    %       connectTerminals(channel, 'StartTrigger', 'FrontPanel0')
    %       disconnectTerminals(channel, 'StartTrigger', 'FrontPanel0')
    %
    %   See also VNT.

        % Check the argument count.
        narginchk(3, 3);
        
        % Validate the channel before using its functionality.
        assertValidity(obj);
        
        % Validate inputs.
        validateattributes(sourceTerminal, {'char'}, ...
            {'nonempty'}, 'disconnectTerminals', 'SourceTerminal');
        validateattributes(destinationTerminal, {'char','cell'}, ...
            {'nonempty'}, 'disconnectTerminals', 'DestinationTerminal');

        % Verify that the channel is offline.
        if obj.Running
            error(message('vnt:Channel:ChannelNotOffline'));
        end
        
        % Call direct to the NI-XNET API.
        can.ni.xnet.NIXNET.nxDisconnectTerminals( ...
            obj.SessionReferenceInput, ...
            sourceTerminal, ...
            destinationTerminal);
    end
    
    function setStartTrigger(obj, sourceTerminal)
    % setStartTrigger Specifies a start trigger terminal.
    %
    %   setStartTrigger(ch, sourceTerminal) connects a source terminal 
    %   to the start trigger on the device. This represents an internal 
    %   hardware connection point. Terminal inputs use NI Terminal I/O names.
    %
    %   Examples:
    %       channel = canChannel('NI', 'CAN0')
    %       setStartTrigger(channel, '/cDAQ1/do/StartTrigger')
    %
    %   See also VNT.

        % Check the argument count.
        narginchk(2, 2);
        
        % Validate the channel before using its functionality.
        assertValidity(obj);

        % Validate inputs.
        validateattributes(sourceTerminal, {'char'}, ...
            {'nonempty'}, 'setStartTrigger', 'SourceTerminal');

        % Verify that the channel is offline.
        if obj.Running
            error(message('vnt:Channel:ChannelNotOffline'));
        end
        
        % Call direct to the NI-XNET API.
        can.ni.xnet.NIXNET.nxSetProperty( ...
            obj.SessionReferenceInput, ... 
            can.ni.xnet.Utility.nxPropSession_IntfSrcTermStartTrigger, ...
            sourceTerminal);
    end
    
    function value = getStartTrigger(obj)
    % getStartTrigger Retrieves the interface start trigger terminal.
    %
    %   getStartTrigger(ch, sourceTerminal) retrieves the interface start
    %   trigger for use as a source terminal. This represents an internal 
    %   hardware connection point.
    %
    %   Examples:
    %       channel = canChannel('NI', 'CAN0')
    %       triggerValue = getStartTrigger(channel)
    %
    %   See also VNT.

        % Check the argument count.
        narginchk(1, 1);
        
        % Validate the channel before using its functionality.
        assertValidity(obj);

        % Verify that the channel is offline.
        if obj.Running
            error(message('vnt:Channel:ChannelNotOffline'));
        end
        
        % Call direct to the NI-XNET API.
        value = can.ni.xnet.NIXNET.nxGetProperty( ...
            obj.SessionReferenceInput, ...
            can.ni.xnet.Utility.nxPropSession_IntfSrcTermStartTrigger, ...
            'cstr');
    end
    
end


methods (Static)
    
    function obj = loadobj(obj)
    % loadobj Load a channel from memory.
    %
    %   OBJ = loadobj(OBJ) loads the channel from memory. The
    %   information loaded comes as a structure from which a new
    %   channel is built and configured to look like the previously
    %   saved channel.
    %
    %   Note that if an error happens while attempting to reconnect or
    %   reconfigure the channel, an empty version of the channel is
    %   returned. The empty object will not reach the user though. The
    %   MATLAB object system will reconstruct an invalid version of the
    %   channel with empty properties. Returning empty is done simply
    %   to avoid a class type warning in the event of a load error.
        
        % Check if the driver is available for devices from this vendor.
        if ~can.ni.xnet.Utility.isDriverAvailable()
            % Warn the user of the load issue.
            warning(message('vnt:Channel:CannotReconnectToDriver'));
            % Return an empty version of the channel.
            obj = can.ni.xnet.Channel.empty();
            return;
        end
        
        % Collect information on all NI devices.
        channelInfo = can.ni.xnet.Utility.getChannelInfoByInterface(obj.Device);
        
        % Check for no match on the device.
        if isempty(channelInfo)
            % Warn the user of the load issue.
            warning(message('vnt:Channel:CannotReconnectToDevice'));
            % Return an empty version of the channel.
            obj = can.ni.xnet.Channel.empty();
            return;
        end
        
        try
            % Recreate the loaded channel to rebuild a connection
            % to the device.
            newObj = can.ni.xnet.Channel('NI', channelInfo.Interface.Name);
        catch err
            % Check the error condition.
            if strfind(err.message, can.ni.xnet.Utility.ActiveConnectionErrorCode)
                % If the error condition matches the case checked, it means
                % this call failed because a connection to the device is
                % already established and running. In this case, we cannot
                % allow another connection to be made because NI hardware
                % only allows a single active connection.
                error(message('vnt:Channel:ActiveConnectionExists'));
                % Return an empty version of the channel.
                obj = can.ni.xnet.Channel.empty();
                return;
            else
                % Warn the user of the load issue.
                warning(message('vnt:Channel:CannotReconfigureChannel'));
                % Return an empty version of the channel.
                obj = can.ni.xnet.Channel.empty();
                return;
            end
        end
        
        try
            % Set the database in the channel only if the database file
            % was loaded properly for use.
            if ~isempty(obj.Database) && obj.Database.Usable
                newObj.Database = obj.Database;
            else
                newObj.Database = [];
            end
            
            % Reset the channel to the previous bus speed.
            configBusSpeed(newObj, obj.BusSpeed);
            
            % Reset silent mode only if the mode was previously set to
            % true. Since some devices from this vendor do not support
            % setting silent mode, we don't want to error and break the
            % load.
            if obj.SilentMode
                newObj.SilentMode = obj.SilentMode;
            end
            
            % Restore the filters to their previous state by performing
            % each filter operation again. Note that due to changes made in
            % version 1.3 of this object, we no longer maintain forward or
            % backward compatibility to restore filter settings. Only 
            % 1.3 and great versions will have state restored on load.
            if obj.Version >= 1.3
                % Check the configured state and set the filters on the new
                % object accordingly for standard and extended filters.
                switch obj.FilterStateStandard.State
                    case 'Allow All'
                        newObj.filterAllowAll('Standard');
                    case 'Block All'
                        newObj.filterBlockAll('Standard');
                    case 'Allow Only'
                        newObj.filterAllowOnly(obj.FilterStateStandard.PassList, 'Standard');
                end
                    
                switch obj.FilterStateExtended.State
                    case 'Allow All'
                        newObj.filterAllowAll('Extended');
                    case 'Block All'
                        newObj.filterBlockAll('Extended');
                    case 'Allow Only'
                        newObj.filterAllowOnly(obj.FilterStateExtended.PassList, 'Extended');
                end
            end
            
            % Reset other properties to match the saved state.
            newObj.MessageReceivedFcn = obj.MessageReceivedFcn;
            newObj.MessageReceivedFcnCount = obj.MessageReceivedFcnCount;
            
            % Set the transceiver mode, but protect it also by only setting
            % it if the value is other than normal mode. Some devices do
            % not support setting of this property.
            if obj.TransceiverState ~= can.ni.xnet.Utility.nxCANTcvrState_Normal
                newObj.TransceiverState = obj.TransceiverState;
            end
            
            % Load the UserData.
            if obj.Version >= 1.4
                newObj.UserData = obj.UserData;
            end
            
            % Load the InitialTimestamp and NI-specific properties.
            if obj.Version >= 1.5
                newObj.PrivateInitialTimestamp = obj.PrivateInitialTimestamp;
                newObj.OnboardTermination = obj.OnboardTermination;
                if ~isempty(obj.StartTriggerTerminal)
                    newObj.StartTriggerTerminal = obj.StartTriggerTerminal;
                end
            end
            
            % Set the new channel to return from loading.
            obj = newObj;
            
        catch err %#ok<NASGU>
            % Warn the user of the load issue.
            warning(message('vnt:Channel:CannotReconfigureChannel'));
            % Return an empty version of the channel.
            obj = can.ni.xnet.Channel.empty();
            return;
        end
    end
    
end
        
end