Kepware软件基本操作及使用Java Utgard实现OPC通信
一、环境搭建(基于win10 64位专业版)
1、Kepware 的下载、安装及使用
https://www.cnblogs.com/ioufev/p/9366877.html
2、重要:OPC 和 DCOM 配置(OPC DA 必须配置)
https://www.cnblogs.com/ioufev/p/9365919.html
二、使用 Utgard 实现 OPC 通信
1、添加 Maven 依赖
<!--utgard start-->
<dependency>
<groupId>org.openscada.external</groupId>
<artifactId>org.openscada.external.jcifs</artifactId>
<version>1.2.25</version>
<exclusions>
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.openscada.jinterop</groupId>
<artifactId>org.openscada.jinterop.core</artifactId>
<version>2.1.8</version>
</dependency>
<dependency>
<groupId>org.openscada.jinterop</groupId>
<artifactId>org.openscada.jinterop.deps</artifactId>
<version>1.5.0</version>
<exclusions>
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.openscada.utgard</groupId>
<artifactId>org.openscada.opc.dcom</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>org.openscada.utgard</groupId>
<artifactId>org.openscada.opc.lib</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.61</version>
</dependency>
<!--utgard end-->
2、简单读取示例
public class ReadTest {
public static void main(String[] args) {
//opc连接信息
final ConnectionInformation ci = new ConnectionInformation();
ci.setHost("127.0.0.1");
ci.setUser("OPCUser");
ci.setPassword("111111");
ci.setClsid("7BC0CC8E-482C-47CA-ABDC-0FE7F9C6E729");
//ci.setProgId("");
//要读取的标记
String item = "通道 1.设备 1.TAG1";
//连接对象
final Server server = new Server(ci, Executors.newSingleThreadScheduledExecutor());
try {
//建立连接
server.connect();
//添加一个组
Group group = server.addGroup();
//将标记添加到组里
Item addItem = group.addItem(item);
//读取标记的值
JIVariant variant = addItem.read(true).getValue();
//获取string类型的值
String value = variant.getObjectAsString().getString();
System.out.println("读到值:" + value);
} catch (UnknownHostException | AlreadyConnectedException | JIException |
NotConnectedException | DuplicateGroupException | AddFailedException e) {
e.printStackTrace();
} finally {
//关闭连接
server.disconnect();
}
}
}
kepware 客户端看到的点位信息:

程序运行结果:

(1)OPC 连接信息解释:
- Host——本地主机/网络主机IP (示例:localhost(默认)、127.0.0.1)
- Domain——域(默认为localhost)
- User——用户名
- Password——用户登录密码
- Clsid——应用在注册表中相对应的CLSID值
- Grogid——应用在注册表中对应的程序名称
Clsid 和 Grogid 作用相同,只要设置一个就可以了,如果两个都设置了,程序会优先选择Clsid。建议使用Clsid,因为使用Grogid时,Openscada的内部处理还是会通过JISystem.getClsidFromProgId( progId )方法将其转换为Clsid,并且还需要进行服务器上用户的权限的高级配置才可以使用。
查找Clsid的方法:搜索“组件服务”,找到“DCOM 配置”选项,找到安装的 kepware 服务器名称,右边可以看到“应用程序 ID”,如下图所示


(2)Kepware 与 Utgard 中类型对应:
在 java Utgard 里面类型使用数字来表示的,比如 String 类型是 8,在上面读取结果的图片中可以看到读取到的类型确实是8

为了得到正确的数据类型我们需要对读到的结果判断一下,工具类如下,添加了常见类型的判断
@Slf4j
public class OpcUtil {
private OpcUtil() {
}
/**
* 读单个值
*/
public static String readValue(Item item) {
try {
ItemState state = item.read(true);
return getValue(state);
} catch (JIException e) {
e.printStackTrace();
}
return null;
}
/**
* 读一组值,对于读取异常的点位会返回null值
*/
public static List<String> readValues(Group group, List<String> tags) {
//添加到group中,如果添加失败则添加null
List<Item> items = tags.stream().map(tag -> {
try {
return group.addItem(tag);
} catch (Exception e) {
log.info(e.toString());
}
return null;
}).collect(Collectors.toList());
List<String> result = new ArrayList<>();
try {
//读取所有的值,过滤null值,否则会出异常
Map<Item, ItemState> map = group.read(true,
items.stream().filter(Objects::nonNull).toArray(Item[]::new));
//解析
for (Item item : items) {
if (item == null) {
result.add(null);
continue;
}
String value = getValue(map.get(item));
result.add(value);
}
} catch (JIException e) {
e.printStackTrace();
}
return result;
}
/**
* 如果是 bool、string、short、int等直接返回字符串;
* 如果是 long 类型的数组,返回数字内容间加点,对应 long,数组,大小为6
* 如果是 float 类型的数组,返回数字内容间加逗号,对应 float,数组,大小为20
*/
private static String getValue(ItemState state) {
JIVariant variant = state.getValue();
try {
int type = variant.getType();
//Boolean
if (type == JIVariant.VT_BOOL) {
boolean value = variant.getObjectAsBoolean();
return String.valueOf(value);
}
//String
else if (type == JIVariant.VT_BSTR) {
return variant.getObjectAsString().getString();
}
//Word DWord
else if (type == JIVariant.VT_UI2 || type == JIVariant.VT_UI4) {
Number value = variant.getObjectAsUnsigned().getValue();
return String.valueOf(value);
}
//Sort
else if (type == JIVariant.VT_I2) {
short value = variant.getObjectAsShort();
return String.valueOf(value);
}
//Float
else if (type == JIVariant.VT_R4) {
float value = variant.getObjectAsFloat();
return String.valueOf(value);
}
//long 类型的数组
else if (type == 8195) {
JIArray jarr = variant.getObjectAsArray();
Integer[] arr = (Integer[]) jarr.getArrayInstance();
StringBuilder value = new StringBuilder();
for (Integer i : arr) {
value.append(i).append(".");
}
String res = value.substring(0, value.length() - 1);
// "25.36087601.1.1.18.36"-->"25.36087601.01.0001.18.36"
String[] array = res.split("[.]");
return array[0] + "." + array[1] + "." + new DecimalFormat("00").format(Long.valueOf(array[2]))
+ "." + new DecimalFormat("0000").format(Long.valueOf(array[3])) + "." + array[4] + "."
+ array[5];
}
//float 类型的数组
else if (type == 8196) {
JIArray jarr = variant.getObjectAsArray();
Float[] arr = (Float[]) jarr.getArrayInstance();
StringBuilder value = new StringBuilder();
for (Float f : arr) {
value.append(f).append(",");
}
return value.substring(0, value.length() - 1);
}
//其他类型
else {
Object value = variant.getObject();
return String.valueOf(value);
}
} catch (JIException e) {
e.printStackTrace();
}
return null;
}
/**
* 写值到变量
*/
public static void writeValue(Item item, String val) {
try {
JIVariant value = new JIVariant(val);
item.write(value);
} catch (JIException e) {
e.printStackTrace();
}
}
/**
* 写值到变量:数组
*/
public static void writeValueToArr(Item item, String[] snArray) {
try {
//构造写入数据
Long[] integerData = new Long[snArray.length];
for (int i = 0; i < snArray.length; i++) {
integerData[i] = Long.valueOf(snArray[i]);
}
final JIArray array = new JIArray(integerData, false);
final JIVariant value = new JIVariant(array);
item.write(value);
} catch (JIException e) {
e.printStackTrace();
}
}
}
连接信息一般是固定的,可以写成常量
public class OpcConnection {
private static final ConnectionInformation CI = new ConnectionInformation();
private OpcConnection() {
}
static {
CI.setHost("127.0.0.1");
CI.setUser("OPCUser");
CI.setPassword("111111");
CI.setClsid("7BC0CC8E-482C-47CA-ABDC-0FE7F9C6E729");
}
public static ConnectionInformation getInfo() {
return CI;
}
}
读取多个值方法的使用:
private static final List<String> ITEMS = Arrays.asList("通道 1.设备 1.TAG1", "通道 1.设备 1.TAG2");
private final Server server = new Server(OpcConnection.getInfo(), Executors.newSingleThreadScheduledExecutor());
public List<String> getData() {
List<String> res = new ArrayList<>();
try {
server.connect();
Group group = server.addGroup();
res = OpcUtil.readValues(group, ITEMS);
} catch (UnknownHostException | JIException | AlreadyConnectedException
| NotConnectedException | DuplicateGroupException e) {
e.printStackTrace();
} finally {
server.disconnect();
}
return res;
}
参考:
https://www.cnblogs.com/ioufev/articles/9894452.html
https://www.cnblogs.com/ioufev/p/9928971.html
https://www.cnblogs.com/ioufev/p/9929170.html
https://www.hifreud.com/2014/12/27/opc-4-client-invoke-use-utgard
三、注意事项
1、kepware频率设置
- kepware 中可以对设备指定几种扫描模式,扫描速率最高可设置到 10ms

- 如果选择“遵循标记指定的扫描速率”,也可以单独对标记设置扫描速率

- kepware 客户端也可以设置速率,不过这里的速率只是客户端的,真实的采集速率还是要根据服务端来定

2、kepware 一个通道放一个设备
一个通道里尽量放一个设备,因为 kepware 通道内是单线程(kepware 客服说的),如果放多个设备会影响扫描速率,造成数据刷新变慢
3、DCOM 配置
按说明配置好 OPC Server 与 OPC Client 所在电脑的组件服务配置和防火墙设置(windows7直接关闭就行了)。注意一定要把本机希望链接 OPC 服务的用户或用户组添加到 DCOM 配置列表中,否则链接会失败。如果其他都配置好了,运行程序还是连接不上的话,首先常看防火墙是否配置(或关闭)。
如果 Java 写的 client 和安装 OPCServer 软件是两台电脑:那两个电脑都要配置相同 DCOM,包括账号密码都要一样
4、Openscada远程链接时常见的问题及解决方法
org.jinterop.dcom.common.JIException: Message not found for errorCode:0xC0000034
原因:未启动RemoteRegistry和Windows Management Instrumentation服务。
解决方法:打开控制面板,点击【管理工具】—>>【服务】,启动RemoteRegistry和Windows ManagementInstrumentation服务。
org.jinterop.dcom.common.JIException:Access is denied, please check whether the [domain-username-password] arecorrect. Also, if not already done please check the GETTING STARTED and FAQsections in readme.htm. They provide information on how to correctly configurethe Windows machine for DCOM access, so as to avoid such exceptions. [0x00000005]
原因:首先检查错误提示的配置信息是否有误,如果都正确,则原因可能是你访问的当前用户没有该访问权限。
解决方法:
1、打开注册列表,
选择HKEY_CLASSES_ROOT\CLSID{76A64158-CB41-11D1-8B02-00600806D9B6}
2、右键点击[权限]>>【高级】>>[所有者]>>添加opc用户到权限项目中,点击应用,确定
四、其他参考资料
opc介绍
- https://www.cnblogs.com/ioufev/articles/9697717.html
- https://www.hifreud.com/2014/12/27/opc-2-what-is-opc/
- https://www.hifreud.com/2014/12/27/opc-3-main-feature-in-opc/
常用的 OPCClient 和 OPCServer 软件推荐
Kepware 激活过程
OPC 技术总结
Kepware软件基本操作及使用Java Utgard实现OPC通信的更多相关文章
- 2019 第十届蓝桥杯大赛软件类省赛 Java A组 题解
2019 第十届蓝桥杯大赛软件类省赛 Java A组 试题A 题解 题目最后一句贴心的提示选手应该使用 long (C/C++ 应该使用 long long). 本题思路很直白,两重循环.外层 ...
- Java实现OPC通信
1.PLC和OPC 使用的PLC:西门子的S7 300,具体型号如下图 使用的OPC server软件: 模拟仿真用的 MatrikonOPCSimulation(50M),百度网盘,密码: mcur ...
- java socket实现全双工通信
java socket实现全双工通信 单工.半双工和全双工的定义 如果在通信过程的任意时刻,信息只能由一方A传到另一方B,则称为单工. 如果在任意时刻,信息既可由A传到B,又能由B传A,但只能由一个方 ...
- Mac OS中Java Servlet与Http通信
Java中一个Servlet其实就是一个类,用来扩展服务器的性能,服务器上驻留着可以通过“请求-响应”编程模型来访问的应用程序.Servlet可以对任何类型的请求产生响应,但通常只用来扩展Web服务器 ...
- Java并发基础--线程通信
java中实现线程通信的四种方式 1.synchronized同步 多个线程之间可以借助synchronized关键字来进行间接通信,本质上是通过共享对象进行通信.如下: public class S ...
- Android NDK开发篇(四):Java与原生代码通信(原生方法声明与定义与数据类型)
Java与原生代码通信涉及到原生方法声明与定义.数据类型.引用数据类型操作.NIO操作.訪问域.异常处理.原生线程 1.原生方法声明与定义 关于原生方法的声明与定义在上一篇已经讲一点了,这次具体分析一 ...
- java下的串口通信-RXTX
关于java实现的串口通信,使用的是开源项目RXTX,之前sun公司也有JCL项目,不过已经很久没有更新了,RXTX项目地址:点击打开,但是两个项目的API用法是一样的,只是导入的包不一样而已.简单的 ...
- Java并发包——线程通信
Java并发包——线程通信 摘要:本文主要学习了Java并发包里有关线程通信的一些知识. 部分内容来自以下博客: https://www.cnblogs.com/skywang12345/p/3496 ...
- 原生 Java 客户端进行消息通信
原生 Java 客户端进行消息通信 Direct 交换器 DirectProducer:direct类型交换器的生产者 NormalConsumer:普通的消费者 MulitBindConsumer: ...
随机推荐
- 批量修改vsphere共享存储多路径选择策略
传统方式修改存储的多路径选择策略 首先说一下传统web界面操作方式: 可以看到至少需要6次才能修改完成一个.在生产环境中一般会挂载很多FC存储,这时候就需要一个批量快捷的方式来修改. 使用Powerc ...
- TurtleBot3使用课程-第二节a(北京智能佳)
目录 1.[第3类]LRF(LDS)传感器 2 1.1 传感器包安装 2 1.1.1 传感器端口访问设置 2 1.1.2 运行hlds_laser_publisher节点 2 1.1.3 在RViz中 ...
- NP问题/NP完全问题(NP-complete problem)如何判断是否是NP完全问题
在算法复杂度分析的过程中,人们常常用特定的函数来描述目标算法,随着变量n的增长,时间或者空间消耗的增长曲线,近而进一步分析算法的可行性(有效性). 引入了Big-O,Big-Ω,来描述目标算法的上限. ...
- Linux下安装svn教程
前言 最近买了新服务器,准备开始弄一些个人的开源项目.有了服务器当然是搞一波svn啦.方便自己的资料上传和下载.于是在此记录搭建svn的方式,方便以后直接使用. 安装 使用yum源进行安装,十分的方便 ...
- HDU6375双端队列
要点分析: 1.本题可以使用C++STL中的deque双端队列来方便解决(底层是一个双向的链表) 2.值得注意的是N的上限为150000,所以直接开这么大的空间会超内存,可以配合map一起使用 关于双 ...
- 【Linux】将ens33修改为eth0 网卡方法
1.编辑 grub 配置文件 vim /etc/sysconfig/grub # 其实是/etc/default/grub的软连接 # 为GRUB_CMDLINE_LINUX变量增加2个参数,添加的内 ...
- 【Oracle】11g direct path read介绍:10949 event、_small_table_threshold与_serial_direct_read
转自刘相兵老师的博文: http://www.askmaclean.com/archives/11g-direct-path-read-10949-_small_table_threshold-_se ...
- XSS - Labs 靶场笔记(上)
上周在网上看到的一个XSS平台,刷一波<doge Less - 1: 1.进入主界面,由图二可知是GET请求,提交name=test,回显在页面 2.查看源代码可知 没有做任何过滤,显然存在反射 ...
- 数据分析 Pandas 简介和它的的数据结构
本文主要讲Pandas 的Series和DataFrame 的相关属性和操作 1.Series的相关属性和操作# --Series是一种类似于一维数组的对象,只能存放一维数组!由以下两部分组成:# v ...
- Django中多表关联的展示问题:
增加一个知识点,当表中有多对多关联时,在前端展示的时候是一个列表,所以在展示的时候需要这样做: 表结构: class ProjectEnv(models.Model): project = model ...