# AGLink-Verbindung zu meheren SPSen



## Raabun (26 September 2008)

Hallo Leute,

mein Problem ist folgendes:

Ich habe ein System mit mehreren SPSen (S7-300). Ich lese nun über ein .Net-Projekt Daten aus den SPSen. Die Komunikation erfolgt über AGLink3.7 und TCP/IP.
Es kann nun aber vor kommen, daß einzelne SPSen abgeschaltet werden (Störung, Umbau oder einfach keine Produktion auf der Anlage)

Mein Problem ist es nun, daß mein Projekt nicht erkennt ob eine SPS *wieder* eingeschlaltet wurde oder nicht.

Die Verbindung wird mit folgendem Code aufgebaut:


```
rv = AGL.OpenDevice(KonfSys.konf_sps(i).DevNr)
        If (rv = AGL.AGL_SUCCESS) Then
            rv = AGL.DialUp(KonfSys.konf_sps(i).DevNr, 1)
            If (rv = AGL.AGL_SUCCESS) Then
                rv = AGL.InitAdapter(KonfSys.konf_sps(i).DevNr, 1)
                If (rv = AGL.AGL_SUCCESS) Then
                    For ii = 1 To KonfSys.AnzSPS
                        rv3 = AGL.PLCConnect(KonfSys.konf_sps(ii).DevNr, KonfSys.konf_sps(ii).PlcNr, AGL.WAIT)
                        KonfSys.konf_sps(ii).rv = rv3
                        If (rv3 = AGL.AGL_SUCCESS) Then
                            KonfSys.konf_sps(ii).Alive = True
                        Else
                            KonfSys.konf_sps(ii).Alive = False
                        End If
                    Next
                Else
                    rv2 = AGL.ExitAdapter(KonfSys.konf_sps(i).DevNr, 1)
                    rv2 = AGL.HangUp(KonfSys.konf_sps(i).DevNr, 1)
                    rv2 = AGL.CloseDevice(KonfSys.konf_sps(i).DevNr)
                End If
            Else
                rv2 = AGL.HangUp(KonfSys.konf_sps(i).DevNr, 1)
                rv2 = AGL.CloseDevice(KonfSys.konf_sps(i).DevNr)
            End If
        Else
            rv2 = AGL.CloseDevice(KonfSys.konf_sps(i).DevNr)
        End If
```

Die Funktion gibt nun an, daß die Verbindung in Ordnung ist, aber wenn ich Daten aus der SPS lese, ergibt sich das die Verbindung nicht in Ordnung ist.

Der Versuch die Verbindungen mit folgendem Code zu kappen


```
For ii = 1 To KonfSys.AnzSPS
            rv = AGL.PLCDisconnect(KonfSys.konf_sps(ii).DevNr, KonfSys.konf_sps(ii).PlcNr, AGL.WAIT)
        Next ii
        If (rv = AGL.AGL_SUCCESS) Then
            rv = AGL.ExitAdapter(KonfSys.konf_sps(i).DevNr, 1)
            If (rv = AGL.AGL_SUCCESS) Then
                rv = AGL.HangUp(KonfSys.konf_sps(i).DevNr, 1)
                If (rv = AGL.AGL_SUCCESS) Then
                    rv = AGL.CloseDevice(KonfSys.konf_sps(i).DevNr)
                End If
            End If
        End If
```

und sie dann erneut auszubauen (mit dem 1. Code) scheitert auch.

Erst wenn das komplette Projekt gestoppt wird und neu gestartet wird, werden die SPSen richtig erkannt. (Auch die *nicht* vorhandenen.

Dieses Problem habe ich nun schon einige Monate, aber keiner konnte helfen...
Ich hoffe ich komme auf diesem Weg zu einer Lösung.

Gruß

Dirk-Uwe


----------



## Rainer Hönle (26 September 2008)

Was ich nicht sehe ist, welcher Rückgabewert letztendlich gilt. Außerdem hat der Code dahingehend Schwachstellen, dass beim Disconnect alle SPSen geschlossen werden, aber der letzte Rückgabewert darüber entscheidet, ob ein AdapterExit ausgeführt wird.
Grundsätzlich gilt, dass beim Schließen Fehler auftreten können (SPS nicht mehr da...), deshalb MUSS das Gerät trotzdem geschlossen werden. Also entweder beim Schließen den Rückgabewert ignorieren (einfacher) und alles der Reihe nach ausführen (wie im oberen Code, und auch die eigenen Variablen entsprechend setzen) oder eine genaue Analyse des Rückgabecodes vornehmen (schwieriger).
Wo wird eigentlich KonfSys.konf_sps(ii).Alive noch geändert? Was passiert mit dieser Variablen im Fehlerfall?
Und noch eine Frage: Wer ist keiner (derjenige der nicht helfen konnte)? Bei mir kam keine Anfrage dieser Art an.


----------



## Raabun (27 September 2008)

Moin,

*KonfSys.konf_sps(ii).Alive* wird verwendet um nur mit erkannten SPSen zu kommunizieren. Meine Applikation ist ein Link zwischen mehren unabhängig voneinander arbeitenden Anlagen, die alle über eine gemeinsame Betreibsdatenerfassung und Rezepturverwaltung verfügen.
Ein Abfragen einer nicht vorhandenen Anlage (abgeschaltet) dauert jedoch zu lange und würde die Performance zu stark senken.

Der Code zum Abmelden (2. Code) der Verbindungen läuft immer fehlerfrei durch. Auch das Anmelden (1. Code) gibt keine Fehler aus. Es werden sogar die nicht vorhandenen SPSen als aktiv gemeldet (wenn sie schon mal angemeldet wurde und dann entfernt wird.)

Bevor ein neutes Anmelden erfolgt, wird erst die Verbindung mit dem 2. Code abgebaut.

Gruß

Dirk-Uwe

PS: Die Anfrage wurde schon im Januar an den Support gesendet und jetzt wieder. (In der Zwischenzeit ruhte die Problematik - Ein work araound ist es immer die Applikation zu stoppen und neu zu starten, wenn sich die verwendeten Anlagen änderten, was Gott sei Dank nur selten passierte)


----------



## Rainer Hönle (27 September 2008)

Dann muss das Alive auch im Fehlerfalle zurückgesetzt werden. 
Dass der Code zum Abmelden immer fehlerfrei durchläuft, ist auszuschließen. Wenn die letzte SPS zufällig ausgeschaltet wurde und hier ein Fehler auftrat, kann es an dieser Stelle beim PLC_Disconnect einen Fehler (PLC_NOT_CONNECTED) geben. Dann werden die restlichen Deinitialisierungsfunktionen nicht ausgeführt. Beim nächsten Init wird dann nur ein interner Referenzzähler hochgezählt und nicht weiter passiert. Die Funktion gibt dann einfach OK zurück. Grundsätzlich gilt: Für jede Init-Funktion *MUSS* die entsprechende Deinit-Funktion aufgerufen werden. Im ersten Code-Teil steht es ja auch so drin. Und dies ist die richtige Vorgehensweise. 
Dass eine SPS einfach ausgeschaltet wird, ist zwar unschön (aus Kommunikationssicht), kommt aber öfters vor. Und AGLink kann damit umgehen. In der aktuellen Version ist dafür extra eine Beispielapplikation im C/C++-Quellcode dabei, die die richtige Fehler-Behandlung zeigt und mir der genau so ein Zustand nachvollzogen werden kann. Ein Beenden und Neustarten der Applikation ist im industriellen Umfeld eine unzumutbare Lösung.
Ich werde mir mal das Projekt ansehen. Vielleicht kann ich ja die Ursache dafür entdecken.


----------



## Raabun (6 Oktober 2008)

Hallo,

auf Anraten der Hotline von Deltalogic habe ich die Aufteilung der SPSen geändert. Nun läuft die Verbindung nicht mehr über ein Gerät mit mehreren AGs, sondern über je eine SPS pro Gerät. Die Verbindungen werden nun einzeln nach den Beispielen von Dletalogic auf und gegebenenfalls wieder abgebaut.
Die funktioniert viel besser als meine 1. Lösung (1 Gerät mit mehreren SPSen), aber leider auch nicht zuverlässig.

Kurz gesagt, eine gezieltes Abbauen einer bestehenden Verbindung um sie später wieder aufzubauen ist mit der Version 3.7 nicht möglich.

Man wird also das Programm komplett schließen und später neu starten müssen. 

Wenn ich eine andere Lösung finde, oder man mir weiterhilft, werde ich es hier erwähnen.

Gruß

Dirk-Uwe


----------



## Rainer Hönle (6 Oktober 2008)

Ich habe selbst mit AGLink 3.x ein Projekt für einen Kunden realisiert an dem mehrere Geräte beteiligt sind und an denen jeweils mehrere SPSen angeschlossen sind. Auch dort werden die SPSen teilweise abgschaltet (Wochenende). Und die Applikation kommuniziert wieder mit den SPSen sobald diese eingeschaltet und verfügbar sind (Prüfung erfolgt im 30 Sekunden-Raster). Deshalb schließe ich einen generellen Fehler in ACCON-AGLink aus.
Hat unser Support Ihr letztes Projekt? Mit welchen Einstellungen (hier speziell Timeout) kommunizieren Sie?


----------



## Raabun (6 Oktober 2008)

Dies sind die beiden Routinen, die den Verbindungsaufbau steuern:
Die anz_sps ist 3. Alle SPSen werden über TCP/IP angesprochen.
Der Timeout ist auf 5000ms eingestellt. Die Verbindungsart ist PG_Verbindung.
KonfSys.multi_device = true



```
Sub ini(ByVal anz_sps As Long)

        Dim rv As Integer
        Dim rv2 As Integer
        Dim rv3 As Integer
        Dim i As Integer = 1
        Dim ii As Integer
        Dim no_err As Boolean = True
        Dim plc_info As Accon.AGL.PLCINFO
        Dim op_state As Integer

        Dim versuch As Boolean = True
        Dim eine_sps_lebt As Boolean = False


        AGL.Activate("A00B5F-DBFA-xxxxxxx")

        If KonfSys.multi_device Then
            For i = 1 To KonfSys.AnzSPS
                rv = AGL.OpenDevice(KonfSys.konf_sps(i).DevNr)
                If (rv = AGL.AGL_SUCCESS) Then
                    rv = AGL.DialUp(KonfSys.konf_sps(i).DevNr, AGL.WAIT)
                    If (rv = AGL.AGL_SUCCESS) Then
                        rv = AGL.InitAdapter(KonfSys.konf_sps(i).DevNr, AGL.WAIT)
                        If (rv = AGL.AGL_SUCCESS) Then
                            rv = AGL.PLCConnect(KonfSys.konf_sps(i).DevNr, KonfSys.konf_sps(i).PlcNr, AGL.WAIT)
                            KonfSys.konf_sps(i).rv = rv
                            If (rv = AGL.AGL_SUCCESS) Then
                                AGL.ReadOpState(KonfSys.konf_sps(i).DevNr, KonfSys.konf_sps(i).PlcNr, op_state, AGL.WAIT)
                                If op_state = AGL.OPSTATE_RUN Then
                                    KonfSys.konf_sps(i).Alive = True
                                    eine_sps_lebt = True
                                Else
                                    KonfSys.konf_sps(i).Alive = False
                                End If
                            Else
                                KonfSys.konf_sps(i).Alive = False
                            End If

                        Else
                            rv2 = AGL.ExitAdapter(KonfSys.konf_sps(i).DevNr, AGL.WAIT)
                            rv2 = AGL.HangUp(KonfSys.konf_sps(i).DevNr, AGL.WAIT)
                            rv2 = AGL.CloseDevice(KonfSys.konf_sps(i).DevNr)
                        End If
                    Else
                        rv2 = AGL.HangUp(KonfSys.konf_sps(i).DevNr, AGL.WAIT)
                        rv2 = AGL.CloseDevice(KonfSys.konf_sps(i).DevNr)
                    End If
                Else
                    rv2 = AGL.CloseDevice(KonfSys.konf_sps(i).DevNr)
                End If


            Next
        Else
            rv = AGL.OpenDevice(KonfSys.konf_sps(i).DevNr)
            If (rv = AGL.AGL_SUCCESS) Then
                rv = AGL.DialUp(KonfSys.konf_sps(i).DevNr, 1)
                If (rv = AGL.AGL_SUCCESS) Then
                    rv = AGL.InitAdapter(KonfSys.konf_sps(i).DevNr, 1)
                    If (rv = AGL.AGL_SUCCESS) Then
                        For ii = 1 To KonfSys.AnzSPS
                            rv3 = AGL.PLCConnect(KonfSys.konf_sps(ii).DevNr, KonfSys.konf_sps(ii).PlcNr, AGL.WAIT)
                            KonfSys.konf_sps(ii).rv = rv3
                            If (rv3 = AGL.AGL_SUCCESS) Then
                                AGL.ReadOpState(KonfSys.konf_sps(ii).DevNr, KonfSys.konf_sps(ii).PlcNr, op_state, AGL.WAIT)
                                AGL.ReadPLCInfo(KonfSys.konf_sps(ii).DevNr, KonfSys.konf_sps(ii).PlcNr, plc_info, AGL.WAIT)
                                If op_state = AGL.OPSTATE_RUN Then
                                    KonfSys.konf_sps(ii).Alive = True
                                Else
                                    KonfSys.konf_sps(ii).Alive = False
                                End If
                            Else
                                KonfSys.konf_sps(ii).Alive = False
                            End If
                        Next
                    Else
                        rv2 = AGL.ExitAdapter(KonfSys.konf_sps(i).DevNr, 1)
                        rv2 = AGL.HangUp(KonfSys.konf_sps(i).DevNr, 1)
                        rv2 = AGL.CloseDevice(KonfSys.konf_sps(i).DevNr)
                    End If
                Else
                    rv2 = AGL.HangUp(KonfSys.konf_sps(i).DevNr, 1)
                    rv2 = AGL.CloseDevice(KonfSys.konf_sps(i).DevNr)
                End If
            Else
                rv2 = AGL.CloseDevice(KonfSys.konf_sps(i).DevNr)
            End If

        End If

        For ii = 1 To KonfSys.AnzSPS
            Dim message As String
            message = "SPS Gerät: " + KonfSys.konf_sps(ii).DevNr.ToString + "- AG-Nr.: " + KonfSys.konf_sps(ii).PlcNr.ToString
            If KonfSys.konf_sps(ii).Alive Then
                message = message + ": AGL Verbindung hergestellt: " + AGL.GetErrorMsg(KonfSys.konf_sps(ii).rv)
            Else
                message = message + ": AGL Verbindung Fehler: " + AGL.GetErrorMsg(KonfSys.konf_sps(ii).rv)
                no_err = False
            End If
            SystemFileHandling.LogData(message, konsole)
        Next
        If (eine_sps_lebt) Then
            SystemFileHandling.LogData("AGL Verbindung hergestellt: " + AGL.GetErrorMsg(rv), konsole)
            start.run = 1
        Else
            SystemFileHandling.LogData("AGL Verbindung Fehler: " + AGL.GetErrorMsg(rv), konsole)
            KonfSys.kill_system = True
            start.run = 1200
        End If
    End Sub

    Sub kill(ByVal i As Long)
        Dim rv As Integer
        Dim ii As Long


        If KonfSys.multi_device = True Then
            For i = 1 To KonfSys.AnzSPS
                rv = AGL.PLCDisconnect(KonfSys.konf_sps(i).DevNr, KonfSys.konf_sps(i).PlcNr, AGL.WAIT)
                SystemFileHandling.LogData("AGL.PLCDisconnect(" + KonfSys.konf_sps(i).DevNr.ToString + ", " + KonfSys.konf_sps(i).PlcNr.ToString + ") : " + AGL.GetErrorMsg(rv), konsole)
                rv = AGL.ExitAdapter(KonfSys.konf_sps(i).DevNr, 1)
                SystemFileHandling.LogData("AGL.ExitAdapter(" + KonfSys.konf_sps(i).DevNr.ToString + ", " + KonfSys.konf_sps(i).PlcNr.ToString + "): " + AGL.GetErrorMsg(rv), konsole)
                rv = AGL.HangUp(KonfSys.konf_sps(i).DevNr, 1)
                SystemFileHandling.LogData("AGL.HangUp(" + KonfSys.konf_sps(i).DevNr.ToString + ", " + KonfSys.konf_sps(i).PlcNr.ToString + "): " + AGL.GetErrorMsg(rv), konsole)
                rv = AGL.CloseDevice(KonfSys.konf_sps(i).DevNr)
                SystemFileHandling.LogData("AGL.CloseDevice(" + KonfSys.konf_sps(i).DevNr.ToString + ", " + KonfSys.konf_sps(i).PlcNr.ToString + "): " + AGL.GetErrorMsg(rv), konsole)
            Next i
        Else
            For ii = 1 To KonfSys.AnzSPS
                rv = AGL.PLCDisconnect(KonfSys.konf_sps(ii).DevNr, KonfSys.konf_sps(ii).PlcNr, AGL.WAIT)
                SystemFileHandling.LogData("AGL.PLCDisconnect(" + KonfSys.konf_sps(i).DevNr.ToString + ", " + KonfSys.konf_sps(i).PlcNr.ToString + ") : " + AGL.GetErrorMsg(rv), konsole)
            Next ii
            rv = AGL.ExitAdapter(KonfSys.konf_sps(i).DevNr, 1)
            SystemFileHandling.LogData("AGL.ExitAdapter(" + KonfSys.konf_sps(i).DevNr.ToString + ", " + KonfSys.konf_sps(i).PlcNr.ToString + "): " + AGL.GetErrorMsg(rv), konsole)
            rv = AGL.HangUp(KonfSys.konf_sps(i).DevNr, 1)
            SystemFileHandling.LogData("AGL.HangUp(" + KonfSys.konf_sps(i).DevNr.ToString + ", " + KonfSys.konf_sps(i).PlcNr.ToString + "): " + AGL.GetErrorMsg(rv), konsole)
            rv = AGL.CloseDevice(KonfSys.konf_sps(i).DevNr)
            SystemFileHandling.LogData("AGL.CloseDevice(" + KonfSys.konf_sps(i).DevNr.ToString + ", " + KonfSys.konf_sps(i).PlcNr.ToString + "): " + AGL.GetErrorMsg(rv), konsole)
        End If


        If (rv = AGL.AGL_SUCCESS) Then
            SystemFileHandling.LogData("AGL Verbindung getrennt: " + AGL.GetErrorMsg(rv), konsole)
        Else
            SystemFileHandling.LogData("AGL  Verbindung  trennen Fehler: " + AGL.GetErrorMsg(rv), konsole)
        End If
        If KonfSys.kill_system Then
            KonfSys.kill_system = False
            start.run = 0
        Else
            start.run = 1000
        End If
    End Sub
```
Im Störfall wird die Verbindung zur SPS gekappt (der Hub, der die Anlagen verbindet wird stromlos und nach kurzer Zeit wird er wieder mit Strom versorgt.)
Jetzt wird automatisch die kill-Funktion ausgeführt und anschließend die ini-Funktion. 
Wenn alle Anlagen wieder online sind, wird dies richtig erkannt, aber es kommt vor, das eine als richtig verbunden erkannte SPS nicht ansprechbar ist. Dies muß nicht die SPS sein, die abgeschaltet wurde.

Gruß

Dirk-Uwe


PS: Leider kann ich nicht beliebig testen, da die Anlagen produzieren...


----------



## Rainer Hönle (6 Oktober 2008)

Wenn ich es richtig interpretiere, dann ist für Sie die SPS vorhanden, wenn der OpState auf RUN ist. Ist dies wirklich so gewollt? 
Die Funktion ReadOpState kann zum Beispiel fehlschlagen, weil die SPS momentan kein Lust hat auf diese Frage zu antworten und dies auch so kundtut (protokollmäßig ist dabei alles ok und die SPS ist auch vorhanden!). Des weiteren kann es sein, dass die SPS sehr lange für das Starten (z.B. wenn DP vorhanden ist) benötigt und ggf. im Anlauf ist. Auch dies wird im Code als nicht vorhanden interpretiert.
Nachdem beim PLCConnect eine Kommunikation mit der SPS abläuft und die Funktion nur OK liefert, wenn die SPS auch wirklich da ist und korrekt geantwortet hat, genügt mir dieses in meinen Programmen um dann weiter mit dieser SPS zu kommunizieren (bis ich wieder einen Kommunikationsfehler erhalte).


----------



## Raabun (6 Oktober 2008)

Mein Problem ist es, daß die Verbindung immer so tut, alsob sie ok und glücklich wäre, aber ich kann nicht von ihr lesen, schreiben.

Wie schnell nach dem Abmelden kann ich mich wieder anmelden?

Gruß
 Dirk-Uwe


----------



## Rainer Hönle (6 Oktober 2008)

Normalerweise gönne ich dem System einige Sekunden Pause nach dem Abmelden. Aber dann sollte es normal weiter gehen können. Bei fehlerhaftem Verbindungsaufbau warte ich in der Regel einige Zeit (30 Sekunden), weil eine SPS die nicht da ist nicht dauernd geprüft werden muss.
Damit wir dem Ganzen schneller auf die Schliche kommen: am Besten Whireshark installieren, eine Aufzeichnung machen und mir diese zukommen lassen (über unsere Support-EMail-Adresse). Da kann ich dann sehen, was auf der Leitung wirklich passiert.


----------



## Rainer Hönle (7 Oktober 2008)

Raabun schrieb:


> Mein Problem ist es, daß die Verbindung immer so tut, alsob sie ok und glücklich wäre, aber ich kann nicht von ihr lesen, schreiben.


Was bedeutet dies? Was kommt genau für eine Fehlernummer?


----------

