Introduction
The GRAccess toolkit enables developers to automate activities that users normally perform manually using the Industrial Application Server Integrated Development Environment (IDE). This blog post describes how to create and configure a DataChange Script using a C# console application.
Prerequisites
To execute the GRAccess sample application that is described in this document, you will need the following prerequisites:
- Visual Studio 2005
- Industrial Application Server 2.1 or higher
Using the IDE to create and configure a DataChange script
Before I start automating IDE tasks using GRAccess, I describe the manual IDE configuration steps that are going to be automated.
First create an object instance of the $UserDefined template. Give the new object the name "UserDefined_001" and open its editor.
In the "UDAs" tab of the editor add a new boolean User Defined Attribute called "Switch" that will be used later as the trigger of the DataChange script.
After the UDA has been created go to the "Scripts" tab of the object editor and start configuring the DataChange script. First declare a variable in the "Declarations" section. This variable will be in scope during the whole runtime of the object. Use the following statement for the declaration:
1: Dim Number as Integer;
In the "Scripts" tab add a new script and name it "HelloWorld". In the "Expression" field write "me.Switch". Select "DataChange" as "Trigger Type". Write the following script as body of the "HelloWorld" script:
1: LogMessage("Hello World!");
2: LogMessage("Number of ScanState changes: " + StringFromIntg(Number, 10));
3: Number = Number + 1;
The following screen shot shows the script settings in the editor:
What happened under the hood?
This paragraph describes in GRAccess API terms what happened under the hood during the configuration of the "UserDefined_001" object:
Create the object instance
Query the Galaxies and Login
1: Galaxies = GR.QueryGalaxies(GRMachineName);
2: G = Galaxies[GalaxyName];
3: G.Login(UserName, Password);
Get the $UserDefined template and create a new object instance
1:// Find $UserDefined template
2: Objects = G.QueryObjectsByName(EgObjectIsTemplateOrInstance.gObjectIsTemplate, ref Names);
3: T = (ITemplate)Objects[1];
4:// Create instance of $UserDefined
5: ObjectInstance = T.CreateInstance(ObjectName, true);
Checkout the object
1: ObjectInstance.CheckOut();
Add new User Defined Attribute
1:// Add UDA
2: ObjectInstance.AddUDA(TriggerUDAName,
3: MxDataType.MxBoolean,
4: MxAttributeCategory.MxCategoryWriteable_USC_Lockable,
5: MxSecurityClassification.MxSecurityFreeAccess, false, null);
Save the object
This step is important. Saving the object at this point will generate the new attribute and make it accessible to the configuration steps to follow.
1: ObjectInstance.Save();
Create the DataChange script
At this point we are ready to add and configure the script (Script Extension Primitive). First we add a new script into the list of scripts. Then we save the object. This will generate all the attributes required to configure the script the conventional way. Here is the list of the configurable attributes of the ScriptExtension primitive:
Attribute Name | Comment |
ExecuteText | |
AliasReferences | |
Aliases | |
TriggerType | |
DataChangeDeadband | |
Expression | |
DeclarationsText | |
StartupText | |
ShutdownText | |
OnScanText | |
OffScanText | |
ExecutionError.Alarmed | |
TriggerPeriod | |
ScriptExecutionGroup | |
ScriptOrder | |
RunsAsync | |
State.Historized | |
ExecuteTimeout.Limit | |
TriggerOnQualityChange | New in 3.0 |
Add the Script Extension Primitive
1: ObjectInstance.AddExtensionPrimitive("ScriptExtension", DataChangeScriptName, true);
Save the object and generate script attributes
1: ObjectInstance.Save();
Configure the Script Extension attributes
1: IAttribute ScriptBodyTextAttribute = ObjectInstance.ConfigurableAttributes[DataChangeScriptName + ".ExecuteText"];
2: IAttribute ScriptDeclarationsTextAttribute = ObjectInstance.ConfigurableAttributes[DataChangeScriptName + ".DeclarationsText"];
3: IAttribute ScriptTriggerTypeAttribute = ObjectInstance.ConfigurableAttributes[DataChangeScriptName + ".TriggerType"];
4: IAttribute ScriptExpessionAttribute = ObjectInstance.ConfigurableAttributes[DataChangeScriptName + ".Expression"];
5:
6: MxValue v = new MxValueClass();
7:
8:
9://Setting the script delarations text
10: v.PutString("dim " + TriggerCounterVariableName + " as Integer;");
11: ScriptDeclarationsTextAttribute.SetValue(v);
12:
13://Setting the script type
14: v.PutString("DataChange");
15: ScriptTriggerTypeAttribute.SetValue(v);
16:
17://Setting the script Expression
18: v.PutString("me." + TriggerUDAName);
19: ScriptExpessionAttribute.SetValue(v);
20:
21://Setting the scripts execute text
22: v.PutString("LogMessage(\"Hello World!\");\n" +
23:"LogMessage(\"Number of ScanState changes: \" + StringFromIntg("+ TriggerCounterVariableName + ", 10));\n"
24: + TriggerCounterVariableName + " = " + TriggerCounterVariableName + " + 1;");
25: ScriptBodyTextAttribute.SetValue(v);
The following screen shots show the now available Script Extension attributes.
Save the object and check it
1: ObjectInstance.Save();
2: ObjectInstance.CheckIn("");
Download source files
Click the following link to download the code sample files: CreateScriptExtension.zipThe complete source listing
Here is the complete listing of the program.cs file.
1:using System;
2:using System.Collections.Generic;
3:using System.Text;
4:using System.IO;
5:using System.Xml;
6:
7:
8://GRAccess
9:using ArchestrA.GRAccess;
10:
11:
12:namespace CreateFieldAttributes
13: {
14:class Program
15: {
16:staticvoid Main(string[] args)
17: {
18:
19:#region Command Line Argument Parsing
20:
21:
22://("CreateOPCClient Usage:");
23://("+++++++++++++++++++++");
24:
25://(" g=<Galaxy Name>");
26://(" gr=<GR Server Node>");
27://(" u=<User Name>");
28://(" pw=<Password>");
29://(" on=<UserDefined Object Name>");
30://(" dc=<Name of UDA that triggers data change script>");
31://(" sn=<Name of data change script>");
32://(" vn=<Name of trigger counter variable>";
33:
34://(" -> The order of the parameters doesn't matter!");
35://(" -> There must be 8 parameters!");
36://(" -> Use empty space characters to separate parameter=value pairs!");
37://(" -> Don't use commas or semi-colon to separate parameter=value pairs!");
38://(" -> The parameter=value pairs can't be longer than 254 characters!");
39://(" -> The parameter specifier can't be longer than 2 characters!");
40:
41://(" Example:");
42://(" CreateScript gr=localhost g=PENGUINS u=Administrator pw=ww on=UserDefined_001
43:// dc=Switch sn=HelloWorld vn=Counter");
44:
45://Initialize dictionary
46: Dictionary<string, string> CmdParams = new Dictionary<string, string>(9);
47:
48: CmdParams.Add("gr", Environment.MachineName);
49: CmdParams.Add("g", "");
50: CmdParams.Add("u", "");
51: CmdParams.Add("pw", "");
52: CmdParams.Add("on", "");
53: CmdParams.Add("dc", "");
54: CmdParams.Add("sn", "");
55: CmdParams.Add("vn", "");
56:
57:if (args.GetLength(0) != CmdParams.Count)
58: {
59: PrintHelp();
60:return;
61: }
62:else
63: {
64:foreach (string s in args)
65: {
66:if (s.Length > 255)
67: {
68: PrintHelp();
69:return;
70: }
71:
72:if (s.IndexOf('=') > 2)
73: {
74: PrintHelp();
75:return;
76: }
77:
78:if (s.Split('=').GetLength(0) != 2)
79: {
80: PrintHelp();
81:return;
82: }
83:else
84: {
85:try
86: {
87:string temp = CmdParams[(s.Split('='))[0]];
88: }
89:catch
90: {
91: PrintHelp();
92:return;
93: }
94:
95: CmdParams[(s.Split('='))[0]] = ((s.Split('='))[1]);
96: }
97: }
98: }
99:
100:if (0 == String.Compare(CmdParams["gr"], "localhost", true))
101: {
102: CmdParams["gr"] = Environment.MachineName;
103: }
104:
105:string GRMachineName = CmdParams["gr"];
106:string GalaxyName = CmdParams["g"]; ;
107:string UserName = CmdParams["u"]; ;
108:string Password = CmdParams["pw"];
109:string ObjectName = CmdParams["on"];
110:string TriggerUDAName = CmdParams["dc"];
111:string DataChangeScriptName = CmdParams["sn"];
112:string TriggerCounterVariableName = CmdParams["vn"];
113:
114:#endregion
115:#region GRAccess Login
116: ICommandResult CR;
117: IGalaxies Galaxies;
118: IGalaxy G;
119: IgObjects Objects;
120: ITemplate T;
121: IInstance ObjectInstance;
122: GRAccessAppClass GR = new GRAccessAppClass();
123:
124: Galaxies = GR.QueryGalaxies(GRMachineName);
125:
126:if (Galaxies == null || GR.CommandResult.Successful == false)
127: {
128: System.Console.Out.WriteLine(GR.CommandResult.Text + " : " + GR.CommandResult.CustomMessage);
129:return;
130: }
131:
132: G = Galaxies[GalaxyName];
133:
134:if (G == null)
135: {
136: System.Console.Out.WriteLine("Galaxy {0} not found on server {1}!", GalaxyName, GRMachineName);
137:return;
138: }
139:
140: G.Login(UserName, Password);
141: CR = GR.CommandResult;
142:if (!CR.Successful)
143: {
144: System.Console.Out.WriteLine("Login Galaxy Failed: " + CR.Text + " : " + CR.CustomMessage);
145:return;
146: }
147:else
148: {
149: System.Console.Out.WriteLine("Login Galaxy Successful!");
150: }
151:
152:#endregion
153:#region Create Instance of $UserDefined
154:string[] Names = { "$UserDefined" };
155:
156:// Find $UserDefined template
157: Objects = G.QueryObjectsByName(EgObjectIsTemplateOrInstance.gObjectIsTemplate, ref Names);
158:
159: CR = G.CommandResult;
160:if (!CR.Successful)
161: {
162: System.Console.Out.WriteLine("QueryObjectsByName Failed for $UserDefined Template: " +
163: CR.Text + " : " +
164: CR.CustomMessage);
165:return;
166: }
167:
168:
169: T = (ITemplate)Objects[1];
170:
171:
172:// Create instance of $UserDefined
173: ObjectInstance = T.CreateInstance(ObjectName, true);
174: CR = T.CommandResult;
175:if (!CR.Successful)
176: {
177: System.Console.Out.WriteLine("Create Instance failed for " + ObjectName + " :" +
178: CR.Text + " : " +
179: CR.CustomMessage);
180: }
181:
182:#endregion
183:#region Create Trigger UDA of type Boolean
184:
185:
186: ObjectInstance.CheckOut();
187:
188:// Add UDA
189: ObjectInstance.AddUDA(TriggerUDAName, MxDataType.MxBoolean, MxAttributeCategory.MxCategoryWriteable_USC_Lockable, MxSecurityClassification.MxSecurityFreeAccess, false, null);
190: CR = ObjectInstance.CommandResult;
191:if (!CR.Successful)
192: {
193: Console.WriteLine("Adding UDA " + TriggerUDAName + " to " + ObjectName + " failed: " +
194: CR.Text + " : " +
195: CR.CustomMessage);
196: ObjectInstance.CheckIn("");
197:return;
198: }
199:
200: ObjectInstance.Save();
201:
202:#endregion
203:#region Add DataChange Script
204:
205: ObjectInstance.AddExtensionPrimitive("ScriptExtension", DataChangeScriptName, true);
206: CR = ObjectInstance.CommandResult;
207:if (!CR.Successful)
208: {
209: Console.WriteLine("AddExtensionPrimitive failed!" +
210: CR.Text + " : " +
211: CR.CustomMessage);
212:return;
213: }
214:
215: ObjectInstance.Save();
216: CR = ObjectInstance.CommandResult;
217:if (!CR.Successful)
218: {
219: Console.WriteLine("Save failed" +
220: CR.Text + " : " +
221: CR.CustomMessage);
222:return;
223: }
224:
225:#endregion
226:#region Edit DataChange Script
227:
228://Configurable attributes of the Scripting Primitive
229://(".ExecuteText");
230://(".AliasReferences");
231://(".Aliases");
232://(".TriggerType");
233://(".DataChangeDeadband");
234://(".Expression");
235://(".DeclarationsText");
236://(".StartupText");
237://(".ShutdownText");
238://(".OnScanText");
239://(".OffScanText");
240://(".ExecutionError.Alarmed");
241://(".TriggerPeriod");
242://(".ScriptExecutionGroup");
243://(".ScriptOrder");
244://(".RunsAsync");
245://(".State.Historized");
246://(".ExecuteTimeout.Limit");
247://(".TriggerOnQualityChange"); //(New in WAS 3.0)
248:
249: IAttribute ScriptBodyTextAttribute = ObjectInstance.ConfigurableAttributes[DataChangeScriptName + ".ExecuteText"];
250: IAttribute ScriptDeclarationsTextAttribute = ObjectInstance.ConfigurableAttributes[DataChangeScriptName + ".DeclarationsText"];
251: IAttribute ScriptTriggerTypeAttribute = ObjectInstance.ConfigurableAttributes[DataChangeScriptName + ".TriggerType"];
252: IAttribute ScriptExpessionAttribute = ObjectInstance.ConfigurableAttributes[DataChangeScriptName + ".Expression"];
253:
254: MxValue v = new MxValueClass();
255:
256:
257://Setting the script delarations text
258: v.PutString("dim " + TriggerCounterVariableName + " as Integer;");
259: ScriptDeclarationsTextAttribute.SetValue(v);
260:
261://Setting the script type
262: v.PutString("DataChange");
263: ScriptTriggerTypeAttribute.SetValue(v);
264:
265://Setting the script Expression
266: v.PutString("me." + TriggerUDAName);
267: ScriptExpessionAttribute.SetValue(v);
268:
269://Setting the scripts execute text
270: v.PutString("LogMessage(\"Hello World!\");\n" +
271:"LogMessage(\"Number of ScanState changes: \" + StringFromIntg("+ TriggerCounterVariableName + ", 10));\n"
272: + TriggerCounterVariableName + " = " + TriggerCounterVariableName + " + 1;");
273: ScriptBodyTextAttribute.SetValue(v);
274:
275:
276: ObjectInstance.Save();
277: CR = ObjectInstance.CommandResult;
278:if (!CR.Successful)
279: {
280: Console.WriteLine("Save failed" +
281: CR.Text + " : " +
282: CR.CustomMessage);
283:return;
284: }
285:
286: ObjectInstance.CheckIn("");
287: CR = ObjectInstance.CommandResult;
288:if (!CR.Successful)
289: {
290: Console.WriteLine("Checkin failed" +
291: CR.Text + " : " +
292: CR.CustomMessage);
293:return;
294: }
295:#endregion
296: System.Console.Out.WriteLine(ObjectName + " has been successfully created and configured!");
297: }
298:#region Helper Methods
299:staticvoid PrintHelp()
300: {
301: System.Console.Write("\n\n\n");
302: System.Console.Out.WriteLine(" g=<Galaxy Name>");
303: System.Console.Out.WriteLine(" gr=<GR Server Node>");
304: System.Console.Out.WriteLine(" u=<User Name>");
305: System.Console.Out.WriteLine(" pw=<Password>");
306: System.Console.Out.WriteLine(" dc=<Name of UDA that triggers data change script>");
307: System.Console.Out.WriteLine(" sn=<Name of data change script>");
308: System.Console.Out.WriteLine(" vn=<Name of trigger counter variable>");
309:
310: System.Console.Out.WriteLine(" -> The order of the parameters doesn't matter!");
311: System.Console.Out.WriteLine(" -> There must be 8 parameters!");
312: System.Console.Out.WriteLine(" -> Use empty space characters to separate parameter=value pairs!");
313: System.Console.Out.WriteLine(" -> Don't use commas or semi-colon to separate parameter=value pairs!");
314: System.Console.Out.WriteLine(" -> The parameter=value pairs can't be longer than 254 characters!");
315: System.Console.Out.WriteLine(" -> The parameter specifier can't be longer than 2 characters!");
316:
317: System.Console.Out.WriteLine(" Example:");
318: System.Console.Out.WriteLine(" CreateScript gr=localhost g=PENGUINS u=Administrator pw=ww on=UserDefined_001 dc=Switch sn=HelloWorld vn=Counter");
319: }
320:#endregion
321: }
322: }
Compilation
To compile the program.cs file run the following command in the Visual Studio 2005 command prompt:
1: csc /out:CreateScript.exe program.cs /r:"C:\Program Files\Common Files\
2: ArchestrA\ArchestrA.GRAccess.dll"
Test run
To execute CreateScript.exe run the following command:
1: CreateScript gr=localhost g=PENGUINS u=Administrator pw=ww on=UserDefined_001 dc=Switch sn=HelloWorld vn=Number
2:
Summary
Creating a script using GRAccess is a two-step process. First the Script Extension Primitive needs to be added to the object using the AddExtension() method. Saving the object will then generate the attributes that are required to configure the script. Setting these Script Extension Attributes in the usual way is the second step.