
C#public class OpcHelper
{
OPCServer MyOpcServer;
OPCGroup MyOpcGroup;
public string Ip { get; set; }
public string ProgId { get; set; } = "Kepware.KEPServerEX.V6";
public bool IsActive { get; set; } = true;
//控制对组的异步通知。已订阅的组从服务器接收数据更改。
public bool IsSubscribed { get; set; } = false;
//当数据变化超过deadband设置时,就会触发datachange事件。或者是,只有当数据变化超过deadband设置时才会触发datachange事件
public int DeadBand { get; set; } = 0;
//可能触发数据更改事件的最快速率。缓慢的进程可能会导致数据更改以低于此速率触发,但它们永远不会超过此速率。速率单位为毫秒。
public int UpdateRate { get; set; } = 100;
//用于保存要访问的数据
private Dictionary<string, OPCItem> _dicOpcItem { get; set; } = new Dictionary<string, OPCItem>();
private event Action<Dictionary<string, object>> ReadFinishEvent;
private List<string> _ReadAddress { get; set; }
private event Action<bool> WriteFinishEvent;
public event Action<Dictionary<string, object>> DataChangeEvent;
public bool IsDataChangeEvent { get; set; } = false;//是否启用DataChange事件
public bool Connect()
{
try
{
MyOpcServer = new OPCServer();
MyOpcServer.Connect(ProgId, Ip);
MyOpcGroup = MyOpcServer.OPCGroups.Add("DefaultGroup");
MyOpcGroup.IsActive = IsActive;
MyOpcGroup.IsSubscribed = IsSubscribed;
MyOpcGroup.DeadBand = DeadBand;
MyOpcGroup.UpdateRate = UpdateRate;
MyOpcGroup.AsyncReadComplete += MyOpcGroup_AsyncReadComplete;
MyOpcGroup.AsyncWriteComplete += MyOpcGroup_AsyncWriteComplete;
if (IsDataChangeEvent)
{
MyOpcGroup.DataChange += MyOpcGroup_DataChange;
}
return true;
}
catch (Exception ex)
{
//do log
return false;
}
}
/// <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 MyOpcGroup_DataChange(int TransactionID, int NumItems
, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)
{
if (DataChangeEvent != null)
{
Dictionary<string, object> dic = new Dictionary<string, object>();
foreach (var item in _dicOpcItem)
{
dic.Add(item.Key, item.Value.Value);
}
DataChangeEvent(dic);
}
}
//添加读写的节点信息
public bool AddOpcItems(string Address)
{
if (_dicOpcItem.ContainsKey(Address))
{
return false;
}
OPCItem item = MyOpcGroup.OPCItems.AddItem(Address, 0);
_dicOpcItem.Add(Address, item);
return true;
}
//删除读写的节点
public bool RemoveOpcItems(string Address)
{
if (_dicOpcItem.ContainsKey(Address))
{
_dicOpcItem[Address] = null;
_dicOpcItem.Remove(Address);
}
return true;
}
//同步读取某一个节点数据
public object ReadValueSyn(string Address)
{
object ItemValues;
object Qualities;
object TimeStamps;
OPCItem item = _dicOpcItem[Address];
item.Read(1, out ItemValues, out Qualities, out TimeStamps);
return ItemValues;
}
//同步读取指定列表节点数据
public Dictionary<string, object> ReadAllSyn(Dictionary<string, object> dic)
{
object ItemValues;
object Qualities;
object TimeStamps;
foreach (var item in dic)
{
OPCItem it = _dicOpcItem[item.Key].Value;
it.Read(1, out ItemValues, out Qualities, out TimeStamps);
dic[item.Key] = ItemValues;
}
return dic;
}
//同步读取所有节点数据
public Dictionary<string, object> ReadAllSyn()
{
object ItemValues;
object Qualities;
object TimeStamps;
Dictionary<string, object> dic = new Dictionary<string, object>();
foreach (var item in _dicOpcItem)
{
OPCItem it = item.Value;
it.Read(1, out ItemValues, out Qualities, out TimeStamps);
dic.Add(item.Key, ItemValues);
}
return dic;
}
//同步写
public void WriteValueSync(string Address, object Value)
{
int[] tmp = new int[] { 0, _dicOpcItem[Address].ServerHandle };
Array serverHandles = (Array)tmp;
Array Errors;
int cancelID;
object[] v = new object[] { "", Value };
Array values = (Array)v;
MyOpcGroup.SyncWrite(1, ref serverHandles, ref values, out Errors);
}
//同步按组写
public void WriteValueSync(Dictionary<string, object> dic)
{
int[] tmp = new int[dic.Count + 1];
int idx = 1;
foreach (var item in dic)
{
tmp[idx] = _dicOpcItem[item.Key].ServerHandle;
idx++;
}
object[] v = new object[dic.Count + 1];
idx = 1;
foreach (var item in dic)
{
v[idx] = item.Value;
idx++;
}
Array serverHandles = (Array)tmp;
Array Errors;
Array values = (Array)v;
MyOpcGroup.SyncWrite(dic.Count, ref serverHandles, ref values, out Errors);
}
/// <summary>
/// 异步读取信息
/// </summary>
public void ReadValueAsync(List<string> Address, Action<Dictionary<string, object>> action)
{
_ReadAddress = Address;
ReadFinishEvent = action;
int[] tmp = new int[Address.Count + 1];
for (int i = 1; i < tmp.Length; i++)
{
tmp[i] = _dicOpcItem[Address[i - 1]].ServerHandle;
}
Array serverHandles = (Array)tmp;
Array Errors;
int cancelID;
MyOpcGroup.AsyncRead(1, ref serverHandles, out Errors, 1, out cancelID);
}
/// <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>
/// <param name="Errors"></param>
private void MyOpcGroup_AsyncReadComplete(int TransactionID, int NumItems, ref Array ClientHandles
, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps, ref Array Errors)
{
if (ReadFinishEvent != null)
{
Dictionary<string, object> dic = new Dictionary<string, object>();
for (int i = 0; i < _ReadAddress.Count; i++)
{
dic.Add(_ReadAddress[i], _dicOpcItem[_ReadAddress[i]].Value);
}
ReadFinishEvent(dic);
}
}
public void WriteValueAsync(Dictionary<string, object> dic, Action<bool> action)
{
this.WriteFinishEvent = action;
int[] tmp = new int[dic.Count + 1];
int idx = 1;
foreach (var item in dic)
{
tmp[idx] = _dicOpcItem[item.Key].ServerHandle;
idx++;
}
object[] v = new object[dic.Count + 1];
idx = 1;
foreach (var item in dic)
{
v[idx] = item.Value;
idx++;
}
Array serverHandles = (Array)tmp;
Array Errors;
Array values = (Array)v;
MyOpcGroup.AsyncWrite(dic.Count, ref serverHandles, ref values, out Errors, 1, out var cancelId);
}
/// <summary>
/// 写完成回调
/// </summary>
/// <param name="TransactionID"></param>
/// <param name="NumItems"></param>
/// <param name="ClientHandles"></param>
/// <param name="Errors"></param>
private void MyOpcGroup_AsyncWriteComplete(int TransactionID, int NumItems, ref Array ClientHandles, ref Array Errors)
{
if (WriteFinishEvent != null)
{
int[] tmp = new int[Errors.Length];
Errors.CopyTo(tmp, 0);
int ret = Array.Find(tmp, x => x == 1);
WriteFinishEvent(ret == 0);
}
}
/// <summary>
/// 断开连接
/// </summary>
public void DisConnect()
{
MyOpcServer.Disconnect();
}
}
在这个基础上可以考虑合并读取
给每个方法加上事件
界面开发
C#using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace opcda01
{
public partial class FrmOpc : Form
{
OpcHelper opc = new OpcHelper();
public FrmOpc()
{
InitializeComponent();
lsvMain.Columns.Add("B1");
lsvMain.Columns.Add("B2");
lsvMain.Columns.Add("B3");
lsvMain.Columns.Add("F1");
lsvMain.Columns.Add("I1");
lsvMain.Columns.Add("L1");
lsvMain.Columns.Add("S1");
lsvMain.Columns.Add("S2");
ListViewItem item = new ListViewItem();
item.Text = "";
item.SubItems.Add("");
item.SubItems.Add("");
item.SubItems.Add("");
item.SubItems.Add("");
item.SubItems.Add("");
item.SubItems.Add("");
item.SubItems.Add("");
lsvMain.Items.Add(item);
}
private void btnConnect_Click(object sender, EventArgs e)
{
opc.Ip = "127.0.0.1";
opc.IsSubscribed= true;
opc.IsDataChangeEvent= true;
opc.Connect();
opc.AddOpcItems("Chanel1.Device1.B1");
opc.AddOpcItems("Chanel1.Device1.B2");
opc.AddOpcItems("Chanel1.Device1.B3");
opc.AddOpcItems("Chanel1.Device1.F1");
opc.AddOpcItems("Chanel1.Device1.I1");
opc.AddOpcItems("Chanel1.Device1.L1");
opc.AddOpcItems("Chanel1.Device1.S1");
opc.AddOpcItems("Chanel1.Device1.S2");
}
private void btnRead_Click(object sender, EventArgs e)
{
var ret = opc.ReadValueSyn("Chanel1.Device1.B1");
lsvMain.Items[0].SubItems[0].Text = ret.ToString();
}
private void btnReadAll_Click(object sender, EventArgs e)
{
Dictionary<string, object> dic = opc.ReadAllSyn();
int idx = 0;
foreach (var item in dic)
{
lsvMain.Items[0].SubItems[idx].Text = item.Value.ToString();
idx++;
}
}
private void btnWrite_Click(object sender, EventArgs e)
{
opc.WriteValueSync("Chanel1.Device1.S2", "Hello");
}
private void btnWriteBatch_Click(object sender, EventArgs e)
{
Dictionary<string, object> dic = new Dictionary<string, object>();
dic.Add("Chanel1.Device1.S1","A");
dic.Add("Chanel1.Device1.S2", "B");
opc.WriteValueSync(dic);
}
private void btnReadValueAsync_Click(object sender, EventArgs e)
{
List<string> Address = new List<string>()
{
"Chanel1.Device1.B1",
"Chanel1.Device1.B2",
"Chanel1.Device1.F1"
};
opc.ReadValueAsync(Address, (obj)=> {
var d = obj;
});
}
private void btnWriteAsync_Click(object sender, EventArgs e)
{
Dictionary<string, object> dic = new Dictionary<string, object>();
dic.Add("Chanel1.Device1.S1", "A");
dic.Add("Chanel1.Device1.S2", "B");
opc.WriteValueAsync(dic, (obj) =>
{
var d = obj;
});
}
private void btnDataChange_Click(object sender, EventArgs e)
{
opc.DataChangeEvent -= Opc_DataChangeEvent;
opc.DataChangeEvent += Opc_DataChangeEvent;
}
private void Opc_DataChangeEvent(Dictionary<string, object> dic)
{
int idx = 0;
foreach (var item in dic)
{
this.Invoke(() =>
{
lsvMain.Items[0].SubItems[idx].Text = item.Value.ToString();
});
idx++;
}
//这个是用来刷chart图的
int l = int.Parse(dic["Chanel1.Device1.L1"].ToString());
chart1.Invoke(Draw,new object[] {l});
}
}
}
考虑加一个chart图,跟踪L1
安装WinForms.DataVisualization
C#private void InitChart()
{
Title title = new Title();
//标题内容
title.Text = "L1";
//标题的字体
title.Font = new System.Drawing.Font("Microsoft Sans Serif", 12, FontStyle.Bold);
//标题字体颜色
title.ForeColor = Color.FromArgb(26, 59, 105);
//标题阴影颜色
title.ShadowColor = Color.FromArgb(32, 0, 0, 0);
//标题阴影偏移量
title.ShadowOffset = 3;
chart1.Titles.Add(title);
Series series = new Series();
series.ChartType = SeriesChartType.Spline;
chart1.Series.Add(series);
ChartArea chartArea = new ChartArea();
//背景色
chartArea.BackColor = Color.FromArgb(64, 165, 191, 228);
//背景渐变方式
chartArea.BackGradientStyle = GradientStyle.TopBottom;
//渐变和阴影的辅助背景色
chartArea.BackSecondaryColor = Color.White;
//边框颜色
chartArea.BorderColor = Color.FromArgb(64, 64, 64, 64);
//阴影颜色
chartArea.ShadowColor = Color.Transparent;
//设置X轴和Y轴线条的颜色和宽度
chartArea.AxisX.LineColor = Color.FromArgb(64, 64, 64, 64);
chartArea.AxisX.LineWidth = 1;
chartArea.AxisY.LineColor = Color.FromArgb(64, 64, 64, 64);
chartArea.AxisY.LineWidth = 1;
chart1.ChartAreas.Add(chartArea);
chart1.ChartAreas[0].AxisX.LabelStyle.Format = "HH:mm:ss";
chart1.ChartAreas[0].AxisX.ScaleView.Size = 5;
chart1.ChartAreas[0].AxisX.ScrollBar.IsPositionedInside = true;
chart1.ChartAreas[0].AxisX.ScrollBar.Enabled = true;
chart1.Series[0].IsXValueIndexed = true;
chart1.Series[0].XValueType = System.Windows.Forms.DataVisualization
.Charting.ChartValueType.Time;
chart1.Dock=DockStyle.Fill;
pnlChart.Controls.Add(chart1);
}
private void Draw(int value)
{
PointMessage point = new PointMessage()
{
Dt = DateTime.Now,
Value =value
};
if (chart1.Series[0].Points.Count >= 10)
{
chart1.Series[0].Points.RemoveAt(0);
}
chart1.Series[0].Points.AddXY(point.Dt, point.Value);
chart1.ChartAreas[0].AxisX.ScaleView.Position = chart1.Series[0].Points.Count - 5;
}
基础类
C#public class PointMessage
{
public DateTime Dt { get; set; }
public int Value { get; set; }
}
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!