Computer Science Experimentation

Sunday, January 9, 2011

OPC.NET client in F#


Introduction



This document presents an example of OPC client in F# interactive using OPC foundation OPC .NET API 1.
The website http://www.opcconnect.com/dotnet.php describes several options on how to use OPC with .NET. Here is part of the text:
"Using .NET for client development
Microsoft doesn't expect us to throw out all our COM code just yet. In fact there are well defined mechanisms for bridging from the .NET virtual machine (the Common Language Runtime) to the old world of COM servers.
An early option, using the OPC Automation interfaces, was to add a reference to the Automation server or DLL directly to a .NET project. Coding with C# or another .NET language then became reasonably straightforward. However, see this discussion (via Google Groups) about problems with the OPC Automation wrapper.
OPC Foundation .NET API and Runtime Callable Wrappers
OPC Foundation supplies a set of Runtime Callable Wrappers (RCWs), allowing OPC custom interfaces to be accessed from .NET clients.
These .NET wrappers are available to all as part of the OPC Core Components set. RCWs are provided for all published specifications, but still leave much COM interop work to be done by the developer.
A better option for Foundation members is to download the OPC .NET API, which supports DA 2 and 3, DX and HDA. The .NET API provides a unified set of interfaces for accessing both COM and SOAP/XML servers, and also includes C# and VB.NET clients which exploit these interfaces.
See this thread from the OPC Foundation Message Board for a comparison of the Runtime Callable Wrappers and the .NET API. This article from Advosol, outlining the requirements of a .NET API, may also be useful.
The Message Board also has this interesting thread on the background to the OPC .NET API. Note that the .NET API does not have the status of a full OPC specification - it is simply an implementation provided as a convenience to OPC Foundation members.
The .NET API requires the .NET Framework version 2.0.
For the low-down on the techniques involved in interfacing COM with .NET, take a look at Adam Nathan's book .NET and COM: The Complete Interoperability Guide."

Example    



This example tests most of the functionalities described in "OPC .NET API Overview Version 1.00 Draft 2 December 1, 2003" from OPC Foundation http://www.opcfoundation.org/ .
#r
"C:\cs\Others\NET API 2.00\Source\Bin\OpcNetApi.dll"

#r
"C:\cs\Others\NET API 2.00\Source\Bin\OpcNetApi.Com.dll"



open Opc
open OpcCom




//5.2 Opc.IDiscovery
//5.2.1 EnumerateHosts Method
let se= new OpcCom.ServerEnumerator()


//5.2.2 GetAvailableServers Method
// Show Servers--------------------------------------------------
let servers=se.GetAvailableServers(Opc.Specification.COM_DA_30)
for i in servers do
printfn "(%s) " i.Name


> (Advosol.DA3CBCS)
(ICONICS.SimulatorOPCDA)


//5.1 Opc.Factory
let mFactory = new OpcCom.Factory()
let mURL = new Opc.URL("opcda://localhost/ICONICS.SimulatorOPCDA");;
> val mFactory : Factory
val mURL : URL = opcda://localhost/ICONICS.SimulatorOPCDA


//5.3 Opc.Server
//5.3.1 Constructor
//5.3.2 Properties
//5.3.3 Duplicate Method
//5.3.4 Connect Method
//5.3.5 Disconnect Method


//5.4 Opc.Da.Server
//5.4.1 Constructor
let mserver = new Opc.Da.Server(mFactory, mURL)


//5.4.2 Properties
mserver;;
> val it : Da.Server =
Opc.Da.Server {Filters = 9;
IsConnected = false;
Locale = null;
Name = "ICONICS.SimulatorOPCDA";
Subscriptions = seq [];
SupportedLocales = null;
Url = opcda://localhost/ICONICS.SimulatorOPCDA;}


//5.4.3 Connect Method
let mCredentials = new System.Net.NetworkCredential()
let mConnectData = new Opc.ConnectData(mCredentials);
mserver.Connect(mURL, mConnectData)


// 4.1 Opc.IServer Interface
//4.1.3 GetSupportedLocale Method

//let l=mserver.GetSupportedLocales

//4.1.2 SetLocale Method

//mserver.SetLocale("en-US")

//4.1.1 GetLocale Method

//mserver.GetLocale()


//mserver.GetErrorText

//4.7 Opc.Da.IServer Interface
//4.7.2 SetResultsFilters Method
mserver.SetResultFilters(0x09) //Minimal
//4.7.1 GetResultsFilters Method
mserver.GetResultFilters()


//4.7.3 GetStatus Method
let Sstat=mserver.GetStatus()
Sstat;;
> val it : Da.ServerStatus =
Opc.Da.ServerStatus
{CurrentTime = 1/9/2011 9:57:36 PM;
LastUpdateTime = 1/1/0001 12:00:00 AM;
ProductVersion = "3.12.0";
ServerState = running;
StartTime = 1/9/2011 3:15:22 PM;
StatusInfo = "The server is running normally.";
VendorInfo = "ICONICS Simulator OPC-DA Server and Simulator OPC-AE Server";}


//4.7.5 Read Method

//4.3 Opc>Da.Item Class

let someItems : Opc.Da.Item array = Array.zeroCreate 2
someItems.[0] <- new Opc.Da.Item()
someItems.[0].ItemName <- "TAG_0000";
someItems.[0].ClientHandle <- 0
someItems.[1] <- new Opc.Da.Item()
someItems.[1].ItemName <- "TAG_0001";
someItems.[1].ClientHandle <- 1


mserver.Read(someItems)
mserver.Read([|someItems.[0]|]);;
> val it : Da.ItemValueResult [] =
[|Opc.Da.ItemValueResult {ClientHandle = null;
DiagnosticInfo = null;
ItemName = "TAG_0000";
ItemPath = null;
Key = "TAG_0000
null";
Quality = good;
QualitySpecified = true;
ResultID = S_OK;
ServerHandle = null;
Timestamp = 1/9/2011 5:00:22 PM;
TimestampSpecified = true;
Value = 20.0;}|]


//4.7.5 Write Method
let v1=new Opc.Da.ItemValue(ItemName="TAG_0000",Value=10.0)
let v2=new Opc.Da.ItemValue(ItemName="TAG_0001",Value=11.0)


mserver.Write([|v1;v2|])
mserver.Read([|someItems.[0];someItems.[1]|])


//4.7.6 CreateSubscription (Group)
let g1State = new Opc.Da.SubscriptionState()
g1State.Name<-"Group1"
g1State.ClientHandle<-1
g1State;;
> val it : Da.SubscriptionState = Opc.Da.SubscriptionState {Active = true;
ClientHandle = 1;
Deadband = 0.0f;
KeepAlive = 0;
Locale = null;
Name = "Group1";
ServerHandle = null;
UpdateRate = 0;}


let g1 = mserver.CreateSubscription(g1State)
printfn "Number of groups:%i" mserver.Subscriptions.Count
seq { for i in mserver.Subscriptions -> i.Name }|>Seq.iter (printfn "%s")
> Number of groups:1
Group1


//4.7.8 Browse Method

//4.5 Opc.Da.BrowseElement Class

//4.7.9 Browse Next Method


//4.7.10 GetProperties Method

//4.2 Opc.ItemIdentifier Class

let i0=Opc.ItemIdentifier("TAG_0000")
let i1=Opc.ItemIdentifier("TAG_0001")


let r=mserver.GetProperties([|i0;i1|],null,true)
printfn "DataType:%A" r.[0].[0].Value
printfn "Value: %A" r.[0].[1].Value
printfn "Quality: %A" r.[0].[2].Value
printfn "Timestamp: %A" r.[0].[3].Value;;
> DataType:System.Double
Value: 10.0
Quality: good
Timestamp: 1/9/2011 4:01:33 PM


//4.8 Opc.Da.ISubscription
//4.8.1 DataChanged Event
//4.8.2 GetResultsFilter Method
//4.8.3 SetResultsFilter Method


//4.8.4 GetState Method
let r2=g1.GetState()
r2;;
> val it : Da.SubscriptionState = Opc.Da.SubscriptionState {Active = true;
ClientHandle = 1;
Deadband = 0.0f;
KeepAlive = 0;
Locale = "";
Name = "Group1";
ServerHandle = 17;
UpdateRate = 50;}


//4.8.5 ModifyState Method


//4.8.6 AddItems Method
let r3=g1.AddItems(someItems)
let g1SH=Seq.toList(seq { for i in r3 -> i.ServerHandle })
seq { for i in r3 -> i.ResultID }|>Seq.iter (printfn "%A");;
> S_OK S_OK


//4.8.7 ModifyItems Method
//4.8.8 RemoveItems Method


//4.8.9 Read Method
let someItems_1 : Opc.Da.Item array = Array.zeroCreate 2
someItems_1.[0] <- new Opc.Da.Item()
someItems_1.[0].ServerHandle <- g1SH.[0]
someItems_1.[1] <- new Opc.Da.Item()
someItems_1.[1].ServerHandle <- g1SH.[1]


let r4=g1.Read(someItems_1)

//Opc.Da.ItemProperty

printfn "Value: %A" r4.[0].Value
printfn "Quality: %A" r4.[0].Quality
printfn "Timestamp: %A" r4.[0].Timestamp;;
> Value: 10.0
Quality: good
Timestamp: 1/9/2011 10:01:33 PM


printfn "Value: %A" r4.[1].Value
printfn "Quality: %A" r4.[1].Quality
printfn "Timestamp: %A" r4.[1].Timestamp;;
> Value: 11.0
Quality: good
Timestamp: 1/9/2011 10:01:33 PM


//4.8.10 Write Method

//4.4 Opc.Da.ItemValue Class

let someValues_1 : Opc.Da.ItemValue array = Array.zeroCreate 2


someValues_1.[0]<-new Opc.Da.ItemValue(ItemName="TAG_0000",Value=20.0)
someValues_1.[0].ServerHandle <- g1SH.[0]
someValues_1.[1]<-new Opc.Da.ItemValue(ItemName="TAG_0001",Value=21.0)
someValues_1.[1].ServerHandle <- g1SH.[1]


let r5=g1.Write(someValues_1)
seq { for i in r5 -> i.ResultID }|>Seq.iter (printfn "%A");;
> S_OK S_OK


//4.8.11 BeginReadMethod
//4.8.12 BeginWriteMethod
//4.8.13 CancelMethod
//4.8.14 Refresh Method
//4.8.15 SetEnable Method
//4.8.16 GetEnable Method


//5 Client API
//5.1.2 System Type Property
//5.1.3 UseRemoting Property
//5.1.4 CreateInstance Method
//5.4.5 CreateSubscription Method


//5.5 Opc.Da.Subscription
//5.5.1 Constructor
//5.5.2 Properties




//4.7.7 CancelSubscription
mserver.CancelSubscription(g1)


//5.4.3 Disconnect Method
mserver.Disconnect();;

1 comment:

  1. Gracias por la ayuda, busco informaciĆ³n para vb.net pero no encuentro.
    saludos

    Marco Salazar
    marcosaam@hotmail.com

    ReplyDelete