Austausch von Daten über synchronisierte Puffer

Bei dieser Synchronisationsmöglichkeit handelt es sich um eine anwendereigene Implementierung. Als solche wird nicht auf spezielle bereitgestellte Funktionsbausteine einer Bibliothek zurückgegriffen. Die Komplexität des Programmcodes ist größer und hängt zudem von der gewählten Umsetzung ab.

Eine prinzipielle Bedingung für die Anwendbarkeit dieser Methode ist, dass nur von einer Task schreibend auf dieselben Daten zugegriffen werden soll.

Mittels zusätzlicher temporärer Datenpuffer wird der schreibende und der lesende Zugriff auf die Daten synchronisiert. Zur Synchronisation dieser Datenpuffer ist wiederum ein MUTEX-Verfahren notwendig. Gegenüber der alleinigen direkten Verwendung von TestAndSet() hat diese Methode jedoch den Vorteil, dass der Zugriff auf einen dieser zusätzlichen Datenpuffer immer gewährt wird und keine Alternativbehandlung notwendig ist.

Eine zweite Bedingung für die Anwendbarkeit dieser Methode ist, dass es sich bei den Daten nicht um Funktionsbausteininstanzen oder Zeiger jeglicher Art (POINTER TO, Interfacepointer/Interfacevariablen, Referenzen, ...) handeln darf. Es ist also hierbei nicht möglich, Methoden von ein und derselben Funktionsbausteininstanz aus zwei unterschiedlichen Taskkontexten aufzurufen.

Es dürfen nur Daten zum Datenaustausch definiert werden, welche im anderen Taskkontext benötigt werden, um sicherzustellen, dass die Datenmenge nicht unnötig groß ist. Die Implementierung führt Kopieraktionen aus, um die Daten zwischen den Puffern auszutauschen. Weil Kopieraktionen mit großen Datenblöcken viel Rechenzeit benötigen (Aufrufe der MEMCPY-Funktion), schränkt dies die mögliche Datenmenge meist auf deutlich weniger als 1 MB ein.

Die für diese Synchronisationsmöglichkeit notwendigen Methoden werden vom Anwender implementiert. Diese spezifische Implementierung kann unterschiedlich ausgeführt werden. Teilweise werden auch Begriffe wie 3-Wege-Puffer oder 3-Puffer-Prinzip für die Art der Implementierung verwendet.

Beispielprogramm: Synchronisation mittels Austausch von Daten über synchronisierte Puffer

Das Beispiel zeigt den Datenaustausch zwischen einer langsamen Task und einer schnellen Task innerhalb einer SPS-Instanz.

Exemplarisch für beliebige Datenblöcke (keine Funktionsbausteininstanzen oder Zeiger) werden zwei Strukturen ST_DataA und ST_DataB zur jeweils anderen Task übertragen. Aus dem Taskkontext 1 soll schreibend und aus dem Taskkontext 2 lesend auf die Daten „DataA“ zugegriffen werden. Zusätzlich soll aus dem Taskkontext 2 schreibend und aus dem Taskkontext 1 lesend auf die Daten „DataB“ zugegriffen werden. „DataA“ und „DataB“ werden für den Taskkontext 1 und Taskkontext 2 jeweils als Strukturinstanz definiert.

Zur Synchronisation dieser Datenpuffer gibt es im Beispiel den Funktionsbaustein FB_DataSync mit den jeweiligen Erweiterungen FB_MyDataASync und FB_MyDataBSync. Instanzen dieser Funktionsbausteine werden in einer globalen Variablenliste deklariert und im Programmablauf verwendet. Hier gilt weiterhin, dass nur ein Taskkontext schreibend – Methode Write() – und ein anderer lesend – Methode Read() – auf diese Funktionsbausteininstanzen zugreifen darf. Zur Sicherheit schlagen die Methoden bei Nichtbeachtung fehl (Rückgabewert FALSE).

Wie eingangs erwähnt ist die Implementierung komplexer als bei anderen SPS-Beispielen. In diesem Beispiel steckt die eigentliche Umsetzung des Datenaustausches im Funktionsbaustein FB_DataSync, der den Zugriff auf mehrere Puffer verwaltet. Wenn Sie die Funktionalität mit einer eigenen neuen Datenstruktur testen möchten, können Sie FB_DataSync unverändert lassen. Analog zu FB_MyDataASync definieren Sie sich einen neuen Funktionsbaustein, der von FB_DataSync ableitet. Weiterhin analog instanziieren Sie Ihre eigene Datenstruktur dort zweimal und fügen die Methoden Read()/Write() an, welche jeweils die Zuweisung einer Referenz Ihrer neuen Datenstruktur verlangen und intern die Methoden ReadData()/WriteData() der Basisklasse aufrufen.

Zur Umsetzung von FB_DataSync in diesem Beispiel sei gesagt, dass zwei interne Puffer zur temporären Datenablage verwendet werden. Der schreibende Taskkontext kopiert seine Daten in diese Puffer hinein und der lesende Taskkontext kopiert seine Daten aus ihnen heraus. Der korrekte Zugriff auf jeweils einen der zwei internen Puffer wird mit der Funktion TestAndSet() sichergestellt. Somit findet kein gleichzeitiger Zugriff auf dieselben Daten statt, obwohl außen im Applikationscode zeitgleich Daten geschrieben und gelesen werden können.

Download: TC3_PlcSample_MultiTaskSync_DataBuffer.zip