Rubrik: Verschiedenes | VB-Versionen: VB6 | 04.09.07 |
Erstellung eines Kommunikation OCX Dieser Workshop erstellt ein Ocx und eine kleine Anwendung die dieses OCX verwendet. Das OCX sendet in definierbaren Abständen eine Anfrage über die COM-Schnittstelle an das Mas-345 Digital Mulitmeter, welches darauf seinen Display-Inhalt zurücksendet. Das Digital Multimeter (DMM) verfügt über eine RS 232 Schnittstelle die mit dem Ocx kommuniziert. | ||
Autor: Joachim Urbanek | Bewertung: | Views: 22.043 |
Abb.1: Digital Mulitmeter Mas-345 |
- Erstellen des ActiveX Steuerelementes
- Erstellen der Anwendung
- Weiterverarbeitung der Daten
Schritt 1: Erstellen des ActiveX Steuerelementes
Öffnet die VB IDE und erstellt ein neues ActiveX Steuerelement
Abb.2: Start IDE
Fügt dem Projekt die Komponente Microsoft Comm Control 6.0 hinzu.
Abb.3: Komponenten
Ändert den Projektnamen (oMas345) und den Usercontrolnamen (usrMas345), fügt dann noch ein Modul zu dem Projekt hinzu (modMas345).
Nun sollte euer Projektbrowser in der IDE so aussehen.
Abb.4: Projektbrowser
Nachdem wir unsere Arbeitsumgebung vorbereitet haben füllen wir jetzt die einzelnen Projektteile.
1. Modul modMas345
In dem Modul passiert nicht viel. Eine API Funktion wird deklariert, die wir aber unbedingt brauchen.
Private Declare Sub Sleep Lib "kernel32" ( _ ByVal lngMilliseconds As Long)
Der Vollständigkeit halber habe ich noch die Main Funktion definiert, benutze sie aber nicht.
Public Sub Main() ' End Sub
Und die Prozedur die zur API gehört.
Public Sub SleepLong(ByVal lngSeconds As Long, _ Optional ByVal blnBeAwake As Boolean = True) Dim t As Single Dim b As Boolean t = Timer Do Sleep 1 If blnBeAwake Then DoEvents End If b = Timer - t > lngSeconds Loop Until b End Sub
Da das Multimeter auf jede Anfrage 1 Sekunde wartet bevor es die Displaydaten sendet benötigen wir diese Prozedur. Daraus ergibt sich auch der kleinste Abfrageinterval von 1 Sekunde. Die Prozedur hält die Anwendung solange an wie im Parameter lngSeconds übergeben worden ist. Damit ist das Modul erst mal fertig gestellt.
2. UserControl usrMas345
Zuerst richten wir die Oberfläche des Controls ein. Dazu ziehen wir aus der Toolbox
- 2 Buttons
- 1 Timer
- 1 ComControl
- 1 Frame
- 1 Textbox - davon erstellen wir eine Kopie auf der Form.
- 3 Lables
- 2 Shapes
auf die Form.
Ein Besonderheit: das 3. Lable ist Visible false auf der Form und wird erst zur Laufzeit sichtbar geschaltet um Fehlermeldungen anzuzeigen. Die Verteilung der Steuerelemente soll jedem selbst überlassen sein; in Abb5. sieht man die von mir gewählte Anordnung.
Abb.5: usrMas345
Beginnen wir jetzt mit der Deklaration der Klassenvariablen:
Public Event ReceiveData(Data As String) Private lInterval As Long Private bKommunikation As Boolean
Das Event ReceiveData wird immer dann ausgelöst, wenn über das Comm Objekt Daten empfangen werden.
IInterval wird vom User gefüllt um den Abfrageintervall einzustellen
bKommunikation wird als Flagg benutzt
A.) Button_Click Set
Private Sub Command1_Click() ' Com Schnittstelle initialisieren With MSComm1 If IsNumeric(Text1(0).Text) Then .CommPort = Text1(0).Text Command2.Enabled = True Else ' Fehler anzeigen wenn die Comport Zuweisung keine Zahl ist Lbl_Error.Caption = "Bitte erneut Comport eingeben !!" Lbl_Error.Visible = True End If End With ' Interval der Messungen einstellen (Default alle 3 Sekunden) If IsNumeric(Text1(1).Text) Then lInterval = Text1(1).Text Else lInterval = 30 End If End Sub
Hier wird das Comm Objekt initialisiert auf das System.
Um falsche Eingaben in der Textbox1(0) zu unterbinden wird auf isNumeric abgefragt, so dass alle nicht numerischen Eingaben eine Fehlermeldung in dem Lbl_Error anzeigen. Erst wenn eine numerische Eingabe erfolgt, wird der ComPort zugewiesen und der Start Button freigeschaltet.
In der Textbox(1) kann der User seine Intervallzeiten vorgeben. (10 = 1 Sekunde), default auf 3 Sekunden eingestellt.
B.) Button_Click Start/Stop
Private Sub Command2_Click() If Tmr_Fetch_Data.Enabled Then MSComm1.PortOpen = False Tmr_Fetch_Data.Enabled = False Else Tmr_Fetch_Data.Enabled = True End If End Sub
Mit dem Click Ereignis des 2. Buttons wird der Timer zum Daten holen gestartet bzw. gestoppt. Wenn das Daten holen gestoppt wird, setzt das Ereignis auch den ComPort zurück.
C.) TextBox_Click
Private Sub Text1_Click(index As Integer) ' Fehleranzeige zurücksetzen If index = 0 Then Lbl_Error.Caption = "" Lbl_Error.Visible = False End If End Sub
Auf das Click_Ereignis der ComPort Textbox werden eventuelle Fehlermeldungen wieder gelöscht.
D.) Konstruktor UserControl_Initialize
Private Sub UserControl_Initialize() ' Com Schnittstelle auf das Multimeter einstellen With MSComm1 .Settings = "600,N,7,1" .RThreshold = 14 .DTREnable = True .Handshaking = comNone End With End Sub
Dies ist jetzt wichtig für die Kommunikation zum DMM. Ohne diese SETTINGS kann keine Kommunikation aufgebaut werden.
E.) ON COMM Ereignis
Private Sub MSComm1_OnComm() Dim str As String ' Ereignise des MsComm Steuerelement auswerten Select Case MSComm1.CommEvent Case comOverrun Case comRxOver Case comEvReceive str = MSComm1.Input SleepLong (1) RaiseEvent ReceiveData(str) bKommunikation = True Case comEvEOF Case comEventBreak End Select End Sub
Im ON_COMM_Ereignis des Comm Objektes bekommen wir die angekommen Daten vom DMM gemeldet bzw. dessen Error Ereignisse, die aber nicht weiter ausprogrammiert sind.
Im Case comEvReceive benötigen wir dann die Sleep Funktion aus modMas345, denn das Multimeter benötigt 1 Sekunde zum Übertragen der Daten. Ohne das Sleep würde die str Variable immer leer sein, da die Wertezuweisung in einem Bruchteil einer Sekunde passiert.
F.) Timer
Private Sub Tmr_Fetch_Data_Timer() ' Timer für den Messinterval Static Delay As Integer Static bk As Boolean Delay = Delay + 1 ' 36000 ^ 1 Std If ((Delay Mod lInterval) = 0) Then Delay = 0 If bk Then If Not bkommunkation Then Lbl_Error.Caption = "Falscher Comm Port Bitte neu Konfigurieren" Lbl_Error.Visible = True End If End If If Not MSComm1.PortOpen Then MSComm1.PortOpen = True MSComm1.Output = vbNewLine bk = True If Shape1(0).Visible Then Shape1(0).Visible = False Shape1(1).Visible = True Else Shape1(0).Visible = True Shape1(1).Visible = False End If End If End Sub
In dem Timer wird das Polling an das DMM angestoßen. Der Timer ist ein 100 ms Timer, daher auch 10 = 1 Sekunde. Hier kommt dann auch die Klassenvariable lInterval zum Tragen, denn nur wenn Delay Modulus lInterval keinen Rest ergibt wird das Polling ausgeführt. Mit den Shapes wird die Kommunikation auf dem Control visualisiert. Auch wird hier das Kommunikations-Flagg abgefragt.
Damit ist das OCX fertig. Jetzt noch kompilieren und das OCX kann in anderen Anwendungen Verwendung finden.
Schritt 2: Erstellen der Anwendung
Öffnet die VB IDE und erstellt eine neue Standard EXE
Abb. 6: IDE
Dann konfiguriert die Komponenten des Projekts und aktiviert das OCX welches ihr vorher erstellt habt.
Abb. 7: Komponenten
Damit diese Anwendung funktioniert muss in den Verweisen noch die Microsoft ActiveX Data Objects 2.8 Library aktiviert werden.
Abb. 8: Verweise
Damit mit ADO auf eine MYSQL Datenbank zugegriffen werden kann, müssen dann noch die ODBC Treiber für MYSQL installiert sein. Ich gebe die Treiber mal mit in das Zip File.
Jetzt wird die Anwendung vorbereitet:
Nennt das Formular um in frmMain. Fügt ein neues Modul hinzu und nennt es modStr. In diesem Modul werden einige nützliche Stringfunktionen definiert. Eine neue Klasse brauchen wir auch noch, nennt die Klasse clsDBCon. In der Klasse wird der Datenbankzugriff programmiert.
Damit steht das Grundgerüst.
Füllen wir jetzt das Modul
A.) modStr - Funktion Replace
' replaces string in string Public Function Replace(ByVal s As String, _ sSearch As String, sReplace As String) As String Dim p As Integer Dim q As Integer q = 1 Do p = InStr(q, s, sSearch) If (p > 0) Then s = Left$(s, p - 1) & sReplace & _ Right$(s, Len(s) - p - Len(sSearch) + 1) q = p + Len(sReplace) End If Loop Until (p = 0) Replace = s End Function
Diese Funktion sucht im übergebenen String s nach einem Zeichen sSearch und ersetzt es durch das in der Parameterliste übergebende Zeichen sReplace. Diese Funktion brauchen wir, um das Datumsformat an das der Datenbank anzupassen.
B.) modStr - DateFunc
Public Function DateFunc(sDate As String) As String Dim YY As String Dim MM As String Dim DD As String Dim sTime As String YY = Mid(Extract(3, sDate, "-"), 1, 4) MM = Extract(2, sDate, "-") DD = Extract(1, sDate, "-") sTime = Extract(2, sDate, " ") DateFunc = YY & "-" & MM & "-" & DD & " " & sTime End Function
Diese Funktion dreht die Datumsnotation von Europäisch in Englisch.
C.) modStr - Extract
' Extract Strings in Strings Public Function Extract(nNo As Variant, sSource As String, sDel As String) As String Dim q As Long Dim p As Long Dim s As String Dim nCount As Long On Error Resume Next ' xxx q = 1 nCount = 0 s = "" Do p = InStr(q, sSource, sDel) If (p = 0) And (q <= Len(sSource)) Then p = Len(sSource) + 1 If (p > 0) Then nCount = nCount + 1 If (nCount >= nNo) Then If (nNo > 1) Then q = q + Len(sDel) - 1 s = Mid$(sSource, q, p - q) Exit Do End If q = p + 1 End If Loop Until (p = 0) Or p >= Len(sSource) Extract = s End Function
Dies ist die letzte Funktion. Sie filtert den String zwischen zwei Trennzeichen heraus; mit nNo gibt man an ab welchen Zeichen die Extraktion beginnen soll.
Schritt 3: Weiterverarbeitung der Daten
Nun wird die Klasse clsDBCon ausprogrammiert
A.) clsDBCon SaveDataInDB
Public Sub SaveDataInDB(RawData As String) Const sConStr = "Provider=MSDASQL;Driver={MySQL ODBC 3.51 Driver};" & _ "Server=Server-2003;UID=...;PWD=...;database=mas345;" Dim adocn As ADODB.Connection Dim adors As ADODB.Recordset Dim sInsert As String Dim sSelect As String Dim iPrimKeyNr As Long Dim sData As String Dim sEinheit As String Dim sType As String Dim sDate As String On Error GoTo handler ' Erstellen und Konfigurieren der DB Connection Set adocn = New ADODB.Connection With adocn .ConnectionString = sConStr .CommandTimeout = 50 .ConnectionTimeout = 20 .CursorLocation = adUseServer .Open End With ' Inkrementieren des Primärkeys Set adors = New ADODB.Recordset sSelect = "SELECT max(id)as MaxNr from Tbl_MessDaten" adors.Open sSelect, adocn, adOpenDynamic, adLockOptimistic If IsNull(adors!MaxNr) Then ' Initialisierung bei ersten Eintrag in Tabelle iPrimKeyNr = 1 Else iPrimKeyNr = adors!MaxNr + 1 End If adors.Close ' Analysieren der Rohdaten sData = Trim(Mid(RawData, 4, 6)) sType = Trim(Mid(RawData, 1, 3)) sEinheit = Trim$(Mid(RawData, Len(RawData) - 3, Len(RawData))) sDate = DateFunc(Replace(Now, ".", "-")) ' Einfügen der Daten in Tabelle sInsert = "INSERT INTO Tbl_Messdaten " & _ "Value ('" & _ iPrimKeyNr & "','" & sData & "','" & sDate & "','" & sType & "','" & _ sEinheit & "')" adocn.BeginTrans adocn.Execute sInsert adocn.CommitTrans adocn.Close Exit Sub handler: Resume Next End Sub
Diese Klasse enthält nur eine Funktion die dafür sorgt, dass die empfangenen Daten vom DMM in die Datenbank transferiert werden.
Dazu erstellen wir eine Datenbankverbindung adocn, der Connectionstring sConStr enthält den ODBC Treiber, den wir verwenden. Mit der ersten Abfrage ermitteln wir den höchsten Wert aus der Spalte ID, die als Primärschlüssel konfiguriert ist. Der Wert wird um 1 inkrementiert, um den nächsten Primärschlüsselwert zu bekommen. Nun zerlegen wir den Rohdatenstring in die Informationen Daten, Type, Einheit und Datum und schicken sie über die Datenbankverbindung via Execute in die Tabelle.
Anmerkungen:
Ich habe das Zerlegen nur für den Type Temperatur angelegt da das DMM aber auch Stromeinheiten messen kann muss hier noch auf den wechselnden Rohdatenstrom
reagiert werden, um die richtigen Informationen herausfiltern zu können.
An letzter Stelle nun noch die Form frmMain
Abb.9: frmMain
Auf die Form ziehen wir nun unser OCX und sehen die Steuerelemente des Controls.
A.) Deklaration der Klassenvariablen
Private cDB As clsDBCon Private WithEvents usrMas345 As usrMas345
cDB - instanzieren der Datenbankklasse
usrMas345 - das Event welches wir aus dem Ocx fangen wollen
B.) Form Load Ereignis
Private Sub Form_Load() Set cDB = New clsDBCon End Sub
Im Load Ereignis wird die Datenbankklasse erzeugt.
C.) Event usrMas3451_ReceiveData
Private Sub usrMas3451_ReceiveData(Data As String) ' Speichern der eingelesenen Daten in MYSQL Datenbank cDB.SaveDataInDB (Data) End Sub
In dem Eventhandling werden die Rohdaten an die Datenbankklasse übergeben.
Projekt kompilieren - FERTIG
Nachdem der Datensammler nun arbeitet habe ich mir eine Auswertung als Diagramm in einem PHP-Script geschrieben.
Einfacher PHP Scriptcode zum Erstellen eines Linien-Diagramms
<?php include ("C:/Inetpub/wwwroot/jpgraph/jpgraph.php"); include ("C:/Inetpub/wwwroot/jpgraph/jpgraph_line.php"); //Konfiguration Datenbank $DatabaseHost = "Server-2003"; $DatabaseUser = "..."; $DatabasePassword = "..."; $Database = "mas345"; $Table = "Tbl_Messdaten"; //Abfragezeitraum aus einem gesonderten Formular übermittelt $sStartdate=$_POST['start']; $sStopdate=$_POST['stop']; //Daten aus der MySQL-Tabelle auslesen $DatabasePointer = @mysql_connect($DatabaseHost, $DatabaseUser, $DatabasePassword); @mysql_select_db($Database, $DatabasePointer); $sSql = "SELECT logData,Einheit,logdate from Tbl_Messdaten where logdate >= '$sStartdate' and logdate <= '$sStopdate'" ; $ResultPointer = @mysql_query("$sSql", $DatabasePointer); for($i=0, $XWerte=""; $i<mysql_num_rows($ResultPointer); $i++) { $MessDaten = mysql_fetch_object($ResultPointer); $XWerte[] = $MessDaten->logData; } //Graphen erstellen //http://www.binnendijk.net/jpgraph/index.php?page=line_1 für nähere Informationen $graph = new Graph(1200,600,"auto"); $graph->SetScale("textlin"); $lineplot=new LinePlot($XWerte); $graph->Add($lineplot); $graph->title->Set("Temperaturkurve von: $sStartdate bis: $sStopdate "); $graph->img->SetMargin(60,20,20,40); $lineplot->SetColor("red"); $graph->yaxis->SetColor("blue"); $graph->Stroke(); ?>
Abb. 10: Diagramm des Script
Fazit:
Das OCX kann in anderen Anwendungen Verwendung finden; ihr könnt Anzeigen oder Kennlinien programmieren oder aber die Daten in einem Textfile speichern und mit
Excel auswerten oder, oder, oder. Nicht jeder hat Zuhause einen Webserver mit MySql und PHP Modul. Auch bin ich beruflich zu sehr eingebunden um dieses Projekt bis ins kleinste
auszuprogrammieren. Ich hoffe allerdings, damit bei euch Interesse geweckt zu haben.
Download
mysql-connector-odbc-3.51.19-win32.zip (3.7 MB)