TcoData
TcOpen Framework contains a set of libraries that provide a simple yet powerful data exchange between PLC and an arbitrary data repository. TcoData implements a series of repository
operations known as CRUD
.
Benefits
The main benefit of this solution is data scalability; once the repository is set up, any modification of the data structure(s) will result in an automatic update of mapping objects. And therefore, there is no need for additional coding and configuration.
How it works
The basic PLC block is TcoDataExchange, which has its .NET counterpart (or .NET twin) that handles complex repository operations using a modified TcoRemoteTask, which is a form of RPC (Remote Procedure Call), that allows you to execute the code from the PLC in a remote .NET application.
Implemented repositories
The TcoData uses a predefined interface IRepository that allows for the unlimited implementation of different kinds of repositories.
At this point, TcOpen supports these repositories directly:
Getting started
For the data exchange to work, we will need to create our block extending the TcoDataExchange
block. We can call it MyDataExchanger
.
FUNCTION_BLOCK MyDataExchanger EXTENDS TcoData.TcoDataExchange
We will also need to add _data
variable to our block. There is no specific reason for the variable to be called _data
; it is just convention; it is, however, important as this _data
variable is the one that will contain the data that we want to exchange between PLC and the repository.
FUNCTION_BLOCK MyDataExchanger EXTENDS TcoData.TcoDataExchange
VAR
_data : MyDataForExchange;
END_VAR
The _data
must be of a structure that extends TcoEntity
. So let's just create STRUCT
that will have some variables.
TYPE
MyDataForExchange EXTENDS TcoData.TcoEntity :
STRUCT
sampleData : REAL;
someInteger : INT;
someString : STRING;
END_STRUCT
END_TYPE
As mentioned earlier, we use remote calls
to execute the CRUD
operations. These calls are a variant of TcoTask which can operate asynchronously, and we will need to call it cyclically. To ensure that, we need to call SUPER^
in the implementation part of the MyDataExchanger
block.
// Implementation of body of the FB
SUPER^();
We will now need to create an instance of MyDataExchanger
in some TcoContext, and call _myDataExchanger
in the Main
method of the context. Just to remind ourselfes all logic in TcOpen framework must be placed in the call tree of a Main method of a context.
FUNCTION_BLOCK MyContext EXTENDS TcoCore.TcoContext
VAR
_myDataExchanger : MyDataExchanger(THIS^);
END_VAR
//-------------------------------------------------
//-------------------------------------------------
METHOD Main()
//-------------------------------------------------
_myDataExchanger();
And we will also need to instantiate the context in a PROGRAM and call the Run
method.
PROGRAM MAIN
VAR
_myContext : MyContext;
END_VAR
//-------------------------------------------------
//-------------------------------------------------
_myContext.Run();
At this point, we have everything ready in the PLC.
We will now need to tell the _myDataExchanger
what repository we will use. First, we will work with data is stored in files in Json format.
Let's create a configuration for the repository:
var storageDir = "C:\MYPLCREPOZIRORY";
var repository = new JsonRepository(new JsonRepositorySettings<PlainMyDataForExchange>(storageDir));
Then we will need to associate the repository with the PLC object and initialize the data exchange operations.
// This will bind the TcoDataExchange object to a repository
Plc.MAIN.sandbox.DataManager.InitializeRepository(repository);
// This will initialize the remote tasks for CRUD operations
Plc.MAIN.sandbox.DataManager.InitializeRemoteDataExchange();
Now we can freely shuffle the data between PLC and the local folder.
IF(_create) THEN
IF(_myDataExchanger.Create(_id).Done) THEN
_create := FALSE;
END_IF;
END_IF;
IF(_read) THEN
IF(_myDataExchanger.Read(_id).Done) THEN
_read := FALSE;
END_IF;
END_IF;
IF(_update) THEN
IF(_myDataExchanger.Update(_id).Done) THEN
_update := FALSE;
END_IF;
END_IF;
IF(_delete) THEN
IF(_myDataExchanger.Delete(_id).Done) THEN
_delete := FALSE;
END_IF;
END_IF;
Let's now change the repository type; what if we have too much data and need a more robust solution?
var repository = new MongoDBRepository(new MongoDbRepositorySettings<PlainMyDataForExchange>("mongodb://localhost:27017","MyDatabase","MyCollection"));
Remarks
If you have more than one .NET program in your automation application (e.g., more HMIs), make sure you do not initialize the data exchange in multiple places.