1. 背景
OPC Data Access 規範是基於COM/DCOM定義的,因此大多數的OPC DA Server和client都是基於C++開發的,因爲C++對COM/DCOM有最好的支持。現在,隨着微軟的開發平臺漸漸的轉移到.NET框架上,好多OPC Client程序都需要建立在.NET平臺上,用.NET提供的技術開發OPC Client就成爲一種需求。網上很多網友都有提過,.NET開發OPC Client不外乎下面三種方法:
OPCNetAPI 2.0由OPC foundation提供,只有註冊會員才能得到,是需要付費的。其他的dll不需要付費,很容易得到。網上有網友已經介紹過使用OPCDAAuto.dll開發.NET Client的方法, 這種方法的優點是比較簡單,缺點是不夠靈活。本文使用自定義接口,藉助OpcRcw.Da.dll,開發出一個OPC .NET Client的類庫,可供其他client程序調用。
必要文件:
OpcRcw.Comn.dll --- 包含對IConnectionPointContainer的包裝。
OpcRcw.Da.dll ---.NET 對OPC COM 接口 定義的包裝。
適應版本:
OPC Data Access specification 2.05
說明:
該類庫正在開發中,這是第一個版本,只實現了一些基本功能,好多功能如OPC Browse等還未實現,代碼也未經過測試,存在bug在所難免,感興趣的朋友請繼續關注。。。
2. VS2008工程項目文件
下圖是OpcDa.Client組件實現的基本類庫:
3. 類庫實現的基本功能
OpcServer:
Connect |
連接OPC Server。 |
Disconnect |
斷開Server。 |
GetStatus |
獲得Server的當前狀態,返回ServerStatus。 |
AddGroup |
添加group |
RemoveGroup |
刪除group |
FindGroupByName |
通過名字獲取OpcGroup對象 |
Opc Group:
AddItems |
添加Opc Items到組 |
RemoveItems |
刪除items |
AsyncRead |
異步讀取Items,調用IOPCAsyncIO2::Read接口 |
AsyncWrite |
異步寫items,調用IOPCAsyncIO2::Write接口 |
Refresh |
刷新當前group,調用IOPCAsyncIO2::Refresh接口 |
GetState |
獲得當前group狀態,返回GroupState |
SetState |
設置當前group狀態,返回設置後的group狀態 |
DataChanged |
事件,客戶端註冊,可用來接收OnDataChange事件 |
4. 類庫使用方法
1) 連接OPC DA server
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
string
serverProgID =
"OPCSample.OpcDa20Server.1"
;
// opc da sample server prog id
string
hostName =
string
.Empty;
//local server
Type tp = Type.GetTypeFromProgID(serverProgID);
this
._opcServer =
new
OpcServer(tp.GUID.ToString(), hostName);
try
{
this
._opcServer.Connect();
}
catch
(Exception ex)
{
MessageBox.Show(ex.Message,
"Error"
,
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
|
2) 斷開OPC Server
1
2
3
4
5
|
if
(
this
._opcServer !=
null
)
{
this
._opcServer.Disconnect();
this
._opcServer =
null
;
}
|
3) 添加Group
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
string
groupName =
"grp_0"
;
// group name
int
updateRate = 1000;
bool
active =
true
;
try
{
OpcGroup grp =
this
._opcServer.AddGroup(groupName, updateRate, active);
grp.DataChanged += OnDataChange;
//register OnDataChange Event
}
catch
(Exception ex)
{
MessageBox.Show(ex.Message,
"Error"
,
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
|
其中OnDataChange 定義爲DataChangedEventHandler類型:
1
2
3
|
public
delegate
void
DataChangedEventHandler(
object
subscriptionHandle,
object
requestHandle, ItemValueResult[] values);
private
void
OnDataChange(
object
subscriptionHandle,
object
requestHandle, ItemValueResult[] values);
|
4) 刪除Group
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
try
{
OpcGroup grp =
this
._opcServer.FindGroupByName(groupName );
if
(grp !=
null
)
{
grp.DataChanged -= OnDataChange;
//unregister OnDataChange Event
this
._opcServer.RemoveGroup(grp);
}
}
catch
(Exception ex)
{
MessageBox.Show(ex.Message,
"Error"
,
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
|
5) 添加Items
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
List<
string
> items =
new
List<
string
>();
Items.Add(
"itemname"
);
OpcGroup grp =
this
._opcServer.FindGroupByName(groupName);
if
(grp !=
null
)
{
try
{
ItemResult[] results = grp.AddItems(items.ToArray());
foreach
(ItemResult result
in
results)
{
if
(result.ResultID.Failed())
{
string
message =
"Failed to add item \'"
+ result.ItemName +
"\'"
+
" Error: "
+ result.ResultID.Name;
MessageBox.Show(message);
}
else
{
AddItemToList(result);
// add item to view list
}
}
}
// end try
catch
(Exception ex)
{
MessageBox.Show(ex.Message,
"Error"
,
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
// end catch
}
// end if
|
6) 刪除Items
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
// remove items
// List<object> itemsClientHandle = new List<object>(); //
if
(itemsClientHandle.Count > 0)
{
try
{
// get group
OpcGroup grp =
this
._opcServer.FindGroupByName(groupName);
if
(grp !=
null
)
{
IdentifiedResult[] results = grp.RemoveItems(itemsClientHandle.ToArray());
for
(
int
i = 0; i < results.Length; i++)
{
if
(results[i].ResultID.Succeeded())
{
// remove opc item from server successfully, remove it from list
RemoveItemFromList(results[i]);
}
else
{
string
message =
"Remove item \'"
+ results[i].ItemName +
"\' error: "
+ results[i].ResultID.ToString();
MessageBox.Show(message);
}
}
}
}
catch
(Exception ex)
{
MessageBox.Show(ex.Message,
"Error"
,
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
// end catch
}
|
7) 異步讀取Items
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
List<
object
> clientHandles =
new
List<
object
>();
foreach
(ListViewItem lvItem
in
items)
{
ItemResult item = (ItemResult)lvItem.Tag;
if
(item !=
null
)
{
clientHandles.Add(item.ClientHandle);
}
}
if
(clientHandles.Count > 0)
{
// get group
OpcGroup grp =
this
._opcServer.FindGroupByName(groupName);
if
(grp !=
null
)
{
try
{
IdentifiedResult[] results = grp.AsyncRead(clientHandles.ToArray(), ++
this
._handle,
new
ReadCompleteEventHandler(OnReadComplete),
out
this
._request);
for
(
int
i = 0; i < results.Length; i++)
{
if
(results[i].ResultID.Failed())
&x;line-height:1.8em;vertical-align:baseline;font-family:Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace;min-height:auto;">
if
(grp !=
null
)
{
try
{
IdentifiedResult[] results = grp.AsyncRead(clientHandles.ToArray(), ++
this
._handle,
new
ReadCompleteEventHandler(OnReadComplete),
out
this
._request);
for
(
int
i = 0; i < results.Length; i++)
{
if
(results[i].ResultID.Failed())
{
|