TIA 'Any-Pointer' im DB speichern?

Fireman_Frank

Level-2
Beiträge
156
Reaktionspunkte
28
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich habe von einem Modbus-Server größere Datenmengen abzuholen, die in verschiedenen Strukturen in verschiedenen Registern zur Verfügung stehen. Für einen 'Datensatz' schreibe ich an den MB_Client Baustein die Registernummer, die Anzahl der Register und den Zielbereich für die Daten in meiner SPS. Soweit funktioniert das auch alles.
Um jetzt viele (auch strukturell verschiedene) Datensätze abholen zu können wollte ich einem DB erstellen mit einem array of (Struktur mit Registernummer, Anzahl und Zieladresse), und dann in einer Schleife mittels Umlaufvariable nacheinander alle Datensätze abzuholen. Aber wie lege ich einen Pointer auf den Zielbereich in einem DB ab? Das in meinem Beispiel angegebene 'DB_Any' funktioniert natürlich nicht. Oder wie löse ich das besser?

Gruß Frank

MB_1.pngMB_2.pngMB_3.png
 
Ich verzichte bei S7-1500 auf Pointer. Für genau sowas wie eben dynamisch Datenempfang und rumschieben nutze ich peek poke etc.
Bsp:

Code:
  #MBWrite: // Bereichsangabe prüfen
        // Wenn nicht beide 0 dann schreiben der Bereiche
        IF #Bereiche[#Bereichszeiger].ZielDB <> 0 THEN
            #ModbusCtrl.MB_DATA_LEN := #Bereiche[#Bereichszeiger].Lange;
            #ModbusCtrl.MB_DATA_ADDR := #Bereiche[#Bereichszeiger].Adresse;
            #ModbusCtrl.req := true;
            #ModbusCtrl.MB_Mode := #Bereiche[#Bereichszeiger].Modus;
            IF #ModbusCtrl.MB_Mode = 1 THEN // Wenn Schreiben dann DB in Buffer schreiben
                IF #ModbusCtrl.MB_DATA_ADDR >= 1 AND #ModbusCtrl.MB_DATA_ADDR <= 19999 THEN // Coils
                    FOR #i := 0 TO UINT_TO_DINT(#ModbusCtrl.MB_DATA_LEN / 8) DO
                        #Buffer[#i] := PEEK(area := 16#84,
                                            dbNumber := #ZielDB,
                                            byteOffset := #Bereiche[#Bereichszeiger].Zieloffset + #i);
                    END_FOR;
                ELSIF #ModbusCtrl.MB_DATA_ADDR >= 40001 AND #ModbusCtrl.MB_DATA_ADDR <= 49999 THEN // Register
                    FOR #i := 0 TO UINT_TO_DINT(#ModbusCtrl.MB_DATA_LEN * 2) - 1 DO
                        #Buffer[#i] := PEEK(area := 16#84,
                                            dbNumber := #ZielDB,
                                            byteOffset := #Bereiche[#Bereichszeiger].Zieloffset + #i);
                    END_FOR;
                ELSIF #ModbusCtrl.MB_DATA_ADDR >= 30001 AND #ModbusCtrl.MB_DATA_ADDR <= 39999 THEN // Register
                    FOR #i := 0 TO UINT_TO_DINT(#ModbusCtrl.MB_DATA_LEN * 2) - 1 DO
 
Um jetzt viele (auch strukturell verschiedene) Datensätze abholen zu können
Wenn die Datenstruktur nicht fest ist, wurde ich Serialize/Deserialize verwenden.
Dies kann man auch mit indizierte Parameter programmieren damit man dynamizieren kann.

Eigentlich glaube ich dass MB_CLIENT und SERIALIZE/DESERIALIZE nicht in die Schleife plaziert werden muss.
MB_CLIENT läuft über mehrere Aufrufe, also kann es nicht in eine Schleife sein.

Eher, eine Index steuern, nach jeden MB_CLIENT sequenz 'DONE', SERIALIZE/DESERIALIZE aufrufen und die Index inkrementieren.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
und dann in einer Schleife mittels Umlaufvariable nacheinander alle Datensätze abzuholen
In einer Schleife geht das nicht weil der MB_CLIENT für einen Auftrag mehrere Durchläufe braucht. Du musst warten, bis ein Leseauftrag fertig ist (DONE oder ERROR) und dann mit anderen Auftragsparametern den nächsten Auftrag starten.
Die Auftragsparameter kann man in einer Jobliste hinterlegen und vor Auftragsstart in die MB_CLIENT-Aufrufparameter umladen, oder bei nur wenigen verschiedenen Aufträgen kann man auch mehrere komplett parametrisierte MB_CLIENT-Aufrufe programmieren und immer nur einen davon aufrufen.
 
Irgendwie muss/will er aber in seiner Jobliste angeben, in welchem Zielbereich die jeweiligen Modbus-Werte abgelegt werden sollen.

Ja, genau darum geht es. Das mit mehreren 'MB_Client'-aufrufen sozusagen 'zu Fuß' erledigen würde ich schon hinbekommen. Dachte das mit meiner Idee vielleicht eleganter hinzubekommen

Um welche SPS geht es hier eigentlich? Und welche TIA Version?

CPU 1516-3 PN/DP und TIA V18

Eigentlich glaube ich dass MB_CLIENT und SERIALIZE/DESERIALIZE nicht in die Schleife plaziert werden muss.
MB_CLIENT läuft über mehrere Aufrufe, also kann es nicht in eine Schleife sein.

In einer Schleife geht das nicht weil der MB_CLIENT für einen Auftrag mehrere Durchläufe braucht. Du musst warten, bis ein Leseauftrag fertig ist (DONE oder ERROR) und dann mit anderen Auftragsparametern den nächsten Auftrag starten.

Ja, das ist soweit klar.

Die Auftragsparameter kann man in einer Jobliste hinterlegen und vor Auftragsstart in die MB_CLIENT-Aufrufparameter umladen,

Wie würde man so eine Jobliste denn deklarieren?

bei nur wenigen verschiedenen Aufträgen kann man auch mehrere komplett parametrisierte MB_CLIENT-Aufrufe programmieren und immer nur einen davon aufrufen.

So habe ich es bisher immer gemacht, davon will ich gerne weg, siehe oben.

Gruß Frank
 
So habe ich es bisher immer gemacht, davon will ich gerne weg, siehe oben.
Wie gesagt Mit peek poke kann man die Daten aus dem Buffer und in den Buffer am ModbusCtrl schreiben.
Code:
  #MBRead:
        IF #ModbusCtrl.DONE THEN
            #Bereiche[#Bereichszeiger].Fehltelegramme := 0;
            IF #ModbusCtrl.MB_Mode = 0 THEN // Wenn Lesen dann DB mit Buffer überschreiben
                #ModbusCtrl.MB_DATA_LEN := #Bereiche[#Bereichszeiger].Lange;
                IF #ModbusCtrl.MB_DATA_ADDR >= 1 AND #ModbusCtrl.MB_DATA_ADDR <= 19999 THEN // Coils
                    FOR #i := 0 TO UINT_TO_DINT(#ModbusCtrl.MB_DATA_LEN / 8) DO
                        POKE(area := 16#84,
                             dbNumber := #ZielDB,
                             byteOffset := #Bereiche[#Bereichszeiger].Zieloffset + #i,
                             value := #Buffer[#i]);
                    END_FOR;
                ELSIF #ModbusCtrl.MB_DATA_ADDR >= 40001 AND #ModbusCtrl.MB_DATA_ADDR <= 49999 THEN // Register
                    FOR #i := 0 TO UINT_TO_DINT(#ModbusCtrl.MB_DATA_LEN * 2) - 1 DO
                        POKE(area := 16#84,
                             dbNumber := #ZielDB,
                             byteOffset := #Bereiche[#Bereichszeiger].Zieloffset + #i,
                             value := #Buffer[#i]);
                    END_FOR;

done abwarten dann den Buffer lesen bzw. beschreiben dann den nächsten Bereich anvisieren.
 
Für genau deinen Anwendungsfall hab ich mir vor einiger Zeit einen ganzen Baustein gebaut, der aus einzelnen Modbus-Datenpunkten "Blöcke" erstellt welche dann sequentiell gelesen bzw. geschrieben werden. Damit kann ich mir sehr schnell die einzelnen DP anlegen und auch hunderte Punkte über verstreute Adressen vernünftig lesen.

Der Block:
1772792784163.png

Bilden kannst du die Blöcke zB. aus einzelnen Datenpunkten, in denen du deinen Speicherbereich und die Adresse hinterlegst.
Im oberen Modbus_Block siehst du einen DpIdx hinterlegt, in meinem Fall steht dort der Start-Arrayindex des ersten Datenpunkts, so werden die Daten auch direkt wieder passend zurückgeschrieben.
1772792795017.png

Beim Schreiben gehe ich sehr ähnlich vor.
1772792665905.png

Die MB_Client selbst schreiben zuerst in einen Buffer, den ich je nach benötigter Area umschalte (Mode 101 / 102 / 115 verlangen einen Byte-Buffer, während 103 / 104 / 116 Words wollen) .
1772793566354.png
1772793538663.png

Ich komme damit im ganzen Baustein sehr gut ohne Pointer / Peek / Poke aus, auf Kosten von 250 zusätzlichen Bytes im Arbeitsspeicher.
 

Anhänge

  • 1772793519640.png
    1772793519640.png
    17,9 KB · Aufrufe: 7
Zurück
Oben