vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#

https://www.vbarchiv.net
Rubrik: Verschiedenes   |   VB-Versionen: VB604.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 UrbanekBewertung:  Views:  22.043 


Abb.1: Digital Mulitmeter Mas-345
Der Workshop besteht aus 3 Schritten:

  1. Erstellen des ActiveX Steuerelementes
  2. Erstellen der Anwendung
  3. 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)



Anzeige

Kauftipp Unser Dauerbrenner!Diesen und auch alle anderen Workshops finden Sie auch auf unserer aktuellen vb@rchiv  Vol.6
(einschl. Beispielprojekt!)

Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv!
- nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten
- Symbol-Galerie mit mehr als 3.200 Icons im modernen Look
Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m.
 
 
Copyright ©2000-2024 vb@rchiv Dieter OtterAlle Rechte vorbehalten.


Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein.