近来因为工程须要,基于OPC DA 2.0搭建通用的取数模块,与远程webscoket服务端链接,并传输数据。在网上找了些资料,修改相应网友公开的源代码,基本达到要求,特供你们参考。前端
1.实体类web
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.源代码json
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<string, string> MAP_CLIENTHANDLE_TAG; public OPCItem[] OPC_ITEMS_ADDED { get; private set; } public Form1() { InitializeComponent(); } #region OPC Client /// <summary> /// 自动链接OPC Server,并websocket链接服务器。 /// </summary> 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(); } /// <summary> /// 新建OPC Group并设置相应的属性,和触发改变事件 /// </summary> 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); } /// <summary> /// 单独设立添加OPCItem方法,用来添加OPCItem /// </summary> public void AddGroupItems() { List<string> str = new List<string>(); str.Add("通道 1.设备 1.标记 1"); str.Add("通道 1.设备 1.标记 2"); str.Add("通道 1.设备 1.test"); List<OPCItem> ItemsAdd = new List<OPCItem>(); MAP_CLIENTHANDLE_TAG = new Dictionary<string, string>(); 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); /// <summary> /// 数据改变事件触发编写,调用委托事件远程发送。 /// </summary> /// <param name="TransactionID"></param> /// <param name="NumItems"></param> /// <param name="ClientHandles"></param> /// <param name="ItemValues"></param> /// <param name="Qualities"></param> /// <param name="TimeStamps"></param> public void KepGroup_DataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps) { Dictionary<string, string> tagValueMap = new Dictionary<string, string>(); 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<string,string> tagValueMap) //{ // foreach (var item in tagValueMap) // { // string key = item.Key; // var value = item.Value; // return "{"+"key:"+ key + "," + "Value:"+ value+"}"; // } // return ""; //} /// <summary> /// 读Dictionary中的值,并传递给实体类。 /// </summary> /// <param name="tagValueMap"></param> /// <returns></returns> public MyOPCItem ParseOPCData(Dictionary<string, string> 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); //} /// <summary> /// websocket传递数据,和测试客户端显示。 /// </summary> /// <param name="opc"></param> 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,其性能优化,线程占有的内存,以及稳定性都有待测试。服务器