Shared memory between TcCOM instances

In some applications it may be advantageous for TcCOM instances to share a memory area, so that certain structures/variables are defined once in an object and all other objects reference this memory location.

To achieve this, you can follow two different paths in TwinCAT:

In the remainder of this chapter, both paths will be described.

While with data pointers you are more flexible in linking the data, when using global variables you have the advantage that the structure/variable exists only once in memory. For data pointers, a local copy of the data is held in each TcCOM instance.

Notice

Data transmission not threadsafe

Use data pointers and global variables with caution. The data exchange is not threadsafe. Therefore, we strongly recommend to run all involved TcCOM instances in the same task context.

Linking TcCOM instances in TwinCAT with data pointers

The following describes how you can specifically use a shared memory area between TcCOM instances via data pointer.

As described in section Configuration of data access to data of a TcCOM object, you can make DataAreas accessible via DataPointer. Input Destination DataArea, Output Source DataArea and Standard DataArea are accessible via DataPointer. In principle, you can create any variable group, model parameter, DWork, BlockIO, etc. as a standard DataArea and thus make it accessible as Data Pointer.

Objective

In the following, we will show you how to make a specific subrange of variables accessible via DataPointer and not the entire variable group range at once. The basic concept is based on the use of data store memory blocks in Simulink®.

Model with DataStore

A Data Store Memory block is created in a model "DataStoreObject". In this sample with only one variable of type double. This variable is read with a Data Store Read, multiplied by an input and set to an output.

Shared memory between TcCOM instances 1:

At TC TcCom Interface, the option DataStore: Data Access is changed from None to Standard DataArea and the Simulink® model is compiled into a TcCOM. If the object is instantiated in TwinCAT, the following representation is obtained:

Shared memory between TcCOM instances 2:

The DataStore DataArea is now displayed in the process image and contains a variable of type LREAL, which can be accessed via DataPointer.

Model with DataPointer

In a second Simulink® model, a double value is generated by a clock and this is set to the TC Module Output.

Shared memory between TcCOM instances 3:

In the configuration of the TC Module Output, the connection type is set to DataPointer and the data type is set to double.

Shared memory between TcCOM instances 4:

If this model is compiled into a TcCOM object, the following representation is obtained in TwinCAT:

Shared memory between TcCOM instances 5:

The name of the TC Module Output "PtrToMyDataStore" of type LREAL is now displayed under Data Pointer. This can now be linked by double-clicking on the DataStoreObject_DW_MyDataStore.

Both TcCOM instances should be called in the same task because the communication is not threadsafe.

Shared memory between TcCOM instances 6:

If different tasks are used, the user must ensure that data is consistent and is read or written at appropriate times.

The behavior of the sample here is shown in the graphic below.
If the input In1 of the DataStore model is manually set to 1 and the output Out1 is observed via the TwinCAT Scope, an increasing straight line is seen. This means that the increasing value from the clock is written to the DataStore of the DataStoreObject-TcCOM via pointer.

Shared memory between TcCOM instances 7:
Shared memory between TcCOM instances 8:

Read access via TC Module Input

Read access to a DataArea can be realized via DataPointer using the TC Module Input.

Shared memory between TcCOM instances 9:

Local data copy in each module

The Simulink CoderTM generates the C/C++ code in such a way that local variables are created for the TC Module Input and TC Module Output and thus each module instance contains a local copy of the data. Accordingly, the memory requirements of the project increase with each instance.

Use of global variables (Exported Global/Imported External)

The following describes how to create a global variable in TcCOM using the Storage Classes in Simulink® and reference it in other TcCOM.

Objective

A Simulink® parameter GlobalParams is created, which contains a structure of 3 elements. The goal is to define this structure via a TcCOM object and to instantiate further TcCOM objects that access this structure via shared memory area, so that changes in the defining TcCOM arrive directly in all connected TcCOM instances.

Modeling in Simulink®

Two Simulink® models are created:

  1. "DataDefiner" model
  2. "DataUser" model

Furthermore, a Simulink® bus is defined and created as a Simulink® parameter named GlobalParam. The DataDefiner model determines the content of GlobalParam at the end, while the DataUser models only read the content.

Shared memory between TcCOM instances 10:

Note that the Storage Class definition of GlobalParam is set differently for the DataDefiner and DataUser below. For the DataDefiner, the Storage Class is set to Exported Global, while for the DataDefiner, the Storage Class is set to Imported Extern.

The following script bundles the two described Simulink® models into one driver, cf. Bundling of several models in one TwinCAT driver.

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);

Configuration in TwinCAT XAE

Create one instance of the DataDefiner in the TwinCAT XAE (more are not allowed!). On the other hand, you can create as many DataUser instances as you like.
1. Set Parameter (Init) > Parameter GlobalParam_sharing for the DataDefiner to "Define".
2. Set Parameter (Init) > Parameter GlobalParam_sharing for all DataUsers to "Inherit".
3. Create a task.
4. Assign this task to all created instances.
Shared memory between TcCOM instances 11:
5. Activate the project.
Change the values of GlobalParam in the DataDefiner, e.g. via the Block Diagram, and observe the direct effect on the DataUser instances.