Lap Trinh .NET DLLs in Vijeo Citect 2015
Lap Trinh .NET DLLs in Vijeo Citect 2015
NET DLLs in
Vijeo Citect 2015
Warwick Black
Validation Specialist
As of v7.50, we have added the following new CiCode functions to support ‘managed’ .NET Dlls:
This guide shows how to use Visual Studio 2013 Professional to create simple .NET DLLs that can be
hosted in SCADA Expert Vijeo Citect 2015.
First, we will create simple ‘Hello World’ style examples in the main .NET languages; C#, VB .NET
and C++ .NET. Then we will finish off by showing a more complex example (in C# only), which will
use CTAPI to push data from the DLL back into SCADA Expert Vijeo Citect 2015.
The full code for all examples will be contained in the appendices.
Constructor
A Constructor is essentially just an initialization routine. The routine runs automatically when an
instance of a Class is created, and its purpose is to set default values for that new instance.
With respect to SCADA Expert Vijeo Citect 2015, the Constructor is triggered when we call the
DllClassCreate Cicode function.
Methods
Methods are simply functions defined in the DLL that have been made accessible from an external
host. After instantiating a Class in SCADA Expert Vijeo Citect 2015, we can call methods via the
Cicode function DllClassCallMethod.
Properties
Properties are essentially Variables defined in the DLL that have been made accessible from an
external host. After instantiating a Class in SCADA Expert Vijeo Citect 2015, we can ‘get’ and ‘set’
the values of these properties via the DllClassGetProperty and DllClassSetProperty
functions.
1.2. C# - “Hello World” DLL
This is a simple example of a C# .Net DLL. The full code for this example can be found in Appendix A
– ‘Hello World’ (C#) – full code.
The first thing that we need to do is start Visual Studio and create a new project.
Select File > New > Project to open the following dialog:
Ensure you have selected Visual C# in the left pane, and Class Library on the right.
Then be sure to fill out the Name field for your project, ours is HelloWorldCsharp
Once you have done this, press OK.
The following code will be generated and ‘Class.cs’ is automatically opened:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HelloWorldCsharp
{
public class HelloWorld
{
}
}
Save your code by clicking on the “Save All” button, or ‘Ctrl + Shift + S’.
1.2.2. C# - Constructor
In C# the constructor must have the same name as the Class. In this case our Constructor needs to
be called ‘HelloWorld’.
The constructor for this DLL simply sets the value of an internal variable _helloWorldString to a
default value of “Hello World!”.
In C#, properties are defined by creating a public variable, with defined Get and Set routines.
For our example, we will create a new property called Message, and we will use its Get and Set
routines to read / write to our internal variable, _helloWorldString.
1.2.4. C# - Methods
In this example, our Method will simply display a ‘MessageBox’, containing the text value of our
property ‘Message’.
The ‘MessageBox’ functionality is not already included in our program by default, so we need to add
its library as a reference to our project.
To do this:
Right click on “References” in the ‘Solution Explorer’ and select “Add Reference…”
In the resulting popup, scroll down until you find System.Windows.Forms
Select the checkbox to the left of it
Press “OK”.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace HelloWorldCsharp
{
public class HelloWorld //Our class definition
{
…
public void DisplayMessage()
//Method ‘DisplayMessage’, called by Cicode: DLLClassCallMethod()
{
MessageBox.Show(Message);
}
}
}
1.2.5. C# - Building the DLL
The Output dialog should show that the Build was successful, and the location of the
generated DLL
Navigate to this DLL and copy it to the directory of your SCADA Expert Vijeo Citect
2015 test project.
1.3. VB .NET - “Hello World” DLL
This is a simple example of a VB .Net DLL. The full code for this example can be found in Appendix B
– ‘Hello World’ (VB .NET) – full code.
The first thing that we need to do is start Visual Studio and create a new project.
Select File > New > Project to open the following dialog:
Ensure you have selected Visual Basic in the left pane, and Class Library on the right.
Then be sure to fill out the Name field for your project, ours is HelloWorldvbnet
Once you have done this, press OK.
The following code will be generated and ‘Class1.vb’ is automatically opened:
Rename the class “Class1” to “HelloWorld”. This will leave you with the following code.
End Class
Save your code by clicking on the “Save All” button, or ‘Ctrl + Shift + S’.
A constructor is defined in VB .Net through a subroutine called ‘New’. It can take arguments, however
our example does not.
The constructor for this DLL simply sets the value of an internal variable _helloWorldString to a
default value of “Hello World!”.
In VB.NET, properties are via a ‘Property’ datatype, and making them Public, ReadOnly or
WriteOnly. You can define the Get and Set routines for them manually, although it may also be
handled automatically. (Note: for a ReadOnly property you would also omit a Set routine, and for a
WriteOnly property you would also omit a Get routine)
For our example, we will create a new public read / write property called Message, and we will use its
Get and Set routines to read / write to our internal variable, _helloWorldString.
…
Public Property Message As String 'Our Public read/write property 'rwMessage'
Get
Return CStr(_helloWorldString)
End Get
Set(value As String)
_helloWorldString = value
End Set
End Property
…
In VB .Net, methods can be Subroutines, or Functions. The difference between the two is that a
Subroutine cannot return a value, whereas a Function can. Either type can accept arguments.
The Output dialog should show that the Build was successful, and the location of the
generated DLL
Navigate to this DLL and copy it to the Directory of you SCADA Expert Vijeo Citect 2015
test project.
1.4. C++ .Net - Hello World
This example will lead you through creating a C++ Managed Class Library (DLL), this example is
written in Visual Studio Professional 2013.
C++ is traditionally unmanaged, however, it is also possible to write a C++ application based on CLR
(Common Language Runtime) of the .NET framework, which is managed.
The C++ code for this example can be found in Appendix C – ‘Hello World’ (C++ .NET) – full code.
Ensure you have selected Visual C++ and its sub-item CLR in the left pane
Select Class Library on the right pane.
Then be sure to fill out the Name field for your project, ours is HelloWorldCplusplus
Once you have done this, press OK.
The following code will be generated and HelloWorldCplusplus.h is automatically
opened:
// HelloWorldCplusplus.h
#pragma once
namespace HelloWorldCplusplus {
public ref class HelloWorld
{
// TODO: Add your methods for this class here.
};
}
Save your code by clicking on the “Save All” button, or ‘Ctrl + Shift + S’.
In C++ a constructor is declared as a Public function, with the same name as the class.
// HelloWorldCplusplus.h
#pragma once
namespace HelloWorldCplusplus {
public:
// Constructor - sets default values
HelloWorld();
…
};
}
In the Program File, add the following code to define the behavior of the HelloWorld function.
// HelloWorldCplusplus.cpp
#include "stdafx.h"
#include "HelloWorldCplusplus.h"
// Constructor implementation
HelloWorldCplusplus::HelloWorld::HelloWorld()
{
MessageString = "Hello World!"; //Defined default Value
}
namespace HelloWorldCplusplus {
In C++ .NET, methods are declared in the Header file as a public function, and defined in the Program
file.
// HelloWorldCplusplus.h
#pragma once
#using <System.Windows.Forms.dll>
using namespace System;
namespace HelloWorldCplusplus {
Add the definition for the Method, into the Program File:
// HelloWorldCplusplus.cpp
#include "stdafx.h"
#include "HelloWorldCplusplus.h"
int HelloWorldCplusplus::HelloWorld::DisplayMessage()
{
System::Windows::Forms::MessageBox::Show(MessageString);
return 1;
}
1.6.2. C++ .NET – Building the DLL
The Output dialog should show that the Build was successful, and the location of the
generated DLL:
Navigate to this DLL and copy it to the Directory of you SCADA Expert Vijeo Citect 2015
test project.
1.7. Interfacing with .NET DLLs using Cicode
The following CiCode functions are provided to interact with .NET DLLs. Their implementation
remains the same regardless of the Programming Language used to write the DLL.
Because we used the same Class Names, Property Names, and Method Names, we can use the
same Cicode below to interact with any of the 3 DLLs we created, C#, VB or C++.
The only line that needs to change is the line which refers to the DLL’s name:
dllHandle = DllClassCreate(PathToStr("[RUN]:MyDLL.dll"),"HelloWorld")
FUNCTION testDll()
STRING sResult;
INT iResult;
OBJECT dllHandle;
//Instantiate Class.
//This will trigger the Constructor in our DLL, which will set the
//default values, and return a handle.
dllHandle = DllClassCreate(PathToStr("[RUN]:MyDLL.dll"),"HelloWorld")
But how can we send unsolicited data to SCADA Expert Vijeo Citect from your DLL? For instance,
your DLL may be collecting data from an external source and you want to push data into SCADA
Expert Vijeo Citect whenever a new value is received.
The only way of passing data back to the host, unsolicited, is to use the CTAPI interface, which we
will demonstrate in this advanced example.
This DLL will generate random values, at a configurable time interval, for a configurable tag name,
and send unsolicited updates to the locally running Citect instance via CTAPI.
Properties:
target – The name of the Citect Tag you wish to update (read / write)
value – The current tag value (read-only)
minValue – The lower bound to the randomly generated data (read / write)
maxValue – The upper bound to the randomly generated data (read / write)
interval – The interval at which data is randomly generated (read / write)
Methods:
CTAPI client:
Includes code to communicate to Citect SCADA via CTAPI and push data into it.
1.8.2. Advanced C# example – Creating a new class library
The first thing that we need to do is start Visual Studio and create a new project.
Select File > New > Project to open the following dialog:
Ensure you have selected Visual C# in the left pane, and Class Library on the right.
Then be sure to fill out the Name field for your project, ours is DLL_CtapiEvents
Once you have done this, press OK.
The following code will be generated and ‘Class1.cs’ is automatically opened:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DLL_CtapiEvents
{
public class DLL_Ctapi
{
}
}
Save your code by clicking on the “Save All” button, or ‘Ctrl + Shift + S’.
The easiest way to add CTAPI support to a C# program is to add existing file (available here) called
CTAPI.cs, which already contains the mapping to the unmanaged CTAPI DLLs.
Add this file as a reference to your project, then add it’s namespace to your ‘class1.cs’ file.
Right click your project, select Add, then Existing Item, then navigate to your
downloaded copy of CTAPI.cs.
namespace DLL_CtapiEvents
{
public class DLL_Ctapi
{
}
}
In C# the constructor must have the same name as the Class. In this case our Constructor needs to
be called Dll_Ctapi.
We have defined private variables outside the constructor, and in the constructor we give these
variables some default values.
using System.Timers;
…
public class DLL_Ctapi
{
static Timer _timer; // From System.Timers
private int _timerInterval;
private string _target;
private double _value;
private double _minValue;
private double _maxValue;
The public methods can be defined as show below, along with some internally called private
functions:
//Non-default libraries:
using Citect.Util; //Our CTAPI class, defined in CTAPI.cs
using System.Timers; //For our timer task
…
public int startGenerating()
{
_timer = new Timer(interval); //sets up new timer, at our interval rate.
_timer.AutoReset = true; //So it triggers everytime, not just once.
_timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed)//Event handler
_timer.Enabled = true; // Enable Timer
return 0;
}
The following Cicode shows how to change the properties from their default values, start, stop and
update the interval at which the random data is generated.
Note: C# is Case Sensitive, calling methods without the exact syntax will fail.
FUNCTION MyComplexDll()
INT iResult;
hComplexDLL = DllClassCreate(PathToStr("[RUN]:DLL_CtapiEvents.dll"),"DLL_Ctapi")
IF DllClassIsValid(hComplexDLL) THEN
iResult = DllClassSetProperty(hComplexDLL,"interval","10000");
ErrLog("setInterval: " + IntToStr(iResult));
iResult = DllClassSetProperty(hComplexDLL,"target","MyTagOne");
ErrLog("setTarget: " + IntToStr(iResult));
iResult = DllClassSetProperty(hComplexDLL,"minValue","10");
ErrLog("setMinValue: " + IntToStr(iResult));
iResult = DllClassSetProperty(hComplexDLL,"maxValue","1100");
ErrLog("setMaxValue: " + IntToStr(iResult));
currentInterval = DllClassGetProperty(hComplexDLL,"interval");
currentTarget = DllClassGetProperty(hComplexDLL,"target");
currentMin = DllClassGetProperty(hComplexDLL,"minValue");
currentMax = DllClassGetProperty(hComplexDLL,"maxValue");
currentValue = DllClassGetProperty(hComplexDLL,"value");
FUNCTION stopComplexDll()
INT iResult;
iResult = DllClassCallMethod(hComplexDLL,"stopGenerating");
ErrLog("stopGenerating: " + IntToStr(iResult));
iResult = DllClassDispose(hComplexDLL);
ErrLog("Dispose: " + IntToStr(iResult));
END
FUNCTION updateComplexDll1()
INT iResult;
iResult = DllClassSetProperty(hComplexDLL,"interval","1000");
ErrLog("setInterval: " + IntToStr(iResult));
END
FUNCTION updateComplexDll5()
INT iResult;
iResult = DllClassSetProperty(hComplexDLL,"interval","5000");
ErrLog("setInterval: " + IntToStr(iResult));
END
1.9. Appendix A – ‘Hello World’ (C#) – full code
//A Simple Class library that will generate random values at a defined interval, and
update a defined tag via CTAPI.
// Author: Warwick Black 2015.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//Non-default libraries:
using Citect.Util; //Our CTAPI class, defined in CTAPI.cs
using System.Timers; //For our timer task
namespace DLL_CtapiEvents
{
public class DLL_Ctapi
{
static Timer _timer; // From System.Timers
MsgBox(sMessage)
Return sMessage
End Function
End Class
1.11. Appendix C – ‘Hello World’ (C++ .NET) – full code
// HelloWorldCplusplus.h
#pragma once
#using <System.Windows.Forms.dll>
using namespace System;
namespace HelloWorldCplusplus {
public:
// Constructor - sets default values
HelloWorld();
// Method
int DisplayMessage();
// Property
property System::String^ Message
{
System::String^ get() {
return MessageString;
}
// HelloWorldCplusplus.cpp
#include "stdafx.h"
#include "HelloWorldCplusplus.h"
// Constructor implementaion
HelloWorldCplusplus::HelloWorld::HelloWorld()
{
MessageString = "Hello World!"; //Defined default Value
}
int HelloWorldCplusplus::HelloWorld::DisplayMessage()
{
System::Windows::Forms::MessageBox::Show(MessageString);
return 1;
}
1.12. Appendix D – Advanced C# Example – Full code
//A Simple Class library that will generate random values at a defined
interval, and update a defined tag via CTAPI.
// Author: Warwick Black 2015.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//Non-default libraries:
using Citect.Util; //Our CTAPI class, defined in CTAPI.cs
using System.Timers; //For our timer task
namespace DLL_CtapiEvents
{
public class DLL_Ctapi
{
static Timer _timer; // From System.Timers