Files
wwtools/aot/dev-guide/appendix-a-ref-guide.md
Joseph Doherty 32f26272ae Initial commit: Wonderware / System Platform tools and reference
Five tools under one repo, all docs organized per DOCS-GUIDE.md:

- aalogcli: .NET 4.8 / x86 CliFx CLI for reading System Platform binary
  logs (*.aaLGX) for LLM debugging, built on aaOpenSource/aaLog. Commands:
  last, tail, range, unread, fields. Stable JSON envelope under --llm-json.
  Build template under lib/build/ for rebuilding aaLogReader.dll.

- aot: ArchestrA Object Toolkit 2014 v4.0 reference material. Dev guide
  (Markdown converted from CHM), API reference for the ArchestrA.Toolkit
  namespace, and the Monitor / Watchdog VS sample solutions.

- graccesscli: .NET 4.8 / x86 CliFx CLI that automates Galaxy
  configuration via the ArchestrA GRAccess COM interop. Includes session
  daemon, IPC protocol, and llm-json envelope contract.

- grdb: SQL/DDL exploration of the Galaxy Repository database. DDL
  captures, reusable queries, hierarchy / contained-name <-> tag-name
  translation notes.

- histdb: LLM-oriented reference for AVEVA Historian retrieval. INSQL
  linked-server, extension tables, every wwXxx time-domain extension,
  every retrieval mode, alarm/event SQL recipes, REST API. Distilled
  from the 243-page Historian Retrieval Guide.

Root contains:
- CLAUDE.md: thin index pointing into each tool's README.
- DOCS-GUIDE.md: doctrine for organizing docs for LLM consumption.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 18:22:20 -04:00

1072 lines
38 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Programming Techniques
Use the following workflow and programming techniques to code within the ArchestrA Object Toolkit (AOT).
## Programming Workflow
Using the AOT, you can seamlessly modify the object shape as the object is coded. The AOT also supports changing the ArchestrA attribute data type and renaming ArchestrA attributes and child primitives. This functionality, referred to as round-tripping, has been implemented by configuring the object shape in code. The code is parsed at build time to form the objects aaDEF file that defines its shape.
Use the Object Designer to modify the the object shape code. The Designer parses the object shape from the code. Modifications made in the Object Designer are then written back to the code. The Object Designer also enables you to perform tasks that impact multiple code sections simultaneously, such as renaming ArchestrA attributes, adding local primitives, and making changes associated with modifying an objects major version.
The basic steps of the workflow are:
1. Define the object shape using either the ArchestrA Object Toolkit Designer or in code directly.
2. Code the Configtime.
3. Code the Editor for the object.
4. Code the Runtime.
5. Build and import the object.
6. Test and debug the object.
> **Note**
> We recommend that you define the object shape with the ArchestrA Object Toolkit Designer and use code only when necessary. Refer to Advanced Techniques for information on coding the shape of the object.
## Configuring Internal and External Names
Internal names apply to objects, primitives, and attributes. Internal names are used in code and must be C# compliant.
Do not use .NET keywords, names of classes in the ArchestrA Object Toolkit, or names used in other libraries that are used in your code as internal names for objects, primitives, and attributes.
Internal names are used in code to refer to the primitive or attribute in C# through provided wrapper classes.
The maximum length of an objects internal name is 255 characters.
The external name is used in the ArchestrA IDE and can be used to create default names for object instances. Each name should be meaningful and suggest the meaning of the object, primitive, or attribute.
External names must be ArchestrA compliant and cannot have any of the following characters: (space) + - * / \ = ( ) ` ~ ! % ^ & @ [ ] { } | : ; " , < > ? "
> **Note**
> For the sake of brevity, do not use the word "Object" or "Template" in an object's name.
## Providing Wrappers for Referencing ArchestrA Attributes
Attribute wrappers provide the following functionality for non-dynamic local attributes and non-virtual child primitive attributes:
- Strongly typed attribute references
- Automatic renaming of attribute references
- IntelliSense
> **Note**
> Renaming an attribute property causes Visual Studio to rename all references to the attribute in code except for string based references. For example, renaming Attribute1 does not modify “GetValue(“Attribute1”)”.
Modifying an attribute internal name using the Object Designer renames the Attribute property (known also as the attribute wrapper).
Using the Object Designer to modify the child primitive internal name, that is, the fully scoped name of the referenced attribute, also renames the Attribute property.
> **Note**
> Wrappers are updated each time the solution is parsed, that is, when the Object Designer is opened, the object is built, the Object Design View is refreshed, or Code Validate is selected.
## Config Time Coding
Use the following techniques to code for the Configtime project.
The Configtime project is used to provide all logic related to configuring the attributes of the object within the Galaxy database.
### Config Time Set Handler
Use the config time set handler to implement any logic required for setting the value of an attribute at configuration time.
This logic could include, for example, range checking and adding or removing virtual primitives.
You can add a Configtime set handler to an attribute by adding a set handler delegate and associated method to the Configtime class.
The Configtime class template provides commented out examples of a set handler delegate and method.
The set handler registration and associated methods are not renamed when the attribute is renamed in code. To remove the set handler, comment out or delete the registration. The Set Handler method can exist without the set handler registration; however, the method is not called.
In the following examples Attribute1 represents the internal and external name of the attribute.
The Configtime set handler is triggered when an attribute is changed at configuration time. It can be used for validation or to trigger a special action such as adding virtual primitives.
For additional information, see Configuring Run Time Set Handlers.
### Set Handler Code
Attribute1.SetHandlerValue enables the set handler code to appear the same for both array and non-array attributes. You can take more control of setting the value by using the following code examples:
- Non-array attribute:
```csharp
Attribute1 = e.Value;
```
- Array attributes:
```csharp
if (!e.IsArrayElement)
{
Attribute1 = e.Value;
}
else
{
Attribute1[e.attributeHandle.shIndex1] = e.Value;
}
```
### Performing Config Time Validation with the ConfigtimeValidate() Method
Use this method to validate the entire object as a whole when it is being saved. The method can put the object in a warning or error state by using EPACKAGESTATUS enum.
Example:
```csharp
private void AOTObject4Configtime_ConfigtimeValidate(object sender, ref EPACKAGESTATUS status)
{
// By default set the object status to Good
if (HiLimit < LoLimit)
{
status = EPACKAGESTATUS.ePackageBad;
AddErrorMessage("Hi Limit must be greater than or equal to Lo Limit");
}
else
{
status = EPACKAGESTATUS.ePackageGood;
}
}
```
### Adding a Virtual Primitive at Config Time with AddPrimitive
You can test the ability to add an instance of a virtual primitive at config time using the following function (Boolean result):
```csharp
bool CanAddPrimitive(string virtualPrimitiveName, string internalName, string externalName);
```
The following example uses CanAddPrimitive to check before adding a primitive:
```csharp
if (CanAddPrimitive("c1", "MyCP1InternalName", "MyCP1ExternalName"))
{
AddPrimitive("c1", "MyCP1InternalName", "MyCP1ExternalName");
}
```
Where:
- c1 is the internal name of the virtual primitive.
- MyCP1InternalName is the internal name of the Primitive Instance.
- MyCP1ExternalName is the external name of the primitive instance.
You can add an instance of a virtual primitive at config time and check the result using PrimitiveResult.message as shown in the following example:
```csharp
private void Attribute1SetHandler(object sender, ref ConfigtimeSetHandlerEventArgs e)
{
Attribute1.SetHandlerValue = e;
if (e.Value)
{
if (!AddPrimitive("c1", "MyCP1InternalName", "MyCP1ExternalName"))
{
e.Message = PrimitiveResult.message;
return; // Add failed
}
}
}
```
Where:
- c1 is the internal name of the virtual primitive
- MyCP1InternalName is the internal name of the Primitive instance
- MyCP1ExternalName is the external name of the Primitive instance
- Attribute1 is a Boolean Attribute with a config time set handler.
> **Note**
> PrimitiveResult.message returns a message only on failure. To return status, use PrimitiveResult.status (EPRIMITIVEOPSTATUS).
Use to following example to iterate through the child primitives or child virtual primitives:
```csharp
object primitives;
this.Site.ChildVirtualPrimitives(ThisPrimitive, out primitives);
foreach (IPrimitiveShape ips in (IEnumerable)primitives)
{
LogInfo(ips.FullName);
}
```
### Removing a Virtual Primitive at Config Time with DeletePrimitive
You can test the ability to delete an instance of a virtual primitive at config time using the following function (Boolean result):
```csharp
bool CanDeletePrimitive(string internalName);
```
> **Note**
> CanDelete checks the lock status of the primitive being deleted.
Example:
```csharp
if (CanDeletePrimitive("MyCP1InternalName"))
{
DeletePrimitive("MyCP1InternalName");
}
```
Where:
MyCP1InternalName is the primitive instance being deleted.
You can delete an instance of a virtual primitive at config time and check the result using PrimitiveResult.message as shown in the following example:
```csharp
private void Attribute1SetHandler(object sender, ref ConfigtimeSetHandlerEventArgs e)
{
Attribute1.SetHandlerValue = e;
if (!e.Value)
{
if (!DeletePrimitive("MyCP1InternalName"))
{
e.Message = PrimitiveResult.message;
return; // Delete failed
}
}
}
```
Where:
- c1 is the internal name of the virtual primitive.
- MyCP1InternalName is the internal name of the Primitive instance.
- MyCP1ExternalName is the external name of the primitive instance.
- Attribute1 is a Boolean attribute with a config time set handler.
> **Note**
> PrimitiveResult.message only returns a message on failure. To return status use PrimitiveResult.status (EPRIMITIVEOPSTATUS).
### Accessing Data in Attributes at Config Time
For static attributes, use attribute wrappers based on internal name to read/write value.
For dynamic attributes or attributes in a virtual primitive, use the GetValue and SetValue methods.
**Examples:**
If the static float attribute is named HiLimit:
```csharp
float myVal;
myVal = HiLimit;
```
If the dynamic float attribute is named HiLimit:
```csharp
float myVal;
myVal = GetValue(“HiLimit”);
```
### Accessing Data in Other Primitives at Config Time
For static primitives, use primitives and attributes wrappers based on internal name to read/write value.
For attributes in a virtual primitive, use the GetValue and SetValue methods.
**Examples:**
To access a float attribute with an internal name of HiLimit, of a static child primitive with the internal name Limits:
```csharp
float myVal;
myVal = Limits.HiLimit;
```
To access a float attribute with an external name of HiLimit, of an instance of a virtual child primitive with the external name Limits:
```csharp
float myVal;
myVal = GetValue(“Limits.HiLimit”);
```
### Adding and Deleting Dynamic Attributes at Config Time
Use the following code to add/remove a dynamic attribute.
```csharp
bool status = AddAttribute("dynAttr1", MxAttributeCategory.MxCategoryCalculated, MxDataType.MxDouble, false);
DeleteAttribute("dynAttr1");
```
For more information on these methods, see the *ArchestrA* *Object Toolkit Reference Guide*.
## Run Time Coding
Use the following techniques to code for run time. For more information on these methods, see the *ArchestrA Object* *Toolkit Reference Guide*.
### Runtime SetHandler
Use a run time set handler to implement any logic required to set an attribute value at run time, including range checking and accepting or rejecting the set.
You can add a run time set handler to an attribute by adding a set handler delegate and associated method to the Runtime class.
The Runtime class template provides commented out examples of a set handler delegate and method.
The set handler registration and associated methods are not renamed when the attribute is renamed in code. To remove the set handler, comment out or delete the registration. The Set Handler method can exist without the set handler registration; however, the method is not called.
In the following examples Attribute1 represents the attribute internal and external name.
The run time set handler registration appears in the Runtime class in the following region:
```csharp
#region Runtime Set Handler Registration - Toolkit generated code
#endregion Runtime Set Handler Registration
```
The run time Set Handler registration for Attribute1 appears as:
```csharp
this.RegisterRuntimeSetHandler("Attribute1.ex", new RuntimeSetHandlerDelegate(Attribute1SetHandler));
```
**Remarks**
The delegate is registered to the external name of the Attribute. In this example, the text "Attribute1.ex" represents the external name for Attribute1.
The run time set handler method for Attribute1 appears at the end of the Runtime class as:
```csharp
private void Attribute1SetHandler(object sender, ref RuntimeSetHandlerEventArgs e)
{
Attribute1.SetHandlerValue = e;
}
```
For additional information, see Configuring Run Time Set Handlers.
### Set Handler Code
Attribute1.SetHandlerValue enables the set handler code to appear the same for both array and non-array Aattributes. You can take more control of setting the value by using the following code examples:
- Non-array attribute:
```csharp
Attribute1 = e.Value;
```
- Array attributes:
```csharp
if (!e.IsArrayElement)
{
Attribute1 = e.Value;
}
else
{
Attribute1[e.attributeHandle.shIndex1] = e.Value;
}
```
#### SetInfo Structure Event Arguments
Event arguments to provide the run time set handler event with the required data:
```csharp
public class RuntimeSetHandlerEventArgs : SetHandlerEventArgs
{
// an attribute to pass in the Set Info:
SetInfo attributeInfo;
// an attribute to pass out the status:
MxStatus status;
// Constructor
RuntimeSetHandlerEventArgs(AttributeHandle pAttributeHandle, SetInfo pInfo, MxStatus _status, IMxValue pMxValue);
}
```
### Coding a RuntimeExecute() Method
Use this method to get inputs, perform calculations, set outputs, and set alarm Booleans.
> **Note**
> This is the most important run time method, but it needs to be efficient and not time-consuming, or it could cause scan overruns.
**Example:**
If the object is called Test:
Test_RuntimeExecute()
### Returning an Error Status String at Run Time
**Use the RuntimeGetStatusDesc method to return an error message string associated with a previously returned error code from a sethandler.**
**Example:**
```csharp
private void xxx_RuntimeGetStatusDesc(object sender, ref RuntimeGetStatusDescEventArgs e)
{
//----------------------------------------------
// TODO: Runtime Event - GetStatusDesc
//
// This routine provides a String for an
// error code when a client requests it.
// By default this method looks for an entry
// in the dictionary that has the
// DetailedErrorCode as the PhraseID.
//
// You need to change this implmentation if
// you want to provide embedded values
// within your messages, or you want to use
// string PhraseIDs instead of integer
// PhraseIDs.
//----------------------------------------------
switch (e.detailedErrorCode)
{
default:
e.status = GetText((int)e.detailedErrorCode);
break;
}
}
```
#### RuntimeGetStatusDesc Event
Delegates added to this event are called when the event is fired by ArchestrA:
```csharp
event RuntimeGetStatusDescDelegate RuntimeGetStatusDesc;
```
### Event Handler for Get Status Description
Use the following to provide the GetStatusDescription event with the required data:
```csharp
class RuntimeGetStatusDescEventArgs : EventArgs
{
// an attribute to pass out the detailed error code:
short detailedErrorCode;
// an attribute to pass out the status:
string status;
// Constructor:
RuntimeGetStatusDescEventArgs();
}
```
### Manipulating Data Quality at Run Time
Use code to read data quality from and write data quality to attributes.
**Example:**
```csharp
myAttribute = Input1.Value.Value;
myAttribute.Quality = Input1.Value.Quality;
if( myAttribute.Quality.isBad )
{
// then do something like set alarm
...
}
```
### Manipulating the Timestamp at Run Time
Use code to read and write the time stamp at run time.
**Example:**
```csharp
myAttribute = Input1.Value.Value;
myAttribute.Time = Input1.Value.Time;
```
### Getting Input (I/O) Values Using Utility Primitives at Run Time
Use code to read input value, status and quality from Input or InputOutput utility primitives. Use wrapper classes if the utility primitive is static; otherwise, use GetValue.
**Examples:**
Input primitive:
```csharp
myAttribute = Input1.Value.Value;
CMxStatus myStatus = Input1.ReadStatus;
```
InputOutput primitive:
```csharp
myAttribute = InputOutput1.ReadValue;
CMxStatus myStatus = InputOutput1.ReadStatus;
myAttribute.Quality = InputOutput1.ReadValue.Quality;
myAttribute.Time = InputOutput1.ReadValue.Time;
```
### Setting Output (I/O) Values Using Utility Primitives at Run Time
Code utility primitives to write output value and read status from Output or InputOutput Utility Primitive. Use wrapper classes if the Utility Primitive is static; otherwise, use SetValue.
**Examples:**
Output primitive:
```csharp
Output1.Value = myValue;
Output1.Value.Time = myTime;
```
InputOutput primitive:
```csharp
InputOutput1.WriteValue = myValue;
InputOutput1.WriteValue.Time = myTime;
```
Writing to the Quality of the Input, Output, or InputOutput primitive wrapper is not supported. The Quality of Value, WriteValue, and ReadValue is read-only. For example, attempting to set InputOutput1.WriteValue.Quality = SomeAttribute.Quality may result in an erroneous value (InputOutput1.WriteValue.Value) to be written to the output location.
### Accessing Data in Attributes at Run Time
For static attributes, use attribute wrappers based on the internal name to read or write values, quality, and time stamps.
**Example:**
If attribute is called Attribute1, just use Attribute1 in code.
```csharp
int i = Attribute1;
```
For dynamic attributes or attributes in a virtual primitive, use the GetValue and SetValue methods.
**Example:**
For virtuals and dynamics:
```csharp
GetValue("attribute1");
```
or
```csharp
GetValue( primitiveId, attributeId);
```
See the AObjectBase class definition for further details on the GetValue member.
### Accessing Data in Other Primitives at Run Time
For static primitives, use primitives and attributes wrappers based on the internal name to read/write value, quality, and time stamp.
For attributes in a virtual primitive, use the GetValue and SetValue methods.
**Examples:**
```csharp
prim1.attribute1 = 23.0; // for static primitives and attributes
int i = GetValue("prim1.attribute1"); // for dynamic primitives and attributes
SetValue("prim1.attribute1",23.0); // for dynamic primitives and attributes
```
### Adding and Deleting Dynamic Attributes at Run Time
Use the following code to add/remove a dynamic attribute in run time code.
```csharp
bool status = AddAttribute("dynAttr1", MxAttributeCategory.MxCategoryCalculated, MxDataType.MxDouble, false);
DeleteAttribute("dynAttr1");
```
### Supporting AdviseOnlyActive at Run Time
Application Server can suspend the input polling required for an attribute when that attribute is not currently being used, such as when it is not being viewed by a client, alarmed, script-referenced, or historized. For more information on the AddAttribute methods and overloads, see the *ArchestrA* *Object Toolkit Reference Guide*.
The Advise Only Active feature of the run time infrastructure determes whether an attribute is currently being used or not, and can suspend and activate that attribute at the appropriate time.
The ApplicationObject must determine which attributes are eligible candidates to be suspended when not being used. The ApplicationObject must also turn off the input polling required for an attribute when suspended, and turn the input polling back on when the attribute is activated.
To enable AdviseOnlyActive:
1. Determine whether or not the Object should support Advise Only Active functionality. If so, enable the **Advise** **Only Active supported** check box in the Object Editor.
2. Determine for each primitive being developed what attributes are eligible for Advise Only Active functionality. These typically will only be attributes that are updated or associated with “live” updates from external sources, usually from input type primitives. They can also be subscriptions using MX.
3. In either the code or the object editor, set “Advise Only Active” in the selected attributes to True.
> **Note**
> In the Runtime Startup method, the auto-generated code checks whether AdviseOnlyActiveEnabled is enabled for the object. If AdviseOnlyActiveEnabled is enabled , the auto-generated code calls SuspendLocalAttribute() for each attribute supporting Advise Only Active.
1. Implement the body of the provided AttributeName_AdviseOnlyActive() method for each attribute supporting Advise Only Active:
a. The method shell is auto-generated.
b. Fill in code in this method to take necessary actions to activate or suspend updates of polled data related to the specified attribute being activated. Typically, an Input primitive or InputOutput primitive wrapper is called, such as:
```csharp
Input1.ActivateUpdatesList()
InputOutput1.ActivateUpdatesList()
```
1. You can also choose to take other actions, including:
a. Activate/suspend updates on an attribute in another object using CMxIndirect.Activate() or CMxIndirect.Suspend().
b. Activate/suspend updates on an attribute in another primitive using the ActivatePrimitiveAttribute() or SuspendPrimitiveAttribute() methods.
> **Note**
> In Runtime Shutdown method, if AdviseOnlyActiveEnabled is enabled, the auto-generated code calls ActivateLocalAttribute() for each attribute supporting AdviseOnlyActive.
#### AdviseOnlyActiveEnabled
Use this method or property to determine whether the object has AdviseOnlyActive functionality enabled. If disabled, the object must not call functions to use AdviseOnlyActive. The AOT prevents functions such as SuspendLocalAttribute() from being used if AdviseOnlyActive functionality is disabled. This method or property determines if AdviseOnlyActive functionality is enabled.
### Other AOT Wrappers for AdviseOnlyActive
In addition to the wrappers indicated in the previous section, the AOT adds the following wrapper function for AdviseOnlyActive.
#### IO Utilities
Input and InputOutput utility primitives class wrappers provide methods to SuspendUpdatesList() and ActivateUpdatesList() that suspend and activate the input polling for the utility primitive.
### Triggering an Alarm at RunTime
Set the Boolean attribute representing the alarm to True to trigger the alarm. Set the attribute to False to clear the alarm condition.
**Example:**
```csharp
myCondition = true;
```
> **Note**
> The Boolean attribute must have an alarm extension added to it in the Object Designer.
## Providing Access to External Attributes (BindTo)
The BindTo method of RuntimeBase provides a simplified method for accessing attributes in other objects at run time using CMxIndirect. For more information, see CMxIndirect.
You can use BindTo to:
- Read the value, time, and quality of an attribute or property.
- Set the value and time of an attribute. Quality cannot be set.
The Value, Quality, Datatype, Length, and Time of the external attribute can be accessed by the properties:
```csharp
myIndirect.Value
myIndirect.DataQualityOfLastRead
myIndirect.Value.GetDataType
myIndirect.Value.Length
myIndirect.TimeStampOfLastRead
```
Like value, time can be set across object boundaries. You can access time without having to access value, but you must do this by binding to the Time property directly.
**Example:**
```csharp
myIndirect = RuntimeBase.BindTo( “obj.attr.time”, “”);
myIndirect.Value = DateTime.Now();
myTime = myIndirect.Value; // where myTime is a System.DateTime variable
```
You can set both time and value together in one call:
```csharp
myIndirect.Set( x, myTime );
```
Value and time can be set together as a pair.
**Example:**
```csharp
private CMxIndirect myIndirect = null;
.
.
.
myIndirect = BindTo("MyTestObject.Attribute1", "", true);
if (myIndirect != null )
{
myIndirect.Set( 180, DateTime.Now() ); // sets V, T in one call
}
```
The developer can get both Time and Value together in one call:
```csharp
if (myindirect.StatusOfLastRead.success == -1 && myindirect.StatusOfLastRead.Category == MxStatusCategory.MxCategoryOk)
{
myIndirect.Get( out x, out myTime, out myQuality );
}
```
After the BindTo operation, check the status before accessing the value as shown in the previous example.
> **Note**
> Declaring the CMxIndirect in the Runtime Declarations Section makes the indirect available to all methods in the Runtime, that is, to Startup and Execute.
### CMxIndirect
Used for referencing external attributes. For more information, see Providing Access to External Attributes (BindTo).
```csharp
public class CMxIndirect
{
public CMxIndirect(string _fullRefString, string _context, IMxSupervisoryConnection _superConn, RuntimeBase _rb, int _refHandle, short _statusId, int _statusIndex);
public MxStatus CallBackStatus { get; }
public string Context { get; }
public short DataQuality { get; }
public string FullReferenceString { get; }
public bool IsGood { get; }
public int RefHandle { get; }
public MxStatus Status { get; }
public short StatusId { get; }
public int StatusIndex { get; }
public CMxValue Value { get; set; }
public CMxTime Time { get; set; }
public void Set { CMxValue value, CMxTime time };
public void Get { out CMxValue value, out CMxTime time, out short quality };
}
```
## Associating an ArchestrA Editor Control with an Attribute in Code
Normally, ArchestrA editor controls are statically configured to point to a single attribute to be configured on the editor tab.
However, on occassion, and especially with virtual primitives, it is useful to dynamically bind the editor control to a particular attribute by setting the Attribute property of the control.
For example, with a text box control, the following shows how to set the attribute name in code:
```csharp
aaTextBox1.Attribute = "myAttribute1";
```
### Referencing Attributes Using GetValue and SetValue
The AOT enables the use of SetValue and GetValue to reference the following types of attributes:
- Child primitive attributes
- Dynamic attributes
- Virtual primitive attributes
You can reference these attributes using SetValue and GetValue with a relative string reference. The string reference is relative to the primitive that contains the code, and can be prefixed with the following modifiers:
- “me”
- “myparent”
- “myobject”
> **Note**
> The prefix “me” is implied when no prefix is provided.
The GetValue and SetValue methods use the primitive external name and attribute external name as shown in the following example:
```csharp
GetValue(PrimitiveExternalName.AttributeExternalName)
```
Examples included in this section are based on the following hierarchy:
### Local References
To reference any local attributes, use the attribute name with no prefixes or scope.
Using the me prefix explicitly sets the reference to local and can be used to make it clear what type of reference is required.
Either of the following statements in Child2 gets the value of AOTObjectx1.Child2.Attribute1:
```csharp
GetValue("Attribute1");
GetValue("me.Attribute1");
```
### Referencing Down (child)
To reference attributes of a child primitive, prefix the reference string with the name of the child primitives.
The following statement in Child2 gets the value of AOTObjectx1.Child2.Child4.Attribute1:
```csharp
GetValue("Child4.Attribute1");
```
The following statement in AOTobjectx1 gets the value of AOTObjectx1.Child2.Child4.Attribute1:
```csharp
GetValue("Child2.Child4.Attribute1");
```
### Referencing Up (parent)
To reference attributes of a parent primitive use the myparent prefix.
The following statement in Child2 gets the value of AOTObjectx1.Attribute1:
```csharp
GetValue("myparent.Attribute1");
```
The following statement in Child4 gets the value of AOTObjectx1.Child2.Attribute1
```csharp
GetValue("myparent.Attribute1");
```
The myparent prefix cannot be used more than once in a reference. To reference attributes of objects or primitives more than one level higher, you must use myobject to locate the reference to the top of the hierarchy and then work relative to that location.
The following statement in Child4 gets the value of AOTObjectx1.Attribute1:
```csharp
GetValue("myobject.Attribute1");
```
The following statement in Child4 gets the value of AOTObjectx1.Child1.Child3.Attribute1:
```csharp
GetValue("myobject.Child1.Child3.Attribute1");
```
### Array Usage
```csharp
// Increment the 3rd element of an array
FloatArray1[3]++;
// Increment all the elements of an array
for (short counter = 1; counter <= FloatArray2.Length; counter++)
{
FloatArray2[counter]++;
}
```
## The External Build Process
The build process is made up of many stages. In general, these stages all occur as a single event.
Sometimes you may want to execute only part of the build process or to add additional events in the middle of the process. To support this, you can start a solution build from the command line as well as repackage the object with the Packager application.
> **Note**
> The project must be built before you execute the Packager, because the Packager requires the aaDEF file and associated assemblies create by the build.
### Command Line Recompile Object
Perform these processes from a command prompt with the Visual Studio Environment variables loaded.
**Build Process - Recompile (debug version)**
```csharp
C:\>devenv "C:\ Projects\AOTObject88\AOTObject88.sln" /build Debug
```
**Build Process - Recompile (release version)**
```csharp
C:\>devenv "C:\ Projects\AOTObject88\AOTObject88.sln" /build Release
```
### Command Line Repackage Object
The AOT includes a utility called Packager.
The Packager packages the object using the files created by the build, that is, it packages the aaDEF files and assemblies created by the build. This allows a build process to repackage the aaPDF after the object is rebuilt using the command line build.
You can start the Packager as a Windows Form application or execute it from the command line using the following switches:
/q - command line mode, no Form.
/f <filename> - The name of a text file containing information needed by DesignServer to repackage the Object.
The file is automatically generated in the Output directory when the AOT builds the object using the name DesignServerInfo.txt. It contains the name of the root aaDEF file and all of the paths to the core object dlls as relative paths, that is, the project is portable.
**Example**
```csharp
C:\Program Files\ArchestrA\Toolkits\AOT\Bin>packager /q /f "C:\ Projects\AOTObject90\Output\designserverinfo.txt" /a "C:\Utility Dlls\"
```
> **Note**
> This feature allows you to modify the aaDEF file and repackage the Object.
## Advanced Techniques
Use these techniques to configure an object or primitive completely in code using C# attributes. Using the integrated Object Desiger causes these C# attributes declarations to be automatically added to the code.
### Configuring an ArchestrA Attribute in Code
You can add ArchestrA attributes to the object. They can be used locally in code as C# variables.
The ArchestrA attributes are declared in the object project as C# variables using a CMx Data Type.
When the object is parsed, properties are automatically added to the Runtime and Configtime class for each ArchestrA attribute defined in the Object class. Parsing occurs while:
- Refreshing the AOT Object Design View
- Opening the AOT Object Designer
- Saving an object in the AOT Object Designer
- Executing build or code validation
The attribute properties allow you to access ArchestrA attributes as though they were strongly typed C# values.
When you reference the ArchestrA attribute in code, the property provides a C# typed wrapper to the attribute using SetValue and GetValue access. The wrapper provides access to Quality, Time, Data Type, and Array Length.
The following table lists the data types and associated C# conversions.
| ArchestrA Data Type | C# Data Type |
| --- | --- |
| Boolean | bool |
| Integer | int |
| Float | float |
| Double | double |
| Time | DateTime |
| Elapsed Time | TimeSpan |
| String | string |
| Big String | string |
| Attribute Reference | string |
| Custom Enumeration | string array |
| DataType | ArchestrA.Core.MxDataType |
| Custom Structure | ArchestrA.Core.MxCustomStruct |
| MxStatus | ArchestrA.Core.MxStatus |
| Variant | CMxValue |
| InternalDumpLoadData | ArchestrA.Core.MxCustomStruct (special) |
| InternalFailoverData | ArchestrA.Core.MxCustomStruct (special) |
> **Note**
> There is no direct conversion from an internationalized string to a C# type. The internationalized string can be represented as an internationalized string structure (string value, locale) or by setting the locale for the attribute, which then allows you to reference the attribute as a C# string.
The variable declaration can be decorated with C# attributes to enable the configuration of the following attribute properties (excluding special data types):
- External Name
- Category
- Security
- Calculated Quality and Time
- Frequently Accessed
- Alarm Extension
- History Extension
> **Note**
> The special data types of CMxInternalDumpLoadData and CMxInternalFailoverData are created and maintained by the toolkit. These data types are not intended for general use. These ArchestrA attributes store data for recreating dynamic attributes and child primitive instances on dump/load and failover.
#### Specifying the ArchestrA Attribute Array Length
You can set and get the array length of an attribute array (static) at config time or run time using the following syntax:
**Syntax**
```csharp
AttributeName.Length = n;
n = AttributeName.Length;
```
**Parameters**
AttributeName
Represents the array attribute name.
n
Represents an integer value.
**Remarks**
You can set the array length of a dynamic attribute array at config time or run time using one of the following Get and Set methods:
```csharp
n = GetNumElements("AttributeName");
n = GetNumElements(AttributeID, PrimitiveID);
SetNumElements("AttributeName", n);
SetNumElements(AttributeID, PrimitiveID, n);
```
Where, AttributeName is the array attribute name and n is an integer value.
You can apply the GetNumElements and SetNumElements methods to attributes. However, when you rename an attribute using the Object Designer, the attribute name referenced by these methods is not updated.
### Referencing Attributes from the Editor of the Object
To reference attributes directly from the Editor Project of the object, you must implement the GetData() and SetData() methods provided by the framework.
Even if the config-time dynamic attribute is located on OnObject, you cannot use Editor functions SetData and GetData, and Runtime functions Set and Get, if the attribute reference contains an object name. For example, **myTestAOT_001.myuda**.
For more information, see Enabling Buffered Data for a Config-time Dynamic Attribute.
In the examples, Attribute1 is a float array with four elements, Attribute2 represents is a float.
**Attribute Get Example:**
```csharp
//Get an Attribute value
float MyData2 = (float)Convert.ToDecimal(GetData("Attribute2"));
```
**Attribute Set Example:**
```csharp
//Set an Attribute value
SetData("Attribute2", 9.0);
```
**Array Attribute Get Example:**
```csharp
//Get a single value of an Array Attribute
float MyData = (float)Convert.ToDecimal(GetData("Attribute1[1]"));
//Get an Array Attribute
object[] MyArrayValues = new object[4];
MyArrayValues = (object[])GetData("Attribute1");
```
**Array Attribute Set Example**
```csharp
//Set an Array Attribute using a locally declared array
object[] MyArrayValues = new object[4];
MyArrayValues[0] = 1.0;
MyArrayValues[1] = 1.1;
MyArrayValues[2] = 1.2;
MyArrayValues[3] = 1.3;
SetData("Attribute1", MyArrayValues);
```
> **Note**
> The array index is 1-based. There are no errors or warnings to indicate that a zero value has been passed to the array index from the editor of the object.
### Local Attribute Wrappers
Based on the attribute category, AOT adds an Attribute property to the Configtime class or the Runtime class for each attribute declared in the Object class. You can use the local attribute wrapper to access attributes using the attributes name attribute_internalname.
The following code example represents an auto-generated Attribute property added to the Configtime or Runtime class for an attribute that supports read and write. In the example, the Set statement would be excluded if the attribute were read-only.
```csharp
private CMxBoolean Attribute1
{
get { return InternalReferenceOnly.Attribute1; }
set { InternalReferenceOnly.Attribute1.Set(value); }
}
```
The get property provides access to the attribute wrapper and allows you to access the features of the wrapper, such as quality and security.
The set property is limited to setting the value. This is how the property is used when the attribute is on the left side of an assignment operator, for example, Attribute1 = 10. You can assign values using this short method. It provides type checking and automatic type conversion of the value.
The property does not provide any other set access to the wrapper.
The following table shows the relationship between the attribute category and the attribute properties Get and Set added to the Configtime and Runtime classes.
| Attribute Category | Configtime Class | Runtime Class |
| --- | --- | --- |
| PackageOnly | Get and Set | - - |
| PackageOnly_Lockable | Get and Set | - - |
| Constant | Get Only | Get Only |
| Writeable_C_Lockable | Get and Set | Get Only |
| Writeable_UC | Get and Set | Get and Set |
| Writeable_UC_Lockable | Get and Set | Get and Set |
| Writeable_USC | Get and Set | Get and Set |
| Writeable_USC_Lockable | Get and Set | Get and Set |
| Calculated | - - | Get and Set |
| Calculated_Retentive | - - | Get and Set |
| Writeable_S | - - | Get and Set |
| Writeable_U | - - | Get and Set |
| Writeable_US | - - | Get and Set |
| SystemInternal | Unsupported | Unsupported |
| SystemSetsOnly | Unsupported | Unsupported |
| SystemWriteable | Unsupported | Unsupported |