近来由于工程需要,基于OPC DA 2.0搭建通用的取数模块,与远程webscoket服务端连接,并传输数据。在网上找了些资料,修改相应网友公开的源代码,基本达到要求,特供大家参考。
1.实体类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OPC_DEMO
{
public class MyOPCItem
{
public string key{ get; set; }
public string Value { get; set; }
}
}
2.源代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using OPCAutomation;
using System.Threading;
using System.IO;
using WebSocketSharp;
using Newtonsoft.Json;
namespace OPC_DEMO
{
public partial class Form1 : Form
{
private OPCServer KepServer;
private OPCGroups KepGroups;
private OPCGroup KepGroup;
private OPCItems KepItems;
public WebSocket wsc;
private string JsonStr;
private MyOPCItem opc;
Dictionary MAP_CLIENTHANDLE_TAG;
public OPCItem[] OPC_ITEMS_ADDED { get; private set; }
public Form1()
{
InitializeComponent();
}
#region OPC Client
///
/// 自动连接OPC Server,并websocket连接服务器。
///
private void KepServerLoad()
{
try
{
KepServer = new OPCServer();
//KepServer.Connect("FBoxOpcServer", "127.0.0.1");
KepServer.Connect("Kepware.KEPServerEx.V6","127.0.0.1");
// KepServer.Connect("Kepware.KEPServerEx.V5","127.0.0.1");
if (KepServer.ServerState == (int)OPCServerState.OPCRunning)
{
wsc = new WebSocket("ws://10.0.0.128:6690/WsServices");
wsc.Connect();
richTextBox1.Text = "OPC Server连接成功";
}
else
{
richTextBox1.Text = "OPC Server连接失败";
return;
}
}
catch (Exception ex)
{
richTextBox1.Text = "OPC Server连接失败," + ex.Message;
return;
}
KepGroups = KepServer.OPCGroups;
Thread t1; // 开1个线程用于读取数据
t1 = new Thread(new ThreadStart(KepProcess));
t1.Start();
}
///
/// 新建OPC Group并设置相应的属性,和触发改变事件
///
public void KepProcess()
{
//KepGroup = KepGroups.Add("Channel.Device.Group");
KepGroup = KepGroups.Add("Channel1.Device1.Group");
KepGroup.UpdateRate = 1000;
KepGroup.IsActive = true;
KepGroup.IsSubscribed = true;
KepItems = KepGroup.OPCItems;
AddGroupItems();
//当KepGroup中数据发生改变的触发事件
KepGroup.DataChange += new DIOPCGroupEvent_DataChangeEventHandler(KepGroup_DataChange);
// //item1 = KepItems.AddItem("My FBox.外泵站1.AI.流量1", 1);
// item1 = KepItems.AddItem("通道 1.设备 1.标记 1", 1);
item2 = KepItems.AddItem("My FBox.外泵站1.DI.格栅运行", 2);
//item2 = KepItems.AddItem("通道 1.设备 1.标记 2", 2);
//item3 = KepItems.AddItem("通道 1.设备 1.test",3);
}
///
/// 单独设立添加OPCItem方法,用来添加OPCItem
///
public void AddGroupItems()
{
List str = new List();
str.Add("通道 1.设备 1.标记 1");
str.Add("通道 1.设备 1.标记 2");
str.Add("通道 1.设备 1.test");
List ItemsAdd = new List();
MAP_CLIENTHANDLE_TAG = new Dictionary();
int n = 0;
foreach (string tag in str)
{
ItemsAdd.Add(KepItems.AddItem(tag, n));
MAP_CLIENTHANDLE_TAG.Add(n + "", tag);
n++;
}
OPC_ITEMS_ADDED = ItemsAdd.ToArray();
}
//当数据改变时触发的事件
//public delegate void DelegateShowMessage(string str);
public delegate void DelegateShowMessage(MyOPCItem str);
///
/// 数据改变事件触发编写,调用委托事件远程发送。
///
///
///
///
///
///
///
public void KepGroup_DataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)
{
Dictionary tagValueMap = new Dictionary();
string str = "";
// DelegateShowMessage show1 = new DelegateShowMessage(ShowMessage);
DelegateShowMessage show1 = new DelegateShowMessage(ShowMessage);
for (int i = 1; i <= NumItems; i++)
{
string clientHandle = ClientHandles.GetValue(i).ToString();
string tag = MAP_CLIENTHANDLE_TAG[clientHandle];
string val = ItemValues.GetValue(i).ToString();
//C# Dictionary 字典 添加数据
tagValueMap.Add(tag, val);
//for (int i = 1; i <= NumItems; i++)
//{
// if (ClientHandles.GetValue(i).Equals(1))
// {
// str = "通道 1.设备 1.标记 1:" + ItemValues.GetValue(i).ToString();
// }
// if (ClientHandles.GetValue(i).Equals(2))
// {
// str = "通道 1.设备 1.标记 2:" + ItemValues.GetValue(i).ToString();
// }
// if (ClientHandles.GetValue(i).Equals(3))
// {
// str = "通道 1.设备 1.test:" + ItemValues.GetValue(i).ToString();
// }
}
//str = tagValueMap["通道 1.设备 1.标记 1"];
// str = tagValueMap["通道 1.设备 1.标记 2"];
//str = tagValueMap["通道 1.设备 1.标记 test"];
// BeginInvoke(show1, new string[] { str });
MyOPCItem json =ParseOPCData(tagValueMap);
BeginInvoke(show1, json);
}
//public string ParseOPCData(Dictionary tagValueMap)
//{
// foreach (var item in tagValueMap)
// {
// string key = item.Key;
// var value = item.Value;
// return "{"+"key:"+ key + "," + "Value:"+ value+"}";
// }
// return "";
//}
///
/// 读Dictionary中的值,并传递给实体类。
///
///
///
public MyOPCItem ParseOPCData(Dictionary tagValueMap)
{
opc = new MyOPCItem();
foreach (var item in tagValueMap)
{
opc.key = item.Key;
opc.Value = item.Value;
return opc;
}
return null;
}
//public void ShowMessage(string str)
//{
// //wsc.Send(JsonConvert.SerializeObject(str));
// // wsc.Send(str);
// richTextBox1.AppendText(str + System.Environment.NewLine);
//}
///
/// websocket传递数据,和测试客户端显示。
///
///
public void ShowMessage(MyOPCItem opc)
{
wsc.Send(JsonConvert.SerializeObject(opc));
richTextBox1.AppendText(opc.Value + System.Environment.NewLine);
}
#endregion
private void Form1_Load_1(object sender, EventArgs e)
{
KepServerLoad();
}
private void Form1_FormClosing_1(object sender, FormClosingEventArgs e)
{
KepServer.Disconnect();
}
}
}
3.结论
初步实现与websocket的数据对接,以json对象的形式传递,还需要websocket服务端将其反序列化为json字符串,并解析给前端使用。当然,这只是一个初步的Demo,其性能优化,线程占有的内存,以及稳定性都有待测试。