Ein benutzerdefinierter Importfilter
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.