Ein benutzerdefinierter Importfilter

23.04.2021

Dieses Beispiel implementiert einen vollständigen Importfilter für ein binäres Dateiformat. Der realisierte Importfilter unterstützt nahezu alle Optionen, die für die Realisierung benutzerdefinierter Importfilter per Automation zur Verfügung stehen. Er wurde bewusst so ausgelegt, dass er als Gerüst für einen eigenen Importfilter verwendet werden kann.

Aus Gründen der Übersichtlichkeit wurde keine Fehlerbehandlung realisiert.

Das zu importierende Format

Bei dem zu importierenden Format handelt es sich um ein binäres Datenformat, das zu Demonstrationszwecken erzeugt wurde, aber einem gebräuchlichen Muster entspricht. Ein Programm, das Dateien im Beispielformat erzeugen kann - und mit dem auch die Beispieldatei Demofile.tst erzeugt wurde - befindet sich als C++-Quelltext im gleichen Unterordner wie die Beispieldatenbank ImportFilter.fpd und die Beispieldatei. Der Pfadname der Projektdatenbank lautet normalerweise C:\Users\Public\Documents\Weisang\FlexPro\2021\Examples\VBA\Import Filter\ImportFilter.fpd bzw. C:\Benutzer\Öffentlich\Öffentliche Dokumente\Weisang\FlexPro\2021\Examples\VBA\Import Filter\ImportFilter.fpd.
 
Sie können eine benutzerdefinierte FPScript-Funktion auch in FPScript implementieren. Siehe hierzu Tutorial benutzerdefinierte FPScript-Funktionen.

Die Binärdateien bestehen aus drei Teilen:

einer Dateiinformationsstruktur, die u. a. die Anzahl der Kanäle und die Abtastrate beinhaltet. Hier die Definition 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

einer der Kanalanzahl entsprechenden Anzahl von Kanalkopfstrukturen, die die enthaltenen Kanäle beschreiben:

Private Type ChannelHeader

    strName As String * 8

    strDescription As String * 64

    strUnit As String * 8

End Type

anschließend folgen die Daten und zwar zunächst alle Daten des ersten Kanals, dann die des zweiten usw.

Importfilter

Zur Realisierung eines Importfilters muss in einer Datenbank ein Klassenmodul angelegt werden, das die Schnittstelle IImportFilter implementiert (im Beispiel: Klassenmodul DemoImportFilter). Durch Eintragen von

Implements IImportFilter

im Code-Fenster des Klassenmoduls wird im Objekt-Fenster der Eintrag IImportFilter verfügbar. Wenn dieser Eintrag ausgewählt wird, werden im Fenster Prozedur die beiden Prozeduren der Schnittstelle IImportFilter gelistet. Nachdem die beiden Schnittstellenprozeduren im Fenster Prozedur ausgewählt wurden, werden im Code-Fenster die entsprechenden Prozedurrümpfe mit korrekten Argumenten eingefügt.

ImportSpy-Prozedur

Nachdem ein Anwender durch Aufruf des Import-Dialogfeldes und Auswahl einer Datei einen Importvorgang gestartet hat, wird die ImportSpy-Prozedur aller Importfilter von FlexPro aufgerufen, um zu ermitteln, welche Importfilter die Datei importieren können.

Im Beispiel prüft IImportFilter_ImportSpy zunächst, welcher Filter angegeben wurde. Dies erfolgt hier nur der Vollständigkeit wegen. In diesem Fall ist es nicht zwingend erforderlich, weil nur ein Format für den Importfilter registriert wurde.

...

If Filter = m_strFilter Then

...

Anschließend wird geprüft, ob die Namenserweiterung der Datei mit der übereinstimmt, für die der Filter registriert wurde:

...

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

...

Erst wenn dies gegeben ist, wird die Datei geöffnet und der Dateikopf gelesen:

...

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

...

Das Beispieldateiformat enthält einen Identifizierungstext und eine Versionsnummer anhand derer das Format erkannt werden kann. Ist der Identifizierungstext korrekt und hat das Dateiformat eine Version, die unterstützt wird, so gibt die Funktion True zurück.

Falls weitere spezifische Importfilter existieren, die das vorliegende Dateiformat unterstützen, so wird anschließend beim Import ein Auswahldialogfeld angezeigt in dem der Anwender den zu verwendenden Importfilter auswählen kann, ansonsten ruft FlexPro als nächstes die Import-Prozedur des Importfilters auf, um die Datei zu importieren.

Import-Prozedur

Die Import-Prozedur wird nur aufgerufen, wenn die ImportSpy-Prozedur des Importfilters erfolgreich aufgerufen wurde.

Wie in der ImportSpy-Prozedur wird hier zunächst auf den Filter geprüft. Anschließend wird der Dateikopf der angegebenen Datei, sowie die Kopfinformation für alle Elemente bzw. Kanäle gelesen:

...

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

...

Die gefundenen Elemente werden zusammen mit allen relevanten Daten zur Darstellung im Dialog bzw. für den Import in einem Collection-Objekt abgelegt. Wenn die Datei nicht automatisch vollständig importiert werden soll, wird ein Dialogfeld angezeigt in dem der Anwender die zu importierenden Elemente auswählen kann.

...

If (Flags And fpImportOptionAutomatic) = 0 Then

    Set oFrm = New ImportItemsSelectFrm

    oFrm.InitAndShow oItemColl, Flags

    bCancel = oFrm.m_bCancel

    Unload oFrm

End If

...

Die ausgewählten Elemente werden schließlich unter Berücksichtigung des Parameters Flags importiert. Wenn für jede zu importierende Datei ein neuer Unterordner angelegt werden soll erfolgt dies zuerst:

...

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

...

Falls die Daten nicht als Signale importiert werden sollen wird zunächst ein X-Datensatz angelegt, den alle weiteren Datensätze als X-Komponente referenzieren können. Im Beispiel ist nur ein Abtastintervall und ein Trigger vorhanden, so dass die X-Komponente über eine Formel berechnet werden kann:

...

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

...

Letztlich erfolgt dann der Import aller gewählten Y-Komponenten in der Datei. Im Beispiel lassen sich diese einfach durch Verwendung der Möglichkeiten des FlexPro-Binärimports importieren. Für jeden zu importierenden Kanal wird ein BinaryDataLink-Objekt angelegt und gemäß den Vorgaben parametriert. Am Ende wird dann entschieden, ob das angelegte Objekt in einen Datensatz ausgewertet wird (das Flag fpImportOptionLink wurde gesetzt) oder ob das BinaryDataLink-Objekt als Verknüpfung erhalten bleibt.

...

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

...

Wenn der Import erfolgreich ausgeführt wurde, gibt die Prozedur True zurück.

Registrierung eines Importfilters

Damit ein benutzerdefinierter Importfilter im Importieren-Dialogfeld von FlexPro überhaupt zur Verfügung steht (Dateiformat wird unter Dateityp gelistet) muss er zunächst mit

Dim oImportFilter As New DemoImportFilter

erzeugt und anschließend beim FlexPro Application-Objekt durch Aufruf von RegisterImport angemeldet werden.

RegisterImport oImportFilter.m_strFilter, _

        fpImportOptionSpecific Or _

        fpImportOptionLink Or fpImportOptionNoLink Or _

        fpImportOptionSubfolder Or fpImportOptionNoSubfolder Or _

        fpImportOptionAutomatic Or fpImportOptionManual Or _

        fpImportOptionSignal Or fpImportOptionNoSignal Or _

        fpImportOptionNoCalendarTime, _

        oImportFilter

Bei der Registrierung wird neben einer Referenz auf den Importfilter selbst auch angegeben welche der Optionen, die im Importfilter-Dialogfeld zur Verfügung stehen, unterstützt werden.

Unterstützt ein Importfilter z. B. keine Verknüpfungen, so gibt man im ersten Argument von RegisterImport nur das Flag fpImportOptionNoLink an. Die Option Verknüpfungen erstellen ist bei Auswahl dieses Importfilters im Importieren-Dialogfeld dann grau und nicht gesetzt. Wird lediglich fpImportOptionLink angegeben ist die Option Verknüpfungen erstellen ebenfalls grau, aber aktiviert. Werden beide Möglichkeiten unterstützt (fpImportOptionLink Or fpImportOptionNoLink), dann kann der Anwender frei bestimmen, wie der Import zu erfolgen hat.

Dieses Verfahren gilt analog für die Optionen:

Neuen Ordner für jede Datei anlegen

Mit Absolutzeit importieren

Als Signale importieren

Wenn ein Importfilter in allen Datenbanken eines Anwenders oder generell für alle Anwender zur Verfügung stehen soll, empfiehlt es sich, ihn in der persönlichen Vorlagendatenbank zu implementieren und in der automatisch beim Laden der Vorlagendatenbank ausgeführten Prozedur AutoExec zu registrieren. Die Deregistrierung kann dann in der AutoExit-Prozedur erfolgen (siehe auch Auto-Makros).

Hinweis   Bei der Entwicklung eines benutzerdefinierten Importfilters ist folgendes zu beachten: Wenn ein VBA-Projekt zurückgesetzt wird, z. B. bei Codeänderungen im Debugger, werden anschließend zuvor registrierte Importfilter dieses Projektes nicht mehr aufgerufen. Der Importfilter muss deregistriert und noch einmal neu registriert werden.

Artikel teilen oder als Email versenden:

Diese Beiträge könnten Sie ebenfalls interessieren