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" ...