Skip to content

openPlant Java 接口说明 ​

1. 概述 ​

1.1 项目简介 ​

openPlant Java API(com.magus:openplant-java-api)是 openPlant 工业实时/历史数据库的 Java 客户端 SDK。它通过 JNI 调用 Rust 编写的原生动态库(opapi_rs4j),使用 OPIO 二进制协议与 openPlant 数据库服务器通信,提供完整的数据访问能力。详细接口说明请见Demo示例。
该项目的 Maven 坐标:

xml
<groupId>com.magus</groupId>
<artifactId>openPlant-java-api</artifactId>
<version>1.0.45.3</version>

1.2 架构分层 ​

┌─────────────────────────────────────┐
│       用户应用层 (你的代码)           │
├─────────────────────────────────────┤
│   JDBC 驱动 (jdbc:openplant://)     │
│   IOPConnect API + OPConnect        │
├─────────────────────────────────────┤
│   请求/响应层 (OPRequest/OPResponse) │
├─────────────────────────────────────┤
│   JNI 层 (OPServiceJNI)             │
├─────────────────────────────────────┤
│   原生动态库 (opapi_rs4j)            │
│   (Windows/Linux/macOS)             │
├─────────────────────────────────────┤
│   OpenPlant 数据库服务器              │
└─────────────────────────────────────┘

1.3 支持的数据类型 ​

OPValue 类定义了所有支持的数据类型:

类型常量值说明
TYPE_NULL0空值
TYPE_BOOL1布尔值
TYPE_INT82字节
TYPE_INT163短整型
TYPE_INT324整型
TYPE_INT645长整型
TYPE_FLOAT6浮点型
TYPE_DOUBLE7双精度
TYPE_DATETIME8日期时间
TYPE_STRING9字符串
TYPE_BINARY10二进制
TYPE_OBJECT11自动类型推断
TYPE_ARRAY12数组类型
TYPE_MAP13Map 类型
TYPE_ROW32行类型
TYPE_BOOL_ARRAY17布尔数组
TYPE_INT32_ARRAY20整型数组
TYPE_INT64_ARRAY21长整型数组
TYPE_DOUBLE_ARRAY23双精度数组
TYPE_STRING_ARRAY25字符串数组

1.4 测点类型 ​

OPPointType 类定义了测点类型:

常量值说明
TYPE_AX0浮点数类型
TYPE_DX1开关量类型
TYPE_I22短整型
TYPE_I43整型
TYPE_R84双精度类型
TYPE_LONG5长整型
TYPE_TEXT6文本类型
TYPE_BLOB7二进制类型

2. 环境搭建与依赖 ​

2.1 Maven 依赖 ​

xml
<dependency>
    <groupId>com.magus</groupId>
    <artifactId>openplant-java-api</artifactId>
    <version>1.0.45.3</version>
</dependency>

2.2 原生库 ​

SDK 通过 JNI 加载原生动态库,已内置以下平台的动态库:

平台文件
Windows x64opapi_rs4j-x64.dll
Windows x86opapi_rs4j-x86.dll
Linux x64opapi_rs4j-lin-x64.so
Linux ARM64opapi_rs4j-lin-aarch64.so
macOS Intelopapi_rs4j-mac-x64.dylib
macOS ARMopapi_rs4j-mac-aarch64.dylib

注意:需要确保操作系统架构与原生库匹配,JNILoader 会自动根据系统加载对应的动态库。

2.3 Java 版本要求 ​

  • Java 8+(项目编译 target 为 1.8)

3. 系统数据表 ​

openPlant 数据库定义了 8 张系统表:

表名常量用途主要字段
DatabaseTABLE_DATABASE数据库信息ID, GN, ED
NodeTABLE_NODE节点(设备/区域)定义ID, GN, ED, ND
PointTABLE_POINT测点定义ID, GN, ED, RT, AV, DS, TM, EU, HH, HL, ...
RealtimeTABLE_REALTIME实时数据ID, GN, AV, DS, TM
ArchiveTABLE_ARCHIVE历史数据ID, GN, AV, DS, TM
AlarmTABLE_ALARM实时报警ID, GN, AV, DS, TM, TA, SP, ...
AAlarmTABLE_AALARM历史报警ID, GN, AV, DS, TM, TA, SP, ...
StatTABLE_STAT统计数据ID, GN, MAXV, MAXTIME, MINV, MINTIME, AVGV, ...

字段说明 ​

字段说明
ID唯一数字标识(int)
GN测点全名(字符串,如 W3.NODE1.TEST1)
UD唯一十进制标识(long)
AV实时值/模拟值
DS数据状态(位掩码)
TM时间戳
ED描述信息
RT测点类型
ND节点ID
TA报警类型
SP报警设定值
MAXV最大值
MINV最小值
AVGV平均值

测点名(GN)命名规范 ​

测点名采用点分格式:数据库名.节点名.测点名

例如:W3.NODE1.TEST1 表示在 W3 数据库下 NODE1 节点中的 TEST1 测点。

4. 连接管理 ​

4.1 直接连接 ​

java
// 创建连接
IOPConnect conn = new OPConnect(host, port, timeout, user, password);

// 带压缩模式的连接 (0=不压缩, 1=frame压缩, 2=block压缩)
IOPConnect conn = new OPConnect(host, port, timeout, user, password, OPConst.MODEL_FRAME);

// TLS加密连接
IOPConnect conn = new OPConnect(host, port, timeout, user, password, model, true);

// 使用连接
OPResultSet rs = conn.execSQL("select * from Database limit 1");

// 关闭连接
conn.close();

参数说明:

  • host:数据库服务器 IP
  • port:数据库服务器端口
  • timeout:连接超时时间(秒)
  • user:数据库用户名
  • password:数据库密码
  • model:压缩模式,0=不压缩,1=frame压缩,2=block压缩

4.2 连接池管理 ​

使用 OPConnectManage 连接池(基于 Apache Commons Pool2)管理连接,推荐生产环境使用。

第一步:创建连接配置

java
OPConf config = new OPConf(ip, port, user, password, useCompress, timeout, useTLS);

第二步:初始化连接池

java
// 参数:配置, 最大连接数, 最小空闲连接数
OPConnectManage manage = OPConnectManage.getInstance(config, 30, 2);

第三步:获取和释放连接

java
IOPConnect conn = manage.getConnect();      // 获取连接
// ... 执行业务操作 ...
manage.freeConnect(conn);                    // 释放连接回池

完整示例:

java
public class OPPool {
    private final static String IP = "61.169.126.131";
    private final static int PORT = 38300;
    private final static String USER = "sis";
    private final static String PWD = "openplant";
    private final static int MAX = 30;
    private final static int MIN = 2;
    private final static int TIMEOUT = 120;
    private static OPConnectManage manage;

    static {
        OPConf config = new OPConf(IP, PORT, USER, PWD, true, TIMEOUT, true);
        manage = OPConnectManage.getInstance(config, MAX, MIN);
    }

    public static IOPConnect getConn() {
        return manage.getConnect();
    }

    public static void freeConn(IOPConnect conn) {
        manage.freeConnect(conn);
    }
}

4.3 连接状态检测 ​

java
conn.isValid();     // 检查连接是否有效(handle > 0)
conn.isActive();    // 检查连接是否活跃(自动心跳检测,每5分钟ping一次)
conn.pingPong();    // 手动心跳检测
conn.status();      // 连接状态:0=正常,非0=异常
conn.getServerTime(); // 获取数据库服务器时间
conn.getVersion();  // 获取版本信息
conn.getServerInfo(); // 获取服务器信息
conn.getCompress(); // 获取当前压缩模式
conn.setCompress(OPConst.MODEL_FRAME); // 设置压缩模式

5. 核心接口 IOPConnect ​

IOPConnect 是 API 的核心接口,定义了所有与 openPlant 数据库交互的方法。

接口总览 ​

方法分类说明
execSQL(String sql)SQL执行执行 SQL 语句
execSQL(String sql, Object keys)SQL执行带索引键的 SQL 执行
execSQLHander(SQLBindHandler)SQL执行防 SQL 注入的参数化查询
find(OPTable, Object keys)数据查询按索引查询数据和测点定义
like(OPTable, String, String)模糊查询模糊匹配查询
notLike(OPTable, String, String)模糊查询反向模糊匹配
getQueryWrapper(OPTable)链式查询创建链式过滤条件包装器
findByQueryWrapper(QueryWrapper)链式查询执行链式过滤查询
findArchive(...)历史查询查询历史数据/历史报警/统计
findArchiveTmAsc(...)历史查询按时间正序查询历史
insert(OPTable)数据写入插入数据
update(OPTable)数据更新更新数据
replace(OPTable)数据写入替换数据
remove(String, Object keys)数据删除按索引删除
removeArchive(Object, Date, Date)数据删除删除历史数据
alarmACK(...)报警操作报警确认
alarmInhibit(...)报警操作报警抑制
padlock(...)控制操作挂牌/解锁
forceOn(...)控制操作手工强制设定值
forceOff(...)控制操作取消强制
control(...)控制操作提交控制指令
controlFeedback(...)控制操作控制反馈
getCompress()连接管理获取压缩模式
setCompress(int)连接管理设置压缩模式
reset()连接管理重置连接状态
createTable(String)工具创建表模型对象
close()连接管理关闭连接
destroy()连接管理销毁连接

6. 数据模型 ​

6.1 OPTable(表模型) ​

OPTable 是数据操作的核心模型类,代表一张数据库表。

创建表模型:

java
OPTable table = conn.createTable("Point");           // 通过连接创建
// 或
OPTable table = new OPTable("Point");                // 直接创建

定义列:

java
// 批量添加列(类型为 TYPE_OBJECT,自动推断)
table.addColumns("ID", "GN", "ED", "AV", "DS", "TM");

// 单个添加列(指定类型)
table.addColumn("GN", OPValue.TYPE_STRING);
table.addColumn("AV", OPValue.TYPE_DOUBLE);
table.addColumn("TM", OPValue.TYPE_DATETIME);
table.addColumn("DS", OPValue.TYPE_INT16);

添加数据行:

java
OPRow row = table.newRow();
row.bind(0, "W3.NODE1.TEST1");          // GN
row.bind(1, 75.5);                       // AV
row.bind(2, System.currentTimeMillis() / 1000.0, OPValue.TYPE_DATETIME); // TM
row.bind(3, (short)0);                  // DS
table.append(row);                       // 绑定到表

遍历结果集:

java
while (table.next()) {
    String gn = table.getString(0);      // 按列序获取字符串
    double av = table.getDouble(1);      // 按列序获取双精度
    long tm = table.getLong(2);          // 按列序获取长整型
    Date date = table.getDate(2);        // 按列序获取日期
    int id = table.getInt(3);            // 按列序获取整型
    boolean bool = table.getBoolean(4);  // 按列序获取布尔
    byte[] bytes = table.getBytes(5);    // 按列序获取二进制
    OPValue val = table.getOPValue(6);   // 按列序获取OPValue
}

获取列元数据:

java
List<OPColumn> columns = table.getColumns();

资源释放:

java
table.destroy();  // 或 table.close();

6.2 OPRow(行模型) ​

OPRow 代表表中的一行记录,用于数据写入。

java
OPRow row = table.newRow();

// 自动类型推断绑定
row.bind(0, "W3.NODE1.TEST1");          // 自动推断为字符串
row.bind(1, 75.5);                       // 自动推断为双精度
row.bind(2, (short)0);                   // 自动推断为短整型

// 指定类型绑定
row.bind(0, "W3.NODE1.TEST1", OPValue.TYPE_STRING);
row.bind(1, 75.5, OPValue.TYPE_DOUBLE);
row.bind(2, System.currentTimeMillis() / 1000.0, OPValue.TYPE_DATETIME);

// 直接绑定OPValue
OPValue value = new OPValue(75.5);
row.bind(1, value);

6.3 OPValue(值封装) ​

OPValue 用于封装带类型的值,支持类型安全转换。

java
// 创建
OPValue v1 = new OPValue(75.5);         // 自动推断为 DOUBLE
OPValue v2 = new OPValue("hello");       // 自动推断为 STRING
OPValue v3 = new OPValue(true);          // 自动推断为 BOOL

// 类型转换
double d = v1.getDouble();              // 作为double获取
String s = v1.getString();              // 作为string获取
long l = v1.getLong();                  // 作为long获取
byte[] b = v1.getBytes();               // 作为byte[]获取
Object o = v1.getObject();              // 获取原始对象

6.4 OPColumn(列元数据) ​

java
OPColumn col = columns.get(0);
String name = col.getColumnName();      // 列名
int type = col.getColumnType();         // 列类型
int index = col.getColumnIndex();       // 列索引

7. 基本数据操作(CRUD) ​

7.1 查询数据(find) ​

按索引键(GN/ID/UD)查询测点、节点、实时数据等。

java
IOPConnect conn = OPPool.getConn();

// 创建表模型并声明要查询的字段
OPTable table = conn.createTable(OPConst.TABLE_POINT);
table.addColumns("ID", "GN", "ED", "RT");

// 按GN查询(字符串数组)
String[] points = {"W3.NODE1.TEST1"};
OPResultSet result = conn.find(table, points);

// 按ID查询(int数组)
int[] ids = {1001, 1002};
OPResultSet result = conn.find(table, ids);

// 按UD查询(long数组)
long[] uds = {1000001L};
OPResultSet result = conn.find(table, uds);

// 解析结果
while (result.next()) {
    int id = result.getInt("ID");
    String gn = result.getString("GN");
    String ed = result.getString("ED");
    System.out.println(id + "\t" + gn + "\t" + ed);
}
result.close();
table.destroy();
OPPool.freeConn(conn);

7.2 插入数据(insert) ​

插入测点 Point:

java
IOPConnect conn = OPPool.getConn();
OPTable table = conn.createTable(OPConst.TABLE_POINT);
table.addColumns("GN", "ED", "RT");

OPRow row = table.newRow();
row.bind(0, "W3.NODE1.TEST1");              // GN
row.bind(1, "测试测点1");                    // ED 描述
row.bind(2, OPPointType.TYPE_AX);           // RT 测点类型
table.append(row);

OPResultSet result = conn.insert(table);
// EC为0表示成功
result.close();
table.destroy();
OPPool.freeConn(conn);

插入实时数据 Realtime:

java
IOPConnect conn = OPPool.getConn();
OPTable table = conn.createTable(OPConst.TABLE_REALTIME);
table.addColumns("GN", "AV", "DS", "TM");

OPRow row = table.newRow();
row.bind(0, "W3.NODE1.TEST1");
row.bind(1, Math.random() * 100);           // AV 实时值
row.bind(2, 0);                        // DS 状态位,0表示GOOD
row.bind(3, System.currentTimeMillis() / 1000.0, OPValue.TYPE_DATETIME); // TM 时间戳
table.append(row);

OPResultSet result = conn.insert(table);
result.close();
table.destroy();
OPPool.freeConn(conn);

插入历史数据 Archive:

java
IOPConnect conn = OPPool.getConn();
OPTable table = conn.createTable(OPConst.TABLE_ARCHIVE);
table.addColumn("GN", OPValue.TYPE_STRING);
table.addColumn("AV", OPValue.TYPE_DOUBLE);
table.addColumn("TM", OPValue.TYPE_DATETIME);
table.addColumn("DS", OPValue.TYPE_INT16);

// 历史数据必须按时间顺序或倒序插入
for (int i = 0; i < 10; i++) {
    OPRow row = table.newRow();
    row.bind(0, "W3.NODE1.TEST1");
    row.bind(1, 77.0 + i);
    row.bind(2, System.currentTimeMillis() / 1000.0 - 10 * i, OPValue.TYPE_DATETIME);
    row.bind(3, (short)0);
    table.append(row);
}

OPResultSet result = conn.insert(table);
result.close();
table.destroy();
OPPool.freeConn(conn);

注意:历史数据插入必须按时间顺序或者倒序,否则会插入失败。

7.3 更新数据(update) ​

java
IOPConnect conn = OPPool.getConn();
OPTable table = conn.createTable(OPConst.TABLE_POINT);
table.addColumns("GN", "ED");  // GN作为唯一标识

OPRow row = table.newRow();
row.bind(0, "W3.NODE1.TEST1");   // 指定要更新的测点
row.bind(1, "新的测点描述");      // 更新描述
table.append(row);

OPResultSet result = conn.update(table);
result.close();
table.destroy();
OPPool.freeConn(conn);

7.4 删除数据(remove) ​

删除测点/节点:

java
IOPConnect conn = OPPool.getConn();

// 按GN删除
String[] points = {"W3.NODE1.TEST1"};
OPResultSet result = conn.remove(OPConst.TABLE_POINT, points);

// 按ID删除
int[] ids = {1001};
OPResultSet result = conn.remove(OPConst.TABLE_POINT, ids);

result.close();
OPPool.freeConn(conn);

删除历史数据:

java
IOPConnect conn = OPPool.getConn();
String[] points = {"W3.NODE1.TEST1"};
Date end = new Date();
Date start = new Date(end.getTime() - 3600000);

OPResultSet result = conn.removeArchive(points, start, end);
result.close();
OPPool.freeConn(conn);

8. 历史数据查询 ​

8.1 查询模式 ​

OPConst.ArchiveModel 枚举定义了多种历史数据查询模式:

模式说明
raw原始值(需加开始/结束时间)
arch归档原始值
span等间距取值
plot绘图值(优化显示)
flow流量累计值
max区间最大值
min区间最小值
avg面积平均值
mean算术平均值
stdev标准差
sum区间求和
prev区间前第一个值
next区间后第一个值
both前后各一条记录
stat统计查询(用于Stat表)
last区间最后一条
first区间第一条

8.2 查询历史数据 ​

java
IOPConnect conn = OPPool.getConn();
OPTable table = conn.createTable(OPConst.TABLE_ARCHIVE);
table.addColumns("GN", "AV", "TM", "DS");

String[] points = {"W3.NODE1.TEST1"};
Date end = new Date();
Date start = new Date(end.getTime() - 3600000);  // 前一小时

// 查询原始值
OPResultSet result = conn.findArchive(
    table, points, start, end,
    OPConst.ArchiveModel.raw, 1  // 原始值模式,interval不生效
);

// 等间距查询(每60秒取一个值)
OPResultSet result = conn.findArchive(
    table, points, start, end,
    OPConst.ArchiveModel.span, 60  // interval=60秒
);

// 查询最大值
OPResultSet result = conn.findArchive(
    table, points, start, end,
    OPConst.ArchiveModel.max, 300  // 每300秒取一个最大值
);

while (result.next()) {
    String gn = result.getString("GN");
    double av = result.getDouble("AV");
    String tm = result.getString("TM");
    System.out.println(gn + "\t" + av + "\t" + tm);
}
result.close();
table.destroy();
OPPool.freeConn(conn);

8.3 字符串形式的时间参数 ​

java
// 使用字符串格式时间 "yyyy-MM-dd HH:mm:ss.SSS"
OPResultSet result = conn.findArchive(
    table, points,
    "2023-01-01 00:00:00.000",      // begin
    "2023-01-02 00:00:00.000",      // end
    "raw",                           // mode
    "60s"                            // interval
);

8.4 按时间正序查询 ​

java
OPResultSet result = conn.findArchiveTmAsc(
    table, points, start, end,
    OPConst.ArchiveModel.raw, 1
);

9. 报警管理 ​

9.1 查询实时报警(Alarm表) ​

java
IOPConnect conn = OPPool.getConn();
OPTable table = conn.createTable(OPConst.TABLE_ALARM);
table.addColumns("ID", "GN", "AV", "TM", "DS", "TA");

String[] points = {"W3.NODE1.TEST1"};
OPResultSet result = conn.find(table, points);
// 或使用SQL
// OPResultSet result = conn.execSQL("select * from Alarm where GN = 'W3.NODE1.TEST1'");

printResult(result);
result.close();
table.destroy();
OPPool.freeConn(conn);

9.2 查询历史报警(AAlarm表) ​

java
IOPConnect conn = OPPool.getConn();
OPTable table = conn.createTable(OPConst.TABLE_AALARM);
table.addColumns("ID", "GN", "AV", "TM", "DS");

String[] points = {"W3.NODE1.TEST1"};
Date end = new Date();
Date start = new Date(end.getTime() - 3600000);

// 历史报警使用findArchive接口,mode和interval参数不生效
OPResultSet result = conn.findArchive(
    table, points, start, end,
    OPConst.ArchiveModel.raw, 1
);
result.close();
table.destroy();
OPPool.freeConn(conn);

9.3 报警确认(alarmACK) ​

java
// 按ID确认
conn.alarmACK(1001);

// 按GN确认
conn.alarmACK("W3.NODE1.TEST1");

// 批量确认
conn.alarmACK(new int[]{1001, 1002, 1003});
conn.alarmACK(new String[]{"W3.NODE1.TEST1", "W3.NODE1.TEST2"});

9.4 报警抑制(alarmInhibit) ​

java
// 打开报警抑制
conn.alarmInhibit(1001, true);
conn.alarmInhibit("W3.NODE1.TEST1", true);

// 关闭报警抑制
conn.alarmInhibit(1001, false);
conn.alarmInhibit("W3.NODE1.TEST1", false);

// 批量操作
conn.alarmInhibit(new int[]{1001, 1002}, true);
conn.alarmInhibit(new String[]{"W3.NODE1.TEST1", "W3.NODE1.TEST2"}, true);

10. 控制操作 ​

10.1 挂牌(padlock) ​

挂牌锁定后测点不能被操作,用于设备检修等场景。

java
// 锁定(挂牌)
conn.padlock(1001, true);                    // 按ID
conn.padlock("W3.NODE1.TEST1", true);       // 按GN
conn.padlock(new int[]{1001, 1002}, true);  // 批量

// 解锁(摘牌)
conn.padlock(1001, false);

10.2 手工强制(forceOn / forceOff) ​

强制后测点数值不能修改,可重复强制设定值。

java
// 强制设定值
conn.forceOn(1001, 80.5);                    // 按ID
conn.forceOn("W3.NODE1.TEST1", 80.5);       // 按GN

// 解除强制
conn.forceOff(1001);
conn.forceOff("W3.NODE1.TEST1");

10.3 控制指令(control) ​

提交控制指令,下发给 IO 驱动执行。

java
conn.control(1001, 50.0);                    // 按ID
conn.control("W3.NODE1.TEST1", 50.0);       // 按GN
conn.control(new int[]{1001, 1002}, 50.0);  // 批量

10.4 控制反馈(controlFeedback) ​

采集端领取指令并执行控制后,通过控制反馈将结果返回。

java
conn.controlFeedback(1001, 50.0);                    // 按ID
conn.controlFeedback("W3.NODE1.TEST1", 50.0);       // 按GN
conn.controlFeedback(new int[]{1001, 1002}, 50.0);  // 批量

11. 订阅机制 ​

订阅功能允许应用实时接收数据库的推送数据,无需轮询查询。

11.1 实时数据订阅 ​

java
IOPConnect conn = OPPool.getConn();

// 创建要订阅的测点列表
ArrayList<String> points = new ArrayList<>();
points.add("W3.SYS.LOAD");
points.add("W3.SYS.DBLOAD");

// 创建订阅(不带nodelay参数,默认缓冲推送)
OPSubscribe subscribe = new OPSubscribe(
    conn,
    OPConst.TABLE_REALTIME,     // 订阅实时数据
    points,                     // 测点列表
    resultSet -> {
        // 回调处理推送的数据
        OPResultSet rs = (OPResultSet) resultSet;
        try {
            while (rs.next()) {
                System.out.println(rs.getString("GN") + "\t" + rs.getDouble("AV"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        rs.close();
    }
);

// 带 isNodelay 参数
OPSubscribe subscribe = new OPSubscribe(
    conn, OPConst.TABLE_REALTIME, points,
    resultSet -> { /* 回调处理 */ },
    false                       // isNodelay: false=缓冲推送, true=实时推送
);

// 带 options 参数(可配置 NoDelay/Snapshot 等选项)
HashMap<String, String> options = new HashMap<>();
options.put("NoDelay", "0");   // 0=缓冲推送, 1=实时推送
options.put("Snapshot", "1");  // 1=先推送当前快照

OPSubscribe subscribe = new OPSubscribe(
    conn, OPConst.TABLE_REALTIME, points, options,
    resultSet -> { /* 回调处理 */ }
);

// 取消订阅指定测点
ArrayList<String> unsubList = new ArrayList<>();
unsubList.add("W3.SYS.LOAD");
subscribe.unsubscribe(unsubList);

// 动态增加订阅测点
subscribe.subscription(newList);

// 关闭订阅
subscribe.close();
OPPool.freeConn(conn);

11.2 报警订阅 ​

java
IOPConnect conn = OPPool.getConn();
ArrayList<String> points = new ArrayList<>();
points.add("W3.SYS.LOAD");

OPSubscribe subscribe = new OPSubscribe(
    conn,
    OPConst.TABLE_ALARM,        // 订阅报警表
    points,
    resultSet -> {
        OPResultSet rs = (OPResultSet) resultSet;
        // 解析推送数据
        rs.close();
    }
);

OPPool.freeConn(conn);

11.3 订阅控制 ​

java
// 动态增加订阅测点
subscribe.subscription(newList);

// 动态取消订阅
subscribe.unsubscribe(removeList);

// 关闭订阅
subscribe.close();

注意:

  • 订阅时测点列表可为空(全库订阅),也可传节点名(订阅整个节点)
  • 订阅使用 ID 时,传 ArrayList<Integer> 或 ArrayList<Long>
  • 回调中处理数据后需及时关闭 ResultSet,避免内存泄漏

11.4 控制订阅(OPSubscribeControl) ​

OPSubscribeControl 用于订阅控制指令,采集端通过此机制接收下发的控制指令并执行控制反馈。

java
IOPConnect conn = new OPConnect("192.168.2.63", 8200, 3600, "sis", "openplant");

ArrayList<String> points = new ArrayList<>();
points.add("W3.TEST");

// 创建控制订阅
OPSubscribeControl subscribeControl = new OPSubscribeControl(
    conn,
    points,
    new SubscribeControlCallBack() {
        @Override
        public boolean onResponse(
                List<LinkedHashMap<String, Object>> needFeedBack,
                List<LinkedHashMap<String, Object>> errList) {
            // needFeedBack: 需要执行控制反馈的记录(DS第12位=1)
            // errList: 不需要反馈的记录
            System.out.println("feedList:" + needFeedBack);
            System.out.println("errList:" + errList);
            // 返回 true 表示 SDK 自动对 needFeedBack 执行 controlFeedback
            // 返回 false 表示由用户自行处理反馈
            return true;
        }
    }
);

// 取消订阅
subscribeControl.unsubscribe(removeList);

// 动态增加订阅
subscribeControl.subscription(newList);

// 关闭订阅
subscribeControl.close();

控制订阅机制说明:

  1. 创建 OPSubscribeControl 时,SDK 会建立一个独立的订阅连接
  2. 当有控制指令下发时,回调 onResponse 方法,传入两个列表:
    • needFeedBack:DS 字段第 12 位(0x1000)为 1 的记录,需要执行控制反馈
    • errList:不需要反馈的记录
  3. 回调返回 true 时,SDK 会自动创建独立连接对 needFeedBack 中的每条记录执行 controlFeedback
  4. 回调返回 false 时,由用户自行处理反馈逻辑

12. 模糊查询与链式过滤 ​

12.1 模糊查询 like / notLike ​

java
IOPConnect conn = OPPool.getConn();
OPTable table = conn.createTable(OPConst.TABLE_POINT);
table.addColumns("ID", "GN", "AV", "ED", "RT");

// like 查询(GN以W3.SYS开头)
OPResultSet result = conn.like(table, "GN", "W3.SYS%");

// notLike 查询(GN不以W3.SYS开头)
OPResultSet result = conn.notLike(table, "GN", "W3.SYS%");

while (result.next()) {
    System.out.println(result.getString("GN"));
}
result.close();
table.destroy();
OPPool.freeConn(conn);

12.2 链式过滤查询(QueryWrapper) ​

QueryWrapper 提供链式编程风格的复杂过滤条件查询:

java
IOPConnect conn = OPPool.getConn();
OPTable table = conn.createTable(OPConst.TABLE_POINT);
table.addColumns("ID", "GN", "AV", "ED", "RT");

OPResultSet result = conn.getQueryWrapper(table)
    .like("GN", "W3.SYS%")     // GN以W3.SYS开头
    .and()                      // 并且
    .ge("ID", 841513)          // ID >= 841513
    .and()                      // 并且
    .lt("ID", 841519)          // ID < 841519
    .execute();                 // 执行查询

while (result.next()) {
    System.out.println(result.getInt("ID") + "\t" + result.getString("GN"));
}
result.close();
table.destroy();
OPPool.freeConn(conn);

支持的过滤操作:

方法说明示例
eq(field, value)等于.eq("GN", "W3.SYS.LOAD")
nq(field, value)不等于.nq("RT", 0)
gt(field, value)大于.gt("ID", 1000)
lt(field, value)小于.lt("ID", 2000)
ge(field, value)大于等于.ge("ID", 1000)
le(field, value)小于等于.le("ID", 2000)
in(field, set)在范围内.in("GN", set)
notIn(field, set)不在范围内.notIn("GN", set)
like(field, value)模糊匹配.like("GN", "W3.%")
notLike(field, value)反向模糊.notLike("GN", "SYS.%")
regex(field, value)正则匹配.regex("GN", "W3\\..*")
and()逻辑与.and()
or()逻辑或.or()
orderBy(value)排序.orderBy("ID desc")
limit(value)限制条数.limit("100")

完整链式示例:

java
OPResultSet result = conn.getQueryWrapper(table)
    .like("GN", "W3.%")
    .and()
    .eq("RT", OPPointType.TYPE_AX)
    .or()
    .eq("RT", OPPointType.TYPE_DX)
    .and()
    .ge("ID", 100)
    .le("ID", 1000)
    .orderBy("GN asc")
    .limit("500")
    .execute();

13. SQL 高级查询 ​

13.1 直接执行 SQL ​

java
IOPConnect conn = OPPool.getConn();

// 简单查询
String sql = "select ID, GN, ED from Point where GN = 'W3.NODE1.TEST1'";
OPResultSet result = conn.execSQL(sql);

// 带索引键的SQL
String sql = "select * from Realtime where ID in ?";
int[] ids = {1001, 1002, 1003};
OPResultSet result = conn.execSQL(sql, ids);

while (result.next()) {
    System.out.println(result.getInt("ID") + "\t" + result.getString("GN"));
}
result.close();
OPPool.freeConn(conn);

13.2 参数化查询(防SQL注入) ​

使用 SQLBindHandler 实现参数化查询:

java
IOPConnect conn = OPPool.getConn();

String sql = "select ID, GN, AV, DS, TM from Archive where GN = ? and TM between ? and ? and mode = ?";
SQLBindHandler handler = new SQLBindHandler(sql);

Date end = new Date();
Date start = new Date(end.getTime() - 3600000);

// 按?占位符顺序传值
handler.setString(0, "W3.NODE1.TEST1");
handler.setDate(1, start);
handler.setDate(2, end);
handler.setString(3, "raw");

OPResultSet result = conn.execSQLHander(handler);

while (result.next()) {
    System.out.println(result.getString("GN") + "\t" + result.getDouble("AV"));
}
result.close();
OPPool.freeConn(conn);

13.3 统计表SQL查询 ​

java
String sql = "select ID, GN, MAXV, MAXTIME, MINV, MINTIME, AVGV from Stat where GN = ? and TM between ? and ?";
SQLBindHandler handler = new SQLBindHandler(sql);
handler.setString(0, "W3.SYS.LOAD");
handler.setDate(1, start);
handler.setDate(2, end);
OPResultSet result = conn.execSQLHander(handler);

13.4 SQL 支持的语法 ​

openPlant SQL 支持标准 SQL 的常用子集,包括但不限于:

  • SELECT 查询
  • INSERT 插入
  • UPDATE 更新
  • DELETE 删除
  • WHERE 条件过滤
  • AND / OR 逻辑运算
  • IN / NOT IN 范围匹配
  • LIKE / NOT LIKE 模糊匹配
  • BETWEEN AND 区间匹配
  • ORDER BY 排序
  • LIMIT 限制行数
  • mode 历史查询模式(如 mode = 'raw')
  • 表名支持:Point, Node, Realtime, Archive, Alarm, AAlarm, Stat, Database

14. JDBC 驱动使用 ​

openPlant 提供了 JDBC 驱动,允许使用标准 JDBC API 操作数据库。

14.1 注册驱动 ​

java
Class.forName("com.magus.jdbc.Driver");

14.2 连接 URL ​

jdbc:openplant://host:port/database?user=xxx&password=xxx

14.3 使用标准 JDBC API ​

java
Connection conn = DriverManager.getConnection(
    "jdbc:openplant://61.169.126.131:38300/sis?user=sis&password=openplant"
);

Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select ID, GN, AV from Realtime limit 10");

while (rs.next()) {
    int id = rs.getInt("ID");
    String gn = rs.getString("GN");
    double av = rs.getDouble("AV");
    System.out.println(id + "\t" + gn + "\t" + av);
}

rs.close();
stmt.close();
conn.close();

14.4 数据库元数据 ​

java
DatabaseMetaData metaData = conn.getMetaData();
// 获取所有表
ResultSet tables = metaData.getTables(null, null, "%", new String[]{"TABLE"});
while (tables.next()) {
    System.out.println(tables.getString("TABLE_NAME"));
}

15. 错误码参考 ​

15.1 OPService 错误码 ​

常量值说明
ERROR_OK0正常
ERROR_ERROR-1未知错误
ERROR_TIMEOUT-2超时
ERROR_EOF-3流已关闭
ERROR_MEMORY-96内存不足
ERROR_NET_IO-97网络I/O错误
ERROR_NET_CLOSED-98网络已关闭
ERROR_NET_CONNECT-99无法连接
ERROR_COMMAND-101命令不支持
ERROR_EXIST-102资源已存在
ERROR_NEXIST-103资源不存在
ERROR_DUP_KEY-104关键字重复
ERROR_READ-105读错误
ERROR_WRITE-108写错误
ERROR_LIMIT-106容量限制
ERROR_FOREIGN_KEY-107引用错误
ERROR_ACCESS-109权限不足
ERROR_BUSY-111系统忙
ERROR_INVALID_TYPE-112类型不匹配
ERROR_PARAM-113参数错误
ERROR_OUTDATED-114数据过时
ERROR_LOCKED-115资源被锁定
ERROR_NOT_INIT-116未初始化
ERROR_PARTIAL-117部分错误

15.2 命令类型常量 ​

常量值说明
CMD_ACK1报警确认
CMD_INHIBIT2报警抑制
CMD_PADLOCK3挂牌
CMD_FORCE_ON4手工强制
CMD_FORCE_OFF5取消强制
CMD_CONTROL6控制命令
CMD_FEEDBACK7控制反馈

15.3 状态位(DS)掩码 ​

常量值说明
ALARM_UNACK0x20报警未确认
ALARM_OFF0x40报警已切除
PADLOCK0x2000挂牌操作位
FORCE0x0100强制输入

15.4 响应格式 ​

所有 API 调用返回 OPResultSet,结果中的 EC 字段表示错误码:

  • EC = 0:操作成功
  • EC != 0:操作失败,可查看错误信息

16. 最佳实践与注意事项 ​

16.1 资源管理 ​

务必正确释放资源,建议使用 try-with-resources 或 finally 块:

java
IOPConnect conn = null;
OPResultSet rs = null;
OPTable table = null;
try {
    conn = OPPool.getConn();
    table = conn.createTable(OPConst.TABLE_POINT);
    table.addColumns("ID", "GN");
    rs = conn.find(table, new String[]{"W3.NODE1.TEST1"});
    // 处理结果...
} finally {
    if (rs != null) rs.close();
    if (table != null) table.destroy();
    if (conn != null) OPPool.freeConn(conn);
}

16.2 连接管理 ​

  • 使用连接池:生产环境务必使用 OPConnectManage 连接池,不要每次调用都创建新连接
  • 连接复用:获取连接后尽快使用并及时归还
  • 空闲超时:连接池配置了 72 小时的空闲回收策略
  • 心跳检测:isActive() 方法每 5 分钟自动执行一次 ping

16.3 历史数据写入 ​

  • 历史数据必须按 时间顺序 或 倒序 插入,否则会失败
  • TM 字段使用 System.currentTimeMillis() / 1000.0 转换为秒级时间戳

16.4 性能建议 ​

  • 批量操作:写入多条数据时,在同一个 OPTable 中追加多行后一次性调用 insert()
  • 查询字段:只查询需要的字段,避免 select *
  • 分页查询:使用 LIMIT 限制返回行数
  • 索引查询:优先使用 ID/GN 等索引字段查询
  • 订阅替代轮询:实时数据变化频繁时,使用订阅机制替代定时轮询

16.5 注意事项 ​

  • 测点的 RT(类型)在创建后 不可修改
  • 测点名(GN)中的特殊字符 [] 需注意转义
  • 并发操作时注意 OPConnect 的线程安全问题(部分方法有 synchronized 修饰)
  • 网闸设备环境下,错误码为 100 时没有结果集返回
  • OPConnect 在网络异常时会自动尝试重连

16.6 常见问题 ​

Q: 连接失败怎么办?

  • 检查网络连通性
  • 确认 IP、端口、用户名、密码是否正确
  • 查看服务器是否支持 TLS 加密

Q: 写入历史数据失败?

  • 确认数据是按时间顺序或倒序写入
  • 检查时间戳格式是否正确(秒级时间戳)

Q: 订阅没有收到数据?

  • 检查测点名是否正确
  • 确认 NoDelay 参数设置
  • 确保程序没有退出(订阅是异步的)

Q: 查询超时?

  • 减小查询时间范围
  • 使用索引字段(ID/GN)作为查询条件
  • 适当增加连接超时时间