# OPC - Client programmieren mit Microsoft Visual Studio 2008



## Neger für alles... (27 Januar 2010)

Hallo,

ich habe follgende Problemstellung:
Es soll an Siemens SPS Steuerungen über 1x S5-Lan & 1x S7-Lan - Stecker von Process-Informatik, über den OPC - Server (OPC-Manager) von Process-Informatik E/A/M etc. beobachtet werden können.

Über diesen OPC Manager komme ich bereits an die Steuerungen Online und kann die Variable auch beobachten / schreiben.

Doch nun brauche ich das Sprungbrett von meinem Visual Studio 2008 C# Projekt raus auf den OPC - Server.

Ich suche den Quelcode eines Client, damit ich den OPC-Server ansprechen kann.

Kann mir jemand weiterhelfen?


----------



## Gerhard Bäurle (27 Januar 2010)

Hallo,

ich würde mal beim *Hersteller* fragen, vielleicht haben die 
ja was im Angebot oder sonst einen Vorschlag.

Sonst:
http://www.opcconnect.com/freecli.php


----------



## Ralle (27 Januar 2010)

Hier gibt es so etwas auch, allerdings nicht kostenlos und ob mit Quellcode???

http://www.kassl.de/opc/index.shtml


----------



## Question_mark (28 Januar 2010)

*Opc da*

Hallo,



			
				Ralle schrieb:
			
		

> allerdings nicht kostenlos und ob mit Quellcode???



Nicht kostenlos, aber noch relativ günstig. Jedenfalls wenn man den Arbeitsaufwand zur Erstellung eines *zuverlässig* funktionierenden OPC-Clients in Relation setzt.

Und die Quelltexte werden vom Herrn Kassl mitgeliefert ...

Natürlich nicht bei der Demoversion  , aber wer eine Lizenz kauft bekommt auch die Quelltexte. Und einen absolut sauber funktionierenden OPC-Client dazu. 

Gruß

Question_mark


----------



## Dr. OPC (28 Januar 2010)

Von der OPC Foundation gibt es (auch für Nicht-Mitglieder) verschiedene Komponenten zum Download. Oft werden diese ins Setup des Servers integriert und befinden sich somit schon auf der Maschine.

Die OPC Schnittstelle ist als sogenanntes "Custom-Interface" definiert. Dabei handelt es sich um eine C++ Schnitstelle, die den Regeln des COM (Component Object Model) von Microsoft unterliegt. Zum "direkten" Zugriff auf Schnittstelle muss auch der Client in C++ geschrieben sein. 

Um aber auch Clients mit anderen Sprachen entwickeln zu können, gibt es das sogenannte "Automation-Interface" eine Schnittstelle für VisualBasic V6 (bzw. VBA von Excel). Dabei handelt es sich um eine DLL, die vom Client geladen wird und dann die Aufrufe an den Server "weiterleitet" (daher auch gerne "Automation-Wrapper" genannt).

Für die (neuen) .NET Sprachen C# und VB.NET gibt es die RCW (Runtime Callable Wrapper) die zwischen managed und unmanaged Code "vermitteln".

http://www.opcfoundation.org/Downloads.aspx?CM=1&CN=KEY&CI=286&CU=7

Für Mitglieder der OPC Foundation gibt es weitere Komponenten, Beispiele und auch Source Code. Unter anderem die ".NET API" eine Schnittstelle, die direkt in .NET verwendet werden kann und quasi die COM und die .NET Technologie verbindet.

http://www.opcfoundation.org/Downloads.aspx?CM=1&CN=KEY&CI=281&CU=16

Darüber hinaus gibt es die schon erwähnten Drittanbieter, die zum Teil die ".NET API" der OPC Foundation weiter vereinfacht haben oder sie als Basis benutzen. Es gibt natürlich auch komplette Neuentwicklungen und sogar Tools, bei denen man nicht mal mehr programmieren muss. (das eignet sich meist nur für einfache Anwendungen)

http://www.kepware.com/Products/OPC_ClientAce.asp

Eine recht große Liste an Anbietern gibt es hier:
http://www.opcconnect.com/dotnet.php


----------



## Neger für alles... (31 Januar 2010)

*Verweis auf OPC Manager*

Ich habe nun wie im Anhang beigefügt einen Verweis auf den OPC Manager gefunden.

Hierbei gibt es die Methode Conect mit Übergabeparameter,
kann mir jemand genau beschreiben, wie ich nun eine Verbindung zu dem OPC Manager aufbaue?

Übergabe 1(string) = Name des OPC Servers
Übergabe 2 (object) was ist das?

------------------------------------------------------
Steht die Verbindung zum Server,
wie spricht man dann die Groups / Items an?


----------



## StefanK (1 Februar 2010)

*Kleines Beispiel...*

Hi, anbei findest du ein kleines Beispiel aus dem Internet. Dieses benötigt eine Interface-DLL: OPCAutomation.dll.
Ansonsten hat Dr. OPC, ich denke, alles relevante erwähnt. OPC ist meines erachtens ein recht komplexes Gebilde, was doch einige Einarbeitung benötigt.
Gruß
Stefan


----------



## Dr. OPC (1 Februar 2010)

Eine Datenkommunikation mit einem OPC Server (DataAccess) sieht prinzipiell immer gleich aus. Es sind (vereinfacht gesagt) nur 4 Schritte erforderlich:

1) Connect
hier Name des Servers (ProgID) und optional Name des Rechners (Node) angeben falls er auf einer anderen Maschine läuft als der Client

2) AddGroup
die Gruppe ist eine "Organisationseinheit", hier stellt man wichtige Dinge ein wie "UpdateRate" und "Deadband", an diesem Objekt meldet man sich für DataChange-Events an (ähnlich wie ein Button, der feuert einen Event wenn drauf geklickt wird)

3) <Browse> optional, nur wenn man die ItemIDs nicht kennt
Wenn man die "Syntax" der ItemsID (Item = Variable = Prozesswert-Repräsentant) nicht genau kennt, kann man den Server durchbrowsen, StefanK hat sich das gespart und die ItemsIDs in einem ini-File hinterlegt.

4) AddItems
Die Items die einen interessieren, müssen zu der Gruppe hinzugefügt werden, beim Hinzufügen sollte die Gruppe "passiv" sein, da sonst sofort Events kommen können

5) Anmelden und Aktivieren der Gruppe
wird die Gruppe "scharf" geschaltet, holt der Server die Daten vom Prozess (meistens pollend aus der SPS) und prüft ob sie sich, im Vergleich zum letzten mal, (um mehr als "Deadband") geändert haben, falls ja feuert das Gruppen-Objekt beim Erreichen der nächsten "UpdateRate" einen DataChanged-Event, der NUR die geänderten Werte enthält (die anderen kennt man ja schon vom letzten mal). Um rauszufinden zu welchen Items die Werte gehören gibt es die ClientHandles (eine Indexnummer, die man dem Server beim AddItems bekanntgegeben hat) diese Nummer schickt er beim Event mit den geänderten Daten zurück.

>> warten auf Daten-Änderungs-Meldungen
Im Eventhandler muss man nun (möglichst schnell) die gemeldeten Daten weg kopieren oder verarbeiten. Kleiner Hinweis: niemals im Eventhandler wieder den OPC Server rufen oder in andere Komponenten (z.B. Datenbank) reinrufen, sonst kann sich das Ganze ineinander verschachteln und klemmt.

6) <SyncWrite> optional, wenn man es will
mit den ServerHandles, die man beim AddItems bekommen hat, kann man auf die Items schreiben. Diese Indexnummern dienen dem Server dazu herauszufinden um welches Item es sich handelt, dass ist viel einfacher (und schneller) als jedesmal wieder die Namen (strings) zu prüfen.

Also im Prinzip ist das ganz einfach. Die ganzen anderen Funktionen von OPC braucht man nur wenn man spezielle Dinge tun will.


----------



## Neger für alles... (2 Februar 2010)

Also unterm Strich gesagt:
Es ist nicht ganz so einfach, einen Client selber zu schreiben,
das einfachste ist über ein Activ X Element die Variablen / Items an den OPC Server zu kommen oder?


----------



## Dr. OPC (3 Februar 2010)

Obwohl ein funktionierendes Beispiel für die beschriebene Aufgabe mit ca. 1 Din A4 Seite Code programmierbar ist, muss man zugeben, dass mit Fehlerbehandlung und zuverlässiger Handhabung von Verbindungsunterbrechungen, etc. es sicher deutlich aufwendiger wird.

Mit einer professionellen Bibliothek (oder auch ActiveX) spart man sich sicher viel Arbeit.

Das einfachste mir bekannte Produkt ist "ClientACE" von Kepware (http://www.kepware.com/Products/OPC_ClientAce.asp). Hier kann man sogar ohne eine Zeile Code zu schreiben einen Client in .NET erstellen, einfach zusammenklickern. Für die "Poweruser" ist aber auch eine schicke .NET API dabei.

Aber es gibt noch ca. ein Dutzend andere, viele davon aufgelistet bei www.opcconnect.com


----------



## imod (16 Juni 2010)

Hallo, ich befasse mich gerade auch mit der Kommunikation mit einem WebIO der Firma Wiesemann und Theis. 
Der Verbindungsaufbau funktioniert soweit einwandfrei.
Sobald ich aber die Methode .OPCGroups der Klasse OPCAutomation.OPCServer aufrufe kommt folgender Fehler.
"Das Objekt des Typs OPCAutomation.OPCServerClass kann nicht in Typ OPCAutomation.IOPCGroups umgewandelt werden."
Wollte ma Fragen ob einer von euch weiß woran das liegen könnte.

Und beim Versuch diese RCW von der OPC Foundation ist ja eine .msm datei. Wie kann ich diese in mein Programm einbinden, als Verweis oder so hab ichs ned hinbekommen...

Vielen Dank schon mal im Vorraus!!


----------



## Dr. OPC (16 Juni 2010)

Das msm Paket ist ein Mergemodul, das in msi Installer eingebunden wird, wenn du also ein msi Setup mit dem VS-Studio erstellst, einfach "hinzufügen>Mergemodul" und fertig. Auch andere Setup z.B. NSIS können solche Mergemodule einbinden und aufrufen. Es ist also quasi ein Sub-Setup das von einem anderen Setup aufgerufen wird. Es gibt auch noch ein msi Paket von der OPC Foundation z.B. CoreComponents.msi das auch als eigenstängiges Setup (stand alone) aufgerufen werden kann.

Das OPCGroups Objekt ist eine Collection (ein Collection Objekt). Diese Collection enthält dan ein oder mehrere OPCGroup (ohne s) Objekte, die mit new angelegt werden müssen. An diesm OPCGroup Objekt werden wiederum Methoden aufgerufen. Das OPCServer Objekt ist ein eigenständiges Objekt mit eigenen Methoden. Ein impliziter Cast ist nicht möglich, das scheint der Fehler zu sein den du bekommst.


----------



## imod (17 Juni 2010)

Okay, das mit den msm Dateien ist dann soweit klar. Vielen dank schon mal hierzu.

Nun zur Programmierung:
Geht man beim Aufbau einer OPC Verbindung nicht folgendermaßen vor?

1. OPCMyServer = New OPCAutomation.OPCServer 
2. OPCMyServer.Connect(txtSvrName.Text, "")
3. OPCMyGroups = OPCMyServer.OPCGroups
4. OPCMyGroup = OPCMyGroups.Add("Group1")

das bedeutet Zeile 3 ist nicht Zulässig weil ein impliziter Cast nicht möglich ist. Aber wie sage ich meinem Groups Objekt dann, dass es die Items vom OPCServer verwenden soll?


----------



## Dr. OPC (17 Juni 2010)

1 und 2 ist ok.

3 kannste weglassen

stattdessen die Updaterate aller Gruppen erstmal auf 1 sec setzen
3) OPCMyServer.OPCGroups.DefaultGroupUpdateRate = 1000

bei 4 gleich das Objekt erzeugen und zuweisen
4) Set OPCMyGroup1 = OPCMyServer.OPCGroups.Add("Group1")
der Server besitzt nun eine Collection mit einer Gruppe, du verwendest das Gruppenobjekt für weitere Aufrufe. Die Collection benötigst du nicht.

5) dann erstmal die Callbacks abschalten
    OPCMyGroup1.IsActive = False
    OPCMyGroup1.IsSubscribed = False

6) nun die Items hinzufügen (alle auf einen Streich)
    'bla,bla, parameter befüllen und dann
    Call OPCMyGroup1.OPCItems.AddItems(bla,bla, parameter)

7) nun die Gruppe "scharf" schalten (sie liefert dann callbacks)
    OPCMyGroup1.IsActive = True
    OPCMyGroup1.IsSubscribed = True

8 )  im Eventhandler die Daten abholen, oder Read(fromCache, x, x) aufrufen oder Write()


----------



## imod (17 Juni 2010)

Hi Dr. OPC

ich habe es grade so ausprobiert wie du es mir empfohlen hast.
jetzt kommt eine Fehlermeldung bei 
OPCMyServer.OPCGroups.DefaultGroupUpdateRate = 1000
und zwar wieder 
"Das Objekt des Typs OPCAutomation.OPCServerClass kann nicht in Typ OPCAutomation.IOPCGroups umgewandelt werden."
kann es sein dass irgendwas an meiner OPCAutomation.OPCServer nicht stimmt?

Danke


----------



## Dr. OPC (17 Juni 2010)

Private WithEvents OPCMyServer As OPCAutomation.OPCServer
    Private WithEvents OPCMyGroup1 As OPCAutomation.OPCGroup

    OPCMyServer = New OPCAutomation.OPCServer
    Call OPCMyServer.Connect("ProgID1", "Node1")
    OPCMyServer.OPCGroups.DefaultGroupUpdateRate = 1000
    Set OPCMyGroup1 = OPCMyServer.OPCGroups.Add("Group1")

funktioniert bei mir ohne Probleme, die Collection gibt es sobald es das Serverobjekt gibt.

Das setzen der DefaultUpdaterate verhindert dass "0" genommen wird (Parameter nicht gesetzt, bedeutet 0). Die UpdateRate=0 hat bei OPC eine "Sonderbedeutung" und bedeutet "so schnell wie der Server kann". Das ist in den meisten Fällen schlecht, denn es wird unnötig Last auf der SPS erzeugt und die Werte mit 50-100 ms gepollt.


----------



## imod (17 Juni 2010)

des ist ja merkwürdig, ich habe genau den gleichen Quelltext und bei mir bekomm ich ne InvalidCastExeption.

Das Objekt des Typs "OPCAutomation.OPCServerClass" kann nicht in Typ "OPCAutomation.IOPCGroups" umgewandelt werden.

Mhh... woran könnte das liegen 
Ich programmiere mit Visual Basic 2008 Express Editon...


----------



## imod (23 Juni 2010)

mir ist grad aufgefallen, dass ich noch den OpcDAuto.dll Version 2.0.1.0 verwende. Können die Fehler daher kommen?
Habe jetzt schon ein paar mal gelesen, dass Version 2.0.2.0 verwendet wird.


----------



## Dr. OPC (24 Juni 2010)

Habe gerade mal geschaut. Die Dateiversion meiner Automation DLL ist 2.02.5.30
Es würde mich wundern wenn das eine Rolle spielt, denn das Teil ist auch schon sehr alt und an der Stelle hat sich bestimmt nichts geändert. Ich vermute eher das es an .NET liegt denn da müssen Objekte erst erzeugt und initialisiert werden bevor man etwas zuweisen kann.

Mein Code ist mit Visual Basic 6 geschrieben, ohne .NET und RCW, und funktioniert auch in Excel VBA.

Kommst du denn weiter wenn du die DefaultUpdaterate nicht setzt sondern direkt die Gruppe anlegst? Die "Add" Methode an der "Groups"-Collection des Server Objekts gibt ein Objekt vom Typ OPCGroup zurück und dieses kann damit direkt meinem OPCMyGroup Objekt zugewiesen werden.

Das OPCGroup Objekt hat dann wiederum eine OPCItems-Collection, der man mit der Methode "AddItems" dann die gewünschen Items hinzufügt.


----------



## imod (25 Juni 2010)

Nein, dann kommt der Fehler in der Zeile, in der ich MyServer.OPCGroups.Add("Group) verwende.
Hier mein Quelltext:

MyServer = New OPCAutomation.OPCServer
MyServer.Connect("Wiesemann-Theis.DigitalEA.1", "")
MyGroup = MyServer.OPCGroups.Add("Group") 'Hier kommt der Fehler:
'Das Objekt des Typs "OPCAutomation.OPCServerClass" kann nicht in Typ "OPCAutomation.IOPCGroups" umgewandelt werden.
MyGroup.IsActive = False
MyGroup.IsSubscribed = False
MyItem = MyGroup.OPCItems.AddItem("box_1.A.", 1234)
MyGroup.IsActive = True
MyGroup.IsSubscribed = True
MyItem.Write(1)
MyServer.Disconnect()
MyServer = Nothing

Aber dass man mit VB6 programmieren muss kann ja fast ned sein, denke dann liegt es eher an meinem veralteten dll, nur ist es ja unglaublich schwer an die dlls zu kommen wenn man kein Mitglied in der OPC Foundation ist...


----------



## Dr. OPC (29 Juni 2010)

> Aber dass man mit VB6 programmieren muss kann ja fast ned sein


nein man kann auch .NET nehmen dann wird es halt nur noch undurchsichtiger, denn das Erzeugen und Löschen von Objekten macht dann .NET und man selber steht doof da wenn man nicht sehen kann was und vor allem wann wirklich was passiert.

In deinem Fall glaubt .NET das ein Objekt gecastet werden muss und das geht natürlich nicht, die Collection gibt es anscheinend nicht. Das Group Objekt soll Erzeugt werden (SET Befehl in VB6) und anschließend soll in dieses nagelneue Objekt eine Zuweisung ("=" Operator) stattfinden und es soll genau das zugewiesen werden was als Rückgabewert aus der Add()-Methode der Collection herauskommt, die am Serverobjekt hängt. Das geht in VB6 in einer Zeile, in .NET offenbar nicht.

Da .NET das nicht kappiert wirst du dieser Supersprache etwas auf die Sprünge helfen müssen. Erst die Objekte (zu Fuß) erzeugen, dann (zu Fuß) initialisieren und wenn sie dann existieren kannst du hoffentlich auch Funktionen an ihnen aufrufen und etwas zuweisen.

Also in VB6 würde dein Programm schon laufen. 

Ein Hinweis noch: ich habe gesehen das du die AddItem() Methode an der OPCItems Collection verwenden willst, wenn es bei einem Item bleibt ist das OK, wenn es aber mehrere Items werden sollen dann solltest du besser die AddItems (mit "s") Methode verwenden. Die hat zwar mehr Parameter aber diese Parameter brauchst du später sowieso (zum Read,Write,Remove, etc.)


----------



## NoTategoi (30 Juni 2010)

Hallo zusammen,

dieses Problem interessiert mich momentan auch, da mir VS2008 die gleiche Fehlermeldung liefert (übrigens auch mit WEb-IO und Windows 7 (32Bit)).

Konnte schon jemand dass Problem erfolgreich lokalisieren und beheben?


Gruß,
Markus


----------



## imod (2 Juli 2010)

Juhu... ich hab meinen Fehler gefunden!!
Mein opcdaauto.dll war zu alt! Ich hatte Version 2.0.1.0.
Jetzt verwende ich 2.2.5.30. Damit geht es so wie wir zuerst dachten...
(Wie bei vb6)

Riesengroßes Dankeschön an Dr OPC für die Hilfe!!

Grüße Imod


----------



## Dr. OPC (5 Juli 2010)

Na das hört sich doch super an, da passte die DLL wohl nicht zum RCW.

Hier nochmal der gesamte Code eines Clients als Orientierung, habe ihn nicht getestet, sollte aber so ungefähr funktionieren bzw. einen Eindruck schaffen was man so beachten sollte.


```
Option Explicit
Option Base 1

Private WithEvents MyServer As OPCServer
Private WithEvents MyGroup As OPCGroup
'globals
Dim MyProgID1 As String
Dim MyNode1 As String
Dim MyItemIDs() As String
Dim MyServerHandles() As Long
Dim vDataBuffer() As Variant

' the main program
Private Sub Main()
' fill the ProgID of the Server
MyProgID1="OPC.Sample.1"
MyNode1="127.0.0.1"

' fill the ItemID wherever they come from, e.g. read from a file
ReDim MyItemIDs(3)
MyItemIDs(1)="Tag1" 
MyItemIDs(2)="Tag45" 
MyItemIDs(3)="Tag23" 

' now start the OPC Client
Call MyConnect()

'  the vDataBuffer(index) will always contain the latest value 
' ToDo: do something with the data

' don't forget to call MyDisconnect() in the Unload function

End Sub

' this function connects and adds one group and all items
Private Sub MyConnect()
Dim Errors() As Long
Dim CHs() As Long
Dim i As Long
Dim msgText As String

On Error GoTo errorhandler
    'connect to server
    Set MyServer = New OPCServer
    msgText = "connecting to " & ProgID1
    Call MyServer.Connect(ProgID1, Node1)
    
    'add group 
    MyServer.OPCGroups.DefaultGroupUpdateRate = 1000
    Set MyGroup = MyServer.OPCGroups.Add()
    'disable callbacks
    MyGroup.IsActive = False
    MyGroup.IsSubscribed = False
    
    'add items 
    ReDim CHs(UBound(MyItemIDs))
    For i = 1 To UBound(MyItemIDs)
        CHs(i) = i
    Next
    msgText = "adding items to " & ProgID1
    Call MyGroup.OPCItems.AddItems(UBound(MyItemIDs), MyItemIDs, CHs, MyServerHandles, Errors)
   'check for errors
    For i = 1 To UBound(MyItemIDs)
        If Errors(i) <> 0 Then
            Call MsgBox("AddItems for " & MyItemIDs(i) & " failed: " & MyServer.GetErrorString(Errors(i)), vbCritical)
        End If
    Next
    ReDim vDataBuffer(UBound(MyItemIDs))
    
    ' enable the callbacks
    MyGroup.IsActive = True
    MyGroup.IsSubscribed = True
    
    Exit Sub
errorhandler:
    Call MsgBox("ERROR when " & msgText, vbCritical)
    If Not MyServer Is Nothing Then Call MyDisconnect
End Sub

' this function cleans up
Private Sub MyDisconnect()
On Error GoTo errorhandler
    'remove the groups
    If Not MyGroup Is Nothing Then
        MyServer.OPCGroups.RemoveAll
        Set MyGroup = Nothing
    End If
    'disconnect the server
    If Not MyServer Is Nothing Then
        Call MyServer.Disconnect()
        Set MyServer = Nothing
    End If

    Exit Sub
    
errorhandler:
    Call MsgBox("Problems during disconnect", vbCritical)
End Sub

' event handler when server shuts down
Private Sub MyServer_ServerShutDown(ByVal Reason As String)
    Call MyDisconnect()
    Call MsgBox(MyServer.ServerName & vbCrLf & Reason, vbCritical)
End Sub

' event handler receiving new data from the server
Private Sub MyGroup_DataChange(ByVal TransactionID As Long, ByVal NumItems As Long, ClientHandles() As Long, ItemValues() As Variant, Qualities() As Long, TimeStamps() As Date)
Dim i As Long

For i = 1 To NumItems
    'check the quality before using the value
    If Qualities(i) = 192 Then
        'do not make call to the server again inside this callback (only fill the data buffer)
        vDataBuffer(ClientHandles(i)) = ItemValues(i)
    End If
Next

End Sub
```
Es braucht nur die ProgID es Servers und die Items befüllt werden und dann sind die Daten im DataBuffer.


----------



## Neuge (30 November 2010)

imod schrieb:


> Juhu... ich hab meinen Fehler gefunden!!
> Mein opcdaauto.dll war zu alt! Ich hatte Version 2.0.1.0.
> Jetzt verwende ich 2.2.5.30. Damit geht es so wie wir zuerst dachten...
> (Wie bei vb6)
> ...


 
Hallo habe das gleiche Problem wie du... könntest du mir die opcdaauto.dll 2.2.5.30. mal zuschicken?? 

schon mal vielen Dank.
tn@solrosso.com


----------

