Libnodave reconnect mit C#

DaMartin

Level-1
Beiträge
8
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,
ich habe ein Problem mit dem reconnect zu S7 Steuerungen.
Mein Programm läuft als Windows Service unter W2k8 und greift auf 10 unterschiedliche Steuerungen nacheinander zu.
Das Ganze passiert per Connection Pooling, d.h. ich erzeuge mir eine Liste mit den Verbindungen, verbinde mich mit der S7 (teilweise über IBH) und bei Bedarf wird die Verbindung von einer Funktion verwendet.
Leider passiert es sporadisch dass die Verbindung kurzzeitig verloren geht und danach nicht wieder neu aufgebaut werden kann.
Dies äußert sich wenn das Ergebnis von Connection.readBytes(flags, area.Db, start, bytesToRead, buf) = 1025 ist.
Was kann man da machen?
Hier mal etwas Code.
Der Connection Pool:

Code:
public S7Connection GetConnectionByServer(string server, int port, bool ibh)
        {
            foreach (var ls in this)
                if (ls.Name.Equals(server))
                    return ls;
            var lib = new S7Connection();
            //libnodave.daveSetDebug(libnodave.daveDeb);
            
            var serialType = new libnodave.daveOSserialType
                {
                    rfd = libnodave.openSocket(port, server)            
                    
                };
            
            serialType.wfd = serialType.rfd;
            
            lib.IsIBH = ibh;
            if (ibh)
            {
                lib.Interface = new libnodave.daveInterface(serialType, server, 0, libnodave.daveProtoMPI_IBH,
                                                            libnodave.daveSpeed187k);
                lib.Interface.setTimeout(1000000);
                lib.Connection = new libnodave.daveConnection(lib.Interface, 2, 0, 0);
                
            }
            else
            {
                lib.Interface = new libnodave.daveInterface(serialType, server, 0, libnodave.daveProtoISOTCP,
                                                            libnodave.daveSpeed500k);
                lib.Interface.setTimeout(1000000);
                lib.Connection = new libnodave.daveConnection(lib.Interface, 0, 0, 2);
            }
//                                                        libnodave.daveSpeed1500k);
//            lib.Interface.initAdapter();
            lib.ConnectResult = lib.Connection.connectPLC();
            lib.Connected = lib.ConnectResult == 0;
            lib.Name = server;
            lib.IP = server;
            lib.Port = port;
            Add(lib);
            return lib;
        }

Auf- und Abbau der Verbindung:
Code:
public bool Connect()
        {
            
            ConnectResult = Connection.connectPLC();
            Interface.initAdapter();
            if (ConnectResult==0)
                Connected = true;
            return true;
        }


        public void Disconnect()
        {
            Connection.disconnectPLC();
            Interface.disconnectAdapter();
       
            Connected = false;
        }

Und der Lesevorgang:
Code:
 public List<string> ReadAll()
        {
            try
            {
                lock (_lockObject)
                {
                    if (_connectionIsReading) // zum Absichern dass nicht doppelt gelsen wird
                        return null;
                    _connectionIsReading = true;
                }


                int i = 0;
                if (!Connected)
                {
                    i = Connection.connectPLC();
                    Connected = true;
                    if (i != 0)
                        LogIt(String.Format("Conection result on reconnect : {0}", i));                 
                    Interface.initAdapter();
                }
                var returnlist = new List<string>();
                // Lesen und Zuordnen der Werte
                foreach (var area in _dataAreaList)
                {
                    var readarea = ReadArea(area);  // area ist eine Klasse welche Speicherbereiche liest und die meinen Eigenschaften zuordnet
                    if (readarea != null)
                        returnlist.AddRange(readarea);
                    else
                    { // Wenn das Ergebnis null ist ist etwas schief gelaufen und die Verbindung muss neu aufgebaut werden. Daher Abbruch des Lesens und Disconnect.
                        Disconnect();                      
                        break;
                    }
                    System.Threading.Thread.Sleep(100);
                }                
                if (!IsIBH)
                {
                    i = Connection.disconnectPLC();
                    if (i != 0) // War bisher immer 0
                        LogIt(String.Format("*****ALERT !!! disconnectPLC result = {0}  !!!!******", i));
                }
                return returnlist;
            }
           catch
           {
               return null;
           }
            finally
            {
                _connectionIsReading = false;
            }
        }

Der Hauptteil von ReadArea :
Code:
 var remainBytes = area.Size;
//                var start = 0;
                var start = area.StartAddress;
                do
                {
                    // Wenn Size > 220 dann in 220er Blöcke teilen und jeweils zuordnen
                    var bytesToRead = remainBytes > 220 ? 220 : remainBytes;
                    remainBytes = remainBytes - bytesToRead;
                    var buf = new byte[bytesToRead];
                    int i = 0;
                    lock (_lockObject)
                    {
                        i = Connection.readBytes(flags, area.Db, start, bytesToRead, buf);
                    }
                    if (i != 0) // Alle paar Tage kommt hier als Ergebnis 1025 und danach geht nichts mehr
                    {
                        LogIt(String.Format("*****ALERT {6} !!! readBytes DB{1}.{2} Size:{3} Start:{4} Length:{5} result = {0} Skip Area !!!!******", i,
                                            area.Db, area.StartAddress, area.Size, start, bytesToRead,CPName ));
                        return null;
                    }
                    Array.Copy(buf, 0, area.Buffer, start-area.StartAddress, bytesToRead);
                    start += bytesToRead;


                } while (remainBytes > 0);

Ich wäre für jede Hilfe dankbar.

Gruß
Martin
 
Zuviel Werbung?
-> Hier kostenlos registrieren
initAdapter() nach connectPLC() ist Unsinn.

Welcher IBH-Link? weil: Neuere können mehr gleichzeitige MPI-Verbindungen offenhalten.
Laufen die Verbindungen zu 10 SPS über denselben IBH-Link?

Grundsätzliches Vorgehen: Wenn readbytes() mit Fehler endet kannst du es wiederholen oder die Verbindung schließen ( disConnectPLC(), disconnectAdapter() ). Je nach deinen Erfahrungen kannst du auf die Wiederholung verzivchten; es bringt meist nichts.

Beim Neuverbinden wird eine neue logische Verbindung auf der MPI/Profibus-Seite aufgebaut. Das verbraucht Resourcen auf der S7-CPU (da kannst du es unter Baugruppenzustand/Kommunikation sehen) und im IBH-Link. Zwar sollte disconnectPLC die Verbindung beenden und damit die Resourcen wieder freimachen. Aber das Problem ist, wenn eh schon ein Kommunikationsproblem besteht, ist es halt fraglich, ob es im IBH-Link auch ausgeführt wird.
Irgendwann sind alle Resourcen belegt. Dann hilft ein Reset des IBH-Links. Die CPU "merkt" dann ,daß sich ihr Partner aus dem Token Ring zurückgezogen hat und macht ihrerseits ihre Resourcen frei.
Libnodave enthält eine Routine, um einen Reset über das Netzwerk auszulösen: daveResetIBH(daveInterface *)
Es scheint, daß die es nicht in die libnodave.net.cs geschafft hat.

Du kannst in libnodave.net.cs ergänzen:
Code:
[DllImport("libnodave.dll")]
protected static extern int daveResetIBH(intPtr di);
public int resetIBH() {
return daveResetIBH(pointer);
}

Nach einem Reset siehst du am IBH-Link dieselebn Aktivitäten der LEDs wie nach Spannung ein.
Er braucht dann ein paar Sekunden, bis er wieder Verbindungen aufbauen kann.
 
Hallo Zottel,

erstmal vielen Dank für deine Antwort. Ich werde das mal versuchen.
Wenn ich es richtig interpretiere ist die sicherste Methode nach einem Verbindungsabbruch und einem fehlerhaften Leseergebnis den Adapter zu resetten.

Das Ganze Verhalten ist übrigens nicht auf IBH beschränkt. Es passiert auch bei einer Verbindung zu einer S7 über einen CP343. Selbes Verhalten. Ergebis vom Read ist -1025. Ich versuche die Verbindung mit Connection.disconnectPLC(); und Interface.disconnectAdapter(); abzubauen und später wieder neu aufzubauen. Beim Aufbauen erhalte ich dann als Ergebnis -1.
Es wird erst wieder eine Verbindung aufgebaut wenn mein Service beendet und wieder neu gestartet wird.
Gibt es evtl auch eine Möglichkeit sich von einen CP343 zurückzuziehen?

Irgendwie scheint mir dass eine einmalig verlorene Verbindung sich nicht dazu bewegen lässt wieder aufgebaut zu werden.

Danke nochmal

Grüße
Martin
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Wenn ich meinen Socket schließe bekomme ich ihn nicht mehr auf.
Problematisch ist auch dass nicht die Verbindung zu allen SPS verloren geht, sondern immer nur eine. Soweit ich das verstanden habe kann ich aber mit libnodave.closePort(102); und libnodave.closeSocket(102); die Verbindungen zu allen SPSen kappen. Dann bekomme ich sie auch nicht wieder auf.
Ich werde mir mal deinen Wrapper ansehen.
Danke dir
 
Wenn ich meinen Socket schließe bekomme ich ihn nicht mehr auf.
Problematisch ist auch dass nicht die Verbindung zu allen SPS verloren geht, sondern immer nur eine. Soweit ich das verstanden habe kann ich aber mit libnodave.closePort(102); und libnodave.closeSocket(102); die Verbindungen zu allen SPSen kappen. Dann bekomme ich sie auch nicht wieder auf.
Ich werde mir mal deinen Wrapper ansehen.
Danke dir

Du musst an libnodave.closeSocket auch dein Socket Handle und nicht den Port übergeben!
 
Hallo Jochen,

Danke dir, das ist vermutlich mein Fehler.

wo bekomme ich mein Socket Handle? Wenn ich closeSocket aufrufe fordert mich Visual Studio auf einen Port einzugeben. Das ist dann aber sicherlich der Quellport auf meinem Rechner, korrekt? Verwirrt etwas.


Gruß
Martin
 
Kurzer Tip zu Connect, bzw. Reconnect:
Bei meinen Libnodave Tools verwende ich vor dem eigentlichen Connect ein "Pre-Ping", um zu sehen ob die SPS überhaupt erreichbar ist.
Wenn sie nicht per Ping erreichbar ist, dann connecte ich erst gar nicht. Dies erspart die relativ lange Wartezeit, wenn libnodave auf eine nicht vorhandene SPS zugreifen will.

Leider kann ich aktuell nicht zum eigentlichen Problem beitragen.

mfG. Klaus Loy
 
Zurück
Oben