c# OPC应用程序相同的代码,但工作方式不同
本文关键字:工作 方式不 代码 OPC 应用程序 | 更新日期: 2023-09-27 17:49:18
我正在开发一个c#自定义OPC客户端,我开始在控制台应用程序中编写速度,一切都像我想要的那样完美。
然后我决定做一个windows窗体应用程序来获得视觉体验。
windows窗体应用程序只是简单地停止工作,停止从OPC服务器读取数据大约一分钟后。而控制台应用程序一直在读取和读取。
我在调试模式下也找不到任何明显的东西。
我绝对是在抓救命稻草,希望有人能给我一些启示。
每个应用程序都使用OPCFoundation提供的.dll文件。
这是控制台应用程序
static void Main(string[] args)
{
Opc.URL url = new Opc.URL("opcda://localhost/RSLinx OPC Server");
Opc.Da.Server server = null;
OpcCom.Factory fact = new OpcCom.Factory();
server = new Opc.Da.Server(fact, null);
server.Connect(url, new Opc.ConnectData(new System.Net.NetworkCredential()));
// Create a group
Opc.Da.Subscription group;
Opc.Da.SubscriptionState groupState = new Opc.Da.SubscriptionState();
groupState.Name = "Group";
groupState.Active = true;
group = (Opc.Da.Subscription)server.CreateSubscription(groupState);
// add items to the group.
Opc.Da.Item[] items = new Opc.Da.Item[6];
items[0] = new Opc.Da.Item();
items[0].ItemName = "[UX1]F20:9";
items[1] = new Opc.Da.Item();
items[1].ItemName = "[UX1]F22:30";
items[2] = new Opc.Da.Item();
items[2].ItemName = "[UX1]F22:6";
items[3] = new Opc.Da.Item();
items[3].ItemName = "[UX1]F18:8";
items[4] = new Opc.Da.Item();
items[4].ItemName = "[UX1]F22:32";
items[5] = new Opc.Da.Item();
items[5].ItemName = "[UX1]F22:5";
items = group.AddItems(items);
group.DataChanged += new Opc.Da.DataChangedEventHandler(OnTransactionCompleted);
}
static void OnTransactionCompleted(object group, object hReq, Opc.Da.ItemValueResult[] items)
{
Console.WriteLine("------------------->");
Console.WriteLine("DataChanged ...");
for (int i = 0; i < items.GetLength(0); i++)
{
Console.WriteLine("Item DataChange - ItemId: {0}", items[i].ItemName);
Console.WriteLine(" Value: {0,-20}", items[i].Value);
Console.WriteLine(" TimeStamp: {0:00}:{1:00}:{2:00}.{3:000}",
items[i].Timestamp.Hour,
items[i].Timestamp.Minute,
items[i].Timestamp.Second,
items[i].Timestamp.Millisecond);
}
Console.WriteLine("-------------------<");
}
这是WinForm应用程序
public Form1()
{
InitializeComponent();
_Form1 = this;
}
public static Form1 _Form1;
public void update(string message)
{
this.richTextBox1.Text = message;
}
private void Form1_Load(object sender, EventArgs e)
{
readplc();
}
static void readplc()
{
Opc.URL url = new Opc.URL("opcda://localhost/RSLinx OPC Server");
Opc.Da.Server server = null;
OpcCom.Factory fact = new OpcCom.Factory();
server = new Opc.Da.Server(fact, null);
server.Connect(url, new Opc.ConnectData(new System.Net.NetworkCredential()));
// Create a group
Opc.Da.Subscription group;
Opc.Da.SubscriptionState groupState = new Opc.Da.SubscriptionState();
groupState.Name = "Group";
groupState.Active = true;
group = (Opc.Da.Subscription)server.CreateSubscription(groupState);
// add items to the group.
Opc.Da.Item[] items = new Opc.Da.Item[6];
items[0] = new Opc.Da.Item();
items[0].ItemName = "[UX1]F20:9";
items[1] = new Opc.Da.Item();
items[1].ItemName = "[UX1]F22:30";
items[2] = new Opc.Da.Item();
items[2].ItemName = "[UX1]F22:6";
items[3] = new Opc.Da.Item();
items[3].ItemName = "[UX1]F18:8";
items[4] = new Opc.Da.Item();
items[4].ItemName = "[UX1]F22:32";
items[5] = new Opc.Da.Item();
items[5].ItemName = "[UX1]F22:5";
items = group.AddItems(items);
group.DataChanged += new Opc.Da.DataChangedEventHandler(OnTransactionCompleted);
}
static void OnTransactionCompleted(object group, object hReq, Opc.Da.ItemValueResult[] items)
{
for (int i = 0; i < items.GetLength(0); i++)
{
UIUpdater TEXT = new UIUpdater();
TEXT.UpdateText(items.GetLength(0).ToString() + " t " + i.ToString() + "Item DataChange - ItemId:" + items[i].ItemName +
"Value: " + items[i].Value + " TimeStamp: " + items[i].Timestamp.Hour + ":" +
items[i].Timestamp.Minute + ":" + items[i].Timestamp.Second + ":" + items[i].Timestamp.Millisecond);
}
}
<<p> UIUpdate类/strong> class UIUpdater
{
public void UpdateText(string DATA)
{
Form1._Form1.update(DATA);
}
public class UpdateUI
{
public int updatedRows { get; set; }
public string Custom1 { get; set; }
public string Custom2 { get; set; }
public string Custom3 { get; set; }
public string exception { get; set; }
public plcTextStatus PLCStatus { get; set; }
}
有任何问题请提问!
正如所怀疑的那样,这是一个跨线程问题。问题是你不能从UI线程以外的任何其他线程更新UI。事务完成的事件实际上在一个单独的线程上被调用,所以它正在更新UI。
它可以工作一段时间,因为它对错误的容错性相对较强,但是你可能会遇到死锁或抛出一个没有被捕获(或报告)的异常。
修复方法很简单。
在此方法中:
public void update(string message)
{
this.richTextBox1.Text = message;
}
改为:
public void update(string message)
{
richTextBox1.Invoke(
(MethodInvoker) delegate
{
richTextBox1.Text = message;
});
}
这样做是告诉richTextBox1
在其拥有的线程(也就是UI线程)上"调用"或运行以下委托(函数)。
你应该尽量避免在这段代码中使用static
方法和引用。我看不出你的代码有什么理由不应该是实例方法,而应该是静态方法。
作为旁注,我编写的OPC程序每秒处理数千个标签和数百个UI更新。你所做的对于小的演示程序是有效的,但是不能很好地扩展。当架构增长时,您需要开始批量处理UI更新,这样您就不会忙于在更新中重复调用UI线程。
编辑
您遇到的另一个问题是使用本地引用(例如对OPC服务器和订阅),并演示了内存泄漏和僵尸对象。正在发生的事情是,readplc
方法超出了范围,你已经创建了对内存中保存的对象的引用。由于无法取消订阅该事件,因此该事件将继续触发。这些变量应该在readplc
方法的范围之外声明,这样您就可以正确地取消订阅事件并关闭OPC服务器。否则,您将离开僵尸订阅(查看RSLinx OPC诊断页面,您将看到所有订阅都在那里)。
将您的服务器作为表单级对象放在readplc()方法的外部。只要您的表单实例化(未关闭)-您的服务器对象将处于活动状态,您的订阅事件也应如此。
服务器很可能正在被垃圾收集器收集。
Opc.Da.Server server = null;
static void readplc()
{
Opc.URL url = new Opc.URL("opcda://localhost/RSLinx OPC Server");
Opc.Da.Server server = null;
OpcCom.Factory fact = new OpcCom.Factory();
**this.server = new Opc.Da.Server(fact, null);**
....
}