云平台制作(1)-OPC Client取数模块的制作
阅读原文时间:2023年07月09日阅读:1

近来由于工程需要,基于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<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,其性能优化,线程占有的内存,以及稳定性都有待测试。