C++ DLL mit komplexer Datenstruktur inLabVIEW verwenden - Druckversion +- LabVIEWForum.de (https://www.labviewforum.de) +-- Forum: LabVIEW (/Forum-LabVIEW) +--- Forum: LabVIEW Allgemein (/Forum-LabVIEW-Allgemein) +---- Forum: DLL & externer Code (/Forum-DLL-externer-Code) +---- Thema: C++ DLL mit komplexer Datenstruktur inLabVIEW verwenden (/Thread-C-DLL-mit-komplexer-Datenstruktur-inLabVIEW-verwenden) |
C++ DLL mit komplexer Datenstruktur inLabVIEW verwenden - DieterSchell - 05.02.2009 18:01 Hallo zusammen, ich habe eine *.dll (DSP-SW) die Radiosondensignale über die Soundkarte erfasst und die Messwerte ausgibt. Ich habe die dll vom Sondenhersteller erhalten und möchte sie verwenden, um in LabVIEW eine für unsere Bedürfnisse angepasste Datenerfassungssoftware zu schreiben. Dabei habe ich größte Schwierigkeiten, die *.dll richtig anzusprechen um an die Messwerte zu gelangen. Die DSP-SW steht als C++ Laufzeit Bibliothek(DLL) zur Verfügung. Nun meine Fragen: Ich habe ein vi mit einem "Library Function Node" erstellt, dem ich alle Variablen und Arrays entsprechend dem "DSPSwFrameType" (s.u.) eingebe. Dabei würde ich nun erwarten, dass dann am entsprechenden Ausgang die Messwerte ausgegeben werden. Das ist wohl aber zu einfach gedacht, denn ich bekomme lediglich Systemabstürze und im besten Falle Fehlermeldungen von LabVIEW (error 1097 oder seltener 1517), was auf einen Speicherkonflikt hinweist. Ich habe generell 2 Versionen probiert: 1)(dspsw_get_frame_LV85_1.vi) Eingangsparameter alle in ein Cluster gepackt, das wiederum aus Clustern, Arrays und Variablen besteht. Der Typ des Ausgangs ist dann als "An Typen anpassen" konfiguriert. In dieser Konfiguration stürzt LabVIEW kommentarlos ab, wenn ich das VI im Gesamtprojekt laufen lasse. 2)(dspsw_get_frame_LV85_2.vi) Eingangsparameter einzeln mit dem node verdrahtet. Die Ausgangstypen sind dann entsprechend der unten aufgeführten Anweisungen konfiguriert. In dieser Konfiguration stürzt LabVIEW nicht ab, meldet jedoch "error 1097", wenn ich das VI im Gesamtprojekt laufen lasse. Nach allem was ich durch stöbern auf diversen NatInst Seiten und Foren gelernt habe, ist es wohl ein Problem, die Speicherbereiche für die arrays richtig zu reservieren. Wie wird ausserdem die Anweisung "MarshalAs...." richtig in LabVIEW umgesetzt? Ich füge zur Verdeutlichung die beiden Versionen des vi bei, mit dem ich versuche, die Daten auszulesen. Ausserdem sind unten alle mir verfügbaren Informationen zu den Datentypen dieser dll-Funktion zusammengestellt. Ich würde mich freuen wenn sich jemand finden würde, der mir bei diesem Problem helfen kann. Vielen Dank schonmal und Grüße, Dieter ------------------------------------------------------------------------------------------ Die DLL Einsprungfunktion in C++ zum Auslesen des Datenframes sieht fogendermassen aus: ------------------------------------------------------------------------------------------ Public Declare Function dspsw_get_frame Lib "./dspsw.dll" ( <MarshalAs(UnmanagedType.Struct)> ByRef dt_gps_frame As DSPSwFrameType ) As Integer ------------------------------------------------------------------------------------------- Folgende C++ Datenarrays stehen zur Verfügung: typedef struct { ieee_double value; u16 status; u16 st_arg; u16 reserved_0; u16 reserved_1; } PhysVal; typedef struct { u32 i_frame_count; // WaveBuffer counter u32 frame_count; // laufende Nr. des BitFrame ieee_double i_frame_time; // i_frame_count * WORDS_I_BUFFER /ADU_ABTASTRATE ieee_double frame_time; // frame_count * frame_bits * T_BIT_TIME ieee_double sync_quality; // Qualität des Synchronworts ieee_double receive_level; // Pegel des Empfangssig. in dB(max. sample) ieee_double bit_drift; // Abweichung Sender/Empfänger in bits ieee_double clock_drift; // Abweichung Sender/Empfänger in ppm s16 t_phase; // Phasendrehung des Eingangssignals: 0, 1 u16 framesync_stat; // Status aus FrameSyncStat FSS_??? u16 framesync_cnt; // bits per frame s16 framesync_diff; // von framesync_cnt abweichende Anzahl u8 rohdaten[40]; // Telemetrie - Frame, original u8 deinterleaved[40]; // Telemetrie - Daten, deInterleaved u8 korrigiert[40]; // Telemetrie - Daten, korrigiert u8 decode_error[40]; // Anzahl Fehlerbits u16 bit_err_0; // Anzahl Bytes ohne Bitfehler u16 bit_err_1; // Anzahl Bytes mit 1 Bitfehler u16 bit_err_2; // Anzahl Bytes mit 2 Bitfehler u16 bit_err_2_val; // Anzahl Bytes mit 2 Bitfehler im Meßwert } InputFrame; typedef struct { // valid for <S>iRF or <J>upiter receiver s16 gps_valid; // S J | 1, wenn GPS-Daten Version 1.4 gueltig (Jupiter) // S J | 3, wenn GPS-Daten Version >= 2.02 gueltig (SiRF) u16 tx_flags; // S | receiver flags u16 nav_type; // S | bitmask u16 tx_rfu; // S | reserved, do not use. u8 tx_version; // S | u8 tx_counter; // S | number of GPS frame sent (8bit) s16 lla_valid; // S J | 1, wenn GPS-Lat/Lon/Alt gueltig u32 ulFrameCount; // S J | number of GPS frame received u32 ulValidItem; // S J | bitmap 0..31: GPS_IV_xxx u32 ulPRNMap; // S J | bitmap, bit<i> --> PRN<i+1> used ieee_double lat; // S J | +- 90° ieee_double lon; // S J | +- 180° ieee_double alt; // S J | m, über Ellipsoid ieee_double alt_eli; // S | m, über Ellipsoid ieee_double alt_msl; // S | m, über MSL s32 slECEFpx; // J | +- 0 ... 9*10^6 m, resolution: 10^-2 s32 slECEFpy; // J | +- 0 ... 9*10^6 m, resolution: 10^-2 s32 slECEFpz; // J | +- 0 ... 9*10^6 m, resolution: 10^-2 s32 ssECEFvx; // J | +- 0 ... 320 m/s, resolution: 10^-2 s32 ssECEFvy; // J | +- 0 ... 320 m/s, resolution: 10^-2 s32 ssECEFvz; // J | +- 0 ... 320 m/s, resolution: 10^-2 u32 usEHPE; // J | 0 ... 1000 m, resolution: 10^-2 u32 usEVPE; // J | 0 ... 1000 m, resolution: 10^-2 u32 usEHVE; // J | 0 ... 300 m, resolution: 10^-2 u16 usErrorID; // J | vom Receiver u16 usUTC1S; // J | seconds u16 usUTC10000S; // J | 1 / 10000s s16 ssMaskAngle; // J | +- 0 ... pi/2, resolution: 10^-3 u16 ausCNo[32]; // J | 0 ... 60dBHz u32 rfu; // J | reserved, do not use. u8 prn[32]; // S | PRN visible: 0 = unused, 1..32 = SV ID u8 cn0[32]; // S | C/N0: 0 ... 60dBHz ieee_double sog; // S | speed over ground, m/s ieee_double cog; // S | course over ground, deg. true north ieee_double climb_rate; // S | climb rate, m/s ieee_double ehpe; // S | m ieee_double evpe; // S | m u16 r52, r53; // S | reserved u16 utc_year; // S | UTC u16 utc_month; // S | UTC u16 utc_day; // S | UTC u16 utc_hour; // S | UTC u16 utc_minute; // S | UTC u16 utc_ms; // S | UTC ieee_double utc_time; // S | 00:00:00.000 ... 23:59:59.999 } GPSFrame; // kombinierter Frame mit Meßdaten und GPS typedef struct { InputFrame input; u8 dfm_id[4]; // ID der Sonde u32 reserved_1; // reserved u16 sensor; // 0 .. 15 u16 status; // bit0: invalid u16 sensor_id; // 0x00 ... 0xff u16 sk_mode; // SKM_ ieee_double messwert; // Frequenz in Hz PhysVal val; // Meßwert // Strahlungskorrektur, Modus, benutzte Werte PhysVal druck, hoehe, elev_sonne, strahl_korr; // alternative Berechnungsverfahren der Strahlungsfehlerkorrektur PhysVal druck_g, hoehe_g, korr_g; // GPS-Höhe, Höhenformel PhysVal druck_p, hoehe_p, korr_p; // Druckmessung GPSFrame gps; } TD_GPS_Frame; ----------------------------------------------------------------------------------- Dazu wurden mir zusätzlich noch folgende VB .NET 2005/2008 Datentypen angegeben: ----------------------------------------------------------------------------------- Public Structure DSPSwPhysValType Dim Value As Double Dim Status As Short Dim StatusArgc As Short Dim Reserved0 As Short Dim Reserved1 As Short End Structure Public DSPSwPhysVal As DSPSwPhysValType Public Structure DSPSwFrameType Dim IFrameCounter As Integer Dim FrameCounter As Integer ' Laufende Nr. des TD_GPS_Frame Dim IFrameTime As Double ' Zeit in s seit dsp_init Dim frameTime As Double Dim SyncQuality As Double ' Qualität des Synchronworts Dim RecLevel As Double ' Pegel des Empfangssig. in dB Dim BitDrift As Double ' Abweichung Sender/Empfänger in bits Dim ClockDrift As Double ' Abweichung Signal/Empfänger in ppm Dim t_phase As Short Dim frame_sync_stat As Short Dim framesync_cnt As Short Dim frame_sync_diff As Short <MarshalAs(UnmanagedType.ByValArray, SizeConst:=40)> Dim RawData()As Byte ' Telemetrie - Frame, original <MarshalAs(UnmanagedType.ByValArray, SizeConst:=40)> Dim DeInterleaved() As Byte ' Telemetrie - Daten, deInterleaved <MarshalAs(UnmanagedType.ByValArray, SizeConst:=40)> Dim Correct() As Byte ' Telemetrie - Daten, korrigiert <MarshalAs(UnmanagedType.ByValArray, SizeConst:=40)> Dim DecodeError() As Byte ' Anzahl Fehlerbits Dim BitError0 As Short ' Anzahl Bytes ohne Bitfehler Dim BitError1 As Short ' Anzahl Bytes mit 1 Bitfehler Dim BitError2 As Short ' Anzahl Bytes mit 2 Bitfehler Dim BitError2Value As Short ' Anzahl Bytes mit 2 Bitfehler im Meßwert <MarshalAs(UnmanagedType.ByValArray, SizeConst:=4)> Dim DFMID() As Byte Dim Reserved1 As Integer Dim Sensor As Short Dim Status As Short Dim SensorId As Short Dim SkMode As Short Dim MeasData As Double Dim MeasValue As DSPSwPhysValType ' Benutzte Werte für Strahlungsfehlerkorrektur Dim Pressure As DSPSwPhysValType Dim Altitude As DSPSwPhysValType Dim SunElevation As DSPSwPhysValType Dim RayCorrection As DSPSwPhysValType Dim ReservedPv1 As DSPSwPhysValType Dim ReservedPv2 As DSPSwPhysValType Dim ReservedPv3 As DSPSwPhysValType Dim ReservedPv4 As DSPSwPhysValType Dim ReservedPv5 As DSPSwPhysValType Dim ReservedPv6 As DSPSwPhysValType ' GPS-Daten bis Ende des Frames Dim GPSValid As Short ' S J - 1, wenn GPS gültig (Jupiter) - 3, wenn GPS gültig (Sirf) Dim tx_rfu1 As Short ' S - do not use. Dim nav_type As Short ' S - bitmask Dim tx_rfu2 As Short ' S - do not use. Dim tx_rfu3 As Byte ' S - do not use. Dim LLAValid As Short ' J - 1, wenn GPS-Lat/Lon/Alt gültig Dim GPSFrameCounter As Integer ' S J - Nummer des empfangenen GPSFrame Dim ValidItem As Integer ' S J - Bitmap 0...31 Dim SatMap As Integer ' S J - Bitmap, Bit(i) --> PRN(i+1) Dim Latit As Double ' S J - +- 90° Dim Longit As Double ' S J - +- 180° Dim Altit As Double ' J - Meter, von ECEF Dim Altit_Eli As Double ' S - Meter, über Ellipsoid Dim Altit_MSL As Double ' S - Meter, über MSL ' ECEF: +- 0 ... 9.000.000m, resolution: e-2 Dim PosX As Integer ' J Dim PosY As Integer ' J Dim PosZ As Integer ' J ' ECEF: +- 0 ... 320m/s, resolution: e-2 Dim VelX As Integer ' J Dim VelY As Integer ' J Dim VelZ As Integer ' J ' 0 ... 1.000m, resolution: e-2 Dim EHPE As Integer ' J Dim EVPE As Integer ' J ' 0 ... 300m, resolution: e-2 Dim EHVE As Integer ' J Dim ErrorID As Short ' J - vom GPS-Empfänger Dim UTCSec As Short ' J - Sekunden Dim UTCTSec As Short ' J - 1/1000 Sekunden Dim MaskAngle As Short ' J - +- 0 ... pi/2, Resolution: e-3 <MarshalAs(UnmanagedType.ByValArray, SizeConst:=32)> Dim CNo() As Short ' J - 0 ... 60dBHz (MaxSatsVisible = 12) Dim rfu As Integer ' J - do not use. <MarshalAs(UnmanagedType.ByValArray, SizeConst:=32)> Dim prn() As Byte ' S - PRN visible: 0 = unused, 1..32 = SV ID <MarshalAs(UnmanagedType.ByValArray, SizeConst:=32)> Dim cn0() As Byte ' S - C/N0: 0 ... 60dBHz Dim sog As Double ' S - speed over ground, m/s Dim cog As Double ' S - course over ground, deg. true north Dim climb_rate As Double ' S - climb rate, m/s Dim s_ehpe As Double ' S - m Dim s_evpe As Double ' S - m Dim rfu52 As Short ' S - do not use. Dim rfu53 As Short ' S - do not use. Dim utc_year As Short ' S - UTC Dim utc_month As Short ' S - UTC Dim utc_day As Short ' S - UTC Dim utc_hour As Short ' S - UTC Dim utc_minute As Short ' S - UTC Dim utc_ms As Short ' S - UTC Dim utc_time As Double ' S - 00:00:00.000 ... 23:59:59.999 Public Sub Initialize() 'ReDim reserved(3) ReDim RawData(39) ReDim DeInterleaved(39) ReDim Correct(39) ReDim DecodeError(39) ReDim DFMID(3) 'ReDim ReservedPv(4) ReDim CNo(31) ReDim prn(31) ReDim cn0(31) End Sub End Structure C++ DLL mit komplexer Datenstruktur inLabVIEW verwenden - IchSelbst - 05.02.2009 20:55 ' schrieb:Dabei habe ich größte Schwierigkeiten, die *.dll richtig anzusprechen um an die Messwerte zu gelangen.Das sehe ich auch so. Zitat:Ich habe ein vi mit einem "Library Function Node" erstellt, dem ich alle Variablen und Arrays entsprechend dem "DSPSwFrameType" (s.u.) eingebe. Dabei würde ich nun erwarten, dass dann am entsprechenden Ausgang die Messwerte ausgegeben werden. Das ist wohl aber zu einfach gedacht,Nein, gedacht ist das genau richtig. Ich gehe davon aus, dass es so geht - so gehen soll. Zitat:Ich habe generell 2 Versionen probiert:Nur eine kann richtig sein. Ich gehe davon aus, deine erste Methode ist richtig. Was richtig ist, muss in der Beschreibung der DLL-Funktionen stehen. Wenn da eine Funktion mit nur einem Parameter steht, kann nur deine erste Version richtig sein. Und die Methode mit den 50 Eingängen - sowas macht keiner. Zitat:In dieser Konfiguration stürzt LabVIEW kommentarlos ab, wenn ich das VI im Gesamtprojekt laufen lasse.Das kommt von folgendem: Verlangt wird ein Pointer auf einen Speicherbereich, der alle Daten enthält. So ein Bereich heißt normalerweise Cluster. Zuerst einmal musst du sicherstellen, dass die Reihenfolge der Daten im Cluster die selbe ist, wie in der Beschreibung des Parameters der Funktion. Hast du jetzt ein Array (oder String) im Cluster - fängt das Problem an. Die DLL erwartet hier ein statisches Array (davon gehe ich aus). D.h. die Daten des Arrays werden direkt im Cluster erwartet. LV schreibt aber nicht die Daten des Arrays in den Cluster, sondern einen Pointer auf die Arraydaten. Letztendlich führt das zum Absturz. So einen großen Cluster mit so vielen Parametern hab ich auch noch nicht gesehen. Drei Möglichkeiten fallen mir ein, die du mal prüfen müsstest. Ich hätte ja die erste bevorzugt. Erstens: Cluster erstellen. Dann ein Array of U8 erstellen, das die Länge des erwarteten Speicherbereiches hat. Danach die Daten aus dem Cluster der Reihe nach manuell hart typcasten in das Array of U8. DLL aufrufen und das ganze rückwärts. Für kleine, komplexe Cluster kann man das so machen. Zweitens: Du kannst mal "Daten serialisieren" probieren. Dabei gibst du den Cluster auf das Serialisier-Element - heraus kommt ein Datenstream in Form eines Strings. Den String gibt du an die DLL als PChar. Rückwärts musst du das selbe probieren: den String aus dem DLL-Knoten auf ein Deserialisierungs-Element. Das Element bekommt den Cluster als Typeingang und liefert am Ausgang einen Datencluster. Ob das geht weiß ich nicht. Ich war noch nie in der Situation, einen derartigen Cluster an eine DLL zu übergeben. Drittens: Eine Wrapper-DLL. Mach dir eine C++-DLL, die auf der einen Seite die User-DLL bedienen kann. Das sollte einfach sein. Und auf der anderen Seite machst du eine oder mehrere Funktionen, die LV bedienen kann. Das ist auch einfach. C++ DLL mit komplexer Datenstruktur inLabVIEW verwenden - rolfk - 06.02.2009 07:10 ' schrieb:Das sehe ich auch so. Also der Prototyp (leider Visual Basic BHÄÄÄÄÄ) sagt deutlich dass die struct (der C Begriff eines Clusters) ByRef übergeben wird. Das heisst es muss ein Pointer übergeben werden. Wenn es ByVal wäre wäre der Zweite Approach richtig (mein lieber Himmel!!!). Zitat:So einen großen Cluster mit so vielen Parametern hab ich auch noch nicht gesehen. Drei Möglichkeiten fallen mir ein, die du mal prüfen müsstest. Ich hätte ja die erste bevorzugt. 1) ist möglich aber immens viel Arbeit. Ich habe nicht im Detail die Definition betrachtet aber ich konnte so auf die Schnelle keine Array und String Pointer sehen. Dann geht es nämlich eigentlich nicht. LabVIEW Arrays und Strings sind nicht direkt kompatibel mit C Strings und Arrays. Direkt als Parameter kann man die durch die Call Library Node übersetzen lassen nach C Pointern aber innerhalb eines Clusters geht das nicht. Was ich sah waren fixed size Arrays (die haben alle eine Nummer zwischen eckigen Klammern hinter dem Variablennamen). Diese kann man durch einen eingebetteten Cluster mit der entsprechenden Anzahl Elemente vom Typ den die Variable hat simulieren. 2) Vergiss es. Das Serialisier Format packt alle Daten im Cluster zusammen und fügt bei Arrays und Strings noch eine i32 Zahl hinzu die die jeweilige Anzahl der Elemente angibt die nun folgt. 3) Mein absoluter Verzug. Diese Funktion alleine macht diesen Wrapper eigentlich schon fast eine Pflicht. Wenn da noch andere Funktionen hinzukommen mit auch nur halb so komplexen Parametern dann hat sich der Wrapper im Endeffekt ganz sicher bezahlt gemacht. Rolf Kalbermatter C++ DLL mit komplexer Datenstruktur inLabVIEW verwenden - IchSelbst - 06.02.2009 08:53 ' schrieb:Was ich sah waren fixed size Arrays (die haben alle eine Nummer zwischen eckigen Klammern hinter dem Variablennamen). Diese kann man durch einen eingebetteten Cluster mit der entsprechenden Anzahl Elemente vom Typ den die Variable hat simulieren. Also, Dieter, probiers mal damit: Anstelle eines Arrays im Cluster einen Cluster in den Cluster und dann den Haupt-Cluster auf den DLL-Knoten. Aber immer dran denken, dass die Reihenfolge im Cluster mit der im Struct übereinstimmen muss. C++ DLL mit komplexer Datenstruktur inLabVIEW verwenden - DieterSchell - 06.02.2009 19:35 ' schrieb:Vielen Dank an alle für die schnellen Antworten. Ich sehe jetzt tatsächlich Messwerte, nachdem ich die arrays durch cluster gleicher Größe ersetzt habe. Ein schönes Wochenende an alle! Gruss, Dieter |