classdef pcapngWriter < handle & comm.internal.ConfigBase & comm_sysmod.internal.pcapCommon
%pcapngWriter Create PCAPNG file writer object
%
%   OBJ = pcapngWriter creates a packet capture next generation (PCAPNG)
%   file writer object, OBJ, that writes protocol packets into a file with
%   the .pcapng extension.
%
%   OBJ = pcapngWriter(Name, Value) creates a PCAPNG file writer
%   object, OBJ, with properties specified by one or more name-value
%   pairs. You can specify additional name-value pair arguments in any
%   order as (Name1,Value1,...,NameN,ValueN). When you do not specify a
%   property name and value, the object uses the default value.
%
%   pcapngWriter methods:
%   writeInterfaceDescriptionBlock - Write packet contents into a file with
%                                    the .pcapng extension
%   write                          - Write enhanced packet block into a
%                                    file with the .pcapng extension
%   writeCustomBlock               - Write custom block into a file with
%                                    the .pcapng extension
%
%   The method <a href="matlab:help('pcapngWriter.writeInterfaceDescriptionBlock')">writeInterfaceDescriptionBlock</a> must be called for each
%   link type before calling the <a href="matlab:help('pcapngWriter.write')">write</a> or <a href="matlab:help('pcapngWriter.writeCustomBlock')">writeCustomBlock</a> method.
%   Every call to 'write' method requires an interface Id returned by the
%   <a href="matlab:help('pcapngWriter.writeInterfaceDescriptionBlock')">writeInterfaceDescriptionBlock</a> method.
%
%   pcapngWriter Name-Value pairs:
%   'FileName'         - File name specified as a character row
%                        vector or string. The default file name is
%                        'capture'.
%   'ByteOrder'        - Byte order, specified as 'little-endian' or
%                        'big-endian'. The default value is 'little-endian'.
%   'FileComment'      - Comment for the file, specified as a character
%                        vector or string. The default value is an empty
%                        character.
%
%   % Example 1:
%   %  Write WLAN packet to PCAPNG format file with packet comment
%
%   % Create a PCAPNG object with file name 'wlanCapture.pcapng'
%   pcapngObj = pcapngWriter('FileName', 'wlanCapture');
%
%   % Create a WLAN MAC packet
%   mpdu = 'B4000000FFFFFFFFFFFF00123456789BA79A5B28';
%
%   % Write interface block
%   interfaceName = 'WLAN';
%   wlanLinkType = 105;
%   interfaceId = writeInterfaceDescriptionBlock(pcapngObj, wlanLinkType, ...
%                                                interfaceName);
%   % Write MPDU to PCAPNG format file
%   timestamp = 0; % Number of microseconds
%   packetComment = 'This is the first packet';
%   write(pcapngObj, mpdu, timestamp, interfaceId, 'PacketComment', ...
%       packetComment);
%
%   % Example 2:
%   %  Write WLAN packet without radiotap and BLE packet to
%   %  PCAPNG format file
%
%   % Create a PCAPNG object with file name 'multiProtocolCapture.pcapng'
%   pcapngObj = pcapngWriter('FileName', 'multiProtocolCapture');
%
%   % Create a WLAN MAC packet
%   mpdu = 'B4000000FFFFFFFFFFFF00123456789BA79A5B28';
%
%   % Write interface block
%   interfaceName = 'WLAN';
%   wlanLinkType = 105;
%   interfaceId = writeInterfaceDescriptionBlock(pcapngObj, wlanLinkType, ...
%                                                interfaceName);
%   % Write MPDU to PCAPNG format file
%   timestamp = 0; % Number of microseconds
%   packetComment = 'This is WLAN packet';
%   write(pcapngObj, mpdu, timestamp, interfaceId, 'PacketComment', ...
%       packetComment);
%
%   % Create a BLE packet
%   llpacket = '42BC13E206120E00050014010A001F0040001700170000007D47C0';
%
%   % Write interface description block for BLE protocol
%   interfaceName = 'BLE interface';
%   bleLinkType = 251;
%   interfaceId = writeInterfaceDescriptionBlock(pcapngObj, bleLinkType, ...
%                                                interfaceName);
%   % Write BLE link layer packet to PCAPNG format file
%   timestamp = 0; % Number of microseconds
%   packetComment = 'This is BLE packet';
%   write(pcapngObj, llpacket, timestamp, interfaceId, 'PacketComment', ...
%       packetComment);
%
%   See also pcapWriter

% Copyright 2020-2024 The MathWorks, Inc.

%#codegen

    properties (GetAccess = public, SetAccess = private)
        %FileName File name
        %   Specify file name as a character row vector or
        %   string. The default file name is 'capture'.
        FileName = 'capture'

        %FileComment Comment for the file
        %   Specify any additional comment for the file as a character
        %   vector or string. The default value is an empty character.
        FileComment = blanks(0)

        %ByteOrder Byte order
        %   Specify the byte order as 'little-endian' or
        %   'big-endian'. The default value is 'little-endian'.
        ByteOrder = 'little-endian'
    end

    properties (Hidden)
        %DisableValidation Disable the validation for input arguments of
        %write method
        %   Specify this property as a scalar logical. When true,
        %   validation is not performed on the input arguments and the
        %   packet is expected to be octets in decimal format.
        DisableValidation (1, 1) logical = false
    end

    properties (Access = private)
        %FID File identifier
        FID = -1

        %SectionHeaderPresent Section header flag
        SectionHeaderPresent (1, 1) logical = false

        %SnapLength Snap length
        %   SnapLength indicates limit on the number of bytes for each
        %   packet shown in the capture file. The value zero indicates no
        %   limit for maximum number of octets captured.
        SnapLength = 0
    end

    properties (SetAccess = private, Hidden)
        %InterfacesCount Number of interface description blocks written
        %into a PCAPNG format file
        %   Represents the count of interface description blocks written.
        %   It increments with the addition of new interface blocks.
        InterfacesCount = 0
    end

    properties (Hidden, Constant)
        ByteOrder_Values = {'little-endian', 'big-endian'}
    end

    methods (Access = private)

        function setFileName(obj, value)
            validateattributes(value, {'char' ,'string'}, {'scalartext', 'nonempty'}, ...
                mfilename, 'FileName');

            coder.internal.errorIf(any(ismember(char(value), '/\*:?"<>|"%')),...
                'shared_comm_sysmod:pcapngWriter:SpecialCharactersNotAccepted', ...
                '\ / : * ? " < > |');

            obj.FileName = char(value);
        end

        function setFileComment(obj, value)
            validateattributes(value, {'char' ,'string'}, {'scalartext'}, ...
                    mfilename, 'FileComment');

            % Max comment length allowed (16-bit)
            coder.internal.errorIf(numel(value) > 65535, ...
                'shared_comm_sysmod:pcapngWriter:CommentLengthExceed');

            obj.FileComment = char(value);
        end

        function setByteOrder(obj, value)
            propName = 'ByteOrder';
            value = validateEnumProperties(obj, propName, value);
            obj.(propName) = '';
            obj.(propName) = value;
        end

        function sectionHeader = sectionHeaderBlock(obj, varargin)
            %sectionHeaderBlock Create a section header block

            % Used to detect the block format (4 bytes)
            blockType = obj.updateEndianness([10; 13; 13; 10]);

            % Used to detect the byte ordering (4 bytes)
            byteOrderMagic = obj.updateEndianness([77; 60; 43; 26]);

            % Major Version Number (2 bytes)
            majorVersion = obj.updateEndianness([1; 0]);
            % Major Version Number (2 bytes)
            minorVersion = [0; 0];

            % Section Length is -1 (0xFFFFFFFFFFFFFFFF), this means that
            % the size of the section is not specified (8 bytes)
            sectionLength = [255; 255; 255; 255; 255; 255; 255; 255];

            %Here, 4 indicates code for application and 6 represents the
            %length of application name ('MATLAB')
            userApplicationCode = [obj.updateEndianness([4; 0]); ...
                obj.updateEndianness([6; 0])];

            % Application used to log packets into file. The user
            % application is "MATLAB" (8 bytes including padding)
            userApplication = [77; 65; 84; 76; 65; 66; 0; 0];

            % Option delimits the end of the optional fields (4 bytes)
            endOfOptions = [0; 0; 0; 0];

            % Default block length
            blockLength = 44;

            % Max block length allowed (32-bit)
            maxBlockLength = 4294967295;

            % Packet comment (variable bytes)
            if nargin > 1
                comment = varargin{1};

                commentLen = numel(comment);

                comment = obj.getByteArray(comment, commentLen);
                pad = obj.getPadBytes(comment);

                % The value [1; 0] represents the comment field
                commentCode = obj.updateEndianness([1; 0]);

                % Form the comment
                comment = [commentCode; obj.getByteArray(commentLen, 2); ...
                    comment; pad];
 
                % Total length of section header block
                blockLength = blockLength + numel(comment);
            else
                comment = [];
            end

            coder.internal.errorIf(blockLength > maxBlockLength, ...
                'shared_comm_sysmod:pcapngWriter:BlockSizeExceed');

            % Block length (4 bytes)
            blockLengthBytes = obj.getByteArray(blockLength, 4);

            % Section header block
            sectionHeader  = [blockType; ...
                blockLengthBytes; ...
                byteOrderMagic; ...
                majorVersion; ...
                minorVersion; ...
                sectionLength; ...
                comment; ...
                userApplicationCode; ...
                userApplication; ...
                endOfOptions; ...
                blockLengthBytes];
       end

        function [interfaceDescription, interfaceID] = interfaceDescriptionBlock(obj, linkType, interface)
            %interfaceDescriptionBlock Create an interface
            %description block as a byte array. Return the interface block
            %and interface ID

            % Block type (4 bytes)
            blockType = obj.updateEndianness([1; 0; 0; 0]);

            % Link type (2 bytes)
            linkType = obj.getByteArray(linkType, 2);

            % Reserved fields (2 bytes)
            reserved = [0; 0];

            % Maximum number of octets captured (4 bytes)
            snapLen = obj.getByteArray(obj.SnapLength, 4);

            % Interface name code (4 bytes)
            interfaceCode = [obj.updateEndianness([2; 0]); ...
                obj.getByteArray(numel(interface), 2)];

            % Interface name (variable bytes)
            interfaceName = obj.getByteArray(interface, numel(interface));

            % Padding (variable bytes)
            pad = obj.getPadBytes(interfaceName);

            % Option delimits the end of the optional fields (4 bytes)
            endOfOptions = [0; 0; 0; 0];

            % Default block length
            blockLength = 28;

            % Max block length (32-bit)
            maxBlockLength = 4294967295;

            % Get block length of interface description block
            blockLength = blockLength+numel(interfaceName)+numel(pad);

            coder.internal.errorIf(blockLength > maxBlockLength, ...
                'shared_comm_sysmod:pcapngWriter:BlockSizeExceed');

            % Block length (4 bytes)
            blockLengthBytes = obj.getByteArray(blockLength, 4);

            % Interface block and the corresponding interface ID
            interfaceDescription = [blockType; ...
                blockLengthBytes; ...
                linkType; ...
                reserved; ...
                snapLen; ...
                interfaceCode; ...
                interfaceName; ...
                pad; ...
                endOfOptions; ...
                blockLengthBytes];
            interfaceID = obj.InterfacesCount;

            % Increment the interface count if a new interface is added
            obj.InterfacesCount = obj.InterfacesCount+1;
        end

        function enhancedPacketBlock = enhancedPacketBlock(obj, ...
                interfaceID, packetData, timeStamp, varargin)
            %enhancedPacketBlock Create the enhanced packet block

            % Block type of enhanced packet block (4 bytes)
            blockType = obj.updateEndianness([6; 0; 0; 0]);

            % Interface ID of the packet (4 bytes)
            interfaceID= obj.getByteArray(interfaceID, 4);

            % Total time (8 bytes)
            totalTime = obj.getByteArray(fix(timeStamp), 8);

            if ~obj.IsLittleEndian
                % Upper 32 bits of a 64-bit timestamp
                timestampHigh = totalTime(1:4);
                % Lower 32 bits of a 64-bit timestamp
                timestampLow = totalTime(5:8);
            else
                % Upper 32 bits of a 64-bit timestamp
                timestampHigh = totalTime(5:8);
                % Lower 32 bits of a 64-bit timestamp
                timestampLow = totalTime(1:4);
            end

            % Packet length (4 bytes)
            packetLen = length(packetData);
            captureLen = obj.getByteArray(packetLen, 4);

            % End of options (4 bytes)
            endOfOptions = [0; 0; 0; 0];

            % Default block length
            blockLength = 32;

            % Get the data to be padded for packet data (variable bytes)
            if isempty(packetData)
                paddingData = [];
            else
                paddingData = obj.getPadBytes(packetData);
            end

            blockLength = blockLength+numel(packetData(:, 1))+numel(paddingData);

            % Check if comment is provided as an input (variable bytes)
            if nargin > 4
                % Add Comments, if any, for the enhanced packet block
                comment = varargin{1};
                commentLength = numel(comment);

                comment = obj.getByteArray(comment, commentLength);

                pad = obj.getPadBytes(comment);
                commentPadLen = length(pad);

                % Value [1; 0] represents the comment field
                commentCode = obj.updateEndianness([1; 0]);

                % Calculate the length of comment
                comment = [commentCode; obj.getByteArray(commentLength, 2);...
                    comment; pad; endOfOptions];

                % Get total length of the enhanced packet block
                blockLength = blockLength+8;
                blockLength = blockLength+commentLength+commentPadLen;
            else
                comment = [];
            end

            % Block length (4 bytes)
            blockLengthBytes = obj.getByteArray(blockLength, 4);

            % Max block length allowed (32-bit)
            maxBlockLength = 4294967295;

            coder.internal.errorIf(blockLength > maxBlockLength, ...
                'shared_comm_sysmod:pcapngWriter:EnhancedPacketBlockSizeExceed');

            % Enhanced packet block
            enhancedPacketBlock = [blockType; ...
                blockLengthBytes; ...
                interfaceID; ...
                timestampHigh; ...
                timestampLow; ...
                captureLen; ...
                captureLen; ...
                packetData;...
                paddingData; ...
                comment; ...
                blockLengthBytes];
        end

        function customBlock = customBlock(obj, customData)
            %customBlock Create a custom block with custom data

            % Custom block type (4 bytes)
            blockTypeCB = obj.updateEndianness([173; 11; 0; 0]);

            % Private enterprise number (to be obtained from IANA)
            %(4 bytes)
            PEN = [0; 0; 0; 0];

            % Default block length
            blockLength = 16;

            % Max block length allowed (32-bit)
            maxBlockLength = 4294967295;

            % Max custom data length allowed
            maxCustomDataLength = maxBlockLength - blockLength;

            % Custom data length
            customDataLength = numel(customData);

            coder.internal.errorIf(customDataLength > maxCustomDataLength, ...
                'shared_comm_sysmod:pcapngWriter:CustomDataSizeExceed');

            % Custom data  (variable bytes)
            if isempty(customData)
                customData = [];
            else
                customData = obj.getByteArray(customData, customDataLength);
            end
            % Get the data to be padded for packet data (variable bytes)
            if isempty(customData)
                paddingData = [];
            else
                paddingData = obj.getPadBytes(customData);
            end

            % Get block length of interface description block
            blockLength = customDataLength+numel(paddingData)+blockLength;

            coder.internal.errorIf(blockLength > maxBlockLength, ...
                'shared_comm_sysmod:pcapngWriter:BlockSizeExceed');

            % Block length (4 bytes)
            blockLengthBytes = obj.getByteArray(blockLength, 4);

            % Custom block
            customBlock = [blockTypeCB; ...
                blockLengthBytes; ...
                PEN; ...
                customData; ...
                paddingData; ...
                blockLengthBytes];
        end

        function openFile(obj, fileName)
            %openFile Open a file

            if isempty(coder.target)
                % Open the file using UTF-8 encoding (fopen default
                % encoding). Due to the limitations related to code
                % generation, encoding is not specified explicitly.
                [obj.FID, errMsg] = fopen(fileName, 'w');
                coder.internal.errorIf(obj.FID == -1, ...
                    'shared_comm_sysmod:pcapngWriter:FileOpenError', errMsg);
            else
                % Open the file using UTF-8 encoding (fopen default
                % encoding). Due to the limitations related to code
                % generation, encoding is not specified explicitly.
                obj.FID = fopen(fileName, 'w');
                coder.internal.errorIf(obj.FID == -1, ...
                    'shared_comm_sysmod:pcapngWriter:FileOpenErrorInCodegen');
            end
        end

        function closeFile(obj)
            %closeFile Close the file

            if obj.FID > 0
                fclose(obj.FID);
                obj.FID = -1;
            end
        end

        function delete(obj)
            %delete Function acts as a destructor

            obj.closeFile;
        end
end

    methods
        function obj = pcapngWriter(varargin)
            %pcapngWriter Create a PCAPNG packet writer object.

            % Name-value pair check
            coder.internal.errorIf(mod(nargin-2,2) == true, ...
                'MATLAB:system:invalidPVPairs');

            obj@comm.internal.ConfigBase();

            if isempty(coder.target) % Simulation path
                % Apply name-value pairs
                for i = 1:2:nargin
                    if ~obj.DisableValidation
                    value = validatestring(varargin{i}, {'FileName', ...
                        'ByteOrder', 'FileComment'}, mfilename);
                    end
                    switch(value)
                        case 'FileName'
                            setFileName(obj, varargin{i+1});
                        case 'FileComment'
                            setFileComment(obj, varargin{i+1});
                        otherwise % ByteOrder
                            setByteOrder(obj, varargin{i+1});
                    end
                end
            else %Codegen path
                nvPairs = struct('FileName', uint32(0), 'FileComment', ...
                    uint32(0), 'ByteOrder', uint32(0));

                % Select parsing options
                popts = struct('PartialMatching', true,...
                'CaseSensitivity', false);

                % Parse inputs
                pStruct = coder.internal.parseParameterInputs(nvPairs, ...
                    popts, varargin{:});

                % Get values for the N-V pair or set defaults for the
                % optional arguments
                setByteOrder(obj, coder.internal.getParameterValue( ...
                    pStruct.ByteOrder, coder.const('little-endian'), ...
                    varargin{:}));

                setFileName(obj, coder.internal.getParameterValue( ...
                    pStruct.FileName, 'capture', varargin{:}));

                setFileComment(obj, coder.internal.getParameterValue( ...
                    pStruct.FileComment, blanks(0), varargin{:}));
            end

            % To check whether file with the same name already exist or
            % not. If the file already exists, it prompts the user to
            % change the filename.
            fileName = [char(obj.FileName), '.',  'pcapng'];
            coder.internal.errorIf(isfile(fileName), ...
                'shared_comm_sysmod:pcapngWriter:UnableToCreateNewFile', obj.FileName);

            obj.ByteOrder = char(obj.ByteOrder);
            if strcmp(obj.ByteOrder, 'big-endian')
                obj.IsLittleEndian = false;
            end

            % Initialize interface count, before writing any blocks
            obj.InterfacesCount = 0;
        end

        function interfaceID = writeInterfaceDescriptionBlock(obj, linkType, ...
                interface)
            %writeInterfaceDescriptionBlock Write interface description
            %block into a file with .pcapng extension
            %
            %   INTERFACEID = writeInterfaceDescriptionBlock(obj, LINKTYPE,
            %   INTERFACE) writes interface description block into a file
            %   with .pcapng extension and return the interface ID. This
            %   interface ID must be used for writing packets of the
            %   specified interface.
            %
            %   INTERFACEID Unique identifier for an interface, specified
            %   as a numeric nonnegative scalar.
            %
            %   LINKTYPE indicates a unique identifier for a protocol,
            %   specified as a scalar integer value in the range [0, 65535].
            %
            %   INTERFACE Name of the device that captures protocol packets,
            %   specified as a character vector or string in UTF-8
            %   format.

            narginchk(3, 3);

            validateattributes(interface, {'char', 'string'}, {'row'}, ...
                mfilename, 'interface');
            if isstring(interface)
                validateattributes(interface, {'string'}, {'scalar'}, ...
                mfilename, 'interface');
            end

            validateattributes(linkType, {'numeric'}, {'scalar', 'integer', ...
                'nonnegative', '<=', 65535}, mfilename, 'linkType');

            linkType = double(linkType);

            % Write the section header block at the start of the file
            if ~obj.SectionHeaderPresent
                % Form file name
                fileName = [char(obj.FileName), '.',  'pcapng'];

                % Open file for writing packets
                openFile(obj, fileName);

                if isempty(char(obj.FileComment))
                    sectionHeaderBlock = obj.sectionHeaderBlock;
                else
                    sectionHeaderBlock = obj.sectionHeaderBlock(char(obj.FileComment));
                end

                % Write the section header block into the file
                fwrite(obj.FID, sectionHeaderBlock);

                % Set the header flag
                obj.SectionHeaderPresent = true;
            end

            [InterfaceDescriptionBlock, interfaceID] = obj.interfaceDescriptionBlock(linkType, char(interface));

            % Write interface header block to PCAPNG format file
            fwrite(obj.FID, InterfaceDescriptionBlock);
        end

        function write(obj, packet, timestamp, interfaceID, varargin)
            %write Write enhanced packet block into a file with .pcapng
            %extension
            %
            %   write(OBJ, PACKET, TIMESTAMP, INTERFACEID) writes enhanced
            %   packet block into a file with .pcapng extension
            %
            %   PACKET is specified as one of these types:
            %    - A binary vector representing bits
            %    - A character vector representing octets in hexadecimal
            %      format
            %    - A string scalar representing octets in hexadecimal
            %      format
            %    - A numeric vector, where each element is in the range
            %      [0, 255], representing octets in decimal format
            %    - An n-by-2 character array, where each row represents
            %      an octet in hexadecimal format
            %
            %   INTERFACEID Unique identifier for an interface, specified
            %   as a numeric positive scalar.
            %
            %   TIMESTAMP is specified as a scalar integer greater than or
            %   equal to 0. Timestamp is the packet arrival time in
            %   microseconds since 1/1/1970.
            %
            %   write(OBJ, PACKET, INTERFACEID, TIMESTAMP, Name, Value)
            %   specifies additional name-value pair arguments described
            %   below. When a name-value pair is not specified, its default
            %   value is used.
            %
            %   'PacketFormat'    Format of the packet, specified as
            %                     'bits' or 'octets'. The default value is
            %                     'octets'. If it is specified as 'octets',
            %                     the packet can be a numeric vector
            %                     representing octets in decimal format or
            %                     alternatively, it can be a character
            %                     array or string scalar representing
            %                     octets in hexadecimal format. Otherwise,
            %                     Packet is a binary vector.
            %   'PacketComment'   Packet comment, specified as a character
            %                     vector or string. The default value is
            %                     an empty character.

            narginchk(4, 8);

            % Check whether the validation is enabled
            if ~obj.DisableValidation
                % Name-value pair check
                coder.internal.errorIf(mod(nargin-4,2) == true, ...
                    'MATLAB:system:invalidPVPairs');
            end

            if isempty(coder.target) % Simulation path
                % Initialize
                packetFormat = 'octets';
                packetComment = blanks(0);

                % Apply name-value pairs
                for i = 1:2:nargin-4

                    value = validatestring(varargin{i}, {'PacketFormat',...
                        'PacketComment'}, mfilename);

                    switch(value)
                        case 'PacketFormat'
                            packetFormat = varargin{i+1};
                        otherwise % PacketComment
                            packetComment = varargin{i+1};
                    end
                end
            else %Codegen path
                nvPairs = struct('PacketFormat', uint32(0), ...
                    'PacketComment', uint32(0));

                % Select parsing options
                popts = struct('PartialMatching', true,...
                    'CaseSensitivity',false);

                % Parse inputs
                pStruct = coder.internal.parseParameterInputs( ...
                    nvPairs,popts,varargin{:});

                % Get values for the N-V pair or set defaults for the optional arguments
                packetFormat = coder.internal.getParameterValue( ...
                    pStruct.PacketFormat, coder.const('octets'), ...
                    varargin{:});

                packetComment = coder.internal.getParameterValue( ...
                    pStruct.PacketComment, blanks(0), varargin{:});
            end

            packetFormat = validatestring(packetFormat, {'bits', 'octets'}, ...
                mfilename, 'PacketFormat');

            % Check whether the validation is enabled
            if ~obj.DisableValidation
                validateattributes(timestamp, {'numeric'}, {'scalar',  ...
                    'integer', 'nonnegative'}, mfilename, 'timestamp');

                validateattributes(interfaceID, {'numeric'}, ...
                    {'scalar', 'integer', 'nonnegative', '<=', ...
                    4294967295}, mfilename, 'interfaceID');

                coder.internal.errorIf(interfaceID > obj.InterfacesCount, ...
                    'shared_comm_sysmod:pcapngWriter:InvalidInterfaceID', ...
                    interfaceID, obj.InterfacesCount);
            end

            % Check whether the validation is enabled
            if ~obj.DisableValidation
                % Validate packet and return octets in decimal format
                packetData = obj.validatePayloadFormat(packet, packetFormat);
            else
                packetData = double(packet);
            end

            interfaceID = double(interfaceID);

            if ~isempty(packetComment)
                % Check whether the validation is enabled
                if ~obj.DisableValidation
                    validateattributes(packetComment, {'char', 'string'}, {'row'}, ...
                        mfilename, 'PacketComment');
                end
                enhancedPacketBlock = obj.enhancedPacketBlock(interfaceID, ...
                    packetData, timestamp, char(packetComment));
            else
                enhancedPacketBlock = obj.enhancedPacketBlock(interfaceID, ...
                    packetData, timestamp);
            end

            coder.internal.errorIf(obj.FID == -1, ...
                'shared_comm_sysmod:pcapngWriter:SectionHeaderRequired');

            % Write packet to PCAPNG format file
            fwrite(obj.FID, enhancedPacketBlock);
        end

        function writeCustomBlock(obj, customData)
            %writeCustomBlock Write custom block into a file with .pcapng
            %extension
            %
            %   writeCustomBlock(OBJ, CUSTOMDATA) writes custom block into
            %   a file with .pcapng extension
            %
            %   CUSTOMDATA Any user defined data, specified as a
            %   character vector or string.

           narginchk(2, 2);

            validateattributes(customData, {'char', 'string'}, {'row'}, ...
                    mfilename, 'customData');

            coder.internal.errorIf(obj.FID == -1, ...
                'shared_comm_sysmod:pcapngWriter:SectionHeaderRequired');

            customBlock = obj.customBlock(char(customData));
            fwrite(obj.FID, customBlock);
        end
    end
end