【源代码】西门子Simatic.NET与Visual C#异步通讯的代码
/********************************************************************* * 模块名称: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);