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: ...
随机推荐
- JDBC(五)—— 批量插入数据
批量插入数据 @Test public void testInsert() throws Exception { Connection conn = null; PreparedStatement p ...
- SSM整合详解
1.基本概念 1.1.Spring Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One ...
- MySql Docker 主主配置
MySql 主主 准备2台Linux服务器,并且在两台服务器上,同时安装docker,国内的同学可以使用aliyun的镜像安装. curl -fsSL https://get.docker.com - ...
- 解决 unknown filesystem type ntfs U盘/移动硬盘挂载出错问题
大内存U盘或者移动硬盘挂在再Linux 时,报错unknown filesystem type ntfs 1.安装ntfs-3g wget http://tuxera.com/opensource/n ...
- 离散傅里叶变换DFT入门
网上对于傅里叶变换相关的文章很多(足够多),有的是从物理相关角度入场,有的从数学分析角度入场.对于有志学习相关概念的同学还是能够很好的理解的. 数学包括三大块:代数学.几何.数学分析.前两块我们在中学 ...
- laravel邮件发送
laravel邮件发送 使用邮件发送类Mail 文本 静态方法 raw() 富文本 静态方法 send() 注:使用邮件发送必须有邮件账号,需要开启smtp协议,现在主流服务器都支持,smtp默认端口 ...
- 基础Markdown语法
Markdown语法 1.标题 //标题语法 # 一级标题 ## 二级标题 ### 三级标题 #### 四级标题 ##### 五级标题 ###### 六级标题 一级标题 二级标题 三级标题 四级标题 ...
- kubernets集群的安全防护(下)
一 集群角色以及集群角色绑定 1.1 前面我们提到过角色以及角色绑定,那么现在为什么会出现集群级别的角色以及角色绑定,作用有如下所示 我们如果需要在所有的命名的空间创建某个角色或者角色绑定的时候 ...
- 惠普电脑(HP PHILIPS系列)安装ubuntu后无法连接WIFI解决方案(手动安装8821CE驱动)
一步一步来, 先说环境: 我的电脑是HP PHILIPS系列,ubuntu版本是16.04 背景: win10安装ubuntu后发现无法连接wifi(但win10系统可以连接WIFI),在ubuntu ...
- 【译】Async/Await(三)——Aysnc/Await模式
原文标题:Async/Await 原文链接:https://os.phil-opp.com/async-await/#multitasking 公众号: Rust 碎碎念 翻译 by: Praying ...