声明:如果这篇文章又被变成了仅对VIP开放,请去我的公众号查看。
项目使用的是Winform+C#,从美卓maxDNA OPCServer取数,接口机系统是Win7,工控机是Win Server2003
之前在OPC取数的时候遇到一个难题,上千个点取数时间太长,大概10秒左右,当时对OPC了解不够,在一个技术群里面问关于OPC取数的问题,很多大牛都说需要给钱才肯提供解决方法,当然我也不小气,但是写一个OPC取数接口开口要一万,未免也太狠了吧;靠人不如靠自己,自己认真捉摸OPC,在遇到问题的第二天下午解决,取数时间控制在秒级;同时也分享一些经验,技术共享,同时有关于OPC或者实时数据库相关的外包项目也可以找我,微信xiaoyiyz 靠谱
废话不多说,下面贴出代码,首先是异步读
异步读是OPCGroup的DataChange事件
当值或组中项目的值的质量触发DataChange事件已经改变。 请注意,事件不会比组的更新速率快。 所以,项目值将由服务器保持并缓冲,直到当前时间+更新速率大于上次更新的时间(事件触发)。这也受组和项的活动状态的影响。 只有活动的项目,和其组是活动的将被发送到事件中的客户端。
class OPCHelper:IDisposable
{
private string strHostIP;
private string strHostName;
private OPCServer opcServer;
private OPCGroups opcGroups;
private OPCGroup opcGroup;
private List<int> itemHandleClient = new List<int>();
private List<int> itemHandleServer = new List<int>();
private List<string> itemNames = new List<string>();
private OPCItems opcItems;
private OPCItem opcItem;
private Dictionary<string, string> itemValues = new Dictionary<string, string>();
public bool Connected = false;
public OPCHelper(string strHostIP, string strHostName, int UpdateRate)
{
this.strHostIP = strHostIP;
this.strHostName = strHostName;
if (!CreateServer())
return;
if (!ConnectServer(strHostIP, strHostName))
return;
Connected = true;
opcGroups = opcServer.OPCGroups;
opcGroup = opcGroups.Add("ZZHOPCGROUP");
SetGroupProperty(opcGroup, UpdateRate);
opcGroup.DataChange += new DIOPCGroupEvent_DataChangeEventHandler(opcGroup_DataChange);
opcGroup.AsyncWriteComplete += new DIOPCGroupEvent_AsyncWriteCompleteEventHandler(opcGroup_AsyncWriteComplete);
opcItems = opcGroup.OPCItems;
}
/// <summary>
/// 创建服务
/// </summary>
/// <returns></returns>
private bool CreateServer()
{
try
{
opcServer = new OPCServer();
}
catch
{
return false;
}
return true;
}
/// <summary>
/// 连接到服务器
/// </summary>
/// <param name="strHostIP"></param>
/// <param name="strHostName"></param>
/// <returns></returns>
private bool ConnectServer(string strHostIP, string strHostName)
{
try
{
opcServer.Connect(strHostName, strHostIP);
}
catch
{
return false;
}
return true;
}
/// <summary>
/// 设置组的属性
/// </summary>
/// <param name="opcGroup"></param>
/// <param name="updateRate"></param>
private void SetGroupProperty(OPCGroup opcGroup, int updateRate)
{
opcGroup.IsActive = true;
opcGroup.DeadBand = 0;
opcGroup.UpdateRate = updateRate;
opcGroup.IsSubscribed = true;
}
public bool Contains(string itemNameContains)
{
foreach (string key in itemValues.Keys)
{
if (key == itemNameContains)
return true;
}
return false;
}
public void AddItems(string[] itemNamesAdded)
{
for (int i = 0; i < itemNamesAdded.Length; i++)
{
this.itemNames.Add(itemNamesAdded[i]);
itemValues.Add(itemNamesAdded[i], "");
}
for (int i = 0; i < itemNamesAdded.Length; i++)
{
itemHandleClient.Add(itemHandleClient.Count != 0 ? itemHandleClient[itemHandleClient.Count - 1] + 1 : 1);
opcItem = opcItems.AddItem(itemNamesAdded[i], itemHandleClient[itemHandleClient.Count - 1]);
itemHandleServer.Add(opcItem.ServerHandle);
}
}
public string[] GetItemValues(string[] getValuesItemNames)
{
string[] getedValues = new string[getValuesItemNames.Length];
for (int i = 0; i < getValuesItemNames.Length; i++)
{
if (Contains(getValuesItemNames[i]))
itemValues.TryGetValue(getValuesItemNames[i], out getedValues[i]);
}
return getedValues;
}
/// <summary>
/// 异步写
/// </summary>
/// <param name="writeItemNames"></param>
/// <param name="writeItemValues"></param>
public void AsyncWrite(string[] writeItemNames, string[] writeItemValues)
{
OPCItem[] bItem = new OPCItem[writeItemNames.Length];
for (int i = 0; i < writeItemNames.Length; i++)
{
for (int j = 0; j < itemNames.Count; j++)
{
if (itemNames[j] == writeItemNames[i])
{
bItem[i] = opcItems.GetOPCItem(itemHandleServer[j]);
break;
}
}
}
int[] temp = new int[writeItemNames.Length + 1];
temp[0] = 0;
for (int i = 1; i < writeItemNames.Length + 1; i++)
{
temp[i] = bItem[i - 1].ServerHandle;
}
Array serverHandles = (Array)temp;
object[] valueTemp = new object[writeItemNames.Length + 1];
valueTemp[0] = "";
for (int i = 1; i < writeItemNames.Length + 1; i++)
{
valueTemp[i] = writeItemValues[i - 1];
}
Array values = (Array)valueTemp;
Array Errors;
int cancelID;
opcGroup.AsyncWrite(writeItemNames.Length, ref serverHandles, ref values, out Errors, 2009, out cancelID);
GC.Collect();
}
public void SyncWrite(string[] writeItemNames, string[] writeItemValues)
{
OPCItem[] bItem = new OPCItem[writeItemNames.Length];
for (int i = 0; i < writeItemNames.Length; i++)
{
for (int j = 0; j < itemNames.Count; j++)
{
if (itemNames[j] == writeItemNames[i])
{
bItem[i] = opcItems.GetOPCItem(itemHandleServer[j]);
}
}
}
int[] temp = new int[writeItemNames.Length + 1];
temp[0] = 0;
for (int i = 1; i < writeItemNames.Length; i++)
{
temp[i] = bItem[i - 1].ServerHandle;
}
Array serverHandles = (Array)temp;
object[] valueTemp = new object[writeItemNames.Length + 1];
valueTemp[0] = "";
for (int i = 1; i < writeItemNames.Length + 1; i++)
{
valueTemp[i] = writeItemValues[i - 1];
}
Array values = (Array)valueTemp;
Array Errors;
opcGroup.SyncWrite(writeItemNames.Length, ref serverHandles, ref values, out Errors);
GC.Collect();
}
void opcGroup_DataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)
{
for (int i = 1; i <= NumItems; i++)
{
itemValues[itemNames[Convert.ToInt32(ClientHandles.GetValue(i)) - 1]] = ItemValues.GetValue(i).ToString();
}
}
void opcGroup_AsyncWriteComplete(int TransactionID, int NumItems, ref Array ClientHandles, ref Array Errors)
{
throw new NotImplementedException();
}
public void Dispose()
{
if (opcGroup != null)
{
opcGroup.DataChange -= new DIOPCGroupEvent_DataChangeEventHandler(opcGroup_DataChange);
opcGroup.AsyncWriteComplete -= new DIOPCGroupEvent_AsyncWriteCompleteEventHandler(opcGroup_AsyncWriteComplete);
}
if (opcServer != null)
{
opcServer.Disconnect();
opcServer = null;
}
Connected = false;
}
}
声明一下,这段代码并非我自己写的,是参考网上的资源
下面贴出同步读方法
public class OPCReadAuto:IDisposable
{
object syncLock = new object();
OPCServer OpcServer;
OPCGroup OpcGroup, OpcWriteGroup;
OPCGroups OpcGroups;
OPCItems OpcItems;
OPCItem OpcItem;
/// <summary>
/// OPC连接状态
/// </summary>
bool IsConnected = false;
/// <summary>
/// 客户端句柄
/// </summary>
int ItmHandleClient = 0;
/// <summary>
/// 服务端句柄
/// </summary>
int ItmHandleServer = 0;
string IpAddr;
//public string[] OpcValues = null;
int index;
public void Dispose()
{
DisConnected();
}
public OPCReadAuto(string opcAddr)
{
this.IpAddr = opcAddr;
bool b = ConnectToServer();
if (b == false)
return;
bool b2 = CreateGroups();
if (b2 == false)
return;
}
string ServerName = "";
/// <summary>
/// 连接OPC Server
/// </summary>
/// <returns></returns>
public bool ConnectToServer()
{
try
{
if (OpcServer != null)
{
try
{
if (OpcServer.ServerState == (int)OPCServerState.OPCRunning)
{
//WriteLog.WriteLogs(OpcServer.ServerName + "-----" + OpcServer.ServerNode + "-----" + OpcServer.ServerState);
GlobalVariables.OPCState = true;
return true;
}
}
catch
{
GlobalVariables.OPCState = false;
}
}
bool isConn = false;
OpcServer = new OPCServer();
//获取IP地址上最后一个 OPC Server 的名字
object serverList = OpcServer.GetOPCServers(IpAddr);
if (serverList == null)
{
GlobalVariables.OPCState = false;
return false;
}
foreach (string turn in (Array)serverList)
{
ServerName = turn;
}
OpcServer.Connect(ServerName, IpAddr); //连接OPC Server
if (OpcServer.ServerState == (int)OPCServerState.OPCRunning)
{
isConn = true;
IsConnected = true;
GlobalVariables.OPCState = true;
}
return isConn;
}
catch (Exception ex)
{
WriteLog.WriteLogs(ex.ToString());
GlobalVariables.OPCState = false;
return false;
}
}
/// <summary>
/// 创建组
/// </summary>
/// <returns></returns>
private bool CreateGroups()
{
if (OpcGroup != null)
return true;
bool isCreate = false;
try
{
OpcGroups = OpcServer.OPCGroups;
OpcGroup = OpcGroups.Add("ZZHGROUP" + DateTime.Now.ToString("yyyyMMddHHmmssfff"));
//设置组属性
OpcServer.OPCGroups.DefaultGroupIsActive = true;
OpcServer.OPCGroups.DefaultGroupDeadband = 0;
OpcItems = OpcGroup.OPCItems;
isCreate = true;
}
catch (Exception ex)
{
WriteLog.WriteLogs(ex.ToString());
isCreate = false;
}
return isCreate;
}
/// <summary>
/// OPC取数
/// </summary>
/// <param name="opcTags"></param>
/// <returns></returns>
public string[] GetOpcValues(List<string> opcTags)
{
bool b = ConnectToServer();
if (b == false)
return null;
CreateGroups();
index = 0;
int badValue = 0;
string[] OpcValue = new string[opcTags.Count];
string strTemp = ""; //临时使用
foreach (string str in opcTags)
{
string strs = GetOpcValueOne(str);
//如果测点加不进去,数量超过10个,说明接口机又取不到数了 ZZH
if (strs.Equals("BAD"))
{
badValue += 1;
if (badValue > 10)
{
GlobalVariables.OPCState = false;
return null;
}
}
OpcValue[index] = strs;
strTemp += str + ",值:" + strs + "\r\n";
index++;
}
WriteLog.WriteLogs(strTemp);
return OpcValue;
}
Hashtable ReadHS = new Hashtable();
int iItemIndex = 1;
/// <summary>
/// 同步取数/一个一个读取
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
private string GetOpcValueOne(string str)
{
OPCItem item;
int IndexItem = 0;
try
{
try
{
if (ReadHS.ContainsKey(str))
item = OpcGroup.OPCItems.GetOPCItem(Convert.ToInt32(ReadHS[str]));
//item = OpcGroup.OPCItems.Item(str);
else
{
iItemIndex += 1;
item = OpcGroup.OPCItems.AddItem(str, iItemIndex);
ReadHS.Add(str, item.ServerHandle);
}
}
catch
{
iItemIndex += 1;
try
{ item = OpcGroup.OPCItems.AddItem(str, iItemIndex); }
catch
{
return "BAD";
}
}
Object value;
Object quality;
Object timestamp;
//直接从设备上取数
item.Read((short)OPCDataSource.OPCDevice, out value, out quality, out timestamp);
return value.ToString();
}
catch (Exception es)
{
WriteLog.WriteLogs(es.ToString());
return "";
}
}
/// <summary>
/// 断开OPC连接
/// </summary>
public void DisConnected()
{
if (!IsConnected)
{
return;
}
//加锁
lock (syncLock)
{
if (OpcServer != null)
{
try
{
//删组
if(OpcGroup != null)
OpcServer.OPCGroups.Remove(OpcGroup.Name);
if(OpcWriteGroup != null)
OpcServer.OPCGroups.Remove(OpcWriteGroup.Name);
}
catch
{ GC.Collect(); }
try
{
OpcServer.Disconnect();
}
catch
{
try
{
GC.Collect();
OpcServer.Disconnect();
}
catch
{
GC.Collect();
}
}
}
}
IsConnected = false;
}
}
注意!
用完一定要Disconnect,如果不断开,以后再连接的时候肯定会出错;另外,opcgroup的名字一定不能重复
Disconnect()
这允许您断开与服务器的连接,然后连接到另一个服务器,或删除物体。 它是良好的编程实践,为客户端应用程序明确删除它创建的对象(包括所有OPCGroup和OPCItem)自动化方法。 调用此函数将删除所有组并释放所有引用底层的OPC自定义服务器。
有什么疑问或者建议可以给我留言,也可以微信,有错误的地方欢迎指正
关注公众号,回复OPC,即可获取OPC相关资料