安装 MatrikonOPC软件,用来模拟OPC_DA服务器。
参考这篇博文:OPCServer:使用Matrikon OPC Server Simulation - ioufev -
从网上下载OPCDAAuto.dll
参考的这篇知乎文章:OPC DA C# 客户端 - 知乎 (zhihu.com)
注册好OPCDAAuto.dll之后,在项目中引用OPCDAAuto.dll,需要注意的是OPCDAAuto.dll不能复制到项目的文件夹中,需要按照知乎文章重新注册一遍。
由于网上的大部分是使用订阅模式,当节点配置好之后等待数据发生变化之后服务器将数据推送过来。与我项目的要求不符,于是自己整理了一些代码。
测试:

连接上之后会将服务器所有节点都列出在左边的listbox中。选中之后添加到右侧的listbox中。点击读取就能获取选中节点的值。

OPC_client代码:


using System;
using System.Collections.Generic;
using OPC_DATest;
using OPCAutomation;
namespace OPC_cli
{
class OPC_client
{
public OPCServer Server;//服务器对象
public OPCGroups Groups;//组列表
public OPCGroup Group;//组对象
public OPCItems Items;//节点列表
//public OPCItem Item;//节点对象
public bool ConnectedState { get; private set; }
public delegate string ServerShutDown(string Reason);
/// <summary>
/// 服务器断开连接事件
/// </summary>
public event ServerShutDown OnServerShutDown;
/// <summary>
/// 连接
/// </summary>
/// <param name="ServerName"></param>
/// <param name="ServerIP"></param>
/// <returns></returns>
public bool Connect(string ServerName,string ServerIP,out string Message)
{
try
{
if (Server == null) Server = new OPCServer();
Server.Connect(ServerName, ServerIP);
if (Server.ServerState == (int)OPCServerState.OPCRunning)
{
Server.ServerShutDown += Server_ServerShutDown;
Message = "连接成功";
ConnectedState = true;
IniServer();
return true;
}
else
{
Message = "连接失败。状态码:" + Server.ServerState.ToString();
ConnectedState = false;
return false;
}
}
catch (Exception ex)
{
Message = ex.Message;
return false;
}
}
/// <summary>
/// 服务器异常断开连接
/// </summary>
/// <param name="Reason"></param>
private void Server_ServerShutDown(string Reason)
{
ConnectedState = false;
OnServerShutDown?.Invoke(Reason);
}
/// <summary>
/// 断开连接
/// </summary>
public void DisConnect()
{
Server?.Disconnect();
}
/// <summary>
/// 添加一个测试组,之后读取都在这个测试组中添加或者删除item
/// </summary>
private void IniServer()
{
Groups = Server.OPCGroups;
Group = Server.OPCGroups.Add("测试1");
Items = Group.OPCItems;
}
/// <summary>
/// 获取OPCDA服务器中所有的节点<br/>
/// 有一些节点服务器自带有Write的,不能读取。
/// </summary>
/// <returns></returns>
public List<string> GetBrowser()
{
try
{
List<string> result = new List<string>();
OPCBrowser browser = Server.CreateBrowser();
browser.ShowBranches();
browser.ShowLeafs(true);
foreach (var item in browser)
{
result.Add(item.ToString());
}
return result;
}
catch (Exception)
{
return null;
}
}
/// <summary>
/// 读取节点数据
/// </summary>
/// <param name="tagAddress">需要读取的节点</param>
/// <returns></returns>
public List<read_result> Read(List<string> tagAddress)
{
List<read_result> results = new List<read_result>();
try
{
OPCItem[] items = new OPCItem[tagAddress.Count];
int[] temp = new int[items.Length + 1];
temp[0] = 0;//这个第一个数据不知道为什么一定不能为空,好像是索引是从1开始的
for (int i = 0; i < items.Length; i++)
{
items[i] = Items.AddItem(tagAddress[i], i);
items[i].Read(1, out object ItemValues, out object Qualities, out object TimeStamps);
read_result read_ = new read_result()
{
name = tagAddress[i],
value = ItemValues.ToString(),
quality = Qualities.ToString(),
timestamp = TimeStamps.ToString()
};
results.Add(read_);
temp[i + 1] = items[i].ServerHandle;
}
Array serverHandles = (Array)temp;
Array errors;
Items.Remove(1,serverHandles,out errors);
return results;
}
catch (Exception)
{
return null;
}
}
/// <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>
private void Group_DataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)
{
Console.ForegroundColor = ConsoleColor.Green;
for (int i = 1; i <= NumItems; i++)
{
int tmpClientHandle = Convert.ToInt32(ClientHandles.GetValue(i));
string tmpValue = ItemValues.GetValue(i).ToString();
string tmpTime = ((DateTime)(TimeStamps.GetValue(i))).ToString();
Console.WriteLine("Time:" + tmpTime);
Console.WriteLine("ClientHandle : " + tmpClientHandle.ToString());
Console.WriteLine("tag value " + tmpValue);
}
}
}
}OPC_client


namespace OPC_DATest
{
public class read_result
{
public string name { get; set; }
public string value { get; set; }
public string quality { get; set; }
public string timestamp { get; set; }
}
}read_result
界面代码:


using OPC_cli;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
namespace OPC_DATest
{
public partial class MainForm : Form
{
OPC_client Client;
public MainForm()
{
InitializeComponent();
}
/// <summary>
/// 连接按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Btn_Connect_Click(object sender, EventArgs e)
{
switch (Btn_Connect.Text)
{
case "连接":
Client = new OPC_client();
bool success = Client.Connect(Txt_serverName.Text, Txt_ip.Text, out string message);
if (success)
{
MessageBox.Show("连接成功");
IniTags();
Btn_Connect.Text = "断开连接";
}
else
{
MessageBox.Show("连接失败:" + message);
}
break;
case "断开连接":
Client.DisConnect();
Btn_Connect.Text = "连接";
break;
}
}
/// <summary>
/// 初始化节点
/// </summary>
private void IniTags()
{
lis_Tags.Items.Clear();
List<string> tags = Client.GetBrowser();
foreach (var item in tags)
{
lis_Tags.Items.Add(item.ToString());
}
}
/// <summary>
/// 窗体关闭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
Client?.DisConnect();
}
/// <summary>
/// 窗体加载
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MainForm_Load(object sender, EventArgs e)
{
Client = new OPC_client();//初始化话OPCClient对象
dgv_ReadResult.DataSource = new BindingList<read_result>();//绑定表格数据源
dgv_ReadResult.Columns[0].HeaderCell.Value = "名称";//初始化表列名称
dgv_ReadResult.Columns[1].HeaderCell.Value = "值";
dgv_ReadResult.Columns[2].HeaderCell.Value = "质量";
dgv_ReadResult.Columns[3].HeaderCell.Value = "时间戳";
}
/// <summary>
/// 读取按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Btn_Read_Click(object sender, EventArgs e)
{
if (!Client.ConnectedState)
{
return;
}
if (lis_Tags_Read.Items.Count == 0)
{
return;
}
List<string> list = new List<string>();
foreach (var item in lis_Tags_Read.Items)
{
list.Add(item.ToString());
}
try
{
List<read_result> re = Client.Read(list);
if (re != null && re.Count > 0)
{
dgv_ReadResult.DataSource = re;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
/// <summary>
/// 选中节点添加到待读取列表
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Btn_Add_Click(object sender, EventArgs e)
{
foreach (var item in lis_Tags.SelectedItems)
{
if (!lis_Tags_Read.Items.Contains(item.ToString()))
{
lis_Tags_Read.Items.Add(item.ToString());
}
}
}
/// <summary>
/// 移除待读取节点
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Btn_Remove_Click(object sender, EventArgs e)
{
try
{
object[] selected = new object[lis_Tags_Read.Items.Count];
lis_Tags_Read.SelectedItems.CopyTo(selected, 0);
foreach (object a in selected)//开始遍历刚刚创立的 数组
{
lis_Tags_Read.Items.Remove(a);
}
}
catch (Exception)
{
}
}
/// <summary>
/// 移除所有待读取节点
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Btn_RemoveAll_Click(object sender, EventArgs e)
{
lis_Tags_Read.Items.Clear();
}
/// <summary>
/// 自动读取时间间隔
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void txt_AutoReadTimes_KeyPress(object sender, KeyPressEventArgs e)
{
if (((int)e.KeyChar < 48 || (int)e.KeyChar > 57) && (int)e.KeyChar != 8 && (int)e.KeyChar != 46)
e.Handled = true;
}
/// <summary>
/// 自动读取
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ckb_AutoRead_CheckedChanged(object sender, EventArgs e)
{
if (ckb_AutoRead.Checked)
{
int times = int.Parse(txt_AutoReadTimes.Text);
if (times < 1000)
{
times = 1000;
timer1.Interval = times;
}
else
{
timer1.Interval = times;
}
Btn_Read.Enabled = false;
timer1.Enabled = true;
}
else
{
timer1.Enabled = false;
Btn_Read.Enabled = true;
}
}
/// <summary>
/// 自动读取定时器
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Enabled = false;
Btn_Read_Click(null, null);
timer1.Enabled = true;
}
}
}界面逻辑代码


namespace OPC_DATest
{
partial class MainForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.Txt_ip = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.Txt_serverName = new System.Windows.Forms.TextBox();
this.Btn_Connect = new System.Windows.Forms.Button();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.lis_Tags = new System.Windows.Forms.ListBox();
this.Btn_Add = new System.Windows.Forms.Button();
this.Btn_Remove = new System.Windows.Forms.Button();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.lis_Tags_Read = new System.Windows.Forms.ListBox();
this.groupBox3 = new System.Windows.Forms.GroupBox();
this.dgv_ReadResult = new System.Windows.Forms.DataGridView();
this.Btn_Read = new System.Windows.Forms.Button();
this.Btn_RemoveAll = new System.Windows.Forms.Button();
this.timer1 = new System.Windows.Forms.Timer(this.components);
this.ckb_AutoRead = new System.Windows.Forms.CheckBox();
this.txt_AutoReadTimes = new System.Windows.Forms.TextBox();
this.groupBox1.SuspendLayout();
this.groupBox2.SuspendLayout();
this.groupBox3.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dgv_ReadResult)).BeginInit();
this.SuspendLayout();
//
// Txt_ip
//
this.Txt_ip.Location = new System.Drawing.Point(94, 17);
this.Txt_ip.Name = "Txt_ip";
this.Txt_ip.Size = new System.Drawing.Size(100, 21);
this.Txt_ip.TabIndex = 0;
this.Txt_ip.Text = "127.0.0.1";
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(35, 20);
= "label1";
this.label1.Size = new System.Drawing.Size(53, 12);
this.label1.TabIndex = 1;
this.label1.Text = "IP地址:";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(11, 47);
= "label2";
this.label2.Size = new System.Drawing.Size(77, 12);
this.label2.TabIndex = 3;
this.label2.Text = "服务器名称:";
//
// Txt_serverName
//
this.Txt_serverName.Location = new System.Drawing.Point(94, 44);
this.Txt_serverName.Name = "Txt_serverName";
this.Txt_serverName.Size = new System.Drawing.Size(183, 21);
this.Txt_serverName.TabIndex = 2;
this.Txt_serverName.Text = "Matrikon.OPC.Simulation.1";
//
// Btn_Connect
//
this.Btn_Connect.Location = new System.Drawing.Point(202, 17);
this.Btn_Connect.Name = "Btn_Connect";
this.Btn_Connect.Size = new System.Drawing.Size(75, 23);
this.Btn_Connect.TabIndex = 4;
this.Btn_Connect.Text = "连接";
this.Btn_Connect.UseVisualStyleBackColor = true;
this.Btn_Connect.Click += new System.EventHandler(this.Btn_Connect_Click);
//
// groupBox1
//
this.groupBox1.Controls.Add(this.lis_Tags);
this.groupBox1.Location = new System.Drawing.Point(13, 71);
= "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(264, 251);
this.groupBox1.TabIndex = 5;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "浏览节点";
//
// lis_Tags
//
this.lis_Tags.Dock = System.Windows.Forms.DockStyle.Fill;
this.lis_Tags.FormattingEnabled = true;
this.lis_Tags.ItemHeight = 12;
this.lis_Tags.Location = new System.Drawing.Point(3, 17);
this.lis_Tags.Name = "lis_Tags";
this.lis_Tags.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended;
this.lis_Tags.Size = new System.Drawing.Size(258, 231);
this.lis_Tags.TabIndex = 0;
//
// Btn_Add
//
this.Btn_Add.Location = new System.Drawing.Point(280, 170);
this.Btn_Add.Name = "Btn_Add";
this.Btn_Add.Size = new System.Drawing.Size(75, 23);
this.Btn_Add.TabIndex = 6;
this.Btn_Add.Text = "添加";
this.Btn_Add.UseVisualStyleBackColor = true;
this.Btn_Add.Click += new System.EventHandler(this.Btn_Add_Click);
//
// Btn_Remove
//
this.Btn_Remove.Location = new System.Drawing.Point(280, 209);
this.Btn_Remove.Name = "Btn_Remove";
this.Btn_Remove.Size = new System.Drawing.Size(75, 23);
this.Btn_Remove.TabIndex = 7;
this.Btn_Remove.Text = "移除";
this.Btn_Remove.UseVisualStyleBackColor = true;
this.Btn_Remove.Click += new System.EventHandler(this.Btn_Remove_Click);
//
// groupBox2
//
this.groupBox2.Controls.Add(this.lis_Tags_Read);
this.groupBox2.Location = new System.Drawing.Point(364, 71);
= "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(264, 251);
this.groupBox2.TabIndex = 8;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "添加节点";
//
// lis_Tags_Read
//
this.lis_Tags_Read.Dock = System.Windows.Forms.DockStyle.Fill;
this.lis_Tags_Read.FormattingEnabled = true;
this.lis_Tags_Read.ItemHeight = 12;
this.lis_Tags_Read.Location = new System.Drawing.Point(3, 17);
this.lis_Tags_Read.Name = "lis_Tags_Read";
this.lis_Tags_Read.Size = new System.Drawing.Size(258, 231);
this.lis_Tags_Read.TabIndex = 0;
//
// groupBox3
//
this.groupBox3.Anchor = ((System.Windows.Forms.AnchorStyles)(((( | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.groupBox3.Controls.Add(this.dgv_ReadResult);
this.groupBox3.Location = new System.Drawing.Point(13, 328);
= "groupBox3";
this.groupBox3.Size = new System.Drawing.Size(522, 153);
this.groupBox3.TabIndex = 9;
this.groupBox3.TabStop = false;
this.groupBox3.Text = "读取节点";
//
// dgv_ReadResult
//
this.dgv_ReadResult.AllowUserToAddRows = false;
this.dgv_ReadResult.AllowUserToDeleteRows = false;
this.dgv_ReadResult.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
this.dgv_ReadResult.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dgv_ReadResult.Dock = System.Windows.Forms.DockStyle.Fill;
this.dgv_ReadResult.Location = new System.Drawing.Point(3, 17);
this.dgv_ReadResult.Name = "dgv_ReadResult";
this.dgv_ReadResult.RowTemplate.Height = 23;
this.dgv_ReadResult.Size = new System.Drawing.Size(516, 133);
this.dgv_ReadResult.TabIndex = 0;
//
// Btn_Read
//
this.Btn_Read.Anchor = ((System.Windows.Forms.AnchorStyles)((( | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Right)));
this.Btn_Read.Font = new System.Drawing.Font("宋体", 20F, System.Drawing.FontStyle.Bold);
this.Btn_Read.Location = new System.Drawing.Point(538, 430);
this.Btn_Read.Name = "Btn_Read";
this.Btn_Read.Size = new System.Drawing.Size(90, 48);
this.Btn_Read.TabIndex = 10;
this.Btn_Read.Text = "读取";
this.Btn_Read.UseVisualStyleBackColor = true;
this.Btn_Read.Click += new System.EventHandler(this.Btn_Read_Click);
//
// Btn_RemoveAll
//
this.Btn_RemoveAll.Location = new System.Drawing.Point(280, 252);
this.Btn_RemoveAll.Name = "Btn_RemoveAll";
this.Btn_RemoveAll.Size = new System.Drawing.Size(75, 23);
this.Btn_RemoveAll.TabIndex = 11;
this.Btn_RemoveAll.Text = "移除全部";
this.Btn_RemoveAll.UseVisualStyleBackColor = true;
this.Btn_RemoveAll.Click += new System.EventHandler(this.Btn_RemoveAll_Click);
//
// timer1
//
this.timer1.Interval = 1000;
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
//
// ckb_AutoRead
//
this.ckb_AutoRead.Anchor = ((System.Windows.Forms.AnchorStyles)((( | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Right)));
this.ckb_AutoRead.AutoSize = true;
this.ckb_AutoRead.Location = new System.Drawing.Point(541, 372);
this.ckb_AutoRead.Name = "ckb_AutoRead";
this.ckb_AutoRead.Size = new System.Drawing.Size(72, 16);
this.ckb_AutoRead.TabIndex = 12;
this.ckb_AutoRead.Text = "自动读取";
this.ckb_AutoRead.UseVisualStyleBackColor = true;
this.ckb_AutoRead.CheckedChanged += new System.EventHandler(this.ckb_AutoRead_CheckedChanged);
//
// txt_AutoReadTimes
//
this.txt_AutoReadTimes.Anchor = ((System.Windows.Forms.AnchorStyles)((( | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Right)));
this.txt_AutoReadTimes.Location = new System.Drawing.Point(538, 345);
this.txt_AutoReadTimes.Name = "txt_AutoReadTimes";
this.txt_AutoReadTimes.Size = new System.Drawing.Size(87, 21);
this.txt_AutoReadTimes.TabIndex = 13;
this.txt_AutoReadTimes.Text = "2000";
this.txt_AutoReadTimes.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.txt_AutoReadTimes_KeyPress);
//
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(649, 493);
this.Controls.Add(this.txt_AutoReadTimes);
this.Controls.Add(this.ckb_AutoRead);
this.Controls.Add(this.Btn_RemoveAll);
this.Controls.Add(this.Btn_Read);
this.Controls.Add(this.groupBox3);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.Btn_Remove);
this.Controls.Add(this.Btn_Add);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.Btn_Connect);
this.Controls.Add(this.label2);
this.Controls.Add(this.Txt_serverName);
this.Controls.Add(this.label1);
this.Controls.Add(this.Txt_ip);
= "MainForm";
this.Text = "MainForm";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing);
this.Load += new System.EventHandler(this.MainForm_Load);
this.groupBox1.ResumeLayout(false);
this.groupBox2.ResumeLayout(false);
this.groupBox3.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.dgv_ReadResult)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox Txt_ip;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox Txt_serverName;
private System.Windows.Forms.Button Btn_Connect;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.ListBox lis_Tags;
private System.Windows.Forms.Button Btn_Add;
private System.Windows.Forms.Button Btn_Remove;
private System.Windows.Forms.GroupBox groupBox2;
private System.Windows.Forms.ListBox lis_Tags_Read;
private System.Windows.Forms.GroupBox groupBox3;
private System.Windows.Forms.Button Btn_Read;
private System.Windows.Forms.Button Btn_RemoveAll;
private System.Windows.Forms.DataGridView dgv_ReadResult;
private System.Windows.Forms.Timer timer1;
private System.Windows.Forms.CheckBox ckb_AutoRead;
private System.Windows.Forms.TextBox txt_AutoReadTimes;
}
}界面布局代码
















