You are on page 1of 34
Creating Custom .NET DLLs in Vijeo Citect 2015 July 2015 — Whitepaper Warwick Black Validation Specialist Make the most of your energy ee acter Contents 1. Creating Custom NET DLLs 4.1. Anatomy of a NET DLL or ‘Class Library’... 4.2. C#t-"Hello World” DLL 1.2.1. C#- Creating a new class library 122. C#-Constructor 1.23, C#t-Propertios 1.24, Cit- Methods. 4.25. C#-Building the DLL 4.3. VB.NET -‘Hello World” DLL 1.3.1. VB.NET - Creating a new class library.... 4.3.2, VB.NET - Constructor 4.3.3. VB.NET - Properties. 4.34. VB.NET- Methods 4.35. VB.NET - Building the DLL..... 1.4, C++ Net - Hello World 1.5. C++ NET - Creating a new class library 4.5.1. C++ NET - Constructor 1.6. C++ NET - Properties. 1.6.1. C++ .NET- Methods 4.6.2. C++ .NET — Building the DLL 4.7. Interfacing with NET DLLs using Cicode 4.7.4. Cicode Example 1.8. Advanced C# example - Completing the loop via CTAPI 1.8.1. Advanced C# example ~ Overview: 1.8.2. Advanced C# example — Creating a new class library. 1.8.3, Advanced Ci# example - Add Reference to CTAPI libraries .. 1.8.4. Advanced C# example — Constructor... 1.85. Advanced C# example ~ Properties. 1.8.6. Advanced C# example - Methods " aA 2 12 13 13 15, 15 16 AT 18 19 20 20 nA 24 22 23 A 25 26 187. 18.8. 19, 1.10. 4M 4.12. Advanced C# example — Building the DLL Advanced C# example - Cicode Appendix A ~ Hello World’ (C#) — full code... Appendix B ~ ‘Hello World’ (VB.NET) ~ full code. Appendix C — ‘Hello World! (C++ INET) full code Appendix D ~ Advanced C# Example — Full code 8 aT 28 3 on 32 33 1. Creating Custom .NET DLLs Prior to SCADA Expert Vijeo Citect 2015 we could only access ‘native’, or unmanaged, DLLs via the CiCode functions: * pio en, D11Call, D11Cal1Ex, DLICL As of v7.50, we have added the following new CiCode functions to support ‘managed’ NET Dils: This guide shows haw 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 wil 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 2045. The full code for all examples will be contained in the appendices. 1.4. Anatomy of a .NET DLL or ‘Class Library’ The basic anatomy of a NET DLL, or Class Library, is the same, regardless of the language used They consist of the following: 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 ‘eate Cicode funetion. Methods Methods are simply functions defined in the DLL that have been made accessible from an extemal host. After instantiating a Class in SCADA Expert Vijeo Citect 2015, we can call methods via the Cicode function »: 1 Properties Properties are essentially Vatiables 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 511 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, ello World! (C#) — full code. 1.2.1. C#- 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: Pe fone =e = aa ited ne —B © SE] amctop cnet 2 te: vaste [EE weconame Apion astro ecu oer FEF verspsocn Bi cesar EAL neteronmee seo ET corer ton Ny casumey 9 ET weve seinen oe © som een Ol + 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 + Onoe you have done this, press OK. ‘+ The following code will be generated and ‘Class.cs’ is automatically opened: 94) Helioworldcsharp- Microsoft Visual Studio DX [oictanncnea __P) = x IRE BOT YEW PROKT RIND DEBUG TAM TOOLS TEST AQALYZE NOOW KEP sine BI OO BL DS hsun-C~ Ramey ME ; feeaon pone ax 2 erence =| "erenaisGapn “0G b-e0aBoe F |) jesing spree cttectin.canrtcs | Sew ote oe alt £ | ine Spaten tins Soin Veto 0 poe) J, |esine syoten-toces | 4B Neteenstare SEN SRS aint > B ropetes > sa netowces Bape terse ec Be otic chavs ctosst : c wos > + Change the line public class Classt to public class HelloWorld + Your code will now look like this: using system; using System-Collections.Generics using System. Ling; using System. Text; using System. Threading. Tasks; namespace Helloworldcsharp public class Hellowerld { } ‘+ 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!”. public class Helloworld { private string _hellovorldstring; //rnternal veriable public Hellonorld() //our constructor -helloworldstring Hello World! 1.2.3. C#- Properties In C#t, 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. The code for this is as follows: public class Hellovorld { public string Message //Our Property ‘Hessage? { get //Called By Cicode: DLLCLassGetProperty(){ return _helloWorldString; 3 set //Called By Cicode: DLLCLassSetProperty(){ _helloworldString = values ,? } 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. @ ee ame oes Search Solution Explorer (Ctl+) a8 2] Solution ‘HelloWoridCsharp’ (1 project) 4 HelloWorldCsharp > & Properties bc Classi Ada Reference.. ‘Add Service Reference. (Manage NuGet Packages... Scope to This 1H) _ New Solution Explorer View Solution Exolorer ‘+ _ Inthe resulting popup, scroll down until you find System.Windows.Forms + Select the checkbox to the left of it + Press “OK. Targeting NET Frameworks Search Asembles (Cri¢6) P= Name SjstemWedSewees Siterindows | SjstemWindowsConzoisfibbon Sistem indowsForms Sistem Windows FormsDataVisualzation Sten Windows FormsDstaisiaizatonDes., SjstemWindowsinpaManpulators “40303191020 bt by Sytem Windows Presetation FRASRTMGOR SjstemMonfowActtes Sjstem orion oriponensModel + Now we have added a reference to System.Windows.Forms to our project ‘+ We still need to reference the new namespace in our code using the line © using System.Windows.Forms + We can now reference the MessageBox.Show() function in the definition of our Method DisplayMessage using System using system.collections Generics using System.Lings using system. Text; using System. Threading. Tasks; using System.Windows.Forms; namespace HelloWorldCsharp { public class Helloworld //our class definition { public void Displaytessage() {/nethod “Displaynessage*, called by Cicode: DLLClasscallmethod() ( NessageBox. Show‘ Message); ) y 1.2.5. C#- Building the DLL + Ensure you have selected the ‘Release’ build profile: 4) HelloWorldCsharp - Microsoft Visual Studio Q wa FILE EDIT VIEW PROJECT BUILD DEBUG TEAM TOOLS TEST ANALYZE ©-0 B-Giw O-S~ DP star~ o ~ [Release RJ Anycru ¢ Debug $ cassie # x no &, Blvetowondcsnap =| AsHetowonacsnarpcles-| Co rguration Manager. 3 7 Gusing Svstem: - * Select Build > Build Solution 4 HelloWorldCsharp - Microsoft Visual Studio D0 auick Laun FILE EDIT VIEW PROJECT BUILD DEBUG TEAM TOOLS TEST ANALYZE WIND! o- {8 - le a | oH Build Solution Ctri+Shift+B | ¢ itd Solution : Rebuild Solutio 3 Bretowondcsnap =| # “isan Solution | Wf ley Boring Syrteor Run Code Analysis on Solution alert ‘+ The Output dialog should show that the Build was successful, and the location of the generated DLL ‘Sovaumeton 946 : =a + 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 ello World (VB NET} il code, 1.3.1. VB.NET - 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: Sec naates tenons Ge) B= + pe nase ‘a ‘css roma) tok gp yaw one + 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 + Onoe you have done this, press OK. + The following code will be generated and ‘Class1vb’ is automatically opened! 0G Helloworldvinet - Microsoft visual studio DB Tio [schon cree Poo x FE EDT VIEW PROMECT BUILD DEBUG TEAM TOOLS TEST ANALYZE WINDOW HELP. sinin e-o Btn? P stat C Relene = oyu «| Me te RS Fcissivor = >| Solution Epler vax e @ dv-20a—a oF ech Sekion Eire (a ai F) sotaion Helowonsnet( proecd 2 Hetotorévonet & ray Prjec > we cast + ‘+ Rename the class “Class1” to "HelloWorid’. This will leave you with the following code. Public Class Hellowerld End Class + Save your code by clicking on the “Save All’ button, or ‘Ctrl + Shift + S" 1.3.2. VB.NET - Constructor A constructor is defined in VB .Net through a subroutine called ‘New. It can take arguments, however our example does not. Public Class Helloworld Private helloworldstring as string Public Sub New() ‘our Class Constructor “helloviorldstring = “Hello world!” end Sub end Class. The constructor for this DLL simply sets the value of an internal variable _helloWorldString to a default value of “Hello World!”. 1.3.3. VB.NET - Properties 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 wil create a new public read / write property called Message, and we will uso its Get and Set routines to read / write to our internal variable, _helloWorldString, Public Property Message As string ‘our public read/write property ‘raMiessage’ et Return cstr(_helloworldstring) End Get Set(value as string) _helloworldstring = value End Set End Property 1.3.4. VB.NET - Methods 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. Public sub Displaymessage() ‘our Public method ‘Displaymessage” Hsgaox(ruttessage) end sub Public Function DisplaythisMessage(Byval sMessage As string) “our Public Methed takes an argument, and returns 2 value Nsgaox(stlessage) Return sMessage End Function 1.3.5. VB.NET - Building the DLL + Ensure you have selected the ‘Release’ build profile: 04 Helloworldvbnet - Microsoft Visual Studio B10 |< FILE EDIT VIEW PROJECT BUILD DEBUG TEAM TOOLS TEST ANALYZE ©-0O B-S O ~~ Dd stat~ o ~ [Release Many cru Debug Etim jn. A Hetowond | Bil (Declarations) configuration Manager. [Public Class HeileNeeld nideg s9A95 + Select Build > Build Solution D€HelloWorldvbnet - Microsoft Visual Studio W109 auick Lau FILE EDIT VIEW PROJECT BUILD DEBUG TEAM TOOLS TEST ANALYZE WINE ©- 0 B~S bauH | ch Build solution Ctreshttes ¢ Cette sottcn —— Clean sotton B Spublic Clase WEIS Run Code Analysis on Solution Aer + The Output dialog should show that the Build was successful, and the location of the generated DLL nm ‘Sor aspa ome Hs = ‘Rilsoranee crs * Saimin Sle 5 ro}e Mestre eli neraene in tlese tee ie ‘+ 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, itis 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, 1.5. C++ .NET - 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: ‘Seach ne emp 8) Te: Vico = oa Riese sores = Craven ocapa emer de 201s ~ (ee) (oe [Tome] + Ensure you have selected Visual C+ and ils sub-item CLR in the left pane + Select Class Library on the right pane. + Then be sure to fil out the Name field for your project, ours is HelleWorldCplusplus ‘+ Once you have done this, press OK. + The following code will be generated and HelloWorldCplusplus.h is automatically opened 9@ HelloWorldCplusplus - Microsoft Visual Studio BAAD cu tuner cutee Pe ox BIE EDT MEW. EROECT UID DEBUG TEAM TOOLS TEST ANALYZE WINDOW HE? son BI =O) B= AHD- 6 ~ m LectWecons Deoigger= O - heen = we = | Te £ acne — IT Soktion oer vax g 1 [Sieononscaupas ~[ everest “lo oa|b-eamlo +E BN steerer # [csscer———— /—_ anne gl [48 Henowonecptasptas BO sctnsnonepece Setens 6 Enteral Dependencies 4 i Hanes fee amspace Helles ( bo 1 Pea ecouen 2 reswcen spine ete ast oe 4 4 Reoce Fes 1 nico BB wave teenbietocoe > % Hatomoracotscc0 jos . B reoshave + Allour changes will be in either HelloWorldCplusplus.h (Header file) or HelloWorldCplusplus.cpp (Program File) + The Header file is for declarations, and the Program file is for the Program code. + Rename the class Class1 to HelloWorld. This will leave you with the following code’ 1 wel lovorlécplusplus.-h pragma once using namespace System; namespace Helloworldcplusplus { public ref class Helloworld { h // 1000: Add your methods for this class here. ‘+ Save your code by clicking on the "Save All’ button, or Ctrl + Shift +S’ 1.5.1. C+#.NET- Constructor In C++ a constructor is declared as a Public function, with the same name as the class. This constructor sets the default value of a private variable. ‘+ Inthe Header file, add the following code to define the private variable MessageString, and declare the public function HelloWorld: 1 Hel lovorlécplusplus.-h ‘pragma once namespace HelloWorldcplusplus { public ref class Helloworld { private: System: istring” Messagestrings //internal variable public: J/ constructor - sets default values Helloworld() ; b ‘+ Inthe Program File, add the following code to define the behavior of the HelloWorld function. 1 HelloWor Lécplusplus..cpp include "stdafx.h” include "HelloWiorldcpLusplus.h" J] constructor implementation Hellowordcplusplus: :Helloworld: :Helloworld() « y NessageString = "Hello world!"; //oefined default value 1.6. C++ .NET - Properties In C++ .NET, properties are defined by creating Public Property data types in the Header file, with defined Get and Set methods. 7 Hellokor1dcplusplus.h ‘pragma once Using nanespace System; namespace Helloworldcplusplus { public ref class HelloWorld t : private: syston: public: 11 Property property system: :String* Message strings Messagestrings //Internal Variable system: :string® get() { return Messagestring; } void set (system: string” value) { Nessagestring = value; 1.6.1. | C++.NET-Methods In C++ NET, methods are deciared in the Header file as a public function, and defined in the Program file, © Inthe header file, add the function declaration: // Wel lokor1écplusplus.h pragma once fusing using nanespace system; namespace HelloWorldcplusplus { public ref class Helloworld { public: int Displaynessage(); // Method b ‘+ Add the definition for the Method, into the Program File: // Wel lokorécplusplus..cpp include "stdafx.h" include "HelloWorld¢pLusplus.h" int Hellobiorlécplusplu: { Hel lobor1d: :Displaytessage() system: :Windous: return 15 -orms: ses sageBox: :Shou(Messagestring) 1.6.2. C++ .NET- Building the DLL + Ensure you have selected the ‘Release’ build profile: i) HelloWorldCplusplus - Microsoft Visual Studio DFO auick Launch (Ctra) FILE EDIT VIEW PROJECT BULD DEBUG TEAM TOOLS TEST ANALYZE WINDOW HEL © - 0 BS MD ~~! P Localwindows Debugger ~ © ~ [Release Bl wink ¢ Debug Bo telialeragivspius.h cette + Select Build > Build Solution 4 HelloWorldcplusplus - Microsoft Visual Studio QG tw FILE EDIT VIEW PROJECT BUILD | DEBUG TEAM TOOLS TEST ANALYZE o- 8-2 ka | Build solution CtrleShifteB. } _HelioWorldcpluspluscpp ebule Solution | 8 [BiHelloworldCplusplus~ ‘Clean Solution | g 7/ MeVloworid¢plus; RUN Code Analysis on Solution AKSF11 The Output dialog should show that the Build was successful, and the location of the generated DLL: Savaentten fale " = ‘+ 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. © piicias eate, DllClassIsValid, D1 assCaliMethod, DllClaseSetProperty, DllClassGetProperty, D1lClassDispose 1.7.1. Cicode Example 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: ALiandle = D11CLassCreste(PataTestr(*[RUN]:MhyOLL.ai1"),"Helloliorlé") Funcrzon testoll() ‘STRING sResults INT iResults oB2eCT dllandles Errset(1); //the enable capturing of 1serror() J/anstantiate Class. {/This will trigger the Constructor in our DLL, which will set the Jidefault values, and return a handle. dilHandle = D1lclasscreate(pathToste( [RUM] :MyDLL -d11"), "Helloworld”) Jicheck validity of handle INT bResult = D11Classisvalid(dL1Mandle) IF CbResult) THEN //If Class is Valid, then our DLL was loaded properly J [try to read our property ‘wessage". 7/The value of this property will be the default value, "Hello World!” sResult = DllclasscetProperty(dllMandle, "Message") U{set the property to a different string Result = D1IClasssetProperty(dl1Mandle, "Message" IF (iResult <> 9) THEN errtog("error (Error Code END. Ry New Test Message") 4 Introstr(rserror()) + ")")s “call our wethod to open a popup containing our new message Result = D11ClassCallMethod(dllMandle, "Displaytessage") af (inesult «> 0) THEN ErrLog("Error (Error Code ~ " + IntTostr(Tserror()) + eno. ew ErrSet(o) //The disable capturing of Istrror() END 1.8. Advanced C# example — Completing the loop via CTAPI So far, all the interaction with our DLL has been initiated from the SCADA Expert Vijeo Citect 2015 side. 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 anew value is received. The only way of passing data back to the host, unsolicited, isto use the CTAPI interface, which we will demonstrate in this advanced example. 1.8.1. Advanced C# example — Overview: 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 This DLL will expose the following: Properties: ‘+ target The name of the Citect Tag you wish to update (read / write) + value ~The current tag value (read-only) ‘+ minVaiue ~ 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: © StartGenerating() - Starts generating data, and pushes results via CTAPI © StopGenerating() - Stops generating data, stop notifying Citect. 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: DeTFamenons3 > Sot. aut se se to ep nen! ae) vomice 7 pe Vance ‘Woon frm Aopen vomice fa Wen 0 Winns ne Shee Rican voice + Ensure you have selected Visual C# in the left pane, and Class Library on the right. + Then be sure to fil 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 74 SILER TAS aS OY [aainaa ena AEE ME pT Yew gokcr BAD Oene TATOOS Est MANE wrOOW sn con 8 ~ GS ~~ PD stat © ~ Release - Any cru ~| Bw se WL £ sexton soe vax otactee eaten alv-coepice | ‘sig Sate clleo core i 8 | Jesine Systems © Bi sotution Ductpstrents (projec) gE SE ees P'S otLemntene Po mete FE ponmssce octets Rees : a public class Classy ‘ » > © Change the line public class Class1 to public class DLL_Ctapi ‘© Your code will now look like this: using System using system.collections Generics using System.Lings using system. Text; using System. Threading. Tasks; amespace DLL_ctapievents public cless DLL_ctapi { } b ‘+ Save your code by clicking on the "Save All’ button, or ‘Ctrl + Shift +S’ 1.8.3. Advanced C# example — Add Reference to CTAPI libraries The easiest way to add CTAPI support to a C# program is to add existing file (available hare) called CTAPL.es, which already contains the mapping to the unmanaged CTAPI DLLs. ‘Add this fle 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. a Foge * g Search Solution Explorer (Ctl+) e-3 21 Solution DUL_Ctapitvents (1 projec) > BP ty uid Dome Rebuild > oa lean view > Analyze > Scope to This New Solution Explorer View Ads > Newnem. Catesnitton BB Manage NuGet Packages. 70. Existing Item... ShifteAlted * Add the namespace Citect. Util to your ‘classt.cs' fle using System using system.collections Generics using System.ting; using system. Text; using System. Threading. Tasks; using Chtect.util; hamespace DLL_ctapiévents { public class OLL_ctapi { } t 1.8.4. Advanced C# example — Constructor In G# the constructor must have the same name as the Class. In this case our Constructor needs to be called Dil_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 _tinerinterval; private string targets private double “value; private double "minvalues private double “maxValue public DLL_ctapi() //Class constructor, called by Cicode: pLLClasscreate() { _timerinterval = 2000; //set default interval to 30 seconds. Tearget = "Yogi"; //set default nane to "Tagi" Tminvalue Tnawvalue = 0 > 1.8.5. Advanced C# example — Properties We expose the requisite properties with the below code: //READ/WAITE, Tag name of the Targeted Tag, public string target get //called By Cicode: DLLClassoetProperty() { return _tangets } set //called by cicode: pLLclasssetProperty() t _target = value; y J/READ ONLY, This is the current value public doubie value { get //called By Cicode: DLLClassGetProperty() { } return _values y //READ/MRITE, This is the lowest generated value public doublé minvalue { get //called by cicode: DLLClasscetProperty() t return _minvalue; } set //Called By Cicode: DLLClassSetProperty() { _minvalue = values } d //neap/unrTe, this is the highest generated value public double maxvalue { get //called By Cicode: DLLClasscetProperty() { return _maxvalues y set //called by cicode: DLLclasssetrroperty() _mawvalue = values y ) //READ/WRITE, The interval that our random data is updated at. public int interval t get //called By Cicode: DLLClassGetProperty() { y Set //called By Cicode: DLLClasssetProperty() { return _timertntervals _timerInterva Teimer. interval value; “timerinterval; //set the interval 1.8.6. | Advanced C# example — Methods The public methods can be defined as show below, along with some internally called private functions: /Mion-default Libraries: using Citect.util; //our ctap1 class, defined in crAaPr.cs using System.Timerss //For our timer task public int stantcenerating() t _tiner = new Timer(interval); //sets up new timer, at our interval rate. _timer.AutoReset = true; //So it triggers everytime, not just once. “einer ‘Elapsed += new Elapsedeventiandler( timer elapsed)//event handler “timer-enabled = true; // Enable Timer Feturn 05 ) private void _timer_elapsed(object sender, Elapsedeventargs e) { Random random = new Random(); “value = Nath.Round(random.textbouble() * (_maxvalue - _minvalue) + “ninvalue,2)3 string sResult = sendViacrapz(_target, _value. ostring()); ) public int stopcenerating() t _timer.enabled = false; // Disable Timer “einer ‘Dispose(); Feturn 05 ) //send data via crap1 to the local running citect client private siring sendViaCTAPT(string tagilane, string tagvalue) { Stringhuilder sResultauffer = new Stringsuilder(255)3 lint RCTAP = CTAPI.ctopen("", "", "", CTAPI.CT_OPEN BATCH); CTAPL.ctTaghrite(hCTAPI, tagName, tagValue); return sResulteuffer.Testring(); d 1.8.7. Advanced C# example — Building the DLL + Ensure you have selected the ‘Release’ build profile: + Select Build > Build Solution + The Output dialog should show that the Build was successful, and the location of the generated DLL + Navigate to this DLL and copy itto the Directory of you SCADA Expert Vijeo Citect 2015 test project. 1.8.8. | Advanced C# example — Cicode 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 fil GLOBAL OBJECT hComplexDLLs FUNCTION MyComplexd11¢) INT iResults hcomplexOLL = D11Classcreate(PathToste(” [RUN] :0LL_Ctapievents.d11"),"0LL_ctapi") TUT currentinterval, currentMin,currentlax, currentvalues STRING currentTargets IF Dl1Classrsvalid¢hComplexDLL) THEN inesult = DilclasssetProperty(hcomplexDLt, “interval” ,"10000")5 Errtog("setinterval: ” + rntrostr(iResult))5 iResult = 011Clascsetpropanty(hcomplexOLL, "target Errtog("setrarget: " + rntrostr(inesult)); yTagone")5 inesult = D11ClascsetPropenty(hcomplexOLL, "winvalue","10")s Errtog(“setiinvalue: ” + IntTestr(iResult))5 iResult = D11ClasssetProperty(hConmplexOLL, "naxValue","1100")5 errtog("setmaxvalue: " + IntTostr(iresult))5 currentunterval = ollclasscetrroperty(hcomplexDLt,"interval")5 currantTarget = 011ClassGetProperty(hcomplexDLL, "target")3 currentttin = DL1ClassGetProperty (hComplexDLL, "minValu Currenttax = D11ClassGetProperty(hComplexOLLy"aaxvalue")3 currentValue = D11ClassGetProperty(hComplexDLL, "value")5 [istart Generating Values iResult = D1lclasscallnetnod(hconplexoLt, “startsenerating")s errLog("starteanersting: " + introstr(drasult) eno ew FUNICTZON stopCompLexD11() Iu iResults iResult = Di1ClassCalIMethod(hComplexDLL,"stopGenerat ing")s stopsenerating: " + Introstr(iResult))3 DL1classpispose(hcomplexoLL) 5 Errtog("Dispose: " + IntTostr(iResult))s en FUNCTION updatecomplexD114() INT iResults inesult = D11Classsetproperty(hcomplexDLL,” ErrLog("setInterval: " + IntTostr(iResult))5 nterval”,"1000")5 EN FUNCTION updatecomplexD115() Tur iResults ikesult = D11ClasssetProperty(hComplexDLL, “interval”,"5000")3 Errtog("settnterval: ” + IntTestr(iResult))5 END 1.9. Appendix A - ‘Hello World’ (C#) — full code //s simple Class Library that will generate random values at a defined interval, and update a defined tag via cTAPT. 1 Author: Warwick Black 2015. using System using system.collections Generis using System.Lings using system. Text; using Systom.threading.tasks; /uon-default Libraries: Using Citect.util; //our CTAPT class, defined in CTAPT.cs using system.Timerss //For our timer task namespace DLL_Ctapiévents public class DLL_Ctapi t static Timer timers // From system. Timers private int _timerrnterval; private string targets private double “value; private double "minvalues private double “maxValue public DLL_Ctapi() //Class Constructor, called by Cicode: DLLClassCreate() © snersntenval = 20003 //Set default interval to 30 seconds. Ttarget = "ragi"; //set default name to ‘Tagi* “ninvalue Tmaxvalue d //READ/WRITE, Tag nane of the Targeted Tag public string target { get //called by cicode: pLLclasscetProperty() t return target; } set //Called By Cicode: DLLClassSetProperty() { _target = values } 7/READ ONLY, This is the current value public double value { get //Called By Cicode: DLLCLassGetProperty() { y return _values 7/READ/WRITE, This is the lowest generated value public double minvalue t get //called by cicode: DLLClasscetProperty() { return _ainvalues y Set //called By Cicode: DLLClasssetProperty() { _minvalue = values } d //ReAD/WRITE, This is the highest generated value public double maxvalue { get //called By Cicode: DLLClasscetProperty() { return _maxvalues , set //called By Cicode: DLLClasssetProperty() { _maxvalue = values y ) //READ/WRITE, The interval that our random data is updated at. public int interval t get //called By cicode: DLLClasscetProperty() { return _timerintervals } set //Called By Cicode: DLLClassSetProperty() { _timerInterval = value; Tsimer.interval = timerinterval; //set the interval } d public int startcenerating() { timer = new Tiner(interval); //sets up new timer, at our interval rate. Timer .AutoReset = true; //So it triggers everytine, not just once. “timer elapsed += new ElapsedeventHandler(_timer elapsed); //Defines event handler for when the timer elapses. “timer enabled = true; // enable Timer Feturn 03 ) private void _tiner_Elapsed(object sender, Elapsedeventargs €) { Randon random = new Random()3 _value = Nath.Round(randon.Nextoouble() * (.maxvalue - _minvalue) + _minvalue,2)5 string sResult = sendviacrapr( target, _value.tostring()); > public int stopsenerating() { _timer.Enabled “timer Dispose()s Feturn a3 false; // Disable Timer //send data via CTAPI to the local running Citect Client private string sendviacTapi(string tagName, string tagvalue) t stringbuilder skesulteuffer = new stringguilder(255)3 int NCTAPE = CTAPT.ctopen("", "", "", CTAPT.CT_OPEN BATCH); CTAPL.ctTaghirite(hcTAPT, tagilame, tagValue)5 return sResulteuffer.Testring(); 1.10. Appendix B - ‘Hello World’ (VB .NET) — full code Public Class Helloworld Private helloWorldstring As String Public Sub New() "Class Constructor ~helloworldString = "Hello world!" end Sub Public Property rwflessage As string ‘A Public read/write property ‘rwtiessage’ cet Return cstr(_helloworldstring) End Get Set(value As String) “Rellowarldstring = value End Set End Property Readonly Property rmessage as String ‘A readonly property ‘rtiessage’ cet Return cSte(_helloWorLéstring) End Get End Property weiteonly Property wNessage As string ‘A writeonly property “wiessage Set(value as string) “helloworldstring = value end Set End Property Public Sub DisplayMessage() "A Public Method (Subroutine) "DisplayMessage’ that cannot return 2 value Hsgaox(rufiessage) end sub “our Public Method (Function) "DisplayThisMessage’ which takes an argument, and returns a value Public Function DisplayThisMessage(fyVal sMessage As String) Nsg2ox(stlessage) Return sMessage End Function End Class 1.11, | Appendix C — ‘Hello World’ (C++ .NET) — full code 7/ Belloworlacpluaplus.h #pragma once fusing using namespace System; namespace HelloWorld¢plusplue { public ref class HelloWorld t private: System: :String* MessageString; //Internal Variable public: J/ Constructor - sets default values Hellotiorld() + 1/ Method int DisplayMeseage () + uw operty perty System: String* Message System: :Strina* get () ¢ veturn MessageString; > void set (System: :String* value) { MessageString = value; , // ¥ellodorldcplusplus.cpp #include “stdafx.h" #include “HelloWorldCplusplus.n” // Constructor implementaion HelloWorldCplusplus: :HelloWorld: : 4 elloworld () Messagestring = //pefined default value d int HelloWorldey « Lusplus: :HelloWorld: :DieplayMeseage () system: return indows orms: :MessageBox: :Show (MessageString) ; 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. // Buthor: Warwick Black 2015. using system; using System.Collections. using System.Ling: using System.Text; using System.Threading. Tasks; /(Non-default libraries: using Citect.Util; //Our CTAPI clase, defined in CTAPI.ce using System.Timers; //For our timer task namespace DLL_Ctapivents 4 public class DLL_Cctapi { static Timer timer; // From System.Timers private int _timerInterval; private string target; private double “value; private double "minValue; private double _maxValue; public DLL_Ctapi() //Class Constructor, called by Cicode: DLLCLassCreate () 3000; //Set default interval to 30 seconds. ; //Set default name to ‘Tagl* 5 20; Tmaxvalue = 100; ) //RERD/WRITE, Tag name of the Targeted Tag public tring tazge { get //Called By Cicode: DLLClassGetProperty () 4 return target; d set //Called By Cicode: DLLClassSetProperty() 4 jet = value; , ) //READ ONLY, This is the current value public double value { get //Called By Cicode: DLLClassGetProperty () 4 return _value; d //RERD/WRITE, This is the lowest generated value public Goublé minValue get //Called By Cicode: DLLClassGetProperty () { return _minvalue; y set //Called By Cicode: DLLClassSetProperty() 4 _minvalue = value; ) //RERD/WRITE, This is the highest generated value public double mazValue get //Called By Cicode: DLLClassGetProperty () 4 return _maxValue; d set //Called By Cicode: DLLClassSetProperty() 4 _maxValue = value; » ) //RERD/WRITE, The interval that our random data is updated at. public int interval { get //Called By Cicode: DLLClassGetProperty () t return _timerInterval; d set //Called By Cicode: DLLClassSetProperty () 4 _timerrnterval = value; Teimer. Interval = _timerInterval; //set the interval d ) public int st. { Generating () _timer = new Timer(interval); //sets up new timer, at our interval rata. _timer .AutoRese: true; //So it triggers everytime, not just once. _timer.Elapsed += new ElapsedEventHandler(_timer_flapsed); //vefines event handler for when the timer elapses. _timer.Znabled = true; // Enable Timer Feturn 0; private void timer _Elapsed(object sender, ElapsediventArgs e) { Random random = new Random(); _value = Math.Round(random.NextDouble() * (_maxValue - _minValue) + “minValue,2); string sResult = sendViaCTAPI(_target, _value.ToString()); public int stopGenerating() { _timer.Enabled = false; // Disable Timer Ttimer.Dispose(); Feturn 0; ) //Send data via CTAPI to the local running Citect Client private string sendViaCTAPI(stving tagName, string tagValue) { StringBuilder sResultBuffer = new StringBuilder (255) ; aint RCTAPI = CTAPI.ctopen("", "", Te "_OPEN_BATCH) CTAPL.ctTagWrite(hCTAPI, tagName, tagValue) ; gaturn sReoultBuffer.ToString() 7

You might also like