最近项目中须要 C#经过OPC方式获取数据,折腾了一段时间,第三方提供OPCServer虚拟环境,我建立C#客户端。json
服务端是力控电子系统,第三方软件,很少作赘述,本文主要讲解OPC客户端的搭建和OPC链接步骤。数组
调用OPCAutomation类访问OPCServer端,并进行可控制读取间隔的OPC数据读取、存储工做。服务器
本文主要介绍OPCAutomation类的使用。dom
简单流程以下:异步
1.建立OPC server的链接ui
2.建立OPC组对象并初始化设置this
3.获取组的OPCItems对象,为读取数据作准备.net
4.opcltem的操做设计
5.退出程序的资源释放code
建立链接很简单,须要指定OPCServer所在的服务器(内网能够指定IP或者计算机名),指定OPC服务的名称(同一服务器可能运行多个OPC服务以适配不一样的下位机)
//经过timer获取OPCServer端数据 void TimerTick(object sender, EventArgs e) { //链接OPCServer KepServer.Connect(remoteServerName, remoteServerIP); if (KepServer.ServerState == (int)OPCServerState.OPCRunning) { label1.Text = "已链接到-" + KepServer.ServerName + " ,starttime:" + KepServer.StartTime.ToString(); //建立并设置组 bool bSucce = this.CreateAndSetGroup(); //goup添加items AddGroupItems(); //采集opc数据 GetOPCData(); }else{ label1.Text = "OPC服务器状态异常: " + KepServer.ServerState.ToString(); } }
其中KepServer.Connect(remoteServerName, remoteServerIP);即OPCAutomation类提供的链接方法。
须要注意的是,在实际配置时,须要彻底对OPC服务端所在服务器上配置防火墙出入站规则后,OPC服务才可以被其它服务器上的中间件访问到。
建立组至关于读取OPC上特定的项目,而具体的数据值是在每一个项目下根据开发人员的定义而肯定。
//建立组并设置组信息 private bool CreateAndSetGroup() { try { KepGroups = KepServer.OPCGroups; KepGroup = KepGroups.Add("PCAuto.OPCServer"); KepServer.OPCGroups.DefaultGroupIsActive = true;//激活组 KepServer.OPCGroups.DefaultGroupDeadband = 0;// 死区值,设为0时,服务器端该组内任何数据变化都通知组 KepGroup.IsSubscribed = true;//使用订阅功能,便可以异步,默认false //绑定写操做的事件 KepGroup.AsyncReadComplete += new DIOPCGroupEvent_AsyncReadCompleteEventHandler(GroupAsyncReadComplete); KepItems = KepGroup.OPCItems; } catch (Exception ex) { } return true; }
这段代码是为OPCGroup对象进行初始化。PCAuto.OPCServer为组名,激活组,使用订阅功能,绑定写操做的时间等等。
异步读完成 运行时,Array数组从下标1开始而非0!
//异步读完成 运行时,Array数组从下标1开始而非0! void GroupAsyncReadComplete(int TransactionID, int NumItems, ref System.Array ClientHandles, ref System.Array ItemValues, ref System.Array Qualities, ref System.Array TimeStamps, ref System.Array Errors) { try { //C# Dictionary 字典 Dictionary<string, string> tagValueMap = new Dictionary<string, string>(); 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); } //构建json string json = ParseOPCData(tagValueMap); } catch (Exception ex) { } }
为全局变量OPCGroup添加items,在OPC中,每一个opcltem会被分配一个客户端句柄值,同时会被配置上服务器句柄。几乎全部有关获取指定Opcltem对象的方法均要求指定客户端句柄值。
上图中有一C# Dictionary 字典,暂不作赘述,待下回分解。
//goup添加items string[] sensorIdArray; void AddGroupItems() { List<string> l_str = new List<string>(); //读取配置文件,获取须要的传感器ID string sensorIdList = getSensorId(); sensorIdArray = sensorIdList.Replace("\r\n","").Split(','); foreach (string sensorId in sensorIdArray){ l_str.Add(sensorId+"_COS"); l_str.Add(sensorId+"_DL1"); l_str.Add(sensorId+"_DL2"); l_str.Add(sensorId+"_DL3"); l_str.Add(sensorId+"_DL4"); l_str.Add(sensorId+"_IA"); l_str.Add(sensorId+"_IB"); l_str.Add(sensorId+"_IC"); l_str.Add(sensorId+"_P"); l_str.Add(sensorId+"_Q"); l_str.Add(sensorId+"_UAB"); l_str.Add(sensorId+"_UBC"); l_str.Add(sensorId+"_UCA"); } List<OPCItem> ItemsAdded = new List<OPCItem>(); int n = 0; MAP_CLIENTHANDLE_TAG = new Dictionary<string, string>(); foreach (string tag in l_str){ ItemsAdded.Add(KepItems.AddItem(tag + ".PV", n)); //clientHandle tag关系 MAP_CLIENTHANDLE_TAG.Add(n + "", tag); n++; } OPC_ITEMS_ADDED = ItemsAdded.ToArray(); }
OPCServer服务端数据源item
这段代码设计为了获取到所有的OPCltem所添加到所建立的OPCGroup对象的opcitem集合中,获得添加到goup中的item(OPC_ITEMS_ADDED)。
经过OPC_ITEMS_ADDED采集opc数据
//采集opc数据 private void GetOPCData() { try { //异步读opc数据 int[] temp = new int[OPC_ITEMS_ADDED.Length + 1]; temp[0] = 0; for (int i = 1; i <= OPC_ITEMS_ADDED.Length; i++) { temp[i] = OPC_ITEMS_ADDED[i - 1].ServerHandle; } Array serverHandles = (Array)temp; Array Errors; int cancelID; Random rd = new Random(); int TransactionID = rd.Next(1, 10); KepGroup.AsyncRead(serverHandles.Length - 1, ref serverHandles, out Errors, TransactionID, out cancelID);//第一参数为item数量 } catch (Exception ex) { MessageBox.Show(ex.Message); } }
解析OPC数据并以json格式存储在本地
//解析opc数据 private string ParseOPCData(Dictionary<string, string> tagValueMap) { string filename = "E:\\mine\\下峪口\\data\\electric_data.txt"; StringBuilder builder = new StringBuilder(); string rowStr = ""; string time = ConvertDateTimeToInt(DateTime.Now).ToString(); rowStr += "["; foreach (string sensorId in sensorIdArray){ rowStr += "{\r\n\"id\":"+ "\"" + sensorId + "\",\r\n"; rowStr += "\"substation\":"+"\"假变电所\""+",\r\n"; rowStr += "\"timestamp\":"+time+",\r\n"; foreach (var item in tagValueMap) { string key = item.Key; string[] data = item.Key.Split('_'); string title = data[0]; string word = data[1]; if(title.Equals(sensorId)){ rowStr += "\""+ word + "\""+ ":" + item.Value + ",\r\n"; } } rowStr += "},"; } rowStr += "]"; rowStr = rowStr.Replace(",\r\n}","\r\n}").Replace(",]","]"); builder.Append(rowStr); File.WriteAllText(@filename, builder.ToString(), Encoding.GetEncoding("gb2312")); return ""; }