A Custom Import Filter

23.04.2021

This example implements a complete import filter for a binary file format. The resulting import filter supports virtually all options available for realizing custom import filters via Automation. It was specifically designed to be able to serve as the framework for your own import filters.

To keep the example simple, it does not include error handling.

The Format to be Imported

The format to be imported is a binary format that was generated for illustrative purposes but features a common pattern. A program that can produce files in the sample format, and that was used to create the Demofile.tst sample file, is located as C++ source code in the same subfolder as the sample ImportFilter.fpd database and sample file. The path to the project database is usually C:\Users\Public\Documents\Weisang\FlexPro\2021\Examples\VBA\ImportFilter\ImportFilter.fpd or C:\Users\Public\Public Documents\Weisang\FlexPro\2021\Examples\VBA\ImportFilter\ImportFilter.fpd.
 
You can implement a custom FPScript function in FPScript as well. See Custom FPScript Functions Tutorial.

The binary files consist of three parts:

A file information structure that includes the number of channels and the sampling rate. Here is how it is defined in Visual Basic:

Private Type FileHeader

    strID As String * 8

    nVersion As Integer

    strOrigin As String * 32

    nNumberOfChannels As Long

    nNumberOfSamples As Long

    fSamplingRate As Double

    nTrigger As Long

End Type

A number of channel header structures corresponding to the number of channels that describe the included channels:

Private Type ChannelHeader

    strName As String * 8

    strDescription As String * 64

    strUnit As String * 8

End Type

The third part contains the data, starting with the data of the first channel, followed by those of the second, etc.

Import Filter

To realize an import filter, a class module has to be created in a database; the module implements the IImportFilter interface (in the example: class module DemoImportFilter). By entering

Implements IImportFilter

in the code window of the class module, the entry IImportFilter becomes available in the object window. If this entry is selected, the two procedures of the interface IImportFilter are listed in the Procedure window. Once the two interface procedures have been selected in the Procedure window, the corresponding procedure roots are added to the Code window, including the correct arguments.

ImportSpy Procedure

Once a user has started an import procedure by calling the Import dialog box and selecting a file, the ImportSpy procedure of all import filters registered with FlexPro is initiated in order to find out which import filters can import the file.

In the example, IImportFilter_ImportSpy first checks which filter was specified. This step only takes place for the sake of thoroughness. In this case it is not imperative because only one format has been registered for the import filter.

...

If Filter = m_strFilter Then

...

The filter verifies whether the name extension of the file corresponds to the one used for registering the filter:

...

If Right(UCase(PathName), 4) = ".TST" Then

...

The file is not opened and the file header is not read until the verification yields a positive result:

...

Open PathName For Binary Access Read As #1 Len = Len(TheFileHeader)

Get #1, , TheFileHeader

Close #1

TheFileHeader.strID = CutString(TheFileHeader.strID)

If TheFileHeader.strID = "DEMO    " And TheFileHeader.nVersion = 1 Then

    IImportFilter_ImportSpy = True

End If

...

The example file format contains an identifying text and a version number that help to identify the format. If the identifying text is correct and the file format features a version that is supported, the function will return True.

If additional specific import filters that support this file format exist, a selection dialog box appears during import in which the user can select the import filter to be used. FlexPro then calls the Import procedure of the import filter in order to import the file.

Import Procedure

The Import procedure is only called if the ImportSpy procedure of the import filter has been called successfully.

As with the ImportSpy procedure, the first step is a verification of the filter. Then, the file header of the specified file as well as the header information for all elements or channels are read:

...

Open PathName For Binary Access Read As #1

Get #1, , TheFileHeader

For i = 1 To TheFileHeader.nNumberOfChannels

    Get #1, , TheChannelHeader

    Set oImportItem = New ImportItem

    oImportItem.strName = CutString(TheChannelHeader.strName)

    oImportItem.strDescription = _

             CutString(TheChannelHeader.strDescription)

    oImportItem.strUnit = CutString(TheChannelHeader.strUnit)

    oImportItem.nSamples = TheFileHeader.nNumberOfSamples

    oItemColl.Add oImportItem

Next i

nDataStartPos = Seek(1) - 1

Close #1

...

The elements found are stored in a Collection object together with all the data relevant for presentation in the dialog box or for import. If the file is automatically imported in its entirety, a dialog box appears where the user can choose the elements to be imported.

...

If (Flags And fpImportOptionAutomatic) = 0 Then

    Set oFrm = New ImportItemsSelectFrm

    oFrm.InitAndShow oItemColl, Flags

    bCancel = oFrm.m_bCancel

    Unload oFrm

End If

...

Eventually, the selected elements are then imported, taking into account the Flags parameter. If a new subfolder is to be created for each file to be imported, these subfolders are created first:

...

If Flags And fpImportOptionSubfolder Then

    Set oFile = oFS.GetFile(PathName)

    Set oImportFolder = Folder.Add(Left(oFile.Name, InStrRev(oFile.Name, ".")_

                        - 1), fpObjectTypeFolder)

Else    '   import into the given folder

    Set oImportFolder = Folder

End If

...

If the data are not to be imported as signals, an X data set is created first; it can be referenced by all further data sets as an X component. In the example, there is only one sampling interval and one trigger, so that the X component can be calculated using a formula:

...

If (Flags And fpImportOptionSignal) = 0 Then

    Set oXItem = oImportFolder.Add("XItem", fpObjectTypeFormula)

    oXItem.Origin = TheFileHeader.strOrigin

    oXItem.Component = fpDataComponentX

    oXItem.Formula = "(" & CStr(TheFileHeader.nNumberOfSamples) & ", " & _

        CStr(-(TheFileHeader.nTrigger * oBinaryDataLink.SamplingInterval)) _

         & ", " & CStr(1 / TheFileHeader.fSamplingRate) & ")"

End If

...

Lastly, all selected Y components in the file are imported. In the example, this can be done by taking advantage of the capabilities FlexPro binary import offers. For every channel to be imported, a BinaryDataLink object is created and parameterized according to the specifications. Lastly, the procedure decides whether the object created is evaluated into a data set (the flag fpImportOptionLink was set) or whether the BinaryDataLink object is preserved as a link.

...

For i = 1 To oItemColl.Count

    If oItemColl(i).bSelected = True Then

        Set oBinaryDataLink = oImportFolder.Add(oItemColl(i).strName_

             , fpObjectTypeBinaryDataLink)

        '   general properties

        oBinaryDataLink.CommentsY = oItemColl(i).strDescription

        oBinaryDataLink.Origin = TheFileHeader.strOrigin

        oBinaryDataLink.Author = Application.UserName

        '   import properties

        oBinaryDataLink.FilePath = PathName

        oBinaryDataLink.NumberOfBlocks = 1

        oBinaryDataLink.BlockSize = 200

        oBinaryDataLink.ByteDistance = 0

        oBinaryDataLink.ResultDataType = fpBinaryDataLinkResultDataTypeFloatingPoint64

        oBinaryDataLink.DataType = fpBinaryDataLinkDataTypeFloatingPoint64

        oBinaryDataLink.ByteOffset = nDataStartPos + ((i - 1)_

                                     * TheFileHeader.nNumberOfSamples * 8)

        '   as signal ?

        If Flags And fpImportOptionSignal Then

            oBinaryDataLink.AsSignal = True

            oBinaryDataLink.SamplingInterval = 1 / TheFileHeader.fSamplingRate

            oBinaryDataLink.SamplingOrigin = -(TheFileHeader.nTrigger_

                                             * oBinaryDataLink.SamplingInterval)

        Else    '   assign the X component created above to this item

            oBinaryDataLink.Component = fpDataComponentY

            oBinaryDataLink.AssignedX = oXItem.Name

        End If

        '   if import action is copy than evaluate the binary data link

        If (Flags And fpImportOptionLink) = 0 Then

            oBinaryDataLink.Evaluate

        End If

    End If

Next i

...

If the import has been completed successfully, the procedure will return True.

Registering an Import Filter

For a customized import filter to be available in FlexPro's Import dialog box (the file format is listed under File Type, it has to be generated using

Dim oImportFilter As New DemoImportFilter

and then be registered with the FlexPro Application object by calling RegisterImport.

RegisterImport oImportFilter.m_strFilter, _

        fpImportOptionSpecific Or _

        fpImportOptionLink Or fpImportOptionNoLink Or _

        fpImportOptionSubfolder Or fpImportOptionNoSubfolder Or _

        fpImportOptionAutomatic Or fpImportOptionManual Or _

        fpImportOptionSignal Or fpImportOptionNoSignal Or _

        fpImportOptionNoCalendarTime, _

        oImportFilter

During registration, the import filter itself is referenced and specifies which of the options available in the Import filter dialog box are supported.

If, for example, an import filter does not support links, only the fpImportOptionNoLink flag is specified in the first argument of RegisterImport. The Create links option will be grayed out and will not be set if this import filter is selected in the Import dialog box. If only fpImportOptionLink is specified, the option Create links is also gray, but activated. If both options are supported (fpImportOptionLink or fpImportOptionNoLink), the user can freely choose how the import will take place.

This procedure also applies to the following options:

Create new folder for each file

Import with absolute time

Import as signals

If an import filter is to be available in all databases of one user or to all users in general, it is recommended that you implement it in the personal template database and to register it in the procedure AutoExec, which is executed automatically when the template database is loaded. De-registration can then be handled by the AutoExit procedure (see also Auto Macros).

Note:   When developing a customized import filter, please consider the following: When a VBA project is reset, such as due to code changes in the debugger, previously registered import filters of this project will no longer be called. The import filter has to be de-registered and re-registered again.

Share article or send as email:

You might be interested in these articles