Libnodave 0.8.4.6 und C# - Verbindungsprobleme bei CPU-Wechsel

flowschi

Level-1
Beiträge
3
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,

ich habe Probleme bei der Verbindung mit meiner C#-Software.

Die Software kommuniziert mit einer S7 CPU über ISOTCP, was grundsätzlich auch sehr gut funktioniert. Nun ist es so, dass es eine zweite, redundante CPU gibt. Bei einem Verbindungsabbruch zur CPU1 wird automatisch auf CPU2 umgeschaltet. Und wieder umgekehrt, falls CPU2 ausfällt.

Das ganze funktioniert in einer Testumgebung an einem Switch auch gut. Dort, wo die Software aber produktiv läuft, funktioniert diese Umschaltung nicht. Ich weiß nicht, wie das Netzwerk zwischen den CPUs aussieht.

Da ich aber davon ausgehen muss dass die CPUs erreichbar sind (über andere Tools nachzuvollziehen) und die Einstellungen für IP+ Rack+Slot passen, suche ich das Problem in meiner Software. Genaue Informationen zu IP Adressen und Einstellungen für Rack und Slot kann ich noch nicht nennen, aber erfahre ich die nächsten Tage.

Hat jemand Anregungen, wo ich das Problem suchen könnte?
Kann es sein, dass ich irgendwas mit libnodave falsch mache, anders/neu initialisieren müsste?

Mein Code sieht - auf's Wesentliche reduziert - so aus:


Code:
        private int Connect()
        {
            int result = -1;
           
            m_OsSerialType.rfd = libnodave.openSocket(102, m_ipAddr);
            m_OsSerialType.wfd = m_OsSerialType.rfd;


            if (m_OsSerialType.rfd > 0)
            {
                m_daveInterface = new libnodave.daveInterface(m_OsSerialType, "IF1", 0, libnodave.daveProtoISOTCP, libnodave.daveSpeed187k);
                m_daveInterface.setTimeout(10000000); // µs
                m_daveConnection = new libnodave.daveConnection(m_daveInterface, 0, m_rack, m_slot);


                result = m_daveConnection.connectPLC();
            }


            return result;
        }


        private void Disconnect()
        {
            if( m_daveConnection != null )
            {
                m_daveConnection.disconnectPLC();
            }
            int result = libnodave.closeSocket( m_OsSerialType.rfd );
        }
        
        
        private void Process()
        {
            if (Connect() == 0)
            {
            }            
            else
            {
                Disconnect();
                m_daveConnection = null;
            }
            
            if ( m_daveConnection == null )
            {
                SwitchCpu();
                continue;
            }


            result = m_daveConnection.readManyBytes( libnodave.daveDB, 700, 0, 552, data.Data );


            if( result != 0 )
            {
                // re-create connection on next loop
                Disconnect();
                m_daveConnection = null;
            }
        }


Kann ich da was besser machen?
Kann ich beim nächsten Test vor Ort irgendetwas ausprobieren oder loggen, um der Ursache näher zu kommen?
Update auf libnodave 0.8.5 habe ich vor - könnte sich da was verbessern?

Vielen Dank!
 
Zuletzt bearbeitet:
Hallo,

ich selbst nochmal...

In meiner Disconnect()-Methode habe ich bisher nur:

Code:
m_daveConnection.disconnectPLC();
libnodave.closeSocket( m_OsSerialType.rfd );

... mir fehlt aber z.B.:

Code:
libnodave.closePort(m_OsSerialType.rfd);

Kann das nachteilig sein?

Bei diversen Bespielen und Codefragmenten die ich im Netz zu libnodave finde, ist immer mal ein "daveFree" zu sehen. Ich weiß aber nicht, wie ich das in der .NET DLL aufrufen kann. Wie geht das, brauche ich das?
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Morgen
Also in 0.8.5.1 gibt es daveFree nicht. (wurde evt. in eine Funktion integriert oder entfernt.)
Der Port sollte immer 102 sein. Mit einem Portscanner kannst du prüfen ob der Port bei Programmende wieder freigegeben wird.
Während der Laufzeit des Programms brauchst du den Port nicht freigeben sondern nur den Socket schließen.
Die Rückgabewerte der einzelnen Funktionen wertest du ja aus. Mit libnodave.daveStrerror(result) kannst du dir im Fehlerfall (also ungleich 0) den Fehlercode ausgeben lassen.
Dies würde ich nach jedem Funktionsaufruf einfügen.
Mein Verbindungsabbau sieht folgendermaßen aus:
Code:
            res = dc.disconnectPLC
            res = di.disconnectAdapter
            res = libnodave.closePort(Port)
            res = libnodave.closeSocket(ph.rfd)
wobei ich ein wechselndes Verbinden zu verschiedenen CPUs noch nicht versucht habe.
Holger


Nachtrag
m_daveInterface = new libnodave.daveInterface(m_OsSerialType, "IF1", 0, libnodave.daveProtoISOTCP, libnodave.daveSpeed187k);
m_daveInterface.setTimeout(10000000); // µs
==> hier würde ich das Interface neu initialisieren
result = m_daveInterface.initAdapter
m_daveConnection = new libnodave.daveConnection(m_daveInterface, 0, m_rack, m_slot);
 
Zuletzt bearbeitet:
Der PC sollte bei jedem openSocket einen (weiteren) lokalen Port (allerhöchstwahrscheinlich <> 102) öffnen.
Es ist nötig closeSocket aufzurufen, sonst bleibt der lokale Port belegt, und nach x Programmstarts bzw. openSocket sind keine Ports mehr frei und openSocket schlägt fehl.
In meinen Anwendungen mit Libnodave und Excel rufe ich zuerst closePort und danach closeSocket auf.
Tip: lasse Dir die belegten Ports vor, während und nach Deinem Programm mit NETSTAT anzeigen.

Daß Deine Umschaltung in "Live" nicht funktioniert könnte daran liegen, daß der Live-PC den Ausfall der S7-CPU nicht mitbekommt bzw. erst seeehr spät.
Wie testest Du in Deiner Testumgebung? Du ziehst das Netzwerkkabel zwischen Deinem PC und dem Testswitch? Das erkennt der PC sofort. Du ziehst das Netzwerkkabel zwischen S7-CPU und Switch - es dauert sehr lange bis der PC den Verbindungsausfall erkennt.

Harald
 
Hallo Harald
Ähnliche Gedankengänge hatte ich auch.
In einem "normalen" Programm würde man einen freien Port vom System anfordern, damit arbeiten und wieder freigeben.

In Libnodave läuft es etwas anders.
Bei openSocket wird die Portnumer als Konstante mit übergeben. (102).
closePort erfordert als Parameter die Übergabe einer Portnummer.
Wenn es so ist wie du schreibst, dem System überlassen wird, welcher lokale Port geöffnet wird, kann ich mit closePort keinen gültigen Port übergeben, da ich dessen Nummer nicht weiß und es auch keine Funktion gibt diesen zu erfahren.

Welchen Port rufst du denn mit closePort beim Programmstart auf?
Vieleicht habe ich Libnodave aber auch völlig falsch verstanden:confused:

Holger
PS. Die Reihenfolge ist leider falsch gewesen. Habe ich korrigiert.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Harald
Ähnliche Gedankengänge hatte ich auch.
In einem "normalen" Programm würde man einen freien Port vom System anfordern, damit arbeiten und wieder freigeben.

In Libnodave läuft es etwas anders.
Bei openSocket wird die Portnumer als Konstante mit übergeben. (102).
closePort erfordert als Parameter die Übergabe einer Portnummer.
Wenn es so ist wie du schreibst, dem System überlassen wird, welcher lokale Port geöffnet wird, kann ich mit closePort keinen gültigen Port übergeben, da ich dessen Nummer nicht weiß und es auch keine Funktion gibt diesen zu erfahren.

Welchen Port rufst du denn mit closePort beim Programmstart auf?

Die Portnummer bei openSocket() ist der Zielport beim Partner, der lokale Port wird hingegen normalerweise vom Betriebssystem vergeben. Wenn du 10 Verbindungen zu einer SPS aufbaust, ist der Zielport immer 102, der lokale aber immer unterschiedlich.

Warum der Parameter bei closePort() mit "Port" bezeichnet wurde weiß ich nicht, aber das ist eigentlich der Datentyp HANDLE (was zwar auch ein int ist). Wahrscheinlich weil du diese Funktion nur verwenden darfst wenn du eine serielle Schnittstelle verwendest, dann ist Port = COM-Port. In closePort() wird die Microsoft OS-Funktion CloseHandle() aufgerufen. Die Funktion darf aber nicht bei Sockets verwendet werden, sondern nur bei z.B. seriellen Schnittstellen. Wenn du das bei einem Netzwerksocket mit Port 102 aufrufst, dann ist das falsch.
Bei einer TCP-Verbindung darf closePort() nicht aufgerufen werden.
 
In einem "normalen" Programm würde man einen freien Port vom System anfordern, damit arbeiten und wieder freigeben.

In Libnodave läuft es etwas anders.
Bei openSocket wird die Portnumer als Konstante mit übergeben. (102).
closePort erfordert als Parameter die Übergabe einer Portnummer.
Wenn es so ist wie du schreibst, dem System überlassen wird, welcher lokale Port geöffnet wird, kann ich mit closePort keinen gültigen Port übergeben, da ich dessen Nummer nicht weiß und es auch keine Funktion gibt diesen zu erfahren.

Welchen Port rufst du denn mit closePort beim Programmstart auf?
Da muß ich Dir leider widersprechen. Auch für Dich der Tip, mit NETSTAT die Belegung der Ports vor/während/nach dem Programm beobachten.

Die 102 ist der Zielport beim Partner!
Damit mehrere Verbindungen zu mehreren S7 aufgebaut werden können, muß der eigene lokale Port variabel und unterschiedlich sein können - kann also spätestens bei der 2. Verbindung nicht Port 102 sein. Deshalb überläßt auch Libnodave die Auswahl einer freien lokalen Portnummer dem Betriebssystem.

Bei closePort() und closeSocket() muß der Porthandle angegeben werden, den man bei openSocket() erhalten hat.
Ob man closePort() bei Socketverbindungen aufrufen "darf" oder nicht weiß ich nicht, ich tue es einfach und ich habe noch keine Fehler bemerkt. Ich gehe mal davon aus, daß das Betriebssystem weiß, ob mit dem angegebenen Porthandle ein Port verbunden ist oder nicht. Außerdem ist Libnodave eigentlich so angelegt, daß man das Verbindung aufbauen/abbauen immer gleich machen kann, egal auf welchem Weg die Verbindung tatsächlich läuft. Notfalls sind eigentlich nicht benötigte Funktionen als Dummy ohne Code vorhanden.

Bei meinen Verwendungen von Libnodave mit Excel funktioniert das Verbindung aufbauen und abbauen schematisch so (Code in VBA):
Code:
[COLOR="#008000"]'Verbindung aufbauen zu Port 102 der S7 an IP-Adresse "192.168.0.123"[/COLOR]

ph = openSocket(102, "192.168.0.123") [COLOR="#008000"]'Port-Handle erzeugen, for ISO over TCP[/COLOR]
If (ph > 0) Then [COLOR="#008000"]'war erfolgreich[/COLOR]
  di = daveNewInterface(ph, ph, "IF1", 0, daveProtoISOTCP, daveSpeed187k)
  res = daveInitAdapter(di)
  If res = 0 Then [COLOR="#008000"]'war erfolgreich[/COLOR]
    dc = daveNewConnection(di, MpiPpi, Rack, Slot)
    res = daveConnectPLC(dc)
    If res = 0 Then [COLOR="#008000"]'Verbindung zu PLC erfolgreich aufgebaut[/COLOR]
      ...



[COLOR="#008000"]'Disconnect from PLC, disconnect from Adapter, close the TCP/IP socket[/COLOR]
If dc <> 0 Then
    res = daveDisconnectPLC(dc)
    Call daveFree(dc)
    dc = 0
End If
If di <> 0 Then
    res = daveDisconnectAdapter(di)
    Call daveFree(di)
    di = 0
End If
If ph <> 0 Then
    res = closePort(ph)
    res = closeSocket(ph) [COLOR="#008000"]'bei ISO over TCP, sonst wird bei jedem opensocket() ein weiterer port belegt[/COLOR]
    ph = 0
End If

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Damit mehrere Verbindungen zu mehreren S7 aufgebaut werden können, muß der eigene lokale Port variabel und unterschiedlich sein können - kann also spätestens bei der 2. Verbindung nicht Port 102 sein. Deshalb überläßt auch Libnodave die Auswahl einer freien lokalen Portnummer dem Betriebssystem.

dein automatischer lokaler Port wird höchst wahrscheinlich niemals 102 sein - das ist immer nur der entfernte(mit der IP-Zusammen dann eindeutig) -
den lokalen Port bei einer Client-Verbindung den selbst zu belegen ist völlig sinnfrei - und geht doch auch gar nicht

Ob man closePort() bei Socketverbindungen aufrufen "darf" oder nicht weiß ich nicht, ich tue es einfach und ich habe noch keine Fehler bemerkt. Ich gehe mal davon aus, daß das Betriebssystem weiß, ob mit dem angegebenen Porthandle ein Port verbunden ist oder nicht.
Auf Windows-Sockets eine CloseHandle aufzurufen ist völlig sinnfrei und falsch deswegen bezieht sich diese Routine wohl doch eher auf den COM-Port - ob da kein Fehler kommt ist eher falsches Libnodave-Verhalten, und macht es trotzdem nicht richtig
 
Zuletzt bearbeitet:
Dann sind wir uns ja einig. Mein Aufbau sieht genauso aus. Ich habe deine Aussage
In meinen Anwendungen mit Libnodave und Excel rufe ich zuerst closePort und danach closeSocket auf.
falsch interpretiert. (Beim Programmstart)

Du arbeitest mit daveFree. Benutzt du die originale DLL? In der interop dll ".net.dll" habe ich die Funktion nicht finden können.
 
Du arbeitest mit daveFree. Benutzt du die originale DLL?
Ich benutze Libnodave ausschließlich zusammen mit Excel/VBA. Bei 32-Bit-Excel benutze ich die originale libnodave.dll, für 64-Bit-Excel benutze ich die Kompilation libnodave_jfkmod64.dll von Jochen Kühner. (siehe meinen Link in #8 )

Das closePort(ph) + closeSocket(ph) auf einen Windows-Socket verwende ich so seit 2009, und ich sowie die täglichen Anwender meiner Excel-Dateien haben noch nie ein Problem deswegen festgestellt. Heute glaube ich, daß das closePort(ph) wohl überflüssig ist, es stört aber offensichtlich auch nicht.

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Das closePort(ph) + closeSocket(ph) auf einen Windows-Socket verwende ich so seit 2009, und ich sowie die täglichen Anwender meiner Excel-Dateien haben noch nie ein Problem deswegen festgestellt. Heute glaube ich, daß das closePort(ph) wohl überflüssig ist, es stört aber offensichtlich auch nicht.

Das Problem sollte im Rückgabewert von closePort() übermittelt werden, dazu muss man den Rückgabewert aber auch auswerten. Die Wahrscheinlichkeit dass der Wert vom Filedescriptor einem gültigen Handle entspricht ist aber wohl so gering, als dass man sich damit ein zufällig gültiges anderweitig verwendetes Handle abschaltet.
 
Zurück
Oben