PFC200 Modbus RTU Slave

jonashorn

Level-2
Beiträge
8
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Guten Abend zusammen,

ich muss auf einem 750-8212 einen Modbus RTU Slave (Codesys 3.5 ST) programmieren. Hierfür nutze ich den Funktionsbaustein FbMbSimpleServerSerial.
Habe ich in der Vergangenheit schon öfters ohne Probleme genutzt, diesmal habe ich nur Vorgaben was die Register angeht:

Registerbereich 1: 13313 bis 13378 (float32/uint16) einige zum beschreiben andere nur zum lesen
Registerbereich 2: 61491 bis 61588 (float32/uint16) nur lesen
Registerbereich 3: 5121 bis 5184 (float32/uint16) einige zum beschreiben andere nur zum lesen
Registerbereich 4: 5351 bis 5352 (float32) nur lesen

Hat jemand eine Idee wie ich dies umgesetz bekomme?

Erster Ansatz war: myHoldingRegisters : ARRAY[13313..13378, 61491..61588, 5121..5184, 5351..5352] OF WORD;
Da kommt jedoch Syntaxfehler C0032

Danke vorab :)
Gruß
Jonas
 
Moin, Du musst die vier Bereiche weiter unterteilen und in zusammenhängende Blöcke mit gleichem Function Code gruppieren und für jede Gruppe einen eigenen Job definieren mit eigener individueller Array-Definition. Dann brauchst Du einen Mechanismus, der zwischen den Jobs wechselt. Ich glaube in der Doku zum FB ist ein Beispiel dazu. Sonst schau auch in der Doku zu den UDP/TCP FBs, da ist das Prinzip identisch.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Moin, danke für deine Rückmeldung, die Doku habe ich schon mehrfach durchgelesen, aber in diese Richtung finde ich nichts. Versthe auch noch nicht ganz was du meinst. HAbe die ganzen Register mal mit RO und W zusammengeschrieben als PDF anbei. Ich weiß nicht wie ich hier was sotieren soll, da immer W und RO abwechselt kommt und so gar keine Blöcke gebildet werden können.

Nur mal zur Info um was es geht:
Fernwirtechnik eines nicht kleinen EVUs, die jetzt neu mit FWA anfangen. Warum die nicht auf das 101 oder 104 gehen ist mir ein Rätsel. Zudem normalerweise ja auch der Slave die Register vorgibt und nicht der Master ;) Aber was soll ich machen :(

Für Ideen bin ich offen, hab noch keine Lösung gefunden.

Danke.
 

Anhänge

Sorry, mein Fehler. Habe überlesen, dass Du Server bist. Vergiss was ich gesagt habe, das bezog sich alles auf einen Client.
Für den Server legst Du einfach alles auf Register mit Schreibrechten, da der Client die auch auch einfach nur lesen kann ohne sie zu beschreiben.
Bei den vier nicht zusammenhängenden Bereichen bleibt Dir nur diese als ein einziges großes Register zu betrachten. Die allokierten aber nicht verwendeten Lücken fressen unnötig Speicher, aber davon hast Du ausreichend (0..65535). Belastung für den Bus oder die CPU entsteht erst bei Zugriff durch den Client, was daher nicht relevant ist.

Anm.: Du kannst ein Union aus dem Array und einer eigenen Strucktur erstellen. Die Struktur enthält dann symbolische Variablennamen für dein Programm und das Array gibst Du dem Server-FB. Dabei muss Du in der Struktur auf die Reihenfolge achten und Lücken mit Platzhalter-Variablen/Arrays füllen.
 
Dann kann ich das nachvollziehen, habe ich mir schon gedacht einfach Holding Register von 5000 bis 62000 nehmen und fertig.
Gehe ich im neuen Jahr mal an.
Danke :)
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Also, ich hätte da auch einen Vorschlag dein Problem zu lösen ...
In der WagoAppPlcModbus gibt es neben den "Simple Modbus Slaves" auch die
"Advanced Modbus Slaves" genau für deine Problemstellung.

Du hast 4 Bereiche (die werden in der Lib "Areas" genannt).
2 Bereiche möchtest du nur lesen
und 2 Bereiche lesen und zumindest teilweise beschreiben.

// Lesen & Schreiben --> Holdingregister
aRegisterArea_1 : ARRAY[13313..13378] OF WORD; // Registerbereich 1: 13313 bis 13378 (float32/uint16) lesen / schreiben
aRegisterArea_3 : ARRAY[5121..5184] OF WORD; // Registerbereich 3: 5121 bis 5184 (float32/uint16) lesen / schreiben

// Nur Lesen --> Inputregister
aRegisterArea_2 : ARRAY[61491..61588] OF WORD; // Registerbereich 2: 61491 bis 61588 (float32/uint16) nur lesen
aRegisterArea_4 : ARRAY[5351..5352] OF WORD; // Registerbereich 4: 5351 bis 5352 (float32) nur lesen

nun nimmst du aus der Lib 2mal den "Fb2PortWordAreaKompositor" und fasst damit einmal die RegisterArea_1 und 3 zusammen (lesen / schreiben --> Holdingregister)
und einmal die RegisterArea_2 und 4 (nur lesen --> Inputregister)

Nun brauchst du noch ein Datenmodel. Hierzu nimmst du den "FbMbKompositorDataModel" und schaltest deine Zusammenfassung der Holdingregister und deine Zusammenfassung der Inputregister auf das Datenmodel.

Am Schluß brauchst du nur noch einen seriellen Modbusserver. Hierzu nimmst du den "FbMbServerSerial" und schaltest dein Datenmodel auf den Server. Fertig ...

Hört sich als Text jetzt wahnsinnig kompliziert an ... ist es aber nicht ....
In "FUP" sieht das schon viel einfacher aus. Hiermit kann man auf die Bereiche 2 und 4 nur lesend zugreifen und auf die Bereiche 1 und 3 lesend und schreibend.
Wenn man nun aus den Bereichen 1 und 3 manche Register für das Schreiben sperren möchte kann man das Ganze noch ergänzen ... aber hierzu später mehr in einem 2. Schritt .... Stichwort Listener ... (siehe auch die ausführliche Doku "WagoAppPlcModbus" im Anhang ... ist übrigens auch in der Bibliothek hinterlegt ... kann man nur leider seit 3.5.17 oder so nicht mehr öffnen).

1767178672857.png
 

Anhänge

Frohes neues noch allen :)
@senex: danke für deine ausführungen, hört sich in der Tat erst mal kompliziert an, werde ich mir aber mal genauer anschauen.
 
Hab das ganze jetzt mal in ST heruntergeschrieben und es funktioniert einwandfrei. Mit den FC muss ich jetzt noch mal klären, da sich die InputRegisters nur mit FC04 lesen lassen, könnte sein dass das EVU alles mit einem FC(03) lesen möchte. Falls das so sein sollte, kann ich die 4 Bereiche alle auf HoldingRegisters mit Fb4PortWordAreaKompositor setzen.

Eine Frage hatte ich vergessen, gibt es die Möglichkeit, von Slave-Seite aus die Verbindung zu überwachen? Sprich zu sehen ob es eine aktive Verbindung zu einem Master gibt? Hat jemand eine Idee?

@senex: Das Beispiel zu dem Listener wäre ja noch mal super :)

Gruß
Jonas
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Prinzipiell könntest Du Dir auch deinen eigenen Kompositor bauen. Mit mehr Eingängen.
Wenn die gleichen Register per FC16 geschrieben und mit FC4 gelesen werden sollen, kannst Du bei beiden Eingängen für Input und Holding Register die gleichen Arrays anlegen.

Zum Umwandeln der Werte ist das Stichwort Union schon gefallen.
 
Nun das Vorgehen mit dem Listener ... mit ein paar Erläuterungen ...

Also,
  • der Master stellt eine Anfrage (Request)
  • dieser Request landet beim Server
  • der Server leitet diesen Request weiter an das Datenmodell
  • im Datenmodell durchläuft dieser Request zuerst den Punkt "A" (kommt noch was das ist)
  • und dann vom Punkt "A" tiefer in das Datenmodell und wird dort bearbeitet (Daten lesen / schreiben ...)
  • danach generiert das Datenmodell die Antwort (Response)
  • diese Response gurchläuft nun zunächst den Punkt "B" und wird dann an den Server weitergeleitet
  • am Schluß sendet der Server die Response zurück zum Master

Mit einem Listener kann man sich nun im Punkt "A" und Punkt "B" einklinken und dort eigenen Code ausführen
und auch die weitere Bearbeitung beeinflussen.

Praktisch sieht das so aus ...
wir erstellen einen Funktionsbaustein der die Schnittstelle "I_DataAccessListener" implementiert.

Code:
FUNCTION_BLOCK FbMbListener IMPLEMENTS I_DataAccessListener

Die Implementierung bewirkt das der Baustein auch die 2 Methoden "beforeAccess" und "afterAccess" haben muss.
Je nach vorgehen werden die Rümpfe der Methoden automatisch angelegt. Sonst kann man auch in den FB mit der
rechten Maustaste clicken und "Schnittstellen implementieren" anwählen.
Zunächst muss auch nichts weiter codiert werden.

Nun können wir eine Instanz dieses FB anlegen.

Code:
myListener  :  FbMbListener;

Danach muss diese Instanz noch beim Datenmodell registriert werden damit das Datenmodell weiss wenn ein Request vom Master
kommt, dann gibt es dort einen "Zuhörer" der informiert werden möchte.
Hierzu wird die Methode "RegisterDataAccessListener(..)" vom Datenmodell aufgerufen.

Code:
myDataModel.RegisterDataAccessListener( myListener );

Diese Registrierung braucht nur einmalig bei der Initalisierung aufgerufen werden ... kann jedoch auch zyklisch aufgerufen werden.

Wenn nun ein Request vom Master kommt dann wird im Punkt "A" meine Methode "beforeAccess" aufgerufen.
Da z.Zt. die Methode leer ist passiert nichts weiter und der Rückgabewert der Methode ist 0.
Die Anfrage wird ganz normal bearbeitet.
Übrigens im Punkt "B" wird entsprechend die Methode "afterAccess" aufgerufen ... ist für uns aber uninteressant ... daher einfach leer lassen.

Die Methode "beforeAccess" bekommt beim Aufruf eine Reihe Parameter mit.

Code:
METHOD beforeAccess : USINT (* error code*)
VAR_INPUT
	(* who is asking*)
	sClientId	: STRING(20);
	(* unit id of the requested data model*)
	bUnitId	: BYTE;
	bFunctionCode	: BYTE;
	wReadAddress	: WORD;
	wReadQuantity	: WORD;
	wWriteAddress	: WORD;
	wWriteQuantity	: WORD;
END_VAR

Mit diesen Informationen können wir nun in der Methode eigenen Code plazieren und z.B. entscheiden
was mit der Anfrage passieren soll.

Mit dieser Entscheidung generieren wir einen Rückgabewert im Bereich 0..127.
Ist der Rückgabewert == 0 dann wird der Request ganz normal bearbeitet.
Ist der Rückgabewert <> 0 dann wird der Request nicht weiter bearbeitet und der Rückgabewert
wird als Fehlercode (Exception) an den Master gesendet.

Beispiel Addresse 13350 sperren für schreiben mit FC16

Code:
CASE bFunctionCode OF
	16#10 : // write multiple register
		
		// Addresse 13350 sperren für schreiben
		IF (wWriteAddress <= 13350) AND ((wWriteAddress + wWriteQuantity) > 13350) THEN
			beforeAccess := 2; // ILLEGAL_ADDRESS
		END_IF
			
	ELSE
		beforeAccess := 0; // alles OK
END_CASE

Hier kann nun natürlich auch ein Timer eingefügt werden der überprüft ob regelmäßig Anfragen vom Master kommen ...
quasi als "Verbindungsüberwachung" ...
 
Moin zusammen,

danke für eure Antworten.
@Tobsucht: Wertumwandlung per Union ist bekannt und wird schon immer so gemacht, das ist kein Thema.

@senex: Danke für deine Ausführung, dass schaue ich mir am Wochenende mal genau an.

Dann habe ich heute morgen bezüglich der FC's Rückmeldung vom EVU erhalten, was mir schon wieder Rätzel aufwirft, hier die ANtwort vom EVU:

"Ihre FWA Anlage ist ein Modbus Slave wir der Master. Wir holen da die Daten als Holding Register FC4 die sie Bereitstellen müssen.
Bisher war es bei allen auch so das wir auch auf FC4 Schreiben."


Geht das überhaupt was die da schreiben/verlangen?

Gruß
Jonas
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,

schau Dir mal den Ausgang oMbAccess des DataModel Baustein an. Da bekommst Du auch schon einige Informationen auf welche Daten zuletzt zugegriffen wurden und von welchem Client. Letzteres zumindest bei TCP, bei der seriellen Schnittstelle erhälst Du nur Info das darüber zugegriffen wurde.

Gruß
 
@jonashorn
"Ihre FWA Anlage ist ein Modbus Slave wir der Master. Wir holen da die Daten als Holding Register FC4 die sie Bereitstellen müssen.
Bisher war es bei allen auch so das wir auch auf FC4 Schreiben."


Ich denke das ist ein Missverständnis ... das EVU schaut aus einer anderen Perspektive auf den Modbus ...

In den Leitsystemen (oder auch in vielen Visualisierungssystemen) wird häufig ein Objekt mit einer Modbusadresse verbunden ...

Diese Adresse bekommt häufig ein Präfix in der Form
  • 0XXXX oder 1XXXX für Bitadressen z.B. discrete inputs / coils
  • 3XXXX für Input Register
  • 4XXXX für Holding Register

Der Programmierer im Leitsystem sieht nur diesen Präfix ... das ist aber nicht der MB-Funktionscode ...
Unter der Haube werden dann Zugriffe auf Adressen mit
Präfix 4 --> Holdingregister mit MB-Funktionscode 3 gelesen und mit MB-FC 16 geschrieben.

Entsprechend Adressen mit Präfix 3 --> Inputregister werden mit MB-FC 4 gelesen (schreiben ist hier nicht möglich)

Zudem gibt es hier häufig (nicht immer) zusätzlich einen Adressversatz von 1.
Also z.B. im Leitsystem wird Adresse 40001 eingetragen ... Präfix = 4 --> Holdingregister
Adresse im Leitsystem = 0001 --> im Modbus (bei der Übertragung) Adresse = 0
 
Guten Abend zusammen,

habe zwischenzeitlich vom EVU antwort erhalten, deine Vermutung senex wurde so bestätigt :)
Habe am Wochenende auch fleisig programmiert und in meinen eigenen Test klappt jetzt alles soweit. Werde für anfang Februar mal einen Testtermin mit dem EVU ausmachen.

Aktuell habe ich den Server in einem eigenen Tast Prio 14 mit 50ms laufen. Passt das so? Hat jemand Erfahrung, was hier das beste wäre?

Gruß
 
Zurück
Oben