C#获取PLC信息 (KepServer)三
有些业务场景不适合异步获取信息,可以使用主动轮询+同步获取的方式获取OPC信息
using OPCAutomation;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Net;
using LitJson;
namespace XSDLOPCClinet
{
public partial class Form1 : Form
{
/*---------------------------静态变量及全局对象段-----------------------*/
OPCServer ObjOPCServer;
OPCGroups ObjOPCGroups;
OPCGroup ObjOPCGroup;
static string OPCServerName = "Kepware.KEPServerEX.V5";// 服务器名称
static string OPCip = "127.0.0.1";// 服务器IP地址
const string DesignLife = "db500dbd450";// 刀具设定寿命地址
const string ActualLife = "dbd500dbd454";// 刀具实际寿命地址
const string ToolName = "dbd500dbW428";// 刀具名称地址
// 数据库配置信息
/*public static string uid = "sa";
public static string psw = "W991224z";
public static string initialcatalog = "test2";
public static string Server = "10.4.51.87\\MYNEWSQL";
public static string timeout = "3"; // 等待时间太长会导致连接失败情况下程序"假死"
*/
public const string sqloptions = "uid=sa;password=W991224z;initial catalog = test2;Server=10.4.51.87\\MYNEWSQL;Connect Timeout=3";
// 变量声明
private OPCItem[] OPC_ITEMS_ADDED;// 声明item添加函数名
Dictionary<string, string> MAP_CLIENTHANDLE_TAG = new Dictionary<string, string>();// 创建字典用于绑定TAG和地址,这里使用全称访问
Dictionary<string, string> tagValueMap = new Dictionary<string, string>();// 绑定TAG和数据信息
List<string> l_str = new List<string>();// 用于存放设备名称
List<OPCItem> ItemsAdded = new List<OPCItem>();// 用于存放设备item/地址信息
// 定义委托
public delegate void Handlelist();
/*--------------------------以下为程序段-----------------------------*/
// 窗体初始化
public Form1()
{
InitializeComponent();
}
// 启动连接,之后进行设备信息初始化操作
private void InitServer()
{
ObjOPCServer = new OPCServer();
ObjOPCServer.Connect(OPCServerName, OPCip);
if (ObjOPCServer.ServerState == (int)OPCServerState.OPCRunning)
{
label1.Text = $"OPC服务器名:{ObjOPCServer.ServerName}";
label4.Text = $"连接启动时间:{ObjOPCServer.StartTime.ToString()}";
label3.Text = "服务运行中,请勿关闭";
//创建并设置组
bool bSucce = this.InitGetDataConfig();
//添加设备信息
GetDeviceGroup();
//group添加items
bool addItems = AddGroupItems();
//初始化Tag字典
bool tagDictionary = InitTagValueMap();
//采集opc数据
bool Sync = InitTimer();
//GetOPCData();
bool Alert = InitAutoAlart();
}
else
{
label3.Text = "OPC服务器状态异常: " + ObjOPCServer.ServerState.ToString();
}
}
// 初始化OPC驱动配置
private bool InitGetDataConfig()
{
ObjOPCGroups = ObjOPCServer.OPCGroups;
ObjOPCGroup = ObjOPCGroups.Add();// 初始化Groups组
//ObjOPCGroup.DataChange += new DIOPCGroupEvent_DataChangeEventHandler(GroupAsyncReadComplete);
//ObjOPCGroup.IsActive = true;
//ObjOPCGroup.IsSubscribed = true;
//ObjOPCServer.OPCGroups.DefaultGroupDeadband = 3;// 死区值,设为0时,服务器端该组内任何数据变化都通知组
return true;
}
// 初始化tagValueMap(Tag与数据容器)
private bool InitTagValueMap()
{
foreach(string tag in l_str)
{
string address1 = $"{tag}.{DesignLife}";// 刀具设定寿命
string address2 = $"{tag}.{ActualLife}";// 刀具实际寿命
string address3 = $"{tag}.{ToolName}";// 刀具名称
tagValueMap.Add(address1,"");
tagValueMap.Add(address2, "");
tagValueMap.Add(address3, "");
}
return true;
}
// 获取设备信息
private void GetDeviceGroup()
{
l_str.Add("10A.PLC.");
l_str.Add("10B.PLC.");
l_str.Add("10C.PLC.");
l_str.Add("10D.PLC.");
l_str.Add("20A.PLC.");
l_str.Add("20B.PLC.");
l_str.Add("20C.PLC.");
l_str.Add("20D.PLC.");
l_str.Add("20E.PLC.");
l_str.Add("20F.PLC.");
l_str.Add("30A.PLC.");
l_str.Add("30B.PLC.");
l_str.Add("30C.PLC.");
l_str.Add("30D.PLC.");
l_str.Add("30E.PLC.");
l_str.Add("30F.PLC.");
l_str.Add("40A.PLC.");
l_str.Add("40B.PLC.");
l_str.Add("40C.PLC.");
l_str.Add("40D.PLC.");
l_str.Add("40E.PLC.");
l_str.Add("40F.PLC.");
l_str.Add("80A.PLC.");
l_str.Add("80B.PLC.");
l_str.Add("80C.PLC.");
l_str.Add("80D.PLC.");
l_str.Add("80E.PLC.");
l_str.Add("80F.PLC.");
}
// 添加Item值(address)
private bool AddGroupItems()
{
try
{
int n = 0;
foreach (string tag in l_str)
{
string address1 = $"{tag}{DesignLife}";// 刀具设定寿命
string address2 = $"{tag}{ActualLife}";// 刀具实际寿命
string address3 = $"{tag}{ToolName}";// 刀具名称
ItemsAdded.Add(ObjOPCGroup.OPCItems.AddItem(address1, n));
ItemsAdded.Add(ObjOPCGroup.OPCItems.AddItem(address2, n + 1));
ItemsAdded.Add(ObjOPCGroup.OPCItems.AddItem(address3, n + 2));
//clientHandle tag关系
MAP_CLIENTHANDLE_TAG.Add(n + "", address1);
MAP_CLIENTHANDLE_TAG.Add(n + 1 + "", address2);
MAP_CLIENTHANDLE_TAG.Add(n + 2 + "", address3);
n = n + 3;
}
OPC_ITEMS_ADDED = ItemsAdded.ToArray();
}
catch (Exception e)
{
}
return true;
}
// 初始化主动轮询计时器
private bool InitTimer()
{
System.Timers.Timer t = new System.Timers.Timer(1000);//实例化Timer类,设置间隔时间为1000毫秒;
t.Elapsed += new System.Timers.ElapsedEventHandler(OrderTimer_Tick);//到时间的时候执行事件;
t.AutoReset = true;//设置是执行一次(false)还是一直执行(true);
t.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件;
return true;
}
// 设置自动报警定时器
private bool InitAutoAlart()
{
System.Timers.Timer p = new System.Timers.Timer(50000);//实例化Timer类,设置间隔时间为5分钟;
p.Elapsed += new System.Timers.ElapsedEventHandler(AutoAlert);//到时间的时候执行事件;
p.AutoReset = true;//设置是执行一次(false)还是一直执行(true);
p.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件;
return true;
}
// 定时报警,查询数据库报警列表,通知golang,去调用钉钉接口
public void AutoAlert(object source, System.Timers.ElapsedEventArgs e)
{
SqlConnection objConnection = new SqlConnection(sqloptions);
objConnection.Open();
string sql = "SELECT * FROM AlertList";
SqlCommand cmd = new SqlCommand(sql, objConnection);
SqlDataReader dr = cmd.ExecuteReader();
if (dr.HasRows)
{
while (dr.Read())
{
try
{
AlertGolang(dr["MachineCode"].ToString(), dr["ToolName"].ToString(), dr["DesignLife"].ToString(), dr["ActualLife"].ToString(), "1");
}
catch(Exception ex)
{
}
}
}
dr = null;
cmd = null;
sql = null;
objConnection.Close();
}
// 定时轮询
public void OrderTimer_Tick(object source, System.Timers.ElapsedEventArgs e)
{
Handlelist m = new Handlelist(InvokedFunc);
Invoke(m);
}
// 被委托的方法
private void InvokedFunc()
{
SyncRead();
}
// 同步读
private void SyncRead()
{
int[] temp = new int[OPC_ITEMS_ADDED.Length + 1];
temp[0] = 0;
for (int i = 1; i <= OPC_ITEMS_ADDED.Length; i++)
{
temp[i] = OPC_ITEMS_ADDED[i - 1].ServerHandle;
}
Array serverHandles = (Array)temp;
ObjOPCGroup.SyncRead((short)OPCDataSource.OPCDevice, serverHandles.Length - 1, ref serverHandles, out Array Values, out Array Errors, out object Qualities, out object TimeStamps);
// 字典Key,Value绑定
int j = 1;
foreach (string tag in l_str)
{
tagValueMap[$"{tag}{DesignLife}"] = Values.GetValue(j).ToString();
tagValueMap[$"{tag}{ActualLife}"] = Values.GetValue(j + 1).ToString();
tagValueMap[$"{tag}{ToolName}"] = Values.GetValue(j + 2).ToString();
j = j + 3;
string sql = $"UPDATE Toolsinfo set ActualLife = '{tagValueMap}',DesignLife = '{tagValueMap}' WHERE MachineCode = '{tag.Substring(0,3)}' AND ToolName = '{tagValueMap}'";
try {
Set(sql);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
// 异步读取信息实现(因业务需求,存在相同数据,废弃)
/*private void GetOPCData()
{
try
{
//异步读opc数据
int[] temp = new int[OPC_ITEMS_ADDED.Length + 1];
temp[0] = 0;
for (int i = 1; i <= OPC_ITEMS_ADDED.Length; i++)
{
temp[i] = OPC_ITEMS_ADDED[i - 1].ServerHandle;
}
Array serverHandles = (Array)temp;
Array Errors;
int cancelID;
Random rd = new Random();
int TransactionID = rd.Next(1, 100);
ObjOPCGroup.AsyncRead(serverHandles.Length - 1, ref serverHandles, out Errors, TransactionID, out cancelID);//第一参数为item数量
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + "cuowu");
}
}*/
// 异步获取t,v信息(废弃)
/*private void GroupAsyncReadComplete(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)
{
//C# Dictionary 字典
for (int i = 1; i <= NumItems; i++)
{
string clientHandle = ClientHandles.GetValue(i).ToString();
string tag = MAP_CLIENTHANDLE_TAG[clientHandle];
string val = ItemValues.GetValue(i).ToString();
tagValueMap.Add(tag, val);
try
{
listBox1.Items.Add(tagValueMap[$"10A.PLC.{ToolName}"]);
listBox1.Items.Add(tagValueMap[$"10A.PLC.{DesignLife}"]);
listBox1.Items.Add(tagValueMap[$"10A.PLC.{ActualLife}"]);
listBox1.Items.Add(tagValueMap[$"10B.PLC.{ToolName}"]);
listBox1.Items.Add(tagValueMap[$"10C.PLC.{DesignLife}"]);
listBox1.Items.Add(tagValueMap[$"10D.PLC.{ActualLife}"]);
}
catch (Exception e)
{
}
//C# Dictionary 字典 添加数据
//MessageBox.Show(tag);
}
// 在这里可以调用SQL语句进行value值的更新,是异步的,不是同步更新
//string sql = $"INSERT INTO Toolsinfo (ToolName,DesignLife,ActualLife,MachineCode) values ('{a}','{b}','{c}','10A')";
//Set(sql);
}
*/
// 退出时释放资源
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
ObjOPCServer.Disconnect();
}
// 启动按钮
private void button1_Click(object sender, EventArgs e)
{
InitServer();// 启动服务
button1.Enabled = false;
button1.Text = "服务已启动";
}
// 数据库SET操作
private void Set(string sql)
{
SqlConnection objConnection = new SqlConnection(sqloptions);
try
{
objConnection.Open();
SqlCommand cmd = new SqlCommand(sql, objConnection);
cmd.ExecuteScalar();
objConnection.Close();
}
catch (Exception err)
{
MessageBox.Show(err.Message);
objConnection.Close();
}
objConnection.Close();
}
/*----------------------与Golang交互--------------------------*/
private void AlertGolang(string MachineCode,string ToolName,string DesignLife,string ActualLife,string warnLevel)
{
ReadOptions Read = new ReadOptions();
string Gosrv = Read.Readini("Srv", "DBsrv");
string Goport = Read.Readini("Srv", "DBport");
string GoRouter = Read.Readini("Router","Ding");
Encoding encode = Encoding.GetEncoding("utf-8");
JsonData parmas = new JsonData
{
["Code"] = warnLevel,
["MachineCode"] = MachineCode,
["ToolName"] = ToolName,
["DesignLife"] = DesignLife,
["ActualLife"] = ActualLife
};
string jsonData = JsonMapper.ToJson(parmas);
HttpWebRequest myReq = null;
HttpWebResponse response = null;
byte[] postdatabyte = Encoding.UTF8.GetBytes(jsonData);
myReq = (HttpWebRequest)WebRequest.Create($"http://{Gosrv}:{Goport}/{GoRouter}");
myReq.Method = "POST";
myReq.ContentType = "application/json; charset=UTF-8";
myReq.ContentLength = postdatabyte.Length;
Stream toPost = myReq.GetRequestStream();
toPost.Write(postdatabyte, 0, postdatabyte.Length);
toPost.Close();
response = (HttpWebResponse)myReq.GetResponse();
var responseString = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("utf-8")).ReadToEnd();
Read = null;
}
/*-----------------------菜单栏-------------------------------*/
private void 增加设备刀具信息ToolStripMenuItem_Click(object sender, EventArgs e)
{
if (button1.Enabled == false)
{
MessageBox.Show("服务运行中,请在停止服务后使用此功能!");
}
else
{
Form2 objFrom = new Form2();
objFrom.ShowDialog();
}
}
}
}