一、环境搭建(基于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介绍

常用的 OPCClient 和 OPCServer 软件推荐

Kepware 激活过程

OPC 技术总结

Kepware软件基本操作及使用Java Utgard实现OPC通信的更多相关文章

  1. 2019 第十届蓝桥杯大赛软件类省赛 Java A组 题解

    2019 第十届蓝桥杯大赛软件类省赛 Java A组 试题A 题解 ​ 题目最后一句贴心的提示选手应该使用 long (C/C++ 应该使用 long long). ​ 本题思路很直白,两重循环.外层 ...

  2. Java实现OPC通信

    1.PLC和OPC 使用的PLC:西门子的S7 300,具体型号如下图 使用的OPC server软件: 模拟仿真用的 MatrikonOPCSimulation(50M),百度网盘,密码: mcur ...

  3. java socket实现全双工通信

    java socket实现全双工通信 单工.半双工和全双工的定义 如果在通信过程的任意时刻,信息只能由一方A传到另一方B,则称为单工. 如果在任意时刻,信息既可由A传到B,又能由B传A,但只能由一个方 ...

  4. Mac OS中Java Servlet与Http通信

    Java中一个Servlet其实就是一个类,用来扩展服务器的性能,服务器上驻留着可以通过“请求-响应”编程模型来访问的应用程序.Servlet可以对任何类型的请求产生响应,但通常只用来扩展Web服务器 ...

  5. Java并发基础--线程通信

    java中实现线程通信的四种方式 1.synchronized同步 多个线程之间可以借助synchronized关键字来进行间接通信,本质上是通过共享对象进行通信.如下: public class S ...

  6. Android NDK开发篇(四):Java与原生代码通信(原生方法声明与定义与数据类型)

    Java与原生代码通信涉及到原生方法声明与定义.数据类型.引用数据类型操作.NIO操作.訪问域.异常处理.原生线程 1.原生方法声明与定义 关于原生方法的声明与定义在上一篇已经讲一点了,这次具体分析一 ...

  7. java下的串口通信-RXTX

    关于java实现的串口通信,使用的是开源项目RXTX,之前sun公司也有JCL项目,不过已经很久没有更新了,RXTX项目地址:点击打开,但是两个项目的API用法是一样的,只是导入的包不一样而已.简单的 ...

  8. Java并发包——线程通信

    Java并发包——线程通信 摘要:本文主要学习了Java并发包里有关线程通信的一些知识. 部分内容来自以下博客: https://www.cnblogs.com/skywang12345/p/3496 ...

  9. 原生 Java 客户端进行消息通信

    原生 Java 客户端进行消息通信 Direct 交换器 DirectProducer:direct类型交换器的生产者 NormalConsumer:普通的消费者 MulitBindConsumer: ...

随机推荐

  1. docker容器中布置静态网站

    docker容器中布置静态网站(基于云服务器ubuntu系统) 服务器准备(ubuntu) docker nginx 静态网页制作 浏览器测试 服务器布置 这里推荐使用云服务器(阿里云.华为云.腾讯云 ...

  2. Maven的安装跟配置(最全)

    一.去官网下载maven 官网地址: https://maven.apache.org/ 点击下载apache-maven-3.6.3-bin.zip 下载完成后解压即可. 二.配置环境变量 在我们的 ...

  3. Spark sql 简单使用

    一.认识Spark sql 1.什么是Sparksql? spark sql是spark的一个模块,主要用于进行结构化数据的处理,它提供的最核心抽象就是DataFrame. 2.SparkSQL的作用 ...

  4. java实现发送短信验证码

    java实现短信验证码发送 由于我们使用第三方平台进行验证码的发送,所以首先,我们要在一个平台进行注册. 在这里我选择是秒嘀科技,因为新人注册会赠送十元,足够测试使用了. 注册完成后,我们需要获取自己 ...

  5. RMI之由浅入深(一)

    0x01.什么是RMI RMI(Remote Method Invocation)即Java远程方法调用,RMI用于构建分布式应用程序,RMI实现了Java程序之间跨JVM的远程通信.顾名思义,远程方 ...

  6. Spring用了哪些设计模式?

    设计模式是一套被反复使用的.多数人知晓的.经过分类编目的.代码设计经验的总结.总共有 23 种设计模式 使用设计模式是为了重用代码.让代码更容易被他人理解.保证代码可靠性. Spring用了哪些设计模 ...

  7. ASP.NET Core 3.1 中间件

    参考微软官方文档 : https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.1 ...

  8. 【MyBatis】MyBatis 多表操作

    MyBatis 多表操作 文章源码 一对一查询 需求:查询所有账户信息,关联查询下单用户信息. 注意:因为一个账户信息只能供某个用户使用,所以从查询账户信息出发关联查询用户信息为一对一查询.如果从用户 ...

  9. Count PAT's (25) PAT甲级真题

    题目分析: 由于本题字符串长度有10^5所以直接暴力是不可取的,猜测最后的算法应该是先预处理一下再走一层循环就能得到答案,所以本题的关键就在于这个预处理的过程,由于本题字符串匹配的内容的固定的PAT, ...

  10. 【Git】2、Linux快速安装Git环境 & oh-my-zsh

    Linux快速安装Git环境 文章目录 Linux快速安装Git环境 1.Linux安装Git 2.安装zsh 3.安装oh-my-zsh 3.1.安装oh-my-zsh 3.2. 测试验证 4.小结 ...