/*********************************************************************
* 模块名称:OPC服务器类
* 版 本:Version 0.97a
* 作 者:龙少爷
* 时 间:2010-11-16 11:43
* 备 注:
* OPC服务器的常用操作:连接,断开,读,写,设置状态
* 附 注:
*
* *******************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using OpcRcw.Da;
using OpcRcw.Comn;
namespace Utility.PLC
{
/// <summary>
/// OPC服务器类 By 龙少爷 2010
/// </summary>
public class OPCServer
{
#region OPCServer Common Fields
private OpcRcw.Da.IOPCServer ServerObj;//OPCServer
private OpcRcw.Da.IOPCAsyncIO2 IOPCAsyncIO2Obj = null;//异步读写对象
private OpcRcw.Da.IOPCGroupStateMgt IOPCGroupStateMgtObj = null;//组管理对象
private IConnectionPointContainer pIConnectionPointContainer = null;
private IConnectionPoint pIConnectionPoint = null;
public const int LOCALE_ID = 0x407;
private Object MyobjGroup1 = null;
private int[] ItemServerHandle;
private int pSvrGroupHandle = 0;
private Int32 dwCookie = 0;
#endregion
#region OPCServer User-Defined Variables
#endregion
#region OPCServer Constructors
public OPCServer()
{
}
#endregion
#region Methods
/// <summary>
/// 建立PC到PLC的连接,返回OPC服务器对象
/// </summary>
/// <param name="programID">将要连接的进程ID</param>
/// <param name="server">服务器IP地址</param>
/// <returns>OPC服务器对象</returns>
public bool CreateServer(string programID, string server)
{
Type svrComponenttyp;
try
{
svrComponenttyp = Type.GetTypeFromProgID(programID, server);//OPCServer
ServerObj = (IOPCServer)Activator.CreateInstance(svrComponenttyp);//注册
return true;
}
catch (Exception ex)
{
return false;
}
}
/// <summary>
/// 添加一个组对象,并返回该组对象的引用
/// </summary>
/// <param name="form">回调对象</param>
/// <returns>是否执行成功</returns>
public bool AddGroup(object form)
{
Int32 dwRequestedUpdateRate = 1000;
Int32 hClientGroup = 1;
Int32 pRevUpdateRate;
float deadband = 0;
int TimeBias = 0;
GCHandle hTimeBias, hDeadband;
hTimeBias = GCHandle.Alloc(TimeBias, GCHandleType.Pinned);
hDeadband = GCHandle.Alloc(deadband, GCHandleType.Pinned);
Guid iidRequiredInterface = typeof(IOPCItemMgt).GUID;
try
{
ServerObj.AddGroup("MyOPCGroup1",//组对象
0,
dwRequestedUpdateRate,
hClientGroup,
hTimeBias.AddrOfPinnedObject(),
hDeadband.AddrOfPinnedObject(),
LOCALE_ID,
out pSvrGroupHandle,
out pRevUpdateRate,
ref iidRequiredInterface,
out MyobjGroup1);
IOPCAsyncIO2Obj = (IOPCAsyncIO2)MyobjGroup1;
//Query interface for Async calls on group object
IOPCGroupStateMgtObj = (IOPCGroupStateMgt)MyobjGroup1;
pIConnectionPointContainer = (IConnectionPointContainer)MyobjGroup1;
//定义特定组的异步调用连接
Guid iid = typeof(IOPCDataCallback).GUID;
// Establish Callback for all async operations
pIConnectionPointContainer.FindConnectionPoint(ref iid, out pIConnectionPoint);
// Creates a connection between the OPC servers's connection point and this client's sink (the callback object)
pIConnectionPoint.Advise(form, out dwCookie);
return true;
}
catch (Exception ex)// catch for group adding
{
return false;
}
finally
{
if (hDeadband.IsAllocated)
hDeadband.Free();
if (hTimeBias.IsAllocated)
hTimeBias.Free();
}
}
/// <summary>
/// 添加一个读写的Items数组对象
/// Ex:
/// OpcRcw.Da.OPCITEMDEF[] ItemArray = new OPCITEMDEF[3];//
/// ItemArray[0].szAccessPath = "";
/// ItemArray[0].szItemID = "S7:[S7 connection_1]DB13,DWORD0";
/// //地址,不同数据类型表示方法不同
/// ItemArray[0].bActive = 1;//是否激活
/// ItemArray[0].hClient = 1;//表示ID
/// ItemArray[0].dwBlobSize = 0;
/// ItemArray[0].pBlob = IntPtr.Zero;
/// ItemArray[0].vtRequestedDataType = 2;
/// </summary>
/// <param name="items">Items读写对象数组</param>
/// <returns>Items是否执行成功</returns>
public bool AddItems(OPCITEMDEF[] items)
{
IntPtr pResults = IntPtr.Zero;
IntPtr pErrors = IntPtr.Zero;
try
{
((IOPCItemMgt)MyobjGroup1).AddItems(items.Length, items, out pResults, out pErrors);
int[] errors = new int[items.Length];
Marshal.Copy(pErrors, errors, 0, items.Length);
ItemServerHandle = new int[items.Length];
IntPtr pos = pResults;
OPCITEMRESULT result;
if (errors[0] == 0)
{
result = (OPCITEMRESULT)Marshal.PtrToStructure(pos, typeof(OPCITEMRESULT));
ItemServerHandle[0] = result.hServer;
}
for (int i = 1; i < errors.Length; i++)
{
if (errors[i] == 0)
{
pos = new IntPtr(pos.ToInt32() + Marshal.SizeOf(typeof(OPCITEMRESULT)));
result = (OPCITEMRESULT)Marshal.PtrToStructure(pos, typeof(OPCITEMRESULT));
ItemServerHandle[i] = result.hServer;
}
}
return true;
}
catch (Exception ex) // catch for add item
{
return false;
}
finally
{
// Free the memory
if (pResults != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(pResults);
pResults = IntPtr.Zero;
}
if (pErrors != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(pErrors);
pErrors = IntPtr.Zero;
}
}
}
/// <summary>
/// 发送异步读命令,结果的读取要通过实现IOPCDataCallback接口的OnReadComplete函数实现
/// </summary>
/// <param name="itemServerHandles">Item读写对象句柄数组</param>
/// <returns>是否执行成功</returns>
public bool Read()
{
int nCancelid;
IntPtr pErrors = IntPtr.Zero;
if (IOPCAsyncIO2Obj != null)
{
try
{
IOPCAsyncIO2Obj.Read(ItemServerHandle.Length, ItemServerHandle, ItemServerHandle.Length, out nCancelid, out pErrors);
int[] errors = new int[ItemServerHandle.Length];
Marshal.Copy(pErrors, errors, 0, ItemServerHandle.Length);
return false;
}
catch (Exception ex)
{
return false;
}
}
else
return false;
}
/// <summary>
/// 发送异步写命令,结果的状态,要通过实现IOPCDataCallback接口的OnWriteComplete函数实现
/// </summary>
/// <param name="itemIndex">要写的Item项</param>
/// <param name="values">要写入到PLC中的值数组</param>
/// <returns>是否执行成功</returns>
public bool Write(int itemIndex, object[] values)
{
int nCancelid;
IntPtr pErrors = IntPtr.Zero;
int[] phServer = new int[1];
phServer[0] = itemIndex;
if (IOPCAsyncIO2Obj != null)
{
try
{
IOPCAsyncIO2Obj.Write(1, phServer, values, itemIndex, out nCancelid, out pErrors);
int[] errors = new int[1];
Marshal.Copy(pErrors, errors, 0, 1);
if (errors[0] != 0)//Error in reading item
{
Marshal.FreeCoTaskMem(pErrors);
pErrors = IntPtr.Zero;
return false;
}
return true;
}
catch (Exception ex)
{
return false;
}
}
else
return false;
}
/// <summary>
/// 发送异步写命令,结果的状态,要通过实现IOPCDataCallback接口的OnWriteComplete函数实现
/// </summary>
/// <param name="values">要写入到PLC中的值数组</param>
/// <returns>是否执行成功</returns>
public bool Write(object[] values)
{
int nCancelid;
IntPtr pErrors = IntPtr.Zero;
if (IOPCAsyncIO2Obj != null)
{
try
{
IOPCAsyncIO2Obj.Write(ItemServerHandle.Length, ItemServerHandle, values, ItemServerHandle.Length, out nCancelid, out pErrors);
int[] errors = new int[ItemServerHandle.Length];
Marshal.Copy(pErrors, errors, 0, ItemServerHandle.Length);
bool bError = false;
foreach (int err in errors)//Error in reading item
{
if (err != 0)
{
bError = true;
break;
}
}
if (bError)
{
Marshal.FreeCoTaskMem(pErrors);
pErrors = IntPtr.Zero;
return false;
}
return true;
}
catch (Exception ex)
{
return false;
}
}
else
return false;
}
/// <summary>
/// 设置PLC状态,使之处罚OnDataChange事件函数
/// </summary>
/// <param name="group"></param>
/// <param name="value"></param>
/// <returns></returns>
public bool SetState(bool value)
{
IntPtr pRequestedUpdateRate = IntPtr.Zero;
int nRevUpdateRate = 0;
IntPtr hClientGroup = IntPtr.Zero;
IntPtr pTimeBias = IntPtr.Zero;
IntPtr pDeadband = IntPtr.Zero;
IntPtr pLCID = IntPtr.Zero;
int nActive = 0;
// activates or deactivates group according to checkbox status
GCHandle hActive = GCHandle.Alloc(nActive, GCHandleType.Pinned);
if (value != true)
hActive.Target = 0;
else
hActive.Target = 1;
try
{
IOPCGroupStateMgtObj.SetState(pRequestedUpdateRate, out nRevUpdateRate, hActive.AddrOfPinnedObject(), pTimeBias, pDeadband, pLCID, hClientGroup);
return true;
}
catch (Exception ex)
{
return false;
}
finally
{
hActive.Free();
}
}
/// <summary>
/// 断开PC到PLC的连接
/// </summary>
/// <returns>是否成功执行</returns>
public bool DisConnect()
{
try
{
if (dwCookie != 0)
{
pIConnectionPoint.Unadvise(dwCookie);
dwCookie = 0;
}
// Free unmanaged code
Marshal.ReleaseComObject(pIConnectionPoint);
pIConnectionPoint = null;
Marshal.ReleaseComObject(pIConnectionPointContainer);
pIConnectionPointContainer = null;
if (IOPCAsyncIO2Obj != null)
{
Marshal.ReleaseComObject(IOPCAsyncIO2Obj);
IOPCAsyncIO2Obj = null;
}
ServerObj.RemoveGroup(pSvrGroupHandle, 0);
if (IOPCGroupStateMgtObj != null)
{
Marshal.ReleaseComObject(IOPCGroupStateMgtObj);
IOPCGroupStateMgtObj = null;
}
if (MyobjGroup1 != null)
{
Marshal.ReleaseComObject(MyobjGroup1);
MyobjGroup1 = null;
}
if (ServerObj != null)
{
Marshal.ReleaseComObject(ServerObj);
ServerObj = null;
}
return true;
}
catch (Exception ex)
{
return false;
}
}
#endregion
#region Public Properity
public OpcRcw.Da.IOPCServer Server
{
get
{
return this.ServerObj;
}
}
#endregion
}
}
如何使用的范例:
由于上面的代码只是简单的封装了OPC的基本操作,然而却在项目中使用并不简洁方便,为此又写了一个接口。
/*********************************************************************
* 模块名称:定义在操作PLC的常用操作和属性
* 作 者:龙少爷
* 时 间:2010-11-24 11:44
* 备 注:
*
* 历史修改记录
* 作 者:
* 修改时间:
* 备 注:
*
* *******************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Picking
{
/// <summary>
/// 定义在操作PLC的常用操作和属性
/// </summary>
public interface IPLCOperator
{
/// <summary>
/// 数据库是否已经连接上
/// </summary>
bool IsConnected { get; set; }
/// <summary>
/// 建立PC到PLC的连接,由指定的adviser参数对象来处理事件的结果
/// </summary>
/// <param name="adviser">接受处理结果的对象</param>
/// <returns>true/false</returns>
bool ConnectToPLC(string programID, string server, object adviser);
bool ReadFromPLCDB();
bool WriteIntoPLCDB(object[] values);
bool SetPLCState(bool state);
bool DisConnectFromPLC();
}
}
与PLC交互的代码,之所以这样写一个单独的类,是为了在同时读写多个ITEM项时,能把每个操作独立出来处理:
/*********************************************************************
* 模块名称:
* 作 者:龙少爷
* 时 间:2010-11-24 13:44
* 备 注:
*
* 历史修改记录
* 作 者:
* 修改时间:
* 备 注:
*
* *******************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Utility.PLC;
using OpcRcw.Da;
namespace Picking
{
/// <summary>
///
/// </summary>
public class DLStopLine : IOPCDataCallback, IPLCOperator
{
#region 私有变量
private OPCServer opcServer = null;
#endregion
#region Public Constructor
public DLStopLine()
{
opcServer = new OPCServer();
}
#endregion
#region IPLCOperator 成员
public bool IsConnected
{
get;
set;
}
public bool ConnectToPLC(string programID, string server, object adviser)
{
if (!opcServer.CreateServer(programID, server))
{
IsConnected = false;
return false;
}
if (!opcServer.AddGroup(adviser))
{
IsConnected = false;
return false;
}
OpcRcw.Da.OPCITEMDEF[] items = new OPCITEMDEF[1];//定义读写的item,共个变量
items[0].szAccessPath = "";
items[0].szItemID = "S7:[S7 connection_1]DB20,B1"; //地址,不同数据类型表示方法不同
items[0].bActive = 1;//是否激活
items[0].hClient = 1;//表示ID
items[0].dwBlobSize = 0;
items[0].pBlob = IntPtr.Zero;
items[0].vtRequestedDataType = 17;
if (!opcServer.AddItems(items))
{
IsConnected = false;
return false;
}
IsConnected = true;
return true;
}
public bool ConnectToPLC(string programID, string server)
{
return ConnectToPLC(programID, server, this);
}
public bool ConnectToPLC(object adviser)
{
return ConnectToPLC("OPC.SimaticNet", "localhost", adviser);
}
public bool ConnectToPLC()
{
return ConnectToPLC("OPC.SimaticNet", "localhost", this);
}
public bool ReadFromPLCDB()
{
return opcServer.Read();
}
public bool WriteIntoPLCDB(object[] values)
{
return opcServer.Write(values);
}
public bool SetPLCState(bool state)
{
return opcServer.SetState(state);
}
public bool DisConnectFromPLC()
{
return opcServer.DisConnect();
}
#endregion
#region IOPCDataCallback 成员
public void OnCancelComplete(int dwTransid, int hGroup)
{
}
public void OnDataChange(int dwTransid, int hGroup, int hrMasterquality, int hrMastererror, int dwCount, int[] phClientItems, object[] pvValues, short[] pwQualities, FILETIME[] pftTimeStamps, int[] pErrors)
{
}
public void OnReadComplete(int dwTransid, int hGroup, int hrMasterquality, int hrMastererror, int dwCount, int[] phClientItems, object[] pvValues, short[] pwQualities, FILETIME[] pftTimeStamps, int[] pErrors)
{
}
public void OnWriteComplete(int dwTransid, int hGroup, int hrMastererr, int dwCount, int[] pClienthandles, int[] pErrors)
{
}
#endregion
#region Destructor
~DLStopLine()
{
if (IsConnected)
opcServer.DisConnect();
}
#endregion
}
}
在项目中,编写相应的调用代码即可。
DLStopLine stopLine = new DLStopLine();
stopLine.ConnectToPLC();
values = new object[1];
values[0] = 1;
stopLine.WriteIntoPLCDB(values);