Visual C++ und OLE Server



  • Hallo!

    Ich habe ein grosses Problem und nach mehreren Stunden erfolgloser Sucherei im Internet wollte ich euch mal fragen, ob ihr mir helfen koennt!

    Es geht darum, dass ich eine SPS habe, die Daten von Sensoren in einer Datenbank im OPC Server speichert. Auf diese Datenbank kann ich mit einer OPC Client Programm zugreifen. Die Verbindung steht! Nun moechte ich diese Daten in meinem (in C++ mit Visual Studio 6.0) geschrieben Programm verarbeiten. Wie bekomme ich die Daten vom Server ins Programm?!

    Ueber Tools -> OLE/COM Object Viewer kann ich den Server sehen. Habe aber sonst keine Ahnung, wie ich darauf zugreifen kann. Eigentlich sollte das doch nicht so schwer sein...das ist doch der Sinn hinter dem OLE Prinzip, oder?! 😕



  • Kleine Anmerkung: Ein OPC-Server hat keine Datenbank um Daten zu speichern...

    Um über OPC mit einem Server zu kommunizieren rate ich Dir zum Softing-OPC-Client-Toolkit.

    Das ganze selber zu machen, mag für einfache und sehr simple Anwendungen ausreichen; ich rate aber jedem davon ab.

    http://www.softing.com/home/de/industrial-automation/products/opc/toolkits.php?navanchor=3010172

    Demo-Version kann hier runtergeladen werden (OPC-Toolbox):
    http://www.softing.com/home/de/industrial-automation/downloads/drivers-demos.php?navanchor=3010470



  • Du musst einen OPC-Client in dein Projekt integrieren/programmieren.

    Ich denke mal es ist ein OPC-DA (Data Access) Server. Dieser hat bestimmte Schnittstellen/Interfaces die du ansprechen kannst. In C++ ist es etwas aufwendig. Suche in Google nach "OPC-Client" und "C++", da müssten Beispielprogramme zu finden sein. Oder wie Vorposter schrieb ein Toolkit nutzen (kostet aber meist).
    Ganz einfach gehts in VBA (Excel):

    'Variablen deklarieren
    Dim Server As New OPCServer
    Dim WithEvents Group As OPCGroup
    Dim Item As OPCItem
    
    'OPC-Server aufrufen
    Private Sub Connect()
        Server.Connect ("xxx OPC Server") 'OPC-Server starten
        Set Group = Server.OPCGroups.Add("Gruppe1") 'OPC-Group anlegen
        Group.UpdateRate = 1000 'Aktualisierungsrate in ms festlegen
        Group.IsActive = True   'OPC-Group aktivieren
        Group.IsSubscribed = True 'bei Wertänderung Funktion DataChange der OPC-Group aufrufen
        Set Item = Group.OPCItems.AddItem("SPS.Füllstand", 1) 'OPC-Item mit ClientHandle=1 anlegen
        Item.RequestedDataType = VT_R4 'Datenformat Real 4-Byte
        Item.IsActive = True 'OPC-Item aktivieren
     End Sub
    
    'OPC-Server beenden
    Private Sub Unconnect()
        Server.Disconnect 'OPC-Server beenden
    End Sub
    
    'Funktion DataChange der OPC-Group, wird vom OPC-Server bei Wertänderung aufgerufen
    Private Sub Group_DataChange(ByVal TransactionID As Long, ByVal NumItems As Long, ClientHandles() As Long, ItemValues() As Variant, Qualities() As Long, TimeStamps() As Date)
        If ClientHandles(1) = 1 Then 'wenn OPC-Item mit ClientHandle=1
            Cells(1, 1) = TimeStamps(1) 'schreibe Zeitstempel in Feld A1
            Cells(1, 2) = ItemValues(1) 'schreibe Wert in Feld B1
        End If
    End Sub
    

    OPC DA Automation Wrapper muss installiert und in VBA unter Extras-Verweise aktiviert sein



  • In VBA "scheint" es nur einfach zu gehen! SObald aber mal die Verbindung abbricht oder sonstwas mit dem Server passiert stirbt auch Deine Anwendung... das nimmt Dir eben das Toolkit ab!



  • Vielen Dank fuer die Antworten...aber leider weiss ich momentan immer noch nicht weiter! Ich moechte/muss die Daten einer SPS in mein C++ Programm bekommen um dort eine Visualisierung zu erstellen.
    Ich habe ein OPC Client Programm von Diagnostic (Diagnostic OPC Client). Mit Hilfe dieses Clients kann ich bereits eine Verbindung herstellen und die Sensoren (Items) abfragen.

    Das Problem besteht darin, diese abfrage nun in mein (mit Visual Studio 6.0 erstelltes) C++ programm zubekommen! Das Programm zeigt mir zwar sogar Function Names an (IOPCServer::AddGroup zum Beispiel) aber irgendwie muss ich ja zumindest ne Headerdatei, etc ins Programm einbringen.

    Vielleicht eine andere Moeglichkeit waere ueber den OLE/COM Object Viewer in VS6.0! Dort sehe ich auch, dass Informationen der SPS in der Registry stehen. Ich habe aber keine Ahnung wie ich dort jetzt weiter komme....

    Ich finde keine vernuenftige und verstaendliche Beschreibung (Anleitung) wie man so etwas macht im Netz...Ich finde immer nur Beispiele fuer Excel, die mir aber nich wirklich was bringen!

    Ich wuerde mich sehr freuen, wenn mir jemand weiter helfen wuerde...



  • Wie gesagt: Verwende das Softing-Toolkit, dass nimmt Dir sehr viel Arbeit ab.

    Wenn Du es wirklich von Hand machen willst, dann lade Dir die Sourcen für OPC-DA bei der OPC-Foundation runter, les Dir die Spezifikation durch und verwende diese:

    OPC DA Sepcification:
    http://www.opcfoundation.org/DownloadFile.aspx?CM=3&RI=66&CU=10

    OPC Core Components 2.00 SDK:
    http://www.opcfoundation.org/DownloadFile.aspx?CM=3&RI=300&CN=KEY&CI=285&CU=19



  • Also wenn ich nach "OPC client C++" google dann bekomme ich viel:

    http://www.opcconnect.com/source.php (Menge Links, manchmal mit Source)
    ftp://ftp.mysst.com/pub/beta/client.zip (Client-Source)
    http://sourceforge.net/projects/opcclient/ (Client-Source/Toolkit ATL)

    und wie Jochen Kalmbach schon schrieb, besonders intensiv auf der OPC-Foundation-Seite suchen. Da gibts Manuals und auch Sample-Source.



  • Ich habe ja bereits ein funktionierenden OPC Client! Nur leider habe ich keine Doku dazu!
    Ein Verbindung herzustellen klappt ja auch ganz gut, aber nun weiss ich nicht, wie ich in meinem C++ Programm darauf zugreifen kann...



  • WAS willst Du nun?
    Willst Du den vorhandenen OPC-Client "fernsteuern"?
    Oder willst Du selber auf den OPC-Server zugreifen?



  • Eigentlich moechte ich Daten von der SPS lesen! Ab das nun ueber einen OPC Client mache oder nicht is egal!

    Ich hatte gehofft, dass ich mit meinem selbstgeschriebenen Programm einen OPC Client oeffne, der die Verbindung herstellt und ich dann in meinem Code ueber ne Read funktion die Items auslesen kann.

    Vom Prinzip her kann ich die Items schon quasi manuell auslesen, indem ich das Programm "Diagnostic OPC Client" starte, eine Verbindung herstelle, eine Group erstelle, auf add all Items gehe und dann auf Read! Da sehe ich auch, wenn sich der Sensor aendert. Sensor = einfacher Schalter! Ich sehe vDataValue[0] = 1 oder 0!



  • Der Test-OPC-Client bringt dir garnichts. Du musst einen OPC-Client selbst erstellen, d.h. in dein Programm integrieren (z.B. mit Hilfe eines OPC-Client-Toolkits oder per Hand).
    Dieser Diagnostic OPC Client dient nur zum Testen von OPC-Servern bzw. um per Hand auf die SPS-Variablen zuzugreifen. Dieser fertige OPC-Client hat keine standardisierte Schnittstelle, mit der du von C++ aus auf diesen zugreifen kannst.
    Du musst in deinem Code den OPC-Server starten (CoCreateInstanceEx) und dir den Pointer auf das IOPC-Server Interface holen. Mit der Funktion AddGroup von IOPC-Server legst du eine Gruppe an und bekommst einen Pointer auf das neue OPC-Group Objekt bzw. auf dessen Interface IOPCItemMgt. Das Interface hat Funktionen mit der du Items (Prozessvariablen) in der Gruppe anlegen kannst. Das Group-Objekt hat auch Funktionen zum Lesen und Schreiben von Werten des angelegten Items (Interface IOPCSyncIO, IOPCASyncIO).

    google nach "opcda204_cust.doc" und nehme den ersten Eintrag, das Doc beschreibt alle Interfaces mit seinen Methoden. (oder Link von Jochen)



  • Hey Softwaremaker!
    Danke fuer deine Antwort! Das hat mich schon mal einen Schritt weitergebracht! Ich habe mir das Doc angeguckt und durchgelesen...allerdings hatte ich eher nach einer Anleitung gesucht. Meiner Meinung nach ist das ja eher eine Dokumentation...

    Kennst du vielleicht ein Tutorial, etc. wo beschrieben ist, wie man einen OPC Client zum laufen bekommt?! Leider reichen meine C/C++ Kenntnisse wohl eher nicht aus um aus dem Doc schlau zu werden!



  • Schau dir die opctest.cpp in meinem vorangegangenen Post-Link ftp://ftp.mysst.com/pub/beta/client.zip an.
    In der Methode OpcStart() wird der OPC-Server gestartet:
    - CLSIDFromProgID um aus Name die CLSID zu bekommen (oder du weisst diese)
    - CoInitializeEx um COM zu initialisieren
    - CoCreateInstance(clsid, ... IID_IOPCServer...) startet den OPC-Server und gibt Zeiger auf IOPCServer zurück
    - g_pIOPCServer->AddGroup(...) legt eine Gruppe an und gibt IUnknown der OPCGroup zurück
    - g_pIGroupUnknown->QueryInterface(IID_IOPCGroupxxx,...) holt Zeiger auf die Interfaces der OPCGroup (werden später benötigt um Items anzulegen und Lesen/Schreiben)

    In AddItems() wird ein Item angelegt
    - g_pIOPCItemMgt->AddItems(...)

    Daten lesen schreiben erfolgt über Methoden der Schnittstelle IOPCAsyncIO oder IOPCSyncIO.

    Um das ganze zu verstehen musst du dich mit dem Komponentenmodell von Microsoft = COM beschäftigen (CoCreateInstance, QueryInterface, ...).



  • Hey Softwaremaker!
    Vielen Dank fuer die Datei! Sie hat mir sehr viel weiter geholfen, allerdings haette ich da noch ein paar Fragen zu. Generell verstehe ich, was das Programm macht. Es laeuft auch mit meiner SPS!
    Was ich nicht verstehe, wo die Anweisung steht, um meine boolean Ausgaenge anzeigen zu lassen.
    Also kurz von anfang an! Ich verbinde zur SPS, fuege Items hinzu und nehm dann "Async2 reads" als auswahl => read from devide und schon sehe ich meine ausgaenge. Wenn ich nun aber im Code gucke sehe ich nicht, wo dort genau diese Zeile geschrieben wird?!? Beim Debuggen habe ich rausgefunden, dass das programm fuer die Updaterate angehalten wird ueber Sleep und waehrend der zeit die Zeile geaendert wird....aber ich seh nix wie

    printf("b\tb\tb",WertItem1,WertItem2,WertItem3);
    

    Ich sehe im Code, dass vorher ueber g_pIOPCAsyncIO2->Read(...) die Werte gelesen werden, aber wo ist die Ausgabe?!



  • Du hast zwei Möglichkeiten Werte zu lesen/schreiben:

    1. Synchron: bei Aufruf g_pIOPCSyncIO->Read gibt der OPC-Server sofort die zu lesenden Werte zurück (Paramater). Nachteil: benötigt der OPC-Server längerere Zeit um die Werte zu ermitteln, dann wird deine Anwendung blockiert, da der Funktionsaufruf länger dauert.

    2. Asynchron: bei Aufruf g_pIOPCAsyncIO2->Read wird eine Leseanforderung an den OPC-Server gestellt, der Funktionsaufruf dauert nur kurz, da die Werte nicht zurückgegeben werden. Nun ermittelt der OPC-Server die Werte und wenn er fertig ist ruft er selbständig eine Funktion in deinem Programm auf und übergibt die Werte.
      Problem: In der DataAcces-Spezifikation 1 wurde dafür IOPCDataCallback benutzt, ab Spezifikation 2 IConnectionPointContainer.
      Für Spez. 2: Du musst das IID_IConnectionPointContainer-Interface von der Gruppe holen und dann den ConnectionPoint IID_IOPCDataCallback, diesem übergibst du den Zeiger auf deine Funktion im Programm mit pCP->Advise(pSink,...);

    In dem Source-Beispiel ist pSink nicht weiter ausgeführt, google mal, da gibts sicher Beispiele im Netz.



  • Hey! Tausenddank für die Antwort!

    Hab's hinbekommen die einzelnen Zustände der Sensoren in "normale" Variable zu bringen!

    jetzt muss ich "nur" noch meine beiden Programme (Visualisierung + Datenabfrage) zusammen bringen...bzw. ich hab´s heute schon versucht und nicht wirklich hinbekommen. Am liebsten würde ich ja einfach die ocptest.cpp includen, aber da steht ja vermutlich höchststrafe drauf, wenn man ne .cpp includet!
    Ansonsten is mir mal wieder aufgefallen, dass es mir da an den Grundlagen fehlt!

    Werd´s die Tage mal weiter machen und würde mich dann melden bei Fragen!

    Also erste mal danke für die OLE Abfrage!



  • Hallo!

    Ich habe es soweit zum Laufen bekommen 😋, sprich die OLE Befehle in meine Visualisierung rein gebracht und es funktioniert soweit!

    Nun wuerde ich gerne an gewissen Stellen einen Status abfragen! Als Beispiel durchlaueft das Program die Funktionen wie "OPCStart" und "AddItems" auch, wenn die SPS gar nicht angeschlossen ist. Beim debuggen ist mir aufgefallen, dass die jeweiligen Anweisungen (CoCreateinstance, g_pIOPCServer->QueryInterface, g_pIOPCServer->AddGroup, etc) auch keine Fehlermeldung zurueck geben! der HRESULT hr ist immer 0!
    Nun habe ich in den Unterlagen der OPC Foundation gesucht und habe die Anweisung OPCSERVERSTATUS bei IOPCServer::GetStatus() gefunden.
    Aber selbst die gibt dwServerState == OPC_STATUS_RUNING nach dem ich CoCreateInstance ausfuere zurueck, obwohl der Server gar nicht angeschlossen ist?!?
    Das Programm laeuft durch, bis irgendwann die Datenabfrage aufgerufen wird und nach einiger Zeit eine Fehlermeldung kommt!
    Die wuerde ich aber natuerlich vorher schon umgehen!

    Hat jemand eine Idee, wie man das machen koennte?!

    Gruss!



  • Du solltest eher die Quality der Items auswerten...
    Der OPC-Server läuft vermutlich auch, wenn die SPS nicht angeschlossen ist...


Anmelden zum Antworten