3

我在一个单线程 OPC 客户端程序中工作,管理连接到同一个 OPC 服务器的 3 个不同的西门子 PLC,也是由西门子提供的。

单线程客户端如下所示:

loop
 begin
  processPLC1;
  processPLC2;
  processPLC3;
end;

每个 processPLC 程序都会调用底层 OPC 库,例如:

 OPCutils.WriteOPCGroupItemValue(FGroupIf, FHandleItemOpc, Value);

好的,现在我想在不同的线程中调用每个 processPLC 并并行工作。

我做了一些研究并使用 OmniThreadLibrary 开始了一些代码,但我不认为 OPC 代码是多线程安全的。是吗?

我应该使用 task.Invoke 还是类似的东西?ReadOPC 函数如何返回 PLC 标签的值?这里的最佳实践是什么?

谢谢!!!

4

3 回答 3

5

我通常以两种方式完成此操作:

1) 应用程序具有由单个线程拥有的单个 OPC 客户端实例。在读取/写入 OPC 值时,由客户端应用程序自动化的所有并行进程随后使用某种消息传递或与拥有 OPC 客户端的线程同步。

2) 每个线程拥有自己的私有 OPC 客户端,每个客户端都与 OPC 服务器独立通信。

就个人而言,我发现最常用的方案是前者。一个 OPC(或其他专有)客户端对象,带有对客户端进行同步调用的线程。确实,在几乎所有流程控制情况下,您都在使用多线程,目的是优雅地封装逻辑现实世界任务并将其与 UI 隔离,而不是为了任何类型的性能。这些线程可以承受等待数据的相对“较长”时间的阻塞——如果需要,PLC 会很高兴地按住堡垒一百毫秒。

您选择哪一种很大程度上取决于您的应用程序的规模和性质。对于花费很长时间等待外部实时事件的大量轻量级线程,在应用程序中保留单个 OPC 客户端实例并节省大量独立连接的开销是有意义的。对于少量繁重、快速移动、OPC 密集型线程,为每个线程提供自己的 OPC 客户端可能是有意义的。

还要记住你的 OPC 标签的刷新率——很多时候服务器只更新它们大约每 100 毫秒左右。即使是执行一次扫描,您的 PLC 也可能至少需要 10 毫秒。当数据永远不会那么快刷新时,让大量线程每秒独立轮询服务器一百次是没有意义的。

对于过程控制软件,您真正想要的是拥有大量空闲或低负载 CPU 时间——线程越轻越好。总体系统响应能力是关键,并且能够让您的软件处理高负载情况(在操作系统决定是时候索引 HDD 时,大量任务突然汇聚……可以说,净空保持齿轮润滑)。您的大多数线程可能大部分时间都在等待。事件和回调通常在这里最有意义。

此外,考虑 PLC 编程在这里也很重要。例如,在我的机器中,我有一些对时间非常关键的操作,同时,它们每次运行时都是唯一计时的——这些进程的持续时间大约为几分钟,计时到十分之一以下。一秒钟或更长时间,每天重复数百到数千次,并且每次运行时都具有关键但不同的持续时间。我已经看到这些处理方式有两种——一种在软件中,一种在 PLC 中。对于前一种情况,软件告诉 PLC 何时启动,然后一直运行,直到软件告诉它停止。这有明显的缺陷;在这种情况下,最好将时间间隔简单地发送到 PLC 并让它进行计时。

于 2013-08-04T22:57:11.487 回答
3

OPC 基于 COM 技术,因此适用相同的规则。如果要从不同的线程访问 OPC 服务器,每个线程必须自己调用 CoInitialize 和 CoUninitialize。

这很可能与从不同进程访问 OPC 服务器相同。OPC 服务器本身是单线程还是多线程并不重要。

可能阻碍您的是您正在使用的 OPC 客户端库的抽象。

于 2013-08-04T07:24:17.183 回答
2

恕我直言,您似乎对其进行了过度设计,最好使事情尽可能简单,但添加线程并不会使事情变得更简单。

如果您有多个 OPC 服务器,那么线程可能是一种更好的抽象,但现在您只需要一个连接到一个 OPC 服务器,那么拥有多个线程和多个连接似乎过于复杂而没有太多实际收益。

而是像以前一样使用常规的 OPC 订阅机制进行顺序读取和写入。

于 2013-08-05T06:23:57.240 回答