对象 PLC 映射
什么是对象 PLC 映射
对象 PLC 映射(Object PLC Mapping,OPM)在很大程度上受到了 Java 持久化 API(JPA)[1] 的启发。PLC4X 项目的主要目标之一,是让与 PLC 设备的通信变得简单,从而便于开发与 PLC 交互的应用程序。由于许多(甚至大多数)应用程序开发人员并非 PLC 通信和协议方面的专家,因此与 PLC 交互应尽可能简单,无需掌握太多领域知识。这正是当年 JPA 被提出的初衷——只需在 POJO(Plain old Java Object,普通 Java 对象)上调用方法,即可与数据库交互。OPM 模块的用途也正是如此:只需与 POJO 进行交互,即可实现 PLC 通信。
定义 OPM 实体
要让某个类能被 OPM 系统处理,你需要创建带有 OPM 注解的类。
这些注解包括:
@PlcEntity:声明在类级别上,将该类声明为有效的 OPM 实体。@PlcTag:声明在属性级别上,将该字段声明为属性,通过从已连接的 PLC 中获取数据来自动填充。
示例:
@PlcEntity
public class MyEntity {
@PlcTag("DB01:DW01:REAL")
private double pressure;
@PlcTag(value = "DB01:DW05:BOOL", cacheDurationMillis = 100)
private boolean running;
public void MyEntity() {
// For OPM
}
public double getPressure() {
return pressure;
}
public boolean isRunning() {
return running;
}
}需要注意的是,该类必须有一个无参构造函数(如果没有其他构造函数,可以省略无参构造函数。但如果存在其他构造函数,则必须提供无参构造函数)。还要注意,从技术上讲并没有直接的方法来设置上述类型的值,不过 OPM 拦截器会负责处理这一点,当然你也可以提供 setter 方法。
其中 @PlcEntity 只是将该类标记为 OPM 实体。
@PlcTag 提供了值所读取的 PLC 地址字符串。在上面的示例中使用了两种写法。对于 preassure 字段,没有提供第二个参数,因此 value 前缀可以省略。
这将创建一个属性,每次调用该字段的 getter 方法时都会从 PLC 获取数据。
在第二个属性 running 中,我们提供了第二个参数 cacheDurationMillis。这将在首次访问 getter 时获取值,但如果在 100 毫秒内再次调用 getter,则会返回最后一次读取的值,而不会调用 PLC。缓存有效期过后的第一次调用将触发一次新的请求(随后该值会再次被缓存 100 毫秒)。
简单示例(分离模式)
如果你的实体中只定义了一组字段,并且希望读取所有字段后断开连接,可以使用实体管理器的 read 方法。
这将创建一个到数据库的连接,读取所有值,然后再次断开与实体的连接。
public static void main(String[] args) {
PlcEntityManager em = new PlcEntityManager();
MyEntity entity = em.read(MyEntity.class, "s7://...");
System.out.println(entity.getPressure());
}对这个游离实体的 getter 的调用不会向 PLC 发送任何请求。
请注意,在这种情况下你创建的是一个 dead 实体。在循环中调用 read 会让 OPM 为每次调用都创建新的动态代理类,这些类会在你的类内存中不断累积,最终导致 OutOfMemory 错误。如果你打算执行重复的读取操作,请使用连接式读取(见下一个示例)。 |
|---|
简单示例(连接式)
connected 实体是 OPM 实体最值得关注的使用场景。
在这种情况下,实体保持 alive。
正如 Defining an OPM Entity 章节中所述,当访问某个属性的 getter 时,若该属性标注了 @PlcTag,则会触发对 PLC 的调用,除非 cacheDurationMillis 允许重复使用上一次的值。
同样的规则适用于所有可能存在的 setter。当调用标注了 @PlcTag 的属性的 setter 时,会向 PLC 发送一个写入请求。这里不使用缓存有效期。
public static void main(String[] args) {
PlcEntityManager em = new PlcEntityManager();
MyEntity entity = em.connect(MyEntity.class, "s7://...");
System.out.println(entity.getPressure());
}隐藏的秘诀
通常,以单独的请求读取实体的每一个属性并不是理想的做法,因此还有一种方法可以在一次请求中强制读取所有字段(前提是协议支持)。
其做法是定义一个不以 get{field-name} 或 is{field-name}(布尔类型的情况)开头的方法。调用该方法时,OPM 系统会更新当前实体中定义的所有属性(但仍会遵守缓存时间的限制)。
因此,将该方法添加到我们的示例实体中:
public void updateAllTheTags() {
// Dummy ...
}简单调用 connectedEntity.updateAllTheTags() 将强制更新缓存中所有数值已过期的标签。
参考文献
[1] https://www.oracle.com/technetwork/java/javaee/tech/persistence-jsp-140049.html
评论
登录后参与评论
KnowForge