Geteilter Speicher zwischen TcCOM-Instanzen

In machen Anwendungen kann es vorteilhaft sein, dass sich TcCOM-Instanzen einen Speicherbereich teilen, sodass bestimmte Strukturen/Variablen einmal in einem Objekt definiert werden und alle anderen Objekte auf diese Speicherstelle referenzieren.

Um dies zu erreichen, können Sie in TwinCAT zwei unterschiedliche Wege verfolgen:

Im weiteren Verlauf dieses Kapitels werden beide Wege beschrieben.

Während Sie mit Data Pointern flexibler sind in der Verknüpfung der Daten, haben Sie bei der Nutzung von globalen Variablen den Vorteil, dass die Struktur/Variable nur einmal im Speicher vorhanden ist. Bei Data Pointern wird in jeder TcCOM-Instanz eine lokale Kopie der Daten gehalten.

Hinweis

Datenübertragung nicht Threadsafe

Nutzen Sie Data Pointer und Globale Variablen mit Vorsicht. Der Datenaustausch erfolgt nicht Threadsafe. Deshalb empfehlen wir dringend, alle beteiligten TcCOM-Instanzen im selben Task-Kontext zu betreiben.

Verknüpfen von TcCOM-Instanzen in TwinCAT mit Data Pointern

Im Folgenden wird beschrieben, wie Sie gezielt per Data Pointer einen gemeinsamen Speicherbereich zwischen TcCOM-Instanzen nutzen können.

Wie im Bereich Konfiguration des Datenzugriffs auf Daten eines TcCOM-Objekts beschrieben, können Sie DataAreas per DataPointer erreichbar machen. Per DataPointer erreichbar sind Input Destination DataArea, Output Source DataArea sowie Standard DataArea. Sie können prinzipiell jede Variablen-Gruppe, Modell-Parameter, DWork, BlockIO usw. als Standard DataArea anlegen und damit als Data Pointer erreichbar machen.

Zielstellung

Im Folgenden wird gezeigt, wie Sie gezielt einen Teilbereich von Variablen per DataPointer zugänglich machen und nicht gleich den ganzen Variablen-Gruppen-Bereich. Das Grundkonzept basiert auf der Nutzung von Data Store Memory Blöcken in Simulink®.

Modell mit DataStore

In einem Modell „DataStoreObject“ wird ein Data Store Memory Block angelegt. In diesem Beispiel mit nur einer einzigen Variable vom Typ double. Diese Variable wird mit einem Data Store Read ausgelesen, mit einem Input multipliziert und auf einen Ausgang gegeben.

Geteilter Speicher zwischen TcCOM-Instanzen 1:

Unter TC TcCom Interface wird die Option DataStore: Data Access von None auf Standard DataArea geändert und das Simulink®-Modell in ein TcCOM übersetzt. Wenn das Objekt in TwinCAT instanziiert wird, sehen Sie die folgende Darstellung:

Geteilter Speicher zwischen TcCOM-Instanzen 2:

Die DataStore DataArea wird nun im Prozessabbild angezeigt und enthält eine Variable vom Typ LREAL, auf welche per DataPointer zugegriffen werden kann.

Modell mit DataPointer

In einem zweiten Simulink®-Modell wird durch eine Clock ein double-Wert erzeugt und dieser auf den TC Module Output gegeben.

Geteilter Speicher zwischen TcCOM-Instanzen 3:

In der Konfiguration des TC Module Output wird der connection type auf DataPointer sowie der Datentyp auf double gestellt.

Geteilter Speicher zwischen TcCOM-Instanzen 4:

Wenn dieses Modell in ein TcCOM-Objekt übersetzt wird, sehen Sie in TwinCAT die folgende Darstellung:

Geteilter Speicher zwischen TcCOM-Instanzen 5:

Unter Data Pointer wird nun der Name des TC Module Output „PtrToMyDataStore“ vom Typ LREAL angezeigt. Diese kann nun mit Doppelklick auf die DataStoreObject_DW_MyDataStore verknüpft werden.

Beide TcCOM-Instanzen sollten in derselben Task aufgerufen werden, da die Kommunikation nicht Threadsafe ist.

Geteilter Speicher zwischen TcCOM-Instanzen 6:

Wenn unterschiedliche Tasks verwendet werden, müssen Sie als Anwender dafür Sorge tragen, dass Daten konsistent sind und zu passenden Zeitpunkten gelesen oder geschrieben werden.

Das Verhalten des hier aufgeführten Beispiels wird in untenstehender Grafik gezeigt.
Wird der Eingang In1 des DataStore Modells manuell auf 1 gesetzt und der Ausgang Out1 über das TwinCAT Scope beobachtet, sieht man eine ansteigende Gerade. Das bedeutet, dass per Pointer der ansteigende Wert aus der Clock in den DataStore des DataStoreObject-TcCOM geschrieben wird.

Geteilter Speicher zwischen TcCOM-Instanzen 7:
Geteilter Speicher zwischen TcCOM-Instanzen 8:

Lesender Zugriff per TC Module Input

Lesender Zugriff auf eine DataArea kann per DataPointer über den TC Module Input realisiert werden.

Geteilter Speicher zwischen TcCOM-Instanzen 9:

Lokale Datenkopie in jedem Modul

Der Simulink CoderTM erzeugt den C/C++ Code in der Art, dass für die TC Module Input und TC Module Output lokale Variablen angelegt werden und somit jede Modulinstanz eine lokale Kopie der Daten enthält. Entsprechend erhöht sich der Speicherbedarf des Projekts mit jeder Instanz.

Nutzen von globalen Variablen (Exported Global/Imported Extern)

Im Folgenden wird beschrieben, wie Sie in Simulink® über die Storage Classes eine globale Variable im TcCOM anlegen können und diese in anderen TcCOM referenzieren.

Zielstellung

Es wird ein Simulink® Parameter GlobalParam angelegt, welcher eine Struktur von 3 Elementen enthält. Ziel ist es, diese Struktur über ein TcCOM-Objekt zu definieren und weitere TcCOM-Objekte zu instanziieren, die auf diese Struktur über geteilten Speicherbereich zugreifen, sodass Änderungen im definierenden TcCOM direkt in allen verbundenen TcCOM-Instanzen ankommen.

Modellierung in Simulink®

Es werden zwei Simulink®-Modelle erstellt:

  1. Modell „DataDefiner“
  2. Modell „DataUser“

Des Weiteren wird ein Simulink®-Bus definiert und als Simulink®-Paramater namens GlobalParam angelegt. Das Modell DataDefiner bestimmt am Ende den Inhalt von GlobalParam, während die Modelle DataUser den Inhalt nur lesen.

Geteilter Speicher zwischen TcCOM-Instanzen 10:

Beachten Sie, dass die Definition der Storage Class von GlobalParam im Folgenden für den DataDefiner und DataUser unterschiedlich gesetzt werden. Für den DataDefiner wird als Storage Class Exported Global gesetzt, während für den DataDefiner die Storage Class auf Imported Extern gestellt wird.

Das folgende Skript bündelt die beiden beschriebenen Simulink®-Modelle in einem Treiber, vgl. Bündelung mehrerer Modelle in einem TwinCAT-Treiber.

thisDir = fileparts(mfilename("fullpath"));

% model names
mdlNames = ["DataDefiner","DataUser"];
codeDirectories = fullfile(thisDir,strcat(mdlNames,'_tcgrt'));
isParamDefiner = [true,false];

% instantiate project configuration
projCfg = TwinCAT.ModuleGenerator.ProjectExportConfig('FullPath',fullfile(thisDir,'TcCppProj','DataSharingModules.vcxproj'));

regenerate = false;
for i=1:length(mdlNames)
   % generate code for the models (only if the code directory doesn't exist) -> remove the directory to rebuild specific models
   if regenerate || ~isfolder(codeDirectories(i))

      % load the model and apply basic settings
      mdl = load_system(mdlNames(i));
      set_param(mdl,'SystemTargetFile','TwinCatGrt.tlc');
      set_param(mdl,'CodeInterfacePackaging','C++ class');
      set_param(mdl,'TcCom_TcComWrapperFb','off');
      set_param(mdl,'TcProject_Generate','off');
      set_param(mdl,'SolverType','Fixed-step');

      % adapt the storage class of the shared parameter structure
      if isParamDefiner(i)
         GlobalParam.CoderInfo.StorageClass = "ExportedGlobal";
      else
         GlobalParam.CoderInfo.StorageClass = "ImportedExtern";
      end

      % generate code and close the model
      slbuild(mdl);
      close_system(mdl,0);
   end

   % add the class export configuration to the "global" project export configuration
   mdlProjCfg = TwinCAT.ModuleGenerator.ProjectExportConfig.Load(codeDirectories(i));
   clsCfg = mdlProjCfg.ClassExportCfg{1};
   projCfg.AddClassExportConfig(clsCfg)
end

% generate and build the project
TwinCAT.ModuleGenerator.ProjectExporter(projCfg);

Konfiguration in der TwinCAT XAE

Erstellen Sie in der TwinCAT XAE eine Instanz des DataDefiners (mehr sind nicht erlaubt!). Hingegen können Sie beliebig viele DataUser-Instanzen erzeugen.
1. Stellen Sie unter Parameter (Init) > Parameter GlobalParam_sharing für den DataDefiner auf „Define“.
2. Stellen Sie unter Parameter (Init) > Parameter GlobalParam_sharing für alle DataUser auf „Inherit“.
3. Erstellen Sie eine Task.
4. Weisen Sie diese Task allen erstellten Instanzen zu.
Geteilter Speicher zwischen TcCOM-Instanzen 11:
5. Aktivieren Sie das Projekt.
Verändern Sie, bspw. über das Block Diagram, die Werte von GlobalParam im DataDefiner und beobachten Sie die direkte Auswirkung auf die DataUser-Instanzen.