Appearance
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_NULL | 0 | 空值 |
TYPE_BOOL | 1 | 布尔值 |
TYPE_INT8 | 2 | 字节 |
TYPE_INT16 | 3 | 短整型 |
TYPE_INT32 | 4 | 整型 |
TYPE_INT64 | 5 | 长整型 |
TYPE_FLOAT | 6 | 浮点型 |
TYPE_DOUBLE | 7 | 双精度 |
TYPE_DATETIME | 8 | 日期时间 |
TYPE_STRING | 9 | 字符串 |
TYPE_BINARY | 10 | 二进制 |
TYPE_OBJECT | 11 | 自动类型推断 |
TYPE_ARRAY | 12 | 数组类型 |
TYPE_MAP | 13 | Map 类型 |
TYPE_ROW | 32 | 行类型 |
TYPE_BOOL_ARRAY | 17 | 布尔数组 |
TYPE_INT32_ARRAY | 20 | 整型数组 |
TYPE_INT64_ARRAY | 21 | 长整型数组 |
TYPE_DOUBLE_ARRAY | 23 | 双精度数组 |
TYPE_STRING_ARRAY | 25 | 字符串数组 |
1.4 测点类型
OPPointType 类定义了测点类型:
| 常量 | 值 | 说明 |
|---|---|---|
TYPE_AX | 0 | 浮点数类型 |
TYPE_DX | 1 | 开关量类型 |
TYPE_I2 | 2 | 短整型 |
TYPE_I4 | 3 | 整型 |
TYPE_R8 | 4 | 双精度类型 |
TYPE_LONG | 5 | 长整型 |
TYPE_TEXT | 6 | 文本类型 |
TYPE_BLOB | 7 | 二进制类型 |
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 x64 | opapi_rs4j-x64.dll |
| Windows x86 | opapi_rs4j-x86.dll |
| Linux x64 | opapi_rs4j-lin-x64.so |
| Linux ARM64 | opapi_rs4j-lin-aarch64.so |
| macOS Intel | opapi_rs4j-mac-x64.dylib |
| macOS ARM | opapi_rs4j-mac-aarch64.dylib |
注意:需要确保操作系统架构与原生库匹配,
JNILoader会自动根据系统加载对应的动态库。
2.3 Java 版本要求
- Java 8+(项目编译 target 为 1.8)
3. 系统数据表
openPlant 数据库定义了 8 张系统表:
| 表名 | 常量 | 用途 | 主要字段 |
|---|---|---|---|
Database | TABLE_DATABASE | 数据库信息 | ID, GN, ED |
Node | TABLE_NODE | 节点(设备/区域)定义 | ID, GN, ED, ND |
Point | TABLE_POINT | 测点定义 | ID, GN, ED, RT, AV, DS, TM, EU, HH, HL, ... |
Realtime | TABLE_REALTIME | 实时数据 | ID, GN, AV, DS, TM |
Archive | TABLE_ARCHIVE | 历史数据 | ID, GN, AV, DS, TM |
Alarm | TABLE_ALARM | 实时报警 | ID, GN, AV, DS, TM, TA, SP, ... |
AAlarm | TABLE_AALARM | 历史报警 | ID, GN, AV, DS, TM, TA, SP, ... |
Stat | TABLE_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:数据库服务器 IPport:数据库服务器端口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();控制订阅机制说明:
- 创建
OPSubscribeControl时,SDK 会建立一个独立的订阅连接 - 当有控制指令下发时,回调
onResponse方法,传入两个列表:needFeedBack:DS 字段第 12 位(0x1000)为 1 的记录,需要执行控制反馈errList:不需要反馈的记录
- 回调返回
true时,SDK 会自动创建独立连接对needFeedBack中的每条记录执行controlFeedback - 回调返回
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=xxx14.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_OK | 0 | 正常 |
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_ACK | 1 | 报警确认 |
CMD_INHIBIT | 2 | 报警抑制 |
CMD_PADLOCK | 3 | 挂牌 |
CMD_FORCE_ON | 4 | 手工强制 |
CMD_FORCE_OFF | 5 | 取消强制 |
CMD_CONTROL | 6 | 控制命令 |
CMD_FEEDBACK | 7 | 控制反馈 |
15.3 状态位(DS)掩码
| 常量 | 值 | 说明 |
|---|---|---|
ALARM_UNACK | 0x20 | 报警未确认 |
ALARM_OFF | 0x40 | 报警已切除 |
PADLOCK | 0x2000 | 挂牌操作位 |
FORCE | 0x0100 | 强制输入 |
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)作为查询条件
- 适当增加连接超时时间
帮助中心