110 KiB
Executable File
ArchestrA MXAccess Toolkit User's Guide
Invensys Systems, Inc. © 2002–2010, 2014. Application Server 2014.
Welcome
This guide describes how to use the MXAccess Toolkit. ArchestrA Galaxy data access is exposed to .NET and COM clients through a programmable object model called MXAccess. You can use the MXAccess object model to write programs that automate reading and writing data values in the attributes of ArchestrA objects.
You can view this document online or you can print it, in part or whole, by using the print feature in Adobe Reader.
This guide assumes you know how to use Microsoft Windows, including navigating menus, moving from application to application, and moving objects on the screen. If you need help with these tasks, see the Microsoft Help.
Documentation Conventions
This documentation uses the following conventions:
| Convention | Used for |
|---|---|
| Initial Capitals | Paths and file names. |
| Bold | Menus, commands, dialog box names, and dialog box options. |
| Monospace | Code samples and display text. |
Technical Support
Wonderware Technical Support offers a variety of support options to answer any questions on Wonderware products and their implementation.
Before you contact Technical Support, refer to the relevant section(s) in this documentation for a possible solution to the problem. If you need to contact technical support for help, have the following information ready:
- The type and version of the operating system you are using.
- Details of how to recreate the problem.
- The exact wording of the error messages you saw.
- Any relevant output listing from the Log Viewer or any other diagnostic applications.
- Details of what you did to try to solve the problem(s) and your results.
- If known, the Wonderware Technical Support case number assigned to your problem, if this is an ongoing problem.
Chapter 1: Getting Started
Use the MXAccess Toolkit to interface a program to ArchestrA Message Exchange. The Toolkit supports a simple, lightweight interface that permits:
- Connecting to an ArchestrA Galaxy.
- Subscribing to one or more attributes and receiving data updates from these attributes.
- Writing new values to these attributes.
- Authenticating one or more users so that the program can write to attributes that require secured writes or verified writes.
At minimum, a deployed ArchestrA Platform must exist on the computer running the client application.
Components of the MXAccess Toolkit
The MXAccess Toolkit contains:
- The MXAccess32.tlb type library .
- The MxAccess.tlb type library.
- The COM/.NET support files:
- The .NET interop DLL ArchestrA.MxAccess.dll
- The ArchestrA.MxAccess.dll .NET policy file.
- Code samples for Visual Basic, .NET, Visual C++, and C#.
- Documentation.
The MXAccess Toolkit installs these files to the following locations.
| Files | Location |
|---|---|
| Type libraries | 32-bit operation systems: \Program Files\ArchestrA\Framework\bin 64-bit operating systems: \Program Files (x86)\ArchestrA\Framework\bin |
| Interop file | 32-bit operation systems: \Program Files\ArchestrA\Framework\bin 64-bit operating systems: \Program Files (x86)\ArchestrA\Framework\bin |
| Code samples | 32-bit operating systems: \Program Files\ArchestrA\Toolkits\Samples 64-bit operating systems: \Program Files (x86)\ArchestrA\Toolkits\Samples |
Registering the Interface Files
The installation registers the type library and interop file for you. You can use the following procedure to copy these files to a computer and register them yourself. A common example requiring manual registration is running the code samples on an earlier version of Application Server. This is because earlier versions of Application Server did not contain the needed files.
To manually register the interface files
1 Copy the files MxAccess.tlb and ArchestrA.MxAccess.dll as follows:
- On 32-bit operating systems, from the \Program Files\ArchestrA\Framework\Bin folder on the MXAccess Toolkit CD to the folder \Program Files\ArchestrA\Framework\bin on your hard drive.
- On 64-bit operating systems, from the \Program Files (x86)\ArchestrA\Framework\Bin folder on the MXAccess Toolkit CD to the folder \Program Files (x86)\ArchestrA\Framework\bin on your hard drive
Registering the Interface Files 2 Register the type library. Open a command prompt window and type the regtlib command followed by the complete path to the type library.
- For example, on a 32-bit operating system:
regtlib "C:\Program
Files\ArchestrA\Framework\bin\MxAccess32.tlb"
- For example, on a 64-bit operating system:
regtlib "C:\Program Files
(x86)\ArchestrA\Framework\bin\MxAccess32.tlb"
Note: You must enclose the folder path within quotation marks because there is a space in the Program Files folder name.
3 Register the interop file. a Open two Explorer windows. Browse to the \WINDOWS\assembly folder in one window. In the other window:
- On a 32-bit operating system, browse to the \Program Files\ArchestrA\Framework\bin folder.
- On a 64-bit operating system, browse to the \Program Files (x86)\ArchesrA\Framework\bin folder. b If an ArchestrA.MxAccess folder exists in the \WINDOWS\assembly folder, you must uninstall it. Do this by right-clicking ArchestrA.MxAccess in the \WINDOWS\assembly folder and then clicking Uninstall. Drag the new ArchestrA.MxAccess.dll from the ArchestrA\Framework\bin folder to the WINDOWS\assembly folder.
Chapter 2: Using the Interface Files in Programs
After you install and register the type library and interop, you can use them in your programs.
To develop new software to use the MXAccess interface, also known as LMXProxy, you must include a reference to the interface in your project. How to do this differs according to which language you are using. This documentation describes using the MXAccess Toolkit with three languages supported by Microsoft Visual Studio: Visual Basic.NET, C#, and Visual C++.
For the program to connect to the Galaxy and access ArchestrA data, a deployed ArchestrA Platform must exist on the computer running the client application. However, you do not need ArchestrA or a MXAccess runtime license installed on your computer to compile and link the program.
Adding a Toolkit Reference Using Visual Basic.NET
You can add a toolkit reference in Visual Basic.NET.
To add a reference
- On the Project menu, click Add Reference. The Add Reference dialog box appears.
- Click the Browse tab.
- Navigate to:
- (32-bit operating system) \Program Files\ArchestrA\Framework\bin and select the ArchestrA.MxAccess.dll interop file.
- (64-bit operating system) \Program Files (x86)\ArchestrA\Framework\bin and select the ArchestrA.MxAccess.dll interop file.
To confirm that the MXAccess reference has been added to your project
- On the Project menu, click Properties, where AppName is the actual name of your project. You can also use Solution Explorer and double-click the My Project icon.
- Click the References tab. The Reference Names list includes ArchestrA.MxAccess.
- Show the path to the DLL file.
- On the View menu, click Object Browser. The Object Browser appears.
- Expand the icon for ArchestrA.MxAccess to see all the interfaces and other members provided by the MXAccess interface.
Adding a Toolkit Reference Using C# # Adding a Toolkit Reference Using C#
You can add a toolkit reference in C#.
To add a reference
- On the Project menu, click Add Reference. The Add Reference dialog box appears.
- Click the Browse tab.
- Navigate to:
- (32-bit operating system) \Program Files\ArchestrA\Framework\bin and select the ArchestrA.MxAccess.dll interop file.
- (64-bit operating system) \Program Files(x86)\ArchestrA\Framework\bin and select the ArchestrA.MxAccess.dll interop file.
To confirm that the MXAccess reference has been added to your project
- Open Solution Explorer and expand the References icon.
- On the View menu, click Object Browser. The Object Browser pane appears.
- You can expand the icon for ArchestrA.MxAccess to see all the interfaces and other members provided by the MXAccess interface.
Adding a Toolkit Reference Using C++
You can add a toolkit reference in C++.
Note: Visual C++ supports smart pointers. When used with COM interface pointers, smart pointers can be a powerful way to ensure that COM objects get properly instantiated at startup and cleaned up at shutdown. The code examples in this toolkit include one C++ project that uses smart pointers (the ActiveX dialog box) and another that makes it clear where the COM objects are being created and destroyed. For more information on using smart pointer templates in your C++ program, see the Visual Studio documentation.
To add a reference
- In the file stdafx.h, add a line such as the following example to import the type library:
#import "C:\Program Files\ArchestrA\Framework\bin\MxAccess32.tlb" no_namespace raw_interfaces_only
When you compile, Visual Studio creates a file named MxAccess.tlh.
- Open this file to see the interfaces and other members provided by the MXAccess interface.
Handling Exceptions in Your API Code
Include exception handling when any of the API calls are invoked.
- Visual Basic.NET Add an OnError statement and handling to your functions and subroutines.
- C# Enclose your code in a try block and provide error handling and display in a catch block.
- Visual C++ Enclose your code in a try block and provide error handling and display in a catch block. You should also inspect the HRESULT returned from the API functions and add code to handle failures.
Declaring and Instantiating the LMXProxyServer Object The code examples show you the basic operations of using the MXAccess API function calls. Although they include rudimentary exception handling, they do not handle all possible errors and exceptions. Your program should include exception and error processing to provide robust operation and problem diagnostics appropriate for your specific application.
Declaring and Instantiating the LMXProxyServer Object
To use the MXAccess Toolkit, your program must declare the LMXProxyServer object. Use the following examples, depending on which language you use.
Examples
[Visual Basic.NET]
Dim WithEvents LMX_Server As ArcestrA.MxAccess.LMXProxyServer
[C#]
ArcestrA.MxAccess.LMXProxyServer LMX_Server;
[Visual C++]
ILMXProxyServer* pLMX_Server;
or, if using a smart pointer
CComPtr<ILMXProxyServer> pLMX_Server;
Create an instance of the LMXProxyServer. Use the following examples, depending on which language you use.
Examples
[Visual Basic.NET]
LMX_Server = New ArcestrA.MxAccess.LMXProxyServer
[C#]
LMX_Server = new ArcestrA.MxAccess.LMXProxyServer;
[Visual C++]
HRESULT hr = CoCreateInstance (__uuidof(LMXProxyServer),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(ILMXProxyServer),
(void **)&pLMX_Server);
or, if using a smart pointer
HRESULT hr =
pLMX_Server.CoCreateInstance (__uuidof( LMXProxyServer ));
In Visual C++ your program must initialize COM before it can perform any operations with COM. Most likely, you will create the LMXProxy in the main program thread. For example:
HRESULT hr =
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
initializes COM for the apartment threaded model. Be sure to call CoUninitialize() when your program exits.
Use the following examples at the end of your program to discard the LMXProxyServer.
[Visual Basic.NET]
LMX_Server = Nothing
(or simply let it go out of scope)
[C#]
LMX_Server = null;
(or simply let it go out of scope)
[Visual C++]
HRESULT hr = pLMX_Server->Release();
pLMX_Server = NULL;
Or, if using a smart pointer, you can simply let it go out of scope.
Connecting Handlers for Events Reported from the LMXProxyServer # Connecting Handlers for Events Reported from the LMXProxyServer
The MXAccess Toolkit supports receiving notifications from the LMXProxyServer. In COM, these notifications are handled as connection points. In .NET, these are handled as delegates. Your program must implement a handler for each type of event you expect to receive and connect it to the events using the mechanisms appropriate for the programming language you are using.
Visual Basic and C# make this implementation fairly easy. Visual C++ requires a little more setup to create a COM object that exposes the necessary interface, including support for the IDispatch interface.
The events that must be handled are:
- OnDataChange() Reports the value and status of an ArcestrA Attribute to which the program has subscribed.
- OnWriteComplete() Reports the status after a write operation completes, indicating such information as whether the write operation succeeded or failed.
- OperationComplete() Reports the status when an Advanced Communication Management operation has been completed, indicating such information as whether it succeeded or failed.
- OnBufferedDataChange() Reports the VTQ buffer and the status of an ArcestrA Attribute to which the program has subscribed.
The specifics of what your event handlers do depend on the purpose of your program. This section focuses primarily on how to connect the handlers in your program to the event callback mechanism. For more information about what should be inside the code for the event handlers, see "Handling the OnDataChange Callback" on page 62, see "Handling the OnWriteComplete Callback" on page 72, see "Handling the OperationComplete Callback" on page 41, and see "Handling the OnBufferedDataChange Callback" on page 63.
Handling events involves implementing required methods, exposing them as event handlers, and connecting the event handlers to the LMXProxyServer's Connection Points.
[Visual Basic.NET]
Declare the methods as:
Private Sub LMX_OnDataChange(
ByVal hLMXServerHandle As Integer,
ByVal phItemHandle As Integer,
ByVal pvItemValue As Object,
ByVal pwItemQuality As Integer,
ByVal pftItemTimeStamp As Object,
ByRef ItemStatus() As ArchestrA.MxAccess.MXSTATUS_PROXY
)
Handles LMX_Server.OnDataChange
and
Private Sub LMX_OnWriteComplete(
ByVal hLMXServerHandle As Integer,
ByVal phItemHandle As Integer,
ByRef ItemStatus() As ArchestrA.MxAccess.MXSTATUS_PROXY
)
Handles LMX_Server.OnWriteComplete
and
Private Sub LMX_OperationComplete(
ByVal hLMXServerHandle As Integer,
ByVal phItemHandle As Integer,
ByRef ItemStatus() As ArchestrA.MxAccess.MXSTATUS_PROXY
)
Handles LMX_Server.OperationComplete
and
Private Sub LMX_OnBufferedDataChange(
ByVal hLMXServerHandle As Integer,
ByVal phItemHandle As Integer,
ByVal dtDataType As ArchestrA.MxAccess.MxDataType,
ByVal pvItemValue As Object,
ByVal pwItemQuality As Object,
ByVal pftItemTimeStamp As Object,
ByRef ItemStatus() As ArchestrA.MxAccess.MXSTATUS_PROXY
)
Handles LMX_Server.OnBufferedDataChange
Connecting Handlers for Events Reported from the LMXProxyServer At run time, Visual Basic automatically connects the event handlers when you instantiate the LMX_Server object.
Note: The names do not have to be LMX_OnDataChange, LMX_OnWriteComplete, and LMX_OperationComplete, but the parameter types in the function syntax must match declarations in the LMXProxy interface.
[C#]
Declare the methods as:
private void LMX_OnDataChange(
int hLMXServerHandle,
int phItemHandle,
object pvItemValue,
int pwItemQuality,
object pftItemTimeStamp,
ref ArchestrA.MxAccess.MXSTATUS_PROXY[ ] ItemStatus
);
and
private void LMX_OnWriteComplete(
int hLMXServerHandle,
int phItemHandle,
ref ArchestrA.MxAccess.MXSTATUS_PROXY[ ] ItemStatus
);
and
private void LMX_OperationComplete(
int hLMXServerHandle,
int phItemHandle,
ref ArchestrA.MxAccess.MXSTATUS_PROXY[ ] ItemStatus
);
and
private void LMX_OnBufferedDataChange(
int hLMXServerHandle,
int phItemHandle,
ArchestrA.MxAccess.MxDataType dtDataType,
object pvItemValue, object pvItemQuality,
object pvItemTimeStamp,
ref ArchestrA.MxAccess.MXSTATUS_PROXY[ ] ItemStatus
);
At run time, C# does not automatically connect these event handlers for you. You must do this in your code. After you have instantiated the LMX_Server object, the following three lines will connect the methods as delegates for the object:
LMX_Server.OnDataChange += new
_ILMProxyServerEvents_OnDataChangeEventHandler(LMX_OnDataChange);
LMX_Server.OnWriteComplete += new
_ILMProxyServerEvents_OnWriteCompleteEventHandler(LMX_OnWriteComplete);
LMX_Server.OperationComplete += new
_ILMProxyServerEvents_OperationCompleteEventHandler(LMX_OperationComplete);
LMX_Server.OnBufferedDataChange += new
_ILMProxyServerEvents2_OnBufferedDataChangeEventHandler(LMX_OnBufferedDataChange);
Note: The names do not have to be LMX_OnDataChange, LMX_OnWriteComplete, and LMX_OperationComplete, but the parameter types in the function syntax must match declarations in the LMXProxy interface.
[Visual C++]
Declare the methods as:
HRESULT CLMX_Events_Shim::OnDataChange (
long hLMXServerHandle,
ITEMHANDLE phItemHandle,
VARIANT pvItemValue,
long pwItemQuality,
VARIANT pftItemTimeStamp,
SAFEARRAY *pSAItemStatus
);
and
HRESULT CLMX_Events_Shim::OnWriteComplete (
long hLMXServerHandle,
ITEMHANDLE phItemHandle,
SAFEARRAY *pSAWriteItemStatus
);
and
HRESULT CLMX_Events_Shim::OperationComplete (
long hLMXServerHandle,
ITEMHANDLE phItemHandle,
SAFEARRAY *pSAWriteItemStatus);
Connecting Handlers for Events Reported from the LMXProxyServer and
HRESULT CLMX_Events_Shim::OnBufferDataChange(
long hLMXServerHandle,
ITEMHANDLE phItemHandle,
ArchestrA.MxAccess.MxDataType dtDataType,
VARIANT pvItemValue,
VARIANT pwItemQuality,
VARIANT pftItemTimeStamp,
SAFEARRAY *pSAItemStatus);
where CLMX_Events_Shim is a class derived from the base class LMXProxyServerEvents.
The sample code includes a header file and implementation of CLMXProxyServerEvents that is derived from IDispatch and handles passing IDispatch methods to the OnDataChange(), OnWriteComplete(), and OperationComplete() methods.
At run time, C++ does not automatically connect these event handlers for you. You must do this in your code. After you have instantiated the LMX_Server object, you must instantiate the CLMX_Events_Shim object. It helps to have two methods in CLMX_Events_Shim that encapsulate the Connect() and Disconnect() operations:
void CLMX_Events_Shim::Connect()
{
// check whether we are already connected
if (!m_bConnected)
{
// attempt to call Advise on the
// IConnectionPoint
DWORD cookie = 0;
IConnectionPointContainer* pcPC = 0;
HRESULT hr = pLMX_Server->QueryInterface(&pcPC);
if (SUCCEEDED(hr))
{
hr = pcPC->FindConnectionPoint(
__uuidof(_ILMXProxyServerEvents), &m_pCP);
if (SUCCEEDED(hr))
{
hr = m_pCP->Advise(this, &cookie);
m_LmxInterfaceCallBacksCookie = cookie;
m_bConnected = true;
}
pcPC->Release();
}
}
}
and
void CLMX_Events_Shim::Disconnect()
{
// If we are connected then call Unadvise on
// the IConnectionPoint
if (m_bConnected)
{
m_bConnected = false;
IConnectionPoint* pConnectionPoint = m_pCP;
m_pCP->Unadvise(m_LmxInterfaceCallBacksCookie);
pConnectionPoint->Release();
}
}
After you instantiate the LMXProxyServer object, make sure that you also instantiate an object of type CLMX_Events_Shim and then call its Connect() method. When you are preparing to delete (or even Unregister) the LMXProxyServer object, you should call the Disconnect() method to stop servicing the event callbacks.
Note: The class names do not have to be CLMX_Events_Shim and CLMXProxyServerEvents but the overall organization of these classes should be similar to the code samples. Because the methods are exposed directly as a COM interface, the names DO have to be OnDataChange, OnWriteComplete, and OperationComplete and the parameter types in the function syntax must match declarations in the LMXProxy interface.
Initializing the LMXProxyServer Connection
The LMXProxyServer must be initialized prior to making other method calls on the COM object's interface. Use the Register() method, which connects your component to LMX.
Register() Method
Connects your component to LMX.
Syntax
[Visual Basic.NET]
Function Register (ByVal pClientName As String) As Integer
[C#]
int Register(string pClientName);
[Visual C++]
HRESULT __stdcall Register(BSTR pClientName, long *phLMXServerHandle);
Shutting Down the LMXProxyServer Connection Parameters
pClientName Name of your component.
Returns
Handle of the connection. The handle that is returned represents the LMX connection. Your program should preserve this handle for the life time of the connection because it is used on all subsequent calls to other methods on the interface.
Examples
[Visual Basic.NET]
hLMX = LMX_Server.Register("TestApp")
[C#]
hLMX = LMX_Server.Register("TestApp");
[Visual C++]
BSTR bstrTemp = ::SysAllocString(L"TestApp");
hr = pLMX_Server->Register(bstrTemp, &hLMX);
::SysFreeString(bstrTemp);
Shutting Down the LMXProxyServer Connection
The LMXProxyServer connection must be released when your application shuts down or when it no longer needs to be connected to the Galaxy. Use the Unregister() method, which disconnects your component from LMX.
Before calling the Unregister() method, you must unadvise all active items and remove them from the internal tables using the UnAdvise() and RemoveItem() methods.
Unregister() Method
Disconnects your component from LMX.
Syntax
[Visual Basic.NET]
Sub Unregister (ByVal hLMXServerHandle As Integer)
[C#]
void Unregister(int hLMXServerHandle);
[Visual C++]
HRESULT __stdcall Unregister(long hLMXServerHandle);
Parameters
- hLMXServerHandle Handle of the connection.
Returns
Nothing.
Remarks
After calling the Unregister() method, you can discard the LMXProxyServer object. If your program is one that connects to LMX for a time, disconnects, and connects again, you may want to keep the LMXProxyServer object, rather than releasing it and creating a new one each time.
Examples
[Visual Basic.NET]
Call LMX_Server.Unregister (hLMX)
[C#]
LMX_Server.Unregister (hLMX);
[Visual C++]
hr = pLMX_Server->Unregister (hLMX);
Adding Item References to the Internal Tables
To access an ArchestrA Attribute, you must first add it to the internal table of attribute references that LMX maintains for your connection. Use the AddItem() or the AddItem2() method.
AddItem() Method
Adds an attribute to the internal table of attributes.
Syntax
[Visual Basic.NET]
Function AddItem (
ByVal hLMXServerHandle As Integer,
ByVal strItemDef As String
)
As Integer
Adding Item References to the Internal Tables [C#]
int AddItem(
int hLMXServerHandle, string strItemDef
);
[Visual C++]
HRESULT __stdcall AddItem(
long hLMXServerHandle,
BSTR strItemDef,
long *phItem
);
Parameters
hLMXServerHandle Handle of the connection.
strItemDef Reference string for the attribute.
Returns
Handle of the item. The handle that is returned represents the item in the LMX connection's internal table. Your program should retain this handle because it is used on all subsequent calls to other methods involved in reading and writing data for that attribute.
Remarks
You can add several attributes to the table by calling the AddItem() method for each one.
Before you can write to an attribute or receive data update notifications for it, you must put the item on advise. For more information, see "Advise() Method" on page 33.
When shutting a program down, you should unadvise all active items and remove all items from the internal table before calling the Unregister() method. For more information, see "UnAdvise() Method" on page 36 and "RemoveItem() Method" on page 32.
Examples
[Visual Basic.NET]
hItem = LMX_Server.AddItem(hLMX, strItemDef)
[C#]
hItem = LMX_Server.AddItem(hLMX, strItemDef);
[Visual C++]
BSTR bstrTemp = ::SysAllocString(szItemDef);
hr = pLMX_Server->AddItem(hLMX, bstrTemp, &hItem);
::SysFreeString (bstrTemp);
If the attribute represents a single data value, reference the item as follows:
[Single data value] ud1.x
[Single element of an array:] ud1.ar[2]
To reference all elements of an attribute that is an array, follow the reference with brackets:
[Entire array:] ud1.ar[ ]
AddItem2() Method
Adds an attribute to the internal table of attributes, using a relative reference and an Object name. This makes it easier to resolve a reference for an attribute relative to an indicated Object.
The reference string must use one of the five relative reference names:
- MyPlatform
- MyEngine
- MyArea
- MyContainer
- Me
The context string identifies the Object to which the reference corresponds.
Syntax
[Visual Basic.NET]
Function AddItem2 (
ByVal hLMXServerHandle As Integer,
ByVal strItemDef As String
ByVal strItemCtxt As String
)
As Integer
Adding Item References to the Internal Tables [C#]
int AddItem2(
int hLMXServerHandle,
string strItemDef
string strItemCtxt
);
[Visual C++]
HRESULT __stdcall AddItem2 (
long hLMXServerHandle,
BSTR strItemDef,
BSTR strItemCtxt,
long *phItem
);
Parameters
hLMXServerHandle Handle of the connection.
strItemDef Reference string for the attribute.
strItemCtxt Context string for the related object.
Returns
Handle of the item. The handle that is returned represents the item in the LMX connection's internal table. Your program should retain this handle because it is used on all subsequent calls to other methods involved in reading and writing data for that attribute.
Remarks
You can add several attributes to the table by calling the AddItem() method for each one.
Before you can write to an attribute or receive data update notifications for it, you must put the item on advise. For more information, see "Advise() Method" on page 33.
When shutting the program down, you should unadvise all active items and remove all items from the internal table before calling the Unregister() method. For more information, see "UnAdvise() Method" on page 36 and "RemoveItem() Method" on page 32.
The AddItem2() method is on the ILMXProxyServer4 interface, which extends the ILMXProxyServer interface.
Examples
[Visual Basic.NET]
hItem = LMX_Server.AddItem2(hLMX, strItemDef, strItemCtxt)
[C#]
hItem = LMX_Server.AddItem2(hLMX, strItemDef, strItemCtxt);
[Visual C++]
BSTR bstrTemp = ::SysAllocString(szItemDef);
BSTR bstrCtxt = ::SysAllocString(szItemCtxt);
hr = pLMX_Server->AddItem2(hLMX, bstrTemp, bstrCtxt,
&hItem);
::SysFreeString(bstrTemp);
::SysFreeString(bstrCtxt);
If the Attribute belongs to the Platform on which an Object is running, you do not need to know the actual name of the Platform. You can add an item using MyPlatform in the reference string, and specify the Object as the context for the reference, using its global name. Other relative references can be specified in the same way:
- CPULoad on the Platform on which AnalogDevice_001 is running:
- hItem = LMX_Server.AddItem2(hLMX, "MyPlatform.CPULoad", "AnalogDevice_001");
- Scan period of the Engine on which ud1 is hosted:
- hItem = LMX_Server.AddItem2(hLMX, "MyEngine.Scheduler.ScanPeriod", "ud1");
AddBufferedItem() Method
You can configure your client program to subscribe to buffered attributes. Buffered attributes support retrieving and processing of multiple data items which are received from field devices during AppEngine's Scan Period. The multiple data items are received by buffered attributes once per scan period in the form of buffer, in order to eliminate data folding.
Syntax
[Visual Basic.NET]
Function AddBufferedItem(
hLMXServerHandle As Integer,
strItemDef As String,
strItemCtxt As String
)
As Integer
Adding Item References to the Internal Tables [C#]
```csharp
int AddBufferedItem(
int hLMXServerHandle,
string strItemDef,
string strItemCtxt)
[Visual C++]
HRESULT __stdcall AddBufferedItem (
long hLMXServerHandle,
BSTR strItemDef,
BSTR strItemCtxt,
long * phItem )
Parameters
hLMXServerHandle Handle of the connection.
strItemDef Reference string for the attribute.
strItemCtxt Context string for the related object.
Returns
Handle of the item. The handle that is returned represents the item in the LMX connection's internal table. Your program should retain this handle because it is used on all subsequent calls to other methods involved in reading and writing data for the buffered attribute.
Remarks
You can add several attributes to the table by calling the AddBufferedItem() method for each one.
Before you can write to an attribute or receive data update notifications for it, you must put the item on advise. For more information, see "Advise() Method" on page 33.
When shutting a program down, you should unadvise all active items and remove all items from the internal table before calling the Unregister() method. For more information, see "UnAdvise() Method" on page 36 and "RemoveItem() Method" on page 32.
Examples
[Visual Basic.NET]
hItem = LMX_Server.AddBufferedItem(hLMX, strItemDef, strItemCtxt)
[C#]
hItem = LMX_Server.AddBufferedItem(hLMX, strItemDef,
strItemCtxt);
[Visual C++]
LMX_Server.AddBufferedItem(hLMXServerHandle, strItemDef, strItemCtxt, phItem);
Removing Item References from Internal Tables
When you no longer need access to a particular attribute, you can remove the reference to it from the internal table. Use the RemoveItem() method.
RemoveItem() Method
Removes an attribute from the internal table of attributes.
Syntax
[Visual Basic.NET]
Sub RemoveItem (
ByVal hLMXServerHandle As Integer,
ByVal hItem As Integer
)
[C#]
void RemoveItem(
int hLMXServerHandle,
int hItem
);
[Visual C++]
HRESULT __stdcall RemoveItem(
long hLMXServerHandle,
long hItem
);
Parameters
hLMXServerHandle Handle of the connection.
hItem Handle of the item.
Putting Items on Advise Returns Nothing.
Remarks If the item is active—that is, if it has been placed on advise—unadvise the item before removing it. For more information, see "UnAdvise() Method" on page 36. When shutting down, unadvise all active items and remove all items from the internal table before calling the Unregister() method. For more information, see "UnAdvise() Method" on page 36 and "RemoveItem() Method" on page 32.
Examples [Visual Basic.NET] Call LMX_Server.RemoveItem(hLMX, hItem) [C#] LMX_Server.RemoveItem(hLMX, hItem); [Visual C++] hr = pLMX_Server->RemoveItem(hLMX, hItem);
Putting Items on Advise
To receive data updates regarding an attribute, you can put it on advise, also known as subscribing to an item. Use the Advise() method or the AdviseSupervisory() method.
Advise() Method
Adds advise to an attribute.
Syntax
[Visual Basic.NET]
Sub Advise(
ByVal hLMXServerHandle As Integer,
ByVal hItem As Integer
)
[C#]
void Advise(
int hLMXServerHandle,
int hItem
);
[Visual C++]
HRESULT __stdcall Advise(
long hLMXServerHandle,
long hItem
);
Parameters
hLMXServerHandle Handle of the connection.
hItem Handle of the item.
Returns
Nothing.
Remarks
After you have put an item on advise, it is considered active. If the data of that attribute changes, your program receives a notification of the update through the OnDataChange() event.
You must put an item on advise before you can write to it.
You must call the AddItem() method on an item before you can put it on advise. When shutting down the program, you should unadvise all active items and remove all items from the internal table before calling Unregister(). For more information, see "UnAdvise() Method" on page 36 and "RemoveItem() Method" on page 32.
Examples
[Visual Basic.NET]
Call LMX_Server.Advise(hLMX, hItem)
[C#]
LMX_Server.Advise(hLMX, hItem);
[Visual C++]
hr = pLMX_Server->Advise(hLMX, hItem);
Putting Items on Advise # AdviseSupervisory() Method
Adds advise to an attribute.
Syntax
[Visual Basic.NET]
Sub AdviseSupervisory(
ByVal hLMXServerHandle As Integer,
ByVal hItem As Integer
)
[C#]
void AdviseSupervisory(
int hLMXServerHandle,
int hItem
);
[Visual C++]
HRESULT __stdcall AdviseSupervisory(
long hLMXServerHandle,
long hItem
);
Parameters
hLMXServerHandle Handle of the connection.
hItem Handle of the item.
Returns
Nothing.
Remarks
The Advise() method establishes a User Connection to ArchestrA data, while the AdviseSupervisory() method establishes a Supervisory Connection. Both of these connections provide updates via the OnDataChange() callback and status information via the OnWriteComplete() and OperationComplete() callbacks. When you attempt to invoke a Write operation on the item, the Supervisory Connection does a Supervisory Set on the Attribute; it does not require a login by a user.
The AdviseSupervisory() method is on the ILMXProxyServer4 interface, which extends the ILMXProxyServer interface.
The Supervisory Connection is appropriate if your application runs in the background, such as a Windows Service, which normally does not interact with users via a GUI. Using AdviseSupervisory() for the items enables the program to write to items that require Secured Writes or Verified Writes, without requiring a user login to permit the write.
You must put an item on advise before you can write to it.
You must call the AddItem() method on an item before you can put it on advise. When shutting down the program, you should unadvise all active items and remove all items from the internal table before calling Unregister(). For more information, see "UnAdvise() Method" on page 36 and "RemoveItem() Method" on page 32.
Examples
[Visual Basic.NET]
Call LMX_Server.AdviseSupervisory(hLMX, hItem)
[C#]
LMX_Server.AdviseSupervisory(hLMX, hItem);
[Visual C++]
hr = pLMX_Server->AdviseSupervisory(hLMX, hItem);
Unadvising Items
When your program no longer needs to receive data updates and no longer needs to write to an attribute, it can unadvise the item—that is, it can take it off advise. Use the UnAdvise() method.
UnAdvise() Method
Stops the advise from the attribute.
Syntax
[Visual Basic.NET]
Sub UnAdvise(
ByVal hLMXServerHandle As Integer,
ByVal hItem As Integer
)
[C#]
void UnAdvise (
int hLMXServerHandle,
int hItem
);
Unadvising Items [Visual C++]
HRESULT __stdcall UnAdvise (
long hLMXServerHandle,
long hItem
);
Parameters
hLMXServerHandle Handle of the connection.
hItem Handle of the item.
Returns
Nothing.
Remarks
When shutting down, unadvise all active items and remove all items from the internal table before calling the Unregister() method. For more information, see "UnAdvise() Method" on page 36 and "RemoveItem() Method" on page 32.
Examples
[Visual Basic.NET]
Call LMX_Server.UnAdvise(hLMX, hItem)
[C#]
LMX_Server.UnAdvise(hLMX, hItem);
[Visual C++]
hr = pLMX_Server->UnAdvise(hLMX, hItem);
Advanced Communication Management
After an attribute has been put on advise, the application can call Suspend() to temporarily stop receiving updates without tearing down the subscription to the attribute. Updates can be resumed by calling Activate(). The callback OperationComplete() returns the status information about processing these function calls.
The Suspend() and Activate() methods are on the ILMXProxyServer4 interface, which extends the ILMXProxyServer interface.
When an item is suspended or activated, that item should not be taken off advise or removed from the internal tables until the OperationComplete() event is received, indicating whether the update to the Advanced Communication Management status is successful or an error has occurred.
If the event indicates that an OperationComplete error has been detected, check the category and error code for further information.
To suspend updates, call the Suspend() method.
Suspend() Method
Temporarily stops receiving updates without removing the subscription to the attribute.
Syntax
[Visual Basic.NET]
Sub Suspend(ByVal hLMXServerHandle As Integer,
ByVal hItem As Integer,
ByRef pMxStatus As ArcestrA.MxAccess.MxStatus)
[C#]
void Suspend(int hLMXServerHandle,
int hItem,
out ArcestrA.MxAccess.MxStatus pMxStatus);
[Visual C++]
HRESULT Suspend(long hLMXServerHandle,
long hItem,
MxStatus *pMxStatus);
Parameters
hLMXServerHandle Handle of the connection
hItem Handle of the item
Advanced Communication Management Returns
MxStatus indicating success, and whether operation is pending.
Remarks
If the returned MxStatus indicates an error, it may be because the handle to the LMXServer or the handle to the item is not registered. If it is successful, check the category of the returned MxStatus, as the operation may still be pending. Upon completion, an OperationComplete event will be received, indicating whether the operation has completed successfully or there is a problem.
To resume updates, call the Activate() method.
Examples
[Visual Basic.NET]
Call LMX_Server.Suspend(hLMX, hItem, hItemStatus)
[C#]
LMX_Server.Suspend(hLMX, hItem, out ItemStatus);
[Visual C++]
hr = pLMX_Server->Suspend(hLMX, hItem, &ItemStatus);
Activate() Method
Starts receiving updates without removing the subscription to the attribute.
Syntax
[Visual Basic.NET]
Sub Activate(ByVal hLMXServerHandle As Integer,
ByVal hItem As Integer,
ByRef pMxStatus As ArcestrA.MxAccess.MxStatus)
[C#]
void Activate(int hLMXServerHandle,
int hItem,
ArcestrA.MxAccess.MxStatus pMxStatus);
[Visual C++]
HRESULT Activate(long hLMXServerHandle,
long hItem,
MxStatus *pMxStatus);
Parameters
- hLMXServerHandle Handle of the connection
- hItem Handle of the item
Returns
MxStatus indicating success, and whether operation is pending.
Remarks
If the returned MxStatus indicates an error, it may be because the handle to the LMXServer or the handle to the item is not registered. If it is successful, check the category of the returned MxStatus, as the operation may still be pending. Upon completion, an OperationComplete event will be received, indicating whether the operation has completed successfully or there is a problem.
When either Suspend() or Activate() is called, the completion of the status change may take some time to process. When the processing is finished, an OperationComplete event is received, indicating which item has been suspended or activated, and whether the operation was successful.
Examples
[Visual Basic.NET]
Call LMX_Server.Activate(hLMX, hItem, ItemStatus)
[C#]
LMX_Server.Activate(hLMX, hItem, out ItemStatus);
[Visual C++]
hr = pLMX_Server->Activate(hLMX, hItem, &ItemStatus);
Handling the OperationComplete Callback # Handling the OperationComplete Callback
The LMXProxyInterface emits an event for OperationComplete when your program invokes a Suspend() or Activate() function. The information your program receives identifies whether the change was successful and any error code information that may be appropriate.
OperationComplete Event
Reports the status of an operation on an Attribute subscription, such as Suspend or Activate.
Syntax
[Visual Basic.NET]
Private Sub LMX_OperationComplete(
ByVal hLMXServerHandle As Integer,
ByVal phItemHandle As Integer,
ByVal pvItemValue As Object,
ByVal pwItemQuality As Integer,
ByVal pftItemTimeStamp As Object,
ByRef ItemStatus() As ArchestrA.MxAccess.MXSTATUS_PROXY
)
Handles LMX_Server.OperationComplete
[C#]
private void LMX_OperationComplete(
int hLMXServerHandle,
int phItemHandle,
object pvItemValue,
int pwItemQuality,
object pftItemTimeStamp,
ref ArchestrA.MxAccess.MXSTATUS_PROXY[] ItemStatus
);
[Visual C++]
HRESULT CLMX_Events_Shim::OperationComplete(
long hLMXServerHandle,
ITEMHANDLE phItemHandle,
VARIANT pvItemValue,
long pwItemQuality,
VARIANT pftItemTimeStamp,
SAFEARRAY *pSAItemStatus
);
Parameters
- hLMXServerHandle Handle of the connection.
- phItemHandle Handle of the item.
- ItemStatus Array of Message Exchange statuses.
Returns
Nothing.
Remarks
For more information, including the function prototypes for the different language, see "Connecting Handlers for Events Reported from the LMXProxyServer" on page 19.
Interpreting the OperationComplete status is very much like interpreting the data change status and involves examining the contents of the MxStatus structure. If the success member of the status is true, the Suspend() or Activate() operation has been accepted and completed. If the success member is false, check the other structure members for additional information about why the operation failed.
Examples
[Visual Basic.NET]
If ItemStatus(0).success Then
CommunicationManagement_Status.Text = "Operation Complete - status OK"
Else
CommunicationManagement_Status.Text = "Operation Complete w/error - cat: " & ItemStatus(0).category & " Src: " & ItemStatus(0).detectedBy & " detail: " & ItemStatus(0).detail
End If
[C#]
if (ItemStatus[0].success != 0) {
CommunicationManagement_Status.Text = "Operation Complete - status OK";
} else {
CommunicationManagement_Status.Text = "Operation Complete w/error - cat: " + ItemStatus[0].category + " Src: " + ItemStatus[0].detectedBy + " detail: " + ItemStatus[0].detail;
}
Writing Data Values [Visual C++]
if (ItemStatus[0].success != 0) {
SetDlgItemText(hMainDlg, IDC_CommunicationManagement_Status,
L"Operation Complete - status OK");
} else {
TCHAR error_text[256];
swprintf_s(error_text,
sizeof(error_text)/sizeof(TCHAR), L"Operation Complete
w/error - cat: %d Src: %d detail: %d",
ItemStatus[0].category, ItemStatus[0].detectedBy,
ItemStatus[0].detail);
SetDlgItemText(hMainDlg,
IDC_CommunicationManagement_Status, error_text);
}
Writing Data Values
To write data to an attribute, call the Write() method or the WriteSecured() method.
To write data to an attribute, call the Write() method or the WriteSecured() method.
To write data and a specific timestamp to an attribute, call the Write2() method or the WriteSecured2() method.
To write to an item, the data must be in an object (or in C++ a VARIANT) of the proper internal type—integer, double, string, or other supported data types.
String data can be used to write to an attribute of any data type, so long as the string can be converted to the appropriate data type. Otherwise, the MxStatus returned by the OnWriteComplete() event indicates an error. For example, writing "1/27/2014 03:43:02 PM" to an integer value generates an error.
Write() Method
Writes data to an attribute.
Syntax
[Visual Basic.NET]
Sub Write(
ByVal hLMXServerHandle As Integer,
ByVal hItem As Integer,
ByVal pItemValue As Object,
ByVal UserID As Integer
)
[C#]
void Write(
int hLMXServerHandle,
int hItem,
object pItemValue,
int UserID
);
[Visual C++]
HRESULT __stdcall Write(
long hLMXServerHandle,
long hItem,
VARIANT pItemValue,
long UserID
);
Parameters
hLMXServerHandle The handle of the connection.
hItem The handle of the item.
pItemValue The new data value.
UserID The ID "cookie" for the user. If ArchestrA security is not enabled, this value is -1 or 0.
Returns
None.
Remarks
Before you can write to an attribute, you must add the item to the internal table and put it on advise. For more information, see "AddItem() Method" on page 26 and "Advise() Method" on page 33.
Upon completion of the write, your program receives notification of the success/failure status through the OnWriteComplete() event.
When an item is written, that item should not be taken off advise or removed from the internal tables until the OnWriteComplete() event is received, indicating whether the write is successful or an error has occurred.
Writing Data Values If the event indicates that an OnWriteComplete error has been detected, check the error code.
- A code of 1008 indicates that the user does not have the proper security to write to this item.
- A code of 1012 indicates that a secured write is required.
- A code of 1013 indicates that a verified write is required.
For more information, see "WriteSecured() Method" on page 47 and "AuthenticateUser() Method" on page 54. Also check the documentation on LMX details for other possible error codes.
If a secured write or verified write is required, call the WriteSecured() method with the same item handle as was used for the Write() method.
Examples
[Visual Basic.NET]
Call LMX_Server.Write(hLMX, hItem, pItemValue, uindex1)
[C#]
LMX_Server.Write(hLMX, hItem, pItemValue, uindex1);
[Visual C++]
hr = pLMX_Server->Write(hLMX, hItem, pItemValue, uindex1);
Write2() Method
Writes data and a timestamp to an attribute.
Syntax
[Visual Basic.NET]
Sub Write2(
ByVal hLMXServerHandle As Integer,
ByVal hItem As Integer,
ByVal pItemValue As Object,
ByVal pItemTime As Object,
ByVal UserID As Integer
)
[C#]
void Write2(
int hLMXServerHandle,
int hItem,
object pItemValue,
object pItemTime,
int UserID
);
[Visual C++]
HRESULT __stdcall Write2(
long hLMXServerHandle,
long hItem,
VARIANT pItemValue,
VARIANT pItemTime,
long UserID
);
Parameters
hLMXServerHandle The handle of the connection.
hItem The handle of the item.
pItemValue The new data value.
pItemTime The new timestamp.
UserID The ID "cookie" for the user. If ArcestrA security is not enabled, this value is -1 or 0.
Returns
None.
Remarks
Before you can write to an attribute, you must add the item to the internal table and put it on advise. For more information, see "AddItem() Method" on page 26 and "Advise() Method" on page 33.
Upon completion of the write, your program receives notification of the success/failure status through the OnWriteComplete() event.
The Write2() method is on the ILMXProxyServer4 interface, which extends the ILMXProxyServer interface.
If a secured write or verified write with a timestamp is required, call the WriteSecured2() method with the same item handle as was used for the Write2() method.
Examples
[Visual Basic.NET]
Call LMX_Server.Write2(hLMX, hItem, pItemValue, pItemTime, uindex1)
[C#]
LMX_Server.Write2(hLMX, hItem, pItemValue, pItemTime, uindex1);
Writing Data Values [Visual C++]
hr = pLMX_Server->Write2(hLMX, hItem, pItemValue, pItemTime,
uindex1);
WriteSecured() Method
Writes data to an attribute that requires authentication, either as a secured write or as a verified write.
Syntax
[Visual Basic.NET]
Sub WriteSecured(
ByVal hLMXServerHandle As Integer,
ByVal hItem As Integer,
ByVal CurrentUserID As Integer,
ByVal VerifiedUserID As Integer,
ByVal pItemValue As Object
)
[C#]
void WriteSecured(
int hLMXServerHandle,
int hItem,
int CurrentUserID,
int VerifiedUserID,
object pItemValue
);
[Visual C++]
HRESULT __stdcall WriteSecured(
long hLMXServerHandle,
long hItem,
long CurrentUserID,
long VerifiedUserID,
VARIANT pItemValue
);
Parameters
hLMXServerHandle
- Handle of the connection.
hItem
- Handle of the item.
CurrentUserID
- ID "cookie" for the current user.
VerifiedUserID ID "cookie" for the verified user, or -1 or 0 if only a secured write is being done.
pItemValue The new data.
Returns Nothing.
Remarks If the attribute requires a secured write or verified write, you write to it with the WriteSecured() method. A secured write is analogous to submitting a change request that requires one signature, while a verified write corresponds to a change request that requires two signatures.
Each user must be authenticated by name and password, thereby obtaining the ID "cookie." For more information, see "AuthenticateUser() Method" on page 54.
Before you can write to an attribute, you must add the item to the internal table and put it on advise. For more information, see "AddItem() Method" on page 26 and "Advise() Method" on page 33.
Upon completion of the write, your program receives notification of the success/failure status through the OnWriteComplete() event.
Examples
[Visual Basic.NET]
Call LMX_Server.WriteSecured(hLMX, hItem, uindex1, uindex2,
pItemValue)
[C#]
LMX_Server.WriteSecured(hLMX, hItem, uindex1, uindex2,
pItemValue);
[Visual C++]
hr = pLMX_Server->WriteSecured(hLMX, hItem, uindex1, uindex2,
pItemValue);
Writing Data Values # WriteSecured2() Method
Writes data and timestamp to an attribute that requires authentication, either as a secured write or as a verified write.
## Syntax
**[Visual Basic.NET]**
```vbnet
Sub WriteSecured2 (
ByVal hLMXServerHandle As Integer,
ByVal hItem As Integer,
ByVal CurrentUserID As Integer,
ByVal VerifiedUserID As Integer,
ByVal pItemValue As Object,
ByVal pItemTime As Object
)
[C#]
void WriteSecured2 (
int hLMXServerHandle,
int hItem,
int CurrentUserID,
int VerifiedUserID,
object pItemValue,
object pItemTime
);
[Visual C++]
HRESULT __stdcall WriteSecured2 (
long hLMXServerHandle,
long hItem,
long CurrentUserID,
long VerifiedUserID,
VARIANT pItemValue,
VARIANT pItemTime
);
Parameters
hLMXServerHandle Handle of the connection.
hItem Handle of the item.
CurrentUserID ID "cookie" for the current user.
VerifiedUserID ID "cookie" for the verified user, or -1 or 0 if only a secured write is being done.
pItemValue The new data.
pItemTime The new timestamp.
Returns Nothing.
Remarks If the attribute requires a secured write or verified write, you write to it with the WriteSecured2() method. A secured write is analogous to submitting a change request that requires one signature, while a verified write corresponds to a change request that requires two signatures.
Each user must be authenticated by name and password, thereby obtaining the ID "cookie." For more information, see "AuthenticateUser() Method" on page 54.
Before you can write to an attribute, you must add the item to the internal table and put it on advise. For more information, see "AddItem() Method" on page 26 and "Advise() Method" on page 33.
Upon completion of the write, your program receives notification of the success/failure status through the OnWriteComplete() event.
The WriteSecured2() method is on the ILMXProxyServer4 interface, which extends the ILMXProxyServer interface.
Examples
[Visual Basic.NET]
Call LMX_Server.WriteSecured2 (hLMX, hItem, uindex1,
uindex2, pItemValue, pItemTime)
[C#]
LMX_Server.WriteSecured2 (hLMX, hItem, uindex1,
uindex2, pItemValue, pItemTime);
[Visual C++]
hr = pLMX_Server->WriteSecured2 (hLMX, hItem,
uindex1, uindex2, pItemValue, pItemTime);
Writing Data Values # Writing to a Single Element
When writing to a single ArchestrA element—that is, when you are not writing to several members of an array—it is possible to pass a string object (or VARIANT) to the Write() or WriteSecured() method:
[Visual Basic.NET]
Dim vValue as String
Call LMX_Server.Write(hLMX, hItem, vValue, uindex1)
[C#]
string vValue;
LMX_Server.Write(hLMX, hItem, vValue, uindex1);
[Visual C++]
TCHAR sValue;
... // set value of sValue to string
// representing the new value
VARIANT varItem;
if (ItemAsVariant (&varItem, sValue) {
hr = pLMX_Server->Write(hLMX, hItem, varItem, uindex1);
}
Writing to an Array
When writing to an array of ArchestrA elements, you must build an object (or in C++ a VARIANT) that contains the data as an array. It can be an array of strings or it can be an array of the actual data type.
For more information on implementing the handling in the ItemArrayAsVariant() and ReleaseItemArrayAsVariant() methods, see "Encapsulating Data Values in Visual C++" on page 52.
[Visual Basic.NET]
Dim vArray(2) as String
vArray(0) = vValue1.Text
vArray(1) = vValue2.Text
vArray(2) = vValue3.Text
Call LMX_Server.Write(hLMX, hItem, vArray, uindex1)
[C#]
string[] vArray = { vValue1.Text, vValue2.Text, vValue3.Text };
LMX_Server.Write(hLMX, hItem, vArray, uindex1);
[Visual C++]
#define INTARRAY_LEN 256
typedef TCHAR INTARRAY_ELEM [INTARRAY_LEN]
INTARRAY_ELEM intarray[3];
... // set values of intarray[0], intarray[1], //intarray[2] to strings representing new values
VARIANT varArray;
// build SAFEARRAY and put it into varArray
// (see example of implementation below)
if (ItemArrayAsVariant (&varArray, VT_BSTR, intarray, 3))
{
hr = pLMX_Server->Write(hLMX, hItem, varItem, uindex1);
ReleaseItemArrayAsVariant (&varItem, 3);
}
Encapsulating Data Values in Visual C++
To encapsulate the new data values as a SAFEARRAY, the simplest approach is to let the VARIANT mechanism handle the data conversion to the appropriate data type. Note that there is a slight difference on how the data should be handled if strings (BSTRs) are being stored in the SAFEARRAY. You must copy the string pointer into the entry, instead of the actual value.
The following example shows how to convert the new entries and put them into a SAFEARRAY. This same function is also included in the sample code for C++.
bool ItemArrayAsVariant (VARIANT* pvarItems,
VARTYPE datatype,
INTARRAY_ELEM* intarray, long NumEntries)
{
bool bRetVal = false;
long i;
HRESULT hr = S_OK;
VariantInit (pvarItems);
// set up a safe array to hold the data
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lBound = 0;
rgsabound[0].cElements = NumEntries;
SAFEARRAY *psa = SafeArrayCreate (datatype, 1, rgsabound);
if (psa)
{
// create entries
VARIANT varEntry;
Encapsulating Data Values in Visual C++ ```c++ for (i = 0; (i < NumEntries) && (SUCCEEDED(hr)); i++) { VariantInit(&varEntry); varEntry.vt = VT_BSTR; varEntry.bstrVal = ::SysAllocString(intarray[i]); switch (datatype) { case VT_BSTR : { SafeArrayPutElement(psa, &i, (void *)varEntry.bstrVal); } break; default : { hr = VariantChangeType(&varEntry, &varEntry, 0, datatype); // since VARIANT is a union, just // point to where value is stored SafeArrayPutElement(psa, &i, (void *)&varEntry.lVal); } break; } // switch // // free the temporary string if (varEntry.vt == VT_BSTR) { ::SysFreeString(varEntry.bstrVal); } } // modify the flags in fFeatures if (datatype == VT_BSTR) { psa->fFeatures |= (unsigned short) FADF_BSTR; } else { psa->fFeatures = 0; } if (SUCCEEDED(hr)) { // attach the array to the variant pvarItems->parray = psa; pvarItems->vt = (unsigned short) (datatype | VT_ARRAY); // indicate successfully created bRetVal = true; } return (bRetVal); }
```c
void ReleaseItemArrayAsVariant (VARIANT* pvarItems, long NumEntries)
{
UNREFERENCED_PARAMETER(NumEntries);
SAFEARRAY *psa = pvarItems->parray;
if (psa) {
SafeArrayDestroy(psa);
}
}
Authenticating Users
Some ArchestraA data is secured. The entire Galaxy can have security enabled and individual attributes can be secured in such a way that they can be updated only if the user has certain login credentials. Some attributes require a verified write, which means that two different users must confirm that the update should take place. This involves calling the AuthenticateUser() method, which asks the Galaxy to check a user's credentials and return an ID "cookie" that can be used in the Write() and WriteSecured() method calls.
AuthenticateUser() Method
Syntax
[Visual Basic.NET]
Function AuthenticateUser(
ByVal hLMXServerHandle As Integer,
ByVal VerifyUser As String,
ByVal VerifyUserPsw As String
)
As Integer
[C#]
int AuthenticateUser(
intLMXServerHandle,
string VerifyUser,
string VerifyUserPsw
);
[Visual C++]
HRESULT __stdcall AuthenticateUser(
long hLMXServerHandle,
BSTR VerifyUser,
BSTR VerifyUserPsw,
long *UserId
);
Authenticating Users Parameters
- hLMXServerHandle Handle of the connection.
- VerifyUser User account name.
- VerifyUserPsw User password.
Returns
ID "cookie" for the user, or zero if the user's credentials are rejected.
Remarks
The AuthenticateUser() method gives a name and password to the Galaxy for authorization. If the user name and password are valid, a GUID is generated for the log-in. However, instead of returning this GUID to the caller, the LMXProxyServer stores the GUID in its internal tables and assigns it an integer index—an ID "cookie"—that can be used in subsequent calls to other methods. If the user's credentials are invalid (wrong name, wrong password or both), the underlying COM function returns HRESULT 0x80070057 indicating that the parameters are invalid. In C#, this will throw a System.ArgumentException; and in VB it will throw an error which is identified as Err.Number 5. Your program should be prepared to check the HRESULT or handle the exception, and indicate that the user's credentials are invalid.
Examples
[Visual Basic.NET]
uindex1 = LMX_Server.AuthenticateUser(hLMX, uidText, pwdText)
[C#]
uindex1 = LMX_Server.AuthenticateUser(hLMX, uidText, pwdText);
[Visual C++]
BSTR bstrUser = ::SysAllocString(szUserId);
BSTR bstrPwd = ::SysAllocString(szUserPwd);
hr = pLMX_Server->AuthenticateUser(hLMX, bstrUser, bstrPwd,
&uindex1);
::SysFreeString(bstrPwd);
::SysFreeString(bstrUser);
Using an Established User Authentication
When your program calls the AuthenticateUser() method, LMX authenticates the user name and password and gets a GUID from the ArchestrA Galaxy that is valid for the current session. To simplify use in MXAccess, the LMXProxyServer then places this GUID in an internal table and associates it with an integer ID "cookie" so that the interfaces do not need to pass a GUID as a parameter every time an operation such as the Write() method or WriteSecured() is invoked. In short, the GUID is obtained, but your program never sees it.
If your program is a component hosted in InTouch—for example, an ActiveX object hosted in an InTouch application window—the policy is for the InTouch HMI to handle the authentication process, using its own log-in dialog boxes. In particular, WindowViewer presents a log-in dialog box when:
- An operator logs into the InTouch application.
- A secured write is to be performed.
- A verified write is to be performed.
WindowViewer keeps track of the GUIDs for these users. For your program to use these GUIDs to perform secured writes and verified writes, you must add them to the internal tables for the LMXProxyServer. Do this by calling the ArchestrAUserId() method, which passes the GUID to the LMXProxyServer, checks it for validity, and returns the corresponding ID cookie. You can then use this cookie with the other interface methods, such as the Write() and WriteSecured() method.
Note that this method is on the ILMXProxyServer2 interface, which extends the ILMXProxyServer interface.
ArchestrAUserId() Method
Adds a GUID to the internal table and returns a user ID.
Syntax
[Visual Basic.NET]
Function ArchestrAUserId(
ByVal hLMXServerHandle As Integer,
ByVal UserIdGuid As String
)
As Integer
Using an Established User Authentication [C#]
int ArcestrAUserToId(
int hLMXServerHandle,
string UserIdGuid);
[Visual C++]
HRESULT __stdcall ArcestrAUserToId(
long hLMXServerHandle,
BSTR UserIdGuid, long *UserId
);
Parameters
hLMXServerHandle Handle of the connection.
UserIdGuid String containing the GUID for the user ID.
Returns
User ID cookie, or zero if the user credentials are rejected.
Remarks
This method is used primarily in the context of a component hosted by the InTouch HMI. The InTouch HMI is written mostly in unmanaged code and therefore does not generally support hosting .NET visual components in an InTouch application window. However, an ActiveX component can be hosted in an InTouch window. Code samples in this Toolkit include an example in C++, implementing a simple ActiveX component that uses this interface.
Every time you call the ArcestrAUserToId() method with a valid GUID, the GUID is added to the internal table and a new ID is returned. If you add the same GUIDs over and over again, the table continues to grow and previous IDs returned for those same GUIDs can still be used to perform Write() and WriteSecured() operations.
To minimize duplicate entries, your program can maintain a map of GUIDs already submitted to the ArcestrAUserToId() method and their corresponding ID cookies and submit a GUID only if it has not already been used.
Examples
[Visual Basic.NET]
uindex1 = LMX_Server.ArcestrAUserToId(hLMX, uidGuid)
[C#]
uindex1 = LMX_Server.ArcestrAUserToId(hLMX, uidGuid);
[Visual C++]
BSTR bstrGuid = ::SysAllocString(szUserIdGuid);
hr = pLMX_Server->ArchestrAUserToId(hLMX, bstrGuid, &uindex1);
::SysFreeString(bstrGuid);
Obtaining User Authentication from InTouch
When a component hosted by the InTouch HMI requires a user log-in, as for a secured write or verified write, let the InTouch HMI handle the authentication using its own user log-in dialog boxes. There are two ways to get user GUIDs from the InTouch HMI:
- Call the get_LoggedInUserGuid() method to retrieve the GUID for the user currently logged in, if any.
- Call the LogInInTouchUser() method to show a confirmation log-in from the currently logged-in user (and, if appropriate, a verification user).
These are not methods of the MXAccess Toolkit, but are instead methods provided by the InTouch HMI itself, through the AppServerSecurity.dll.
To access the AppServerSecurity methods, you must import a reference to the AppServerSecurity.dll into your program.
[Visual C++]
- In the file stdafx.h, add a line such as the following to import the dll:
#import "C:\Program Files\Wonderware\InTouch\AppServerSecurity.dll"
no_namespace, raw_interfaces_only
- When you compile, Visual Studio creates a file AppServerSecurity.tlh. You can open this file to see the interface methods and other members provided by the AppServerSecurity interface.
In addition to the methods described above, the DLL includes the following enumerations for the LogInInTouchUser() method:
enum WriteType
{
WriteType_Undefined = 0,
WriteType_VerifiedWrite = 1,
WriteType_SecuredWrite = 2
};
enum LoginDetailError {
LoginDetailError_NoError = 0,
LoginDetailError_SecuredLoginFailed = 1,
LoginDetailError_VerifierLoginFailed = 2
};
To retrieve the GUID of the currently logged-in user, call the get_LoggedInUserGuid() method.
[Visual C++]
## Syntax
```c++
HRESULT get_LoggedInUserGuid (BSTR * loggedInUserId);
Example
// get GUID from InTouch for user
// currently logged in, if any
CComPtr<IInTouchLogin> pInTouchLogin;
HRESULT hr = pInTouchLogin.CoCreateInstance(
_uuidof( CurrentInTouchSecurity ));
if (SUCCEEDED(hr))
{
CComBSTR bstrGUID;
hr = pInTouchLogin->get_LoggedInUserGuid(&bstrGUID);
if (SUCCEEDED(hr))
{
// add GUID to internal table
if ((m_pPLMX_Server != NULL) && (m_hLMX != 0))
{
long tempIndex = 0;
hr = pLMX_Server->ArchestrAUserToId(m_hLMX,
bstrGUID, &tempIndex);
if (SUCCEEDED(hr) && (tempIndex != 0))
{
// use tempIndex as new user index
...
}
}
}
}
To invoke an InTouch log-in dialog and get GUIDs for the currently logged-in user (if any) and a verification user (if appropriate), call the LogInInTouchUser() method.
[Visual C++]
Syntax
HRESULT LogInInTouchUser ( enum WriteType typeOfWrite,
BSTR * userId,
BSTR * verifierId,
VARIANT_BOOL * success,
enum LoginDetailError * detailCode);
Example
// get GUID(s) from InTouch by invoking
// log-in dialog
CComPtr<IInTouchLogin> pInTouchLogin;
HRESULT hr = pInTouchLogin.CoCreateInstance(
_uuidof( CurrentInTouchSecurity ));
if (SUCCEEDED(hr))
{
WriteType typeOfWrite =
WriteType_VerifiedWrite;
CComBSTR bstrUserID;
CComBSTR bstrVerifierID;
VARIANT_BOOL varbSuccess = VARIANT_FALSE;
LoginDetailError detailCode =
LoginDetailError_NoError;
// determine type of login required
if (bNeedVerifiedWrite)
typeOfWrite = WriteType_VerifiedWrite;
else
typeOfWrite = WriteType_SecuredWrite;
// Ask InTouch to get user login(s) and
// return GUIDs
hr = pInTouchLogin->LogInInTouchUser(typeOfWrite,
&bstrUserID,
&bstrVerifierID, &varbSuccess, &detailCode);
if (SUCCEEDED(hr))
{
// get status information
bool bSuccess =
(varbSuccess != VARIANT_FALSE);
if (bSuccess)
{
// GUID(s) obtained
// Add them to internal tables,
// get corresponding ID(s)
if ((pLMX_Server != NULL) && (hLMX != 0))
{
long tempIndex1 = 0;
long tempIndex2 = 0;
hr =
m_pLMX_Server->ArchestrAUserToId(m_hLMX,
bstrUserID, &tempIndex1);
if (SUCCEEDED(hr) &&
(typeOfWrite == WriteType_VerifiedWrite))
{
hr =
m_pLMX_Server->ArchestrAUserToId(m_hLMX,
bstrVerifierID, &tempIndex2);
}
// now use tempIndex1,
// tempIndex2 to update the IDs
if (tempIndex1 != 0)
// secured user
uindex1 = tempIndex1;
if (tempIndex2 != 0)
// verifier user
uindex2 = tempIndex2;
}
}
else
{
// handle error or indicate problem,
// using returned detailCode for
// further information
...
}
}
}
The new user ID "cookies" may now be used in the WriteSecured()
method.
Handling the OnDataChange Callback
The LMXProxyInterface triggers an OnDataChange event when an update occurs to an ArcestrA Attribute that your program has on advise. The information your program receives contains the updated data values and status information. If appropriate, it may also provide error code information.
OnDataChange Event
Reports the value and the status of an ArcestrA Attribute to which the program has subscribed.
Syntax
[Visual Basic.NET]
Private Sub LMX_OnDataChange(
ByVal hLMXServerHandle As Integer,
ByVal phItemHandle As Integer,
ByVal pvItemValue As Object,
ByVal pwItemQuality As Integer,
ByVal pftItemTimeStamp As Object,
ByRef ItemStatus() As ArcestrA.MxAccess.MXSTATUS_PROXY
)
Handles LMX_Server.OnDataChange
[C#]
private void LMX_OnDataChange (
int hLMXServerHandle,
int phItemHandle,
object pvItemValue,
int pwItemQuality,
object pftItemTimeStamp,
ref ArcestrA.MxAccess.MXSTATUS_PROXY[] ItemStatus
);
[Visual C++]
HRESULT CLMX_Events_Shim::OnDataChange (
long hLMXServerHandle,
ITEMHANDLE phItemHandle,
VARIANT pvItemValue,
long pwItemQuality,
VARIANT pftItemTimeStamp,
SAFEARRAY *pSAItemStatus
);
Handling the OnBufferedDataChange Callback Parameters
- hLMXServerHandle
- Handle of the connection
- phItemHandle
- Handle of the item
- pvItemValue
- New data value
- pwItemQuality
- New quality
- pftItemTimeStamp
- New timestamp
- ItemStatus
- Array of Message Exchange statuses
Returns
Nothing.
Remarks
For more information, including the function syntax for the different languages, see "Connecting Handlers for Events Reported from the LMXProxyServer" on page 19.
Handling the OnBufferedDataChange Callback
You can configure your client program to subscribe to buffered attributes. Your client program can also be configured to receive data change events for those attributes.
When LMXProxy passes buffered data back to the client application, it builds three parallel SafeArray Variants, one each for:
- Value (V)
- Timestamp (T)
- Quality (Q)
OnBufferDataChange Event
Reports the VTQ buffer and the status of an ArchestrA Attribute to which the program has subscribed.
Syntax
[Visual Basic.NET]
Sub LMX_OnBufferDataChange(
ByVal hLMXServerHandle As Integer,
ByVal phItemHandle As Integer,
ByVal dtDataType As ArchestrA.MxAccess.MxDataType,
ByVal pvItemValue As Object,
ByVal pwItemQuality As Object,
ByVal pftItemTimeStamp As Object,
ByRef ItemStatus() As ArchestrA.MxAccess.MXSTATUS_PROXY
)
Handles LMX_Server.OnBufferDataChange
[C#]
LMX_OnBufferDataChange(
int hLMXServerHandle,
int phItemHandle,
ArchestrA.MxAccess.MxDataType dtDataType,
object pvItemValue, object pvItemQuality,
object pvItemTimeStamp,
ref ArchestrA.MxAccess.MXSTATUS_PROXY[] ItemStatus)
[Visual C++]
HRESULT OnBufferDataChange (
long hLMXServerHandle,
ITEMHANDLE phItemHandle,
MxDataType dtDataType,
VARIANT pvItemValue,
VARIANT pwItemQuality,
VARIANT pftItemTimeStamp,
SAFEARRAY *pSAItemStatus)
Parameters
hLMXServerHandle Handle of the connection.
phItemHandle Handle of the item.
dtDataType Data type of the buffered data.
pvItemValue New data value.
Determining Data Change Status * pwItemQuality New quality.
- pftItemTimeStamp New timestamp.
- ItemStatus Array of Message Exchange statuses.
Returns
Nothing.
Remarks
You must note that when your client program attempts a buffered subscription to an attribute that does not have buffering enabled, a data change event occurs. This data change event message will have an MXStatus category of MXCategoryConfigurationError and will have a detail code of MX_E_InvalidPropertyId.
For more information, including the function syntax for the different languages, see "Connecting Handlers for Events Reported from the LMXProxyServer" on page 19.
Determining Data Change Status
If the data change notification indicates success, then you should process the received data. Otherwise, you should notify the user or log the error, as is appropriate for your application.
[Visual Basic.NET]
Dim sErrorStatus As String
If ItemStatus(0).success Then
' process data, update displays, etc.
Else
sErrorStatus = "Data Change Error - C:" & _
ItemStatus(0).category & " S:" & ItemStatus(0).detectedBy & _
" D:" & ItemStatus(0).detail
End If
[C#]
if (ItemStatus[0].success != 0) {
// process data, update displays, etc.
} else {
string sErrorStatus;
sErrorStatus = "Data Change Error - C:" +
ItemStatus[0].category + " S:" + ItemStatus[0].detectedBy +
" D:" + ItemStatus[0].detail;
}
[Visual C++]
MXSTATUS_PROXY *ItemStatus;
hr = SafeArrayAccessData(pSAItemStatus, (void HUGEP* FAR*)&ItemStatus);
if (ItemStatus[0].success != 0) { // process data, update displays, etc. } else { TCHAR sErrorStatus [256]; swprintf_s (sErrorStatus, sizeof(sErrorStatus)/sizeof(TCHAR), L"OnDataChange w/error - cat: %d Src: %d detail: %d", ItemStatus[0].category, ItemStatus[0].detectedBy, ItemStatus[0].detail); }
hr = SafeArrayUnaccessData(pSAItemStatus);
Displaying Data Quality and Time
The new data value has an associated Quality, which is an integer, and represents whether the data is good, bad, pending, and initializing. The new data value also has an associated timestamp, which represents when the data was updated. These properties can be easily displayed in .NET. For displaying the Time in Visual C++, refer to the section below on Retrieving Data Types in Visual C++.
Syntax
[Visual Basic.NET]
Private Sub LMX_OnDataChange(
ByVal hLMXServerHandle As Integer,
ByVal phItemHandle As Integer,
ByVal pvItemValue As Object,
ByVal pwItemQuality As Integer,
ByVal pftItemTimeStamp As Object,
ByRef ItemStatus() As ArchestrA.MxAccess.MXSTATUS_PROXY
)
Handles LMX_Server.OnDataChange
Example
Quality.Text = pwItemQuality
Time.Text = pftItemTimeStamp
Displaying Data Quality and Time [C#]
private void LMX_OnDataChange(
int hLMXServerHandle,
int phItemHandle,
object pvItemValue,
int pwItemQuality,
object pftItemTimeStamp,
ref ArchestrA.MxAccess.MXSTATUS_PROXY[ ] ItemStatus
);
Example
Quality.Text = pwItemQuality.ToString();
Time.Text = pftItemTimeStamp.ToString();
[Visual C++]
HRESULT CLMX_Events_Shim::OnDataChange (
long hLMXServerHandle,
ITEMHANDLE phItemHandle,
VARIANT pvItemValue,
long pwItemQuality,
VARIANT pftItemTimeStamp,
SAFEARRAY *pSAIItemStatus
);
Example
swprintf_s (strDisplayedText, TotalLen, L"%d", pwItemQuality);
SetDlgItemText (hMainDlg, IDC_Quality, strDisplayedText);
Variant_to_String (&pftItemTimeStamp, strDisplayedText,
sizeof(strDisplayedText)/sizeof(TCHAR));
SetDlgItemText (hMainDlg, IDC_Time, strDisplayedText);
[ActiveX]
Example
swprintf_s (strDisplayedText, TotalLen, L"%d", pwItemQuality);
m_pMxAccessAxDlg->SetDlgItemText(IDC_Quality,
strDisplayedText);
Variant_to_String (&pftItemTimeStamp, strDisplayedText,
sizeof(strDisplayedText)/sizeof(TCHAR));
m_pMxAccessAxDlg->SetDlgItemText(IDC_Time, strDisplayedText);
Determining Data Type
Because the new data value is encapsulated as an object (or in Visual C++ as a VARIANT), it is necessary to cast the new data value to the appropriate data type to process it. If the data is an array of values, you must un-package the object and treat it as an array of the appropriate type. This is easily handled in .NET, but requires more effort in Visual C++.
[Visual Basic.NET]
Dim objectType As Type
Dim count As Integer
' check whether data is array or single item
objectType = pvItemValue.GetType()
If Not objectType.IsArray Then
' received data is a single entry, handle it
' do something with pvItemValue
...
Else
' received data is an array,
' handle all the values
For count = 0 To UBound(pvItemValue, 1) Step 1
' do something with pvItemValue(count)
...
Next count
End If
[C#]
Type objectType;
int count;
objectType = pvItemValue.GetType();
if (!objectType.IsArray) {
// received data is a single entry, handle it
// do something with pvItemValue
...
}
Determining Data Type ```csharp } else { // received data is an array, re-cast data type // and handle all the values Array pvArray = (Array) pvItemValue; for (count = pvArray.GetLowerBound(0); count < pvArray.GetUpperBound(0); count++) { // do something with pvArray[count] ... } }
[Visual C++]
```c
VARTYPE Variant_Type;
long count;
Variant_Type = pvItemValue.vt;
if ((Variant_Type & VT_ARRAY) == 0) {
// received data is a single entry, handle it
// do something with pvItemValue
...
} else {
// received data is an array,
// re-cast data type and handle all the values
Variant_Type = (VARTYPE) (Variant_Type ^ VT_ARRAY);
SAFEARRAY *pvArray = pvItemValue.parray;
SAFEARRAYBOUND* pSABound = pvArray->rgsabound;
long LowerBound = pSABound->lbound;
long UpperBound = pSABound->cElements + LowerBound;
for (count = LowerBound; count < UpperBound; count++) {
// do something with pvArray at index count
// see example implementation below
Process_SafeArrayElement (pvArray, Variant_Type, count,
...);
}
}
For more information about implementing handling in the Process_SafeArrayElement() function, see "Retrieving Data Types in Visual C++" on page 70.
When you have the new data value in the appropriate form, you can use it and the array of Message Exchange statuses to update the values that are tracked by your program. What your program does with status information up to you—whether to update status displays, perform error handling, and so on.
Retrieving Data Types in Visual C++
The most reliable way to retrieve data from a SAFEARRAY of values is to use the SafeArrayGetElement() method. However, this method call must be made with a data variable of the appropriate type, so it can be processed correctly. For an array of BSTR, it is slightly more efficient to access the entire array and reference the individual strings by index.
There are several ways to handle the DATE data type. The following are two simple approaches to coerce the value to a string:
- Use the VariantChangeType() function to convert the DATE to a BSTR.
- Use the VariantTimeToDosDateTime() function to convert the DATE to a DOS date and a DOS time, which can then be formatted into a string.
The following code example shows how to extract entries from the SAFEARRAY. This function is included in the sample code for C++.
void SafeArrayElement_to_String(SAFEARRAY *pvArray, VARTYPE datatype, long count, LPTSTR szValue, size_t BufLen)
{
switch (datatype)
{
case VT_BOOL :
{
VARIANT varBoolItem;
VariantInit(&varBoolItem);
SafeArrayGetElement(pvArray, &count, &varBoolItem);
swprintf_s(szValue, BufLen, L"%s",
(varBoolItem.boolVal ? L"true" : L"false"));
}
break;
case VT_I4 :
{
long iLongElement = 0;
SafeArrayGetElement(pvArray, &count, &iLongElement);
swprintf_s(szValue, BufLen, L"%ld", iLongElement);
}
break;
case VT_R8 :
{
double iDoubleElement = 0.0;
SafeArrayGetElement(pvArray, &count,
&iDoubleElement);
swprintf_s(szValue, BufLen, L"%f", iDoubleElement);
}
break;
}
}
Retrieving Data Types in Visual C++ ```c++ case VT_R4 : { float iFloatElement = 0.0; SafeArrayGetElement (pvArray, &count, &iFloatElement); swprintf_s (szValue, BufLen, L"%f", iFloatElement); } break; case VT_DATE : { DATE dtElement; SafeArrayGetElement (pvArray, &count, &dtElement); if (bDisplayDateUsingVariant) { // One way to display is to let // the VARIANT mechanism handle it VARIANT varDate; VariantInit (&varDate); varDate.vt = VT_DATE; varDate.date = dtElement; HRESULT hr = VariantChangeType (&varDate, &varDate, 0, VT_BSTR); if (SUCCEEDED(hr)) { wscpy_s (szValue, BufLen, varDate.bstrVal); ::SysFreeString(varDate.bstrVal); } else { wcscpy_s (szValue, BufLen, L"????"); } } else { // Another way is to extract the // date/time fields explicitly // note that a DOS time is only // accurate to a resolution of // 2 seconds unsigned short wDosDate; unsigned short wDosTime; if (VariantTimeToDosDateTime (dtElement, &wDosDate, &wDosTime)) { int da = wDosDate & 0x001F; int mo = (wDosDate & 0x01E0) >> 5; int yr = ((wDosDate & 0xFE00) >> 9) + 1980; int sc = (wDosTime & 0x001F) << 1; int mn = (wDosTime & 0x07E0) >> 5; int hr = (wDosTime & 0xF800) >> 11; swprintf_s (szValue, BufLen, L"%02d/%02d/%04d %02d:%02d", mo, da, yr, hr, mn, sc); } }
```c
else
{
wcscpy_s (szValue, BufLen, L"????");
}
}
break;
case VT_BSTR :
{
BSTR HUGE *pbstr;
HRESULT hr = SafeArrayAccessData(pvArray, (void
HUGE**) &pbstr);
swprintf_s (szValue, BufLen, L"%s", pbstr[count]);
hr = SafeArrayUnaccessData(pvArray);
}
break;
default :
{
wcscpy_s (szValue, BufLen, L"????");
}
break;
} // switch //
}
Handling the OnWriteComplete Callback
The LMXProxyInterface triggers an event for OnWriteComplete when your program calls the Write() or WriteSecured() function. The information your program receives identifies whether the write was successful and any error code information that may be appropriate.
OnWriteComplete Event
Reports the status when a write operation has been completed, indicating such information as whether it succeeded or failed.
Syntax
[Visual Basic.NET]
Private Sub LMX_OnWriteComplete(
ByVal hLMXServerHandle As Integer,
ByVal phItemHandle As Integer,
ByRef ItemStatus() As ArchestrA.MxAccess.MXSTATUS_PROXY
)
Handles LMX_Server.OnWriteComplete
Handling the OnWriteComplete Callback [C#]
private void LMX_OnWriteComplete(
int hLMXServerHandle,
int phItemHandle,
ref ArchestrA.MxAccess.MXSTATUS_PROXY[ ] ItemStatus
);
[Visual C++]
HRESULT CLMX_Events_Shim::OnWriteComplete(
long hLMXServerHandle,
ITEMHANDLE phItemHandle,
SAFEARRAY *pSAWriteItemStatus
);
where CLMX_Events_Shim is a class derived from the base class LMXProxyServerEvents.
Parameters
hLMXServerHandle Handle of the connection.
phItemHandle Handle of the item.
ItemStatus Array of Message Exchange statuses.
Returns
Nothing
Remarks
For more information, including the function syntax for the different languages, see "Connecting Handlers for Events Reported from the LMXProxyServer" on page 19.
Interpreting the write status is very much like interpreting the data change status and involves examining the contents of the MxStatus structure.
- If the success member of the status is true, the write operation was accepted, but it may not be complete. Check the category member of the status to determine whether the write is still pending.
- If the success member is false, check the other structure members for additional information about why the write operation failed. It could be that the operation requires a secured or verified write, instead of an "ordinary" write.
You can check the Message Exchange statuses to determine whether the write was successful and to examine any error codes that have been returned. What your program does with the status information up to you—whether to update status displays, perform error handling, and so on.
Any attempt to write to a buffered item will result in a OnWriteComplete status event returning an MxStatus with an MxCategoryOperationalError category and an detail code of MX_E_NotWriteable.
Examples
[Visual Basic.NET]
If ItemStatus(0).success Then
If ItemStatus(0).category = ArchestrA.MxAccess.MxStatusCategory.MxCategoryPending Then
WriteStatus.Text = "Write Pending..."
Else
WriteStatus.Text = "Write Complete - status OK"
End If
Else
If ItemStatus(0).detail = MX_E_SecuredWrite Then
' 1012, secured write
' either re-try as a secured write
' or generate an error message
...
Else
If ItemStatus(0).detail = MX_E_VerifiedWrite Then
' 1013, verified write
' either re-try as a verified write
' or show an error message
...
Else
' some other kind of error,
' display information
WriteStatus.Text = "Write Complete w/error - cat: " &
ItemStatus(0).category & " Src: " &
ItemStatus(0).detectedBy & " detail: " &
ItemStatus(0).detail
End If
End If
End If
Handling the OnWriteComplete Callback [C#]
if (ItemStatus[0].success != 0) {
if (ItemStatus[0].category == ArchestrA.MxAccess.MxStatusCategory.MxCatgeoryPending) {
WriteStatus.Text = "Write Pending...";
} else {
WriteStatus.Text = "Write Complete - status OK";
}
} else {
if (ItemStatus[0].detail == MX_E_SecuredWrite) {
// 1012, secured write
// either re-try as a secured write
// or generate an error message
...
} else {
if (ItemStatus[0].detail == MX_E_VerifiedWrite_val) {
// 1013, verified write
// either re-try as a verified write
// or show an error message
} else {
// some other kind of error,
// display information
PokeValue.Text =
"Write Complete w/error - cat: " +
ItemStatus[0].category + " Src: " +
ItemStatus[0].detectedBy +
" detail: " + ItemStatus[0].detail;
}
}
}
[Visual C++]
if (ItemStatus[0].success != 0) {
if (ItemStatus[0].category == MxCatgeoryPending) {
SetDlgItemText(hMainDlg, IDC_PokeValue,
L"Write Pending...");
} else {
SetDlgItemText(hMainDlg, IDC_PokeValue,
L"Write Complete - status OK");
}
}
```c
} else {
if (ItemStatus[0].detail == MX_E_SecuredWrite) {
// 1012, secured write
// either re-try as a secured write
// or generate an error message
...
} else {
if (ItemStatus[0].detail == MX_E_VerifiedWrite) {
// 1013, verified write
// either re-try as a verified write
// or show an error message
...
} else {
// some other kind of error,
// display information
TCHAR error_text[256];
swprintf_s (error_text,
sizeof(error_text)/sizeof(TCHAR),
L"Write Complete w/error - cat: "
L"%d Src: %d" "detail: %d",
ItemStatus[0].category, ItemStatus[0].detectedBy,
ItemStatus[0].detail);
SetDlgItemText (hMainDlg, IDC_WriteStatus,
error_text);
}
}
}
Setting Update Intervals for Buffered Attributes
LMXProxy will also allow your client program to configure the rate at which buffered data change updates are received. If you do not set this rate, the buffered data update events are sent to the client program once per second.
Syntax
[Visual Basic.NET]
Sub SetBufferedUpdateInterval (
ByVal hLMXServerHandle As Integer,
ByVal lUpdateInterval As Integer)
[C#]
void SetBufferedUpdateInterval (
int hLMXServerHandle,
int lUpdateInterval)
Setting Update Intervals for Buffered Attributes [Visual C++]
HRESULT __stdcall SetBufferedUpdateInterval(
long hLMXServerHandle,
long lUpdateInterval)
Parameters
-
hLMXServerHandle
- Handle of the connection
-
lUpdateInterval
- Specifies the update interval in milliseconds
Examples
[Visual Basic.NET]
LMX_Server.SetBufferedUpdateInterval(hLMX, Interval)
[C#]
LMX_Server.SetBufferedUpdateInterval(hLMX, Interval);
[Visual C++]
m_pServerCMInterface->SetBufferedUpdateInterval(hLMXServerHandle, 3000);
Remarks
If you set a Negative or Zero (0) value, it will return an error with value E-INVALIDARG HResult.
If you set a Positive value, the value will be rounded to the next modulo 100 value. For example, a value of 1-100 will be 100, and 101 will be set to 200 and so on.
Chapter 3: Using Code Samples
Code samples support Microsoft Visual Studio 2012 or later. Code samples run on Application Server 2014 or later version nodes that have a Platform deployed. For information about running the samples on a node with an earlier version of Application Server (for example, 2.1 Patch 02), see "Registering the Interface Files" on page 10.
To build the sample projects VcppMxAccessActiveX and VcppMxSample on a 64-bit operating system, you must modify the import statement to include (x86) in the file path.
To build the sample projects on a 64-bit operating system
- Modify the import statement in stdafx.h.as shown in the following example:
#import "C:\Program Files (x86)\ArchestrA\Framework\Bin\MxAccess32.tlb" no_namespace, raw_interfaces_only
- Modify the import path in stdafx.h to match the InTouch installation directory as shown in the following example:
#import "C:\Program Files (x86)\Wonderware\InTouch\AppServerSecurity.dll" no_namespace, raw_interfaces_only
To implement the basic program
The examples for a stand-alone program are in Visual Basic, C#, and Visual C++. All three examples implement the basic program.
MxAccess C# Example window showing various buttons and text fields for application registration, item functions, item value, poke item, security, operation status, communication management functions, and buffered data interval management.
The code examples show how to connect to Message Exchange, access ArchestrA data, and shut down. The text fields and buttons allow you to select an attribute, add a reference for it, put it on advise, receive data updates, and write to the attribute. It also supports logging on under one or two names, to demonstrate secured writes and verified writes.
For information about setting up a simple ArchestrA Galaxy configuration for running the sample application, see "Setting Up a Simple Galaxy Configuration for the Sample Applications" on page 86.
This is a very simple demonstration program and does not keep track of adding and advising multiple items, even though the LMXProxy is capable of doing so. If you enter a new item name and add or advise that item, the program unadvises and removes the present item, if any.
The sample program has a few safeguards in place to demonstrate the possibility of including such safeguards in your own code:
- If you click Add Item before clicking Register, the program registers for you.
- If you click Advise before clicking Add Item, the program adds the item and registers it, if necessary.
- If you click Remove Item, the program checks whether there are any items on advise. If so, it unadvises them for you.
- If you click Unregister, the program checks whether there are any items on advise. If so, it unadvises them for you. It then checks whether there are any items in the reference table. If so, it removes those items for you and then unregisters.
To access data in an ArchestrA Galaxy
- Start the program.
- In the Application Level Functions area, click Register to connect to LMX Message Exchange.
- In the Item Functions area, do the following:
- a Type the item name.
- b Click AddItem to add the item to the reference tables.
- c If you wish use AdviseSupervisory, check the box marked Supervisory Connection. If you wish to use Advise, clear the box.
- d Click Advise to put the item on advise. Updates for the item appear. If item is not on advise, you do not receive data updates.
4 In the Poke into item area, do the following: a Type the value in the first text box. b If you are poking to only a single-valued attribute, leave the remaining text boxes blank. If you are poking to an array, type values for the array in the two remaining text boxes. c If you wish to write the timestamp as well as the value, type the desired timestamp in the Time text box. Otherwise, leave the Time text box blank. d Click Poke to write the value.
Note: Success or failure is shown in the first text box. If the attribute requires a secured or verified write, the program attempts it automatically. It succeeds only if you have logged on under one or more account names.
5 In the Security area, do the following: a Type the name and password for User1 and User2. If you are logging on to one account, use the User1 and Pwd1 text boxes. If you are logging on to two accounts, use User2 and Pwd2 text boxes. b Click Register UserIds to log on. After logging on, the program succeeds in secured writes and verified writes, if the accounts have the appropriate credentials for the selected item.
6 In the Communication Management Functions area, do the following: a Click Suspend to pause data updates. b Click Activate to resume updates.
7 In the Items Function area, do the following: a Click Unadvise to take the item off advise. You no longer receive data updates. b Click RemoveItem to remove the item from the reference tables.
8 In the Applications Level Functions area, click Unregister to disconnect from LMX Message Exchange.
9 Shut down the program.
Using an ActiveX Code Sample # Using an ActiveX Code Sample
This example of ActiveX code demonstrates the ArchestrAUserToId() method. Use this method in applications, such as an InTouch application, where the user log on is done outside your component. The program is similar to the other code samples, although there are some minor differences.
MxAccess ActiveX Sample window with various functions and fields including Application Level Functions (Register, Unregister), Item Functions (Advise, UserDefined_001.Attr1, Unadvise), Item (Value 1000, Quality 192, Time 8/6/2008 4:28:59.696 PM, Status), Poke into item (Value 1000, Time 8/6/2008 4:28:59.696 PM, Status Write Complete - status OK), Communication Management Functions (Suspend, Activate, Status Operation Complete - status OK), GUID of Logged-in User (Get GUID, ID {84F0B924-B158-4777-BEFD-A4BE10CC774A}), Security (Secured Write, Verified Write, Log In), IDs (IDs 1, 2, 3), Status SUCCESS, Detail OK.
- The edit fields for PokeValue1 and PokeValue2 are omitted to leave more room for other buttons and display fields. Although the control shows array attributes, it does not support writing an array of new values. See the other code examples for an implementation of writing an array.
- Buttons and edit fields have been added for retrieving and displaying the GUIDs and corresponding ID "cookies".
The ActiveX control must be registered on your computer and then registered with WindowMaker. Create a simple InTouch application with a single window and place an instance of the ActiveX control on the window. Configure the application for ArchestrA security. Configure WindowViewer so the application window is the start-up window for the application.
To access data in an ArchestrA Galaxy using ActiveX
- Start WindowViewer.
- In the Application Level Functions area, click Register to connect to LMX Message Exchange.
- In the Item Functions area, do the following: a. Type the item name. b. Click Advise to put the item on advise. Updates for the item appear.
- In the Poke into item area, do the following: a. Type the value in the Value text box. b. If you wish to write the timestamp as well as the value, type the desired timestamp in the Time text box. Otherwise, leave the Time text box blank. c. Click Poke to write the value. Results appear in the Status box. If the Galaxy is secured, and you have not obtained the ID for the logged on user, the write should fail. If the attribute requires a secured write or a verified write, and the program has not yet obtained the required IDs (of the logged on user and of a verified user, if needed), then the write fails.
- In the GUID of Logged in User area, click Get GUID. If the user has not logged on, zeros appear for the GUID. The ID "cookie" should also be zero. In the InTouch application, click Log-in on the Security menu to log on, and then click Get GUID. The display shows the GUID of the logged on user and a non-zero ID "cookie."
- In the Poke into item area, click Poke. Attributes that require only an ordinary write, or a secured write succeed.
- In the Item Functions area, click Unadvise and edit the item name to select an attribute that requires a secured write or a verified write. Then, click Advise to put the new item on advise.
Using an ActiveX Code Sample 8 In the Security area, click Log In to show an InTouch log on dialog box.
- If Secured Write is selected, a confirmation dialog box just for the logged on user is shown. If Verified Write is selected, a dialog box is shown that prompts the logged on user to confirm the password and asks the verifying user to enter a name and password.
- The display should show the GUID of the user(s) and corresponding ID "cookies," along with the status and detail information about the log on.
- If the user password is incorrect, the function indicates it failed to obtain a GUID.
- If Verified Write is selected and the logged on user password is incorrect, the function indicates it failed to obtain the GUIDs, even if the verifying user logon is correct.
- If Verified Write is selected and the verifying user logon is incorrect, the function indicates it failed to obtain the GUIDs, even if the logged-in user's password is correct.
- If the proper GUIDs have been obtained, secured and verified writes should now work.
9 In the Communication Management Functions area, do the following: a Click Suspend to pause data updates. b Click Activate to resume updates.
10 In the Items Function area, click Unadvise to take the item off advise. You no longer receive data updates.
11 In the Applications Level Functions area, click Unregister to disconnect from LMX Message Exchange.
This sample program has the same safeguards in place as the other examples to ensure that the Register operation is invoked before attempting any other operations and to ensure that a proper shut down takes place, even if the user does not click Unregister before terminating WindowViewer.
Setting Up a Simple Galaxy Configuration for the Sample Applications
You must have an ArchestrA Galaxy and a MXAccess runtime license to run the sample applications. For most operations you can use a Galaxy with no security. You can register, add items, put them on advise, see the updates, and write to them.
If you already have a Galaxy set up, you may want to simply run the program and enter the name of an attribute of some object that has been created and deployed.
Setting up Attributes
You can establish a simple set of attributes that can be referenced in the sample application dialog box and will update automatically. If no security is enabled, you can simply start up the sample application and even write to the selected attributes.
Note: To advise an entire array, the reference should be of the form "ud1.ar[ ]".
To set up a simple set of attributes
- Start the ArchestrA IDE.
- In the Deployment window, do the following: a. Create an instance of a WinPlatform. b. Create an AppEngine and assign it to the WinPlatform. c. Create an Area and assign it to the AppEngine. d. Create an instance of $UserDefined, give it the name "ud1", and assign it to the Area. e. Double click ud1 to start the editor.
- On the UDAs tab, add the following attributes and make them all user writable:
- x, of data type Integer
- ar, of data type Integer, an array of 3 elements
- dt, of data type Time
- ardt, of data type Time, an array of 3 elements
Setting Up a Simple Galaxy Configuration for the Sample Applications 4 On the Scripts tab, create the following script s1: Execution type: execute Expression: 1 Trigger type: While true Trigger period and Deadband 0 Statements
- me.x = me.x + 1;
- me.ar[1] = me.ar[1] + 1;
- me.ar[2] = me.ar[2] + 2;
- me.ar[3] = me.ar[3] + 3;
5 Save and close the editor.
6 Deploy the WinPlatform and all objects underneath.
Setting up a Simple Security Configuration
To exercise secured writes and verified writes, you must configure the Galaxy for security and have at least two users that can be configured to log in.
To set up a simple security configuration
1 Start ArchestrA IDE, and select Galaxy, and then Configure and Security. The Configure Security dialog box appears.
2 Click the Authentication Mode tab and review the settings. If the current security mode is "none" you must enable security. For the purposes of this exercise, select Galaxy security. If the security mode changes, you must shut down the Galaxy, open the IDE again and log on. A recommended log on in this case is as Administrator. See the ArchestrA documentation for more information.
3 Click the Roles tab, and then do the following: a Select the Default role, and then clear all the permissions. b Create the Writers role. Check all the General permissions and all the Operational permissions. c Create the NonWriters role. Check all the General permissions and all the Operational permissions, except for the Can modify Operate attributes. Leave that box unchecked.
4 Click the Users tab, then do the following: a Add user Writer1 with Default and Writers roles. b Add user Writer2 with Default and Writers roles. c Add user NonWriter with Default and NonWriters roles.
5 Create a unique password for each user.
With security in place, you can put an attribute on advise and see updates, without logging on. But to write to an attribute, even one with simple Operate security, you must log on as Writer1 or Writer2. If you are logged on as NonWriter, an attempt to write produces an error message with error code 1008.
To exercise the secured write and verified write operations, you can edit ud1 and add two more attributes.
To add more attributes
1 Click the UDAs tab. a Add y, of data type Integer, with security mode of Secured Write. b Add z, of data type Integer, with security mode of Verified Write.
2 Click the Scripts tab, and then add the following lines to the existing script s1:
me.y = me.y + 1; me.z = me.z + 1;
3 Save and close the editor.
4 Undeploy and re-deploy ud1.
This adds ud1.y and ud1.z as attributes that you can advise and write, using the appropriate secured or verified write options.
The sample application does a Write() first to obtain a status using the OnWriteComplete() event and from that status determines whether the attribute requires a secured write or a verified write, then attempts the appropriate call to WriteSecured() - or reports an error status. If you are running the application in debug mode, you can set breakpoints inside the OnDataChange() event handler to trace through the code and see this process in action.
Setting Up a Simple Galaxy Configuration for the Sample Applications # Example Security for the Role of Writers
The following dialog box shows how you can configure security for the role of writers.
Screenshot of the "Configure Security" dialog box showing roles and permissions configuration.
Example Security for the Role of NonWriters
The following dialog box shows how you can configure security for the role of non-writers.
Screenshot of the "Configure Security" dialog box showing roles and permissions configuration.
Appendix A: Status and Error Codes
MXAccess status and error codes report on the health of the application.
The status and error codes are presented in one of four types:
- MxStatusDetail Values
- MxStatusCategory Values
- MxStatusSource Values
- ResolutionStatus Values
An MxStatus is actually a structure, which contains four data fields:
typedef struct MxStatus {
VARIANT_BOOL success;
MxStatusCategory category;
MxStatusSource detectedBy;
short detail;
} MxStatus;
MxStatusDetail Values
The MxStatusDetail values are shown in the following table:
| Value | Status |
|---|---|
| 0 | MX_S_Success |
| 1 | MX_E_RequestTimedOut |
| 2 | MX_E_PlatformCommunicationError |
| 3 | MX_E_InvalidPlatformId |
| 4 | MX_E_InvalidEngineId |
| 5 | MX_E_EngineCommunicationError |
| 6 | MX_E_InvalidReference |
| 7 | MX_E_NoGalaxyRepository |
| 8 | MX_E_InvalidObjectId |
| 9 | MX_E_ObjectSignatureMismatch |
| 10 | MX_E_AttributeSignatureMismatch |
| 11 | MX_E_ResolvingAttribute |
| 12 | MX_E_ResolvingObject |
| 13 | MX_E_WrongDataType |
| 14 | MX_E_WrongNumberOfDimensions |
| 15 | MX_E_InvalidIndex |
| 16 | MX_E_IndexOutOfOrder |
| 17 | MX_E_DimensionDoesNotExist |
| 18 | MX_E_ConversionNotSupported |
| 19 | MX_E_UnableToConvertString |
| 20 | MX_E_Overflow |
| 21 | MX_E_NmxVersionMismatch |
| 22 | MX_E_NmxInvalidCommand |
| 23 | MX_E_LmxVersionMismatch |
| 24 | MX_E_LmxInvalidCommand |
| 25 | MX_E_GalaxyRepositoryBusy |
| 26 | MX_E_EngineOverloaded |
| 1000 | MX_E_InvalidPrimitiveId |
| 1001 | MX_E_InvalidAttributeId |
| 1002 | MX_E_InvalidPropertyId |
| 1003 | MX_E_IndexOutOfRange |
| 1004 | MX_E_DataOutOfRange |
| 1005 | MX_E_IncorrectDataType |
| Value | Status |
|---|---|
| 1006 | MX_E_NotReadable |
| 1007 | MX_E_NotWriteable |
| 1008 | MX_E_WriteAccessDenied |
| 1009 | MX_E_UnknownError |
| 1010 | MX_E_ObjectInitializing |
| 1011 | MX_E_EngineInitializing |
| 1012 | MX_E_SecuredWrite |
| 1013 | MX_E_VerifiedWrite |
| 1014 | MX_E_NoAlarmAckPrivilege |
| 8000 | MX_E_AutomationObjectSpecificError |
MxStatusCategory Values
The MxStatusCategory values are shown in the following table:
| Value | Status |
|---|---|
| -1 | MxStatusCategoryUnknown |
| 0 | MxCategoryOk |
| 1 | MxCategoryPending |
| 2 | MxCategoryWarning |
| 3 | MxCategoryCommunicationError |
| 4 | MxCategoryConfigurationError |
| 5 | MxCategoryOperationalError |
| 6 | MxCategorySecurityError |
| 7 | MxCategorySoftwareError |
| 8 | MxCategoryOtherError |
MxStatusSource Values
The MxStatusSource values are shown in the following table:
| Value | Status |
|---|---|
| -1 | MxSourceUnknown |
| 0 | MxSourceRequestingLmx |
| 1 | MxSourceRespondingLmx |
| 2 | MxSourceRequestingNmx |
| 3 | MxSourceRespondingNmx |
| 4 | MxSourceRequestingAutomationObject |
| 5 | MxSourceRespondingAutomationObject |
ResolutionStatus Values
The ResolutionStatus values are shown in the following table:
| Value | Status |
|---|---|
| 0 | unresolved |
| 1 | resolvingObject |
| 2 | resolvingAttribute |
| 3 | resolved |
| 4 | invalidReference |
| 5 | noGalaxyRepository |
| 6 | waitingToResolveAgainstDb |
| 7 | waitingToResolveAttribute |
| 8 | retrievingRedundancyStatus |