Quantcast
Channel: Tellingmachine - Toolkits

Creating an Application Object script using GRAccess

$
0
0

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:

  1. Visual Studio 2005
  2. 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.

UDAs Editor Tab in IDE

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:

Script Extension 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 NameComment
ExecuteText 
AliasReferences 
Aliases 
TriggerType 
DataChangeDeadband 
Expression 
DeclarationsText 
StartupText 
ShutdownText 
OnScanText 
OffScanText 
ExecutionError.Alarmed 
TriggerPeriod 
ScriptExecutionGroup 
ScriptOrder 
RunsAsync 
State.Historized 
ExecuteTimeout.Limit 
TriggerOnQualityChangeNew 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.

Script Extension Attributes Part 1

Script Extension Attributes Part 2

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.zip

The 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.


How to use Dynamic attributes to expose ConfigOnly attribute values in runtime

$
0
0

Introduction

The building blocks of the Industrial Application Server are Application Objects. These objects themselves are made out of components called Primitives. Primitives have Attributes and Attributes have Properties. Each Application Object has at least the Common Primitive that is shared amongst all objects. In addition to this Primitive Application Objects can contain several other Primitives that are provided by the Industrial Application Server. On top of the basic set of Primitives one or more Custom Primitives can be added to an Application Objects. The Application Object Toolkit provides the tools to create Custom Primitives and glue them together with other standard Primitives to create a new type of Application Object.

Components of the International Spacestation

Figure 1: Components of the International Spacestation

The AOT allows a developer to statically define Attributes of a new Custom Primitive in the Object Designer, or to dynamically create Attributes in the PackageServer component of the new Application Object. This post explains how to create Dynamic Attributes to expose other Attributes that are for example only available during configuration to the runtime sub system.

Prerequisites

The sample Application Object discussed in this post has been developed using the following tools:

  • Visual Studio.NET 2003
  • IAS 3.0 SP1
  • Application Object Toolkit 1.0

Problem description

Some Attributes values are only available during the configuration of the object and are not accessible at runtime. The Attributes "MinorVersion" and "CodeBase", which are part of the Common Primitive, are configuration only Attributes. The following sample describes a simple approach to exposing these values to the runtime sub system by creating new Dynamic Attributes that are visble in runtime and copying the values of the configuration Attributes into the new Dynamic attributes

MinorVersion and CodeBase Attributes are ConfigOnly 

Figure 2: MinorVersion and CodeBase Attributes are ConfigOnly

Solution

Create new AOT project

Open the AOT Object Designer and create a new object called "ExportPackageOnlyAttributes". Then add one static Attribute in the Designer of type integer with the name "PV". Assign it the category "Writeable_USC_Lockable"

Creating new object with the ObjectDesigner

Figure 3: Creating a new object with the Object Designer

Now generate the source code, open the Visual Studio solution and build the new Application Object. Import it into the IDE and deploy it to an Application Engine.

Modify the Package Server

From this point on all modifications are made to the "ExportPackageOnlyAttributesPackage.cpp" file of the Package Server Visual Studio project. The code that we are going to add will be executed by the "CExportPackageOnlyAttributesPackage::OnPostCreate" event handler.

Add Attribute

To add a new attribute we need access to two functions that are provided by the Site COM object, which we get by calling the GetSite() function. First we want to make sure that the Attribute hasn't already been added by checking "GetAttributeHandle" passing in the name of our Dynamic Attribute. If it returns a valid handle, then we know that the Attribute has already been added, if not we are going to add the Attribute.

   1:// Create two dynamic attributes if they don't exist already
   2:// First check whether they exist
   3:  
   4: ATL::CComBSTR MinorVersionExName = L"MinorVersionEx";
   5: ATL::CComBSTR MajorVersionExName = L"MajorVersionEx";
   6: AttributeHandle MinorVersionExAttributeID;
   7: AttributeHandle MajorVersionExAttributeID;
   8:  
   9:  
  10: IPrimitivePackageSite * P = GetSite();
  11:  
  12:// 1) MINOR VERSION
  13: HRESULT    r = P->GetAttributeHandle( MinorVersionExName, &MinorVersionExAttributeID);
  14:  
  15:if(!(SUCCEEDED(r)))
  16: {
  17:     LogWarning(L"GetAttributeHandle - HRESULT = %x", r);
  18:return;
  19: }
  20:  
  21: LogInfo(L"%s has AttributeID = %d" , MinorVersionExName, MinorVersionExAttributeID.shAttributeId);
  22:  
  23:// If Attribute doesn't exist, create one
  24:if( MinorVersionExAttributeID.shAttributeId == 0)
  25: {
  26:  
  27:     AttributeInfo ai;
  28:     AttributeID ID;
  29:  
  30:     ai.Name = MinorVersionExName;
  31:     ai.Category = MxCategoryWriteable_C_Lockable; 
  32:     ai.Security = MxSecurityFreeAccess;
  33:     ai.Datatype = MxInteger;
  34:     ai.LockType = MxLockedInMe;
  35:  
  36://Add dynamic attribute
  37:     r = P->AddAttribute(eTopLevelCustomPrimitiveId, &ai, &ID);
  38:  
  39:if(!(SUCCEEDED(r)))
  40:     {
  41:         LogWarning(L"AddAttribute - HRESULT = %x", r);
  42:return;
  43:     }
  44:  
  45:     LogInfo(L"Added Attribute %s! Its AttributeID = %d" , MinorVersionExName, MinorVersionExAttributeID.shAttributeId);

The category of the two Dynamic Attributes is set "Writeable_C_Locable". This way the Attribute values are constants and cannot be changed in runtime.

Set Value

Once we created the new Dynamic Attributes we can get the values from the "MinorVersion" and "CodeBase" and copy them to the new Dynamic Attributes "MinorVersionEx" and "MajorVersionEx". For the latter one we have to parse the "CodeBase" Attribute value string and get the version number at the end of the string. Here is the source code that does it.

   1:// Set value of dynamic attribute equal to PackageOnly MajorVersion attribute
   2: CMxValue MxCodeBase = Get(idxCommonCodeBase, eCommonPrimitiveId);
   3:longint MajorVersion = 0;
   4: ATL::CComBSTR CodeBase = L"";
   5:  
   6:// Getting CodeBase attribute from Common Primitive
   7: r = MxCodeBase->GetString(&CodeBase);
   8:if(!(SUCCEEDED(r)))
   9: {
  10:     LogWarning(L"GetString - HRESULT = %x", r);
  11:return;
  12: }
  13: LogInfo(L"CodeBase is: %s", CodeBase);
  14:  
  15:// Converting BSTR to STL wstring
  16: std::wstring strCodeBase(CodeBase);
  17:  
  18:// Parsing version number from ProgID string contained in CodeBase attribute
  19: size_t b = strCodeBase.find_last_of(L".");
  20: LogInfo(L"b %d", (int) b);
  21: std::wstring strMajorVersion = L"";
  22: strMajorVersion = strCodeBase.substr(b + 1);
  23:  
  24:// Converting string to long integer
  25: swscanf(strMajorVersion.c_str(), L"%d", &MajorVersion);
  26:  
  27: LogInfo(L"MajorVersion String: %s", strMajorVersion.c_str());
  28: LogInfo(L"MajorVersion Integer: %d", MajorVersion);
  29:  
  30:// Converting long integer to MxValue object
  31: CMxValue MxMajorVersion(MajorVersion);
  32:  
  33:// Setting MajorVersionEx attribute value
  34: Set(ID, MxMajorVersion);

Build and test

Compile the modified project, import the new object and deploy it. The new dynamic attributes should now display the values of the configuration counter parts. Check the object property page in the IDE and the runtime values of object attributes in the ObjectViewer.

Object properties displayed by the IDE

Figure 4: Object properties displayed by the IDE

ObjectViewer displays the new dynamic attributes

Figure 5: ObjectViewer displays the new Dynamic Attributes

Download

The complete Visual Studio .NET 2003 solution can be downloaded here: CExportPackageOnlyAttributes.zip

Summary

Creating Dynamic Attributes is without any doubt an advanced feature of the Application Object toolkit. On the other hand Dynamic Attributes can increase productivity of an application developer by providing a mechanism to automate the "shaping" of an Application Object.

What is the coolest thing on the planet?

$
0
0

Walter and I gave a presentation at the WonderWorld 2008 conference in Las Vegas last week. To get many people exited about our talk, we asked customers what they think the coolest thing on the planet is. They thought first that this is a trick question, but we didn't keep them in the dark for too long. The coolest thing on the planet is:

Power Shell

Figure 1: The coolest thing on the planet

The Presentation

The presentation had the title: Managing Wonderware Galaxies with PowerShell. The first part of the one hour session covered the basic language concepts of PowerShell and the second half was filled with various applications of PowerShell scripts for building and diagnosing Wonderware Galaxies.

PowerPoint

The PowerPoint presentation can be downloaded here: CoolestThingPPT.zip

Sample scripts

  1. Compare DAServer item lists
  2. Get-WWSoftware
  3. WMI get command line from aaEngine
  4. Export errors from log file
  5. Set-LogFlag

Sample cmdlets

  1. LMX Get Runtime Value Cmdlet
  2. Validate Instances of Template

The sample files can be downloaded here: CoolestThingDemos.zip

Conference impressions

The budgets are tight. We were carpooling to Las Vegas to keep the expenses at a minimum. This didn't affect our great vibes!

[youtube: nRF0bll01S0]

Here are some pictures of the conference and the city.

All hands meetingThe Palazzo towerPalazzo Hotel

No more roller coasterFashion Show MallTesla car

Experts in the hallMeet the ExpertsSchedule

The Doobie BrothersPresentation roomPremier support in action

The Party

Of course, as always, The Party is just the night before our presentation. Here is a short clip of the Doobie Brothers, who mastered a great performance.

[youtube: _AdM-HgPO4w]

The greatest thing on the planet

Jump to the answer