Java实现OPC DA通信

本文介绍了如何使用Java通过OPC和DCOM与PLC进行通信,详细阐述了OPC的分层结构,并提供了具体的配置步骤。在代码实现部分,展示了如何导入依赖并编写主方法实现数据读取,同时解释了JIVariant类中不同数据类型的对应关系。运行结果显示了监控项的数据类型和值。
摘要由CSDN通过智能技术生成

OPC介绍

OPC:是工业控制和生产自动化领域中使用的硬件和软件的接口标准,以便有效的在应用和过程设备之间读写数据。

OPC服务对象:服务器对象(Server),项对象(Item),组对象(Group)

OPC标准采用C/S模式,OPC服务器负责向OPC客户端不断提供数据

Java和PLC之间通信

在这里插入图片描述
       在OPCServer上设置地址变量,不同的Client去读写这些变量的值,实现不同的Client之间的通信。

OPC分层结构

在这里插入图片描述

       OPC对象中最上层的对象是OPC服务器。一个OPC服务器可以设置一个以上的OPC组,OPC组是可以进行某种目的的数据访问的多个OPC标签集合。有了OPC组,OPC应用程序从同时需要的数据为一批进行数据访问,也可以从OPC组为单位启动或停止数据访问。此外OPC组还提供组内任何OPC标签的数值变化时向OPC应用程序通知的数据变化事件。

  • 通道 Channel:从PC到一个或多个外部设备之间的传播媒介,一个通道可以代表一个串行端口。

  • 设备Device:代表了与服务器进行通信的PLC或其他硬件,受限于Channel的设备驱动程序

  • 标签Tag:一个tag代表与服务器进行通话的PLC或其他硬件设备上的一个地址,服务器允许动态标签(客户端自定义创建)和用户定义的静态标签(服务端管理人员创建的标签)。动态标签是直接进入OPC客户端和指定设备存取数据;静态标签在服务器被创建的且支持标签拓展,可以从OPC客户端浏览,支持标签浏览。

配置OPC和DCOM

       使用java实现OPC时需要配置用户、DCOM和防火墙等,C/C++不用。
       系统要Win10专业版或企业版,家庭版不行。
       配置OPC

实现代码
导入依赖
	<dependencies>
		<dependency>
			<groupId>org.openscada.external</groupId>
			<artifactId>org.openscada.external.jcifs</artifactId>
			<version>1.2.25</version>
		</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>
		</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>
	</dependencies>
主方法
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;

import org.jinterop.dcom.common.JIException;
import org.jinterop.dcom.core.JIString;
import org.jinterop.dcom.core.JIVariant;
import org.openscada.opc.lib.common.ConnectionInformation;
import org.openscada.opc.lib.da.AccessBase;
import org.openscada.opc.lib.da.DataCallback;
import org.openscada.opc.lib.da.Item;
import org.openscada.opc.lib.da.ItemState;
import org.openscada.opc.lib.da.Server;
import org.openscada.opc.lib.da.SyncAccess;

/**
 * 读取数值
 */
public class UtgardTutorial01 {
	public static void main(String[] args) throws Exception {
		// 连接信息
		final ConnectionInformation ci = new ConnectionInformation();
		ci.setHost("192.168.1.205"); // KEPServer服务器所在IP
		ci.setDomain(""); // 域 为空
		ci.setUser("OPCUser");
		ci.setPassword("123456");

		// 使用KEPServer的配置
		ci.setClsid("7BC0CC8E-482C-47CA-ABDC-0FE7F9C6E729"); // KEPServer的注册表ID,可以在“组件服务”中查到
		// final String itemId = "Channel01.Device01.TAG1"; //
		// KEPServer上配置的项的名字,没有实际PLC,用的模拟器:simulator
		// final String itemId = "通道1.设备1.标记1";
		final List<String> itemList = new ArrayList<>();
		itemList.add("Channel01.Device02.TAG1");
		//itemList.add("Channel01.Device02.TAG2");
		itemList.add("Channel01.Device02.TAG3");
		itemList.add("Channel01.Device02.TAG4");
		itemList.add("Channel01.Device02.TAG5");
		itemList.add("Channel01.Device02.TAG6");
		itemList.add("Channel01.Device02.TAG7");

		// 启动服务
		final Server server = new Server(ci, Executors.newSingleThreadScheduledExecutor());

		try {
			// 连接到服务
			server.connect();
			// 启动一个同步的access用来读取地址上的值,线程池每XXms读值一次
			final AccessBase access = new SyncAccess(server, 5 * 1000);

			for (String itemId : itemList) {
				// 回调函数,就是读到值后执行这个打印,用匿名类写
				access.addItem(itemId, new DataCallback() {

					@Override
					public void changed(Item item, ItemState itemState) {
						int type = 0;
						try {
							type = itemState.getValue().getType();// 类型实际是数字
						} catch (JIException e) {
							e.printStackTrace();
						}
						System.out.println("监控项的数据类型:" + type);
						System.out.println("监控项的详细位置:" + itemId);
						System.out.println("监控项的详细信息:" + itemState);

						switch (type) {
							case JIVariant.VT_I2 :// 如果读到short类型
								short s = 0;
								try {
									s = itemState.getValue().getObjectAsShort();
								} catch (JIException e) {
									e.printStackTrace();
								}
								System.out.println("short类型值:" + s);
								System.out.println("-------------");
								break;

							case JIVariant.VT_BSTR :// 如果读到String类型值
								JIString value = null;
								try {
									value = itemState.getValue().getObjectAsString();
								} catch (JIException e) {
									e.printStackTrace();
								}
								String str = value.getString();
								System.out.println("String类型值:" + str);
								System.out.println("----------------");
								break;

							case JIVariant.VT_R4 :// 如果读到float类型值
								float f = 0.0f;
								try {
									f = itemState.getValue().getObjectAsFloat();
								} catch (JIException e) {
									e.printStackTrace();
								}
								System.out.println("float值:" + f);
								System.out.println("----------");
								break;

							case JIVariant.VT_I4 : // 读到int类型
								int i = 0;
								try {
									i = itemState.getValue().getObjectAsInt();
								} catch (JIException e) {
									e.printStackTrace();
								}
								System.out.println("int值:"+i);
								System.out.println("--------");
								break;

							case JIVariant.VT_R8 : // 读到double类型
								double d = 0.0;
								try {
									d = itemState.getValue().getObjectAsDouble();
								} catch (JIException e) {
									e.printStackTrace();
								}
								System.out.println("double值:"+d);
								System.out.println("-----------");
								break;

							case JIVariant.VT_I8 : // 读到long类型
								long l = 0;
								try {
									l = itemState.getValue().getObjectAsLong();
								} catch (JIException e) {
									e.printStackTrace();
								}
								System.out.println("long值:"+l);
								System.out.println("---------");
								break;

							case JIVariant.VT_BOOL : // 读到boolean类型
								boolean b = false;
								try {
									b = itemState.getValue().getObjectAsBoolean();
								} catch (JIException e) {
									e.printStackTrace();
								}
								System.out.println("boolean值:"+b);
								System.out.println("------------");
								break;
								
							case JIVariant.VT_UI2: // 读到word类型
								Number n = null;
								try {
									n = itemState.getValue().getObjectAsUnsigned().getValue();
								} catch (JIException e) {
									e.printStackTrace();
								}
								System.out.println("word值:" + n);
								System.out.println("---------");
								break;

							default :
								break;
						}
					}
				});
			}

			// 死循环 一直读下去
			while (true) {
				// 开始读值
				access.bind();
				// 10s延迟
				// Thread.sleep(10*1000);
				// 停止读值
				// access.unbind();
			}
		} catch (final JIException e) {
			System.out.println(String.format("%08X: %s", e.getErrorCode(), server.getErrorMessage(e.getErrorCode())));
		}
	}
}

运行结果

KEPServer服务端

监控项的数据类型:5
监控项的详细位置:Channel01.Device02.TAG4
监控项的详细信息:Value: [[21.03]], Timestamp: 星期五 四月 02 15:21:31 CST 2021, Quality: 192, ErrorCode: 00000000
double值:21.03
-----------
监控项的数据类型:2
监控项的详细位置:Channel01.Device02.TAG1
监控项的详细信息:Value: [[555]], Timestamp: 星期五 四月 02 15:21:31 CST 2021, Quality: 192, ErrorCode: 00000000
short类型值:555
-------------
监控项的数据类型:3
监控项的详细位置:Channel01.Device02.TAG5
监控项的详细信息:Value: [[4578]], Timestamp: 星期五 四月 02 15:21:31 CST 2021, Quality: 192, ErrorCode: 00000000
int值:4578
--------
监控项的数据类型:4
监控项的详细位置:Channel01.Device02.TAG3
监控项的详细信息:Value: [[6.88]], Timestamp: 星期五 四月 02 15:21:31 CST 2021, Quality: 192, ErrorCode: 00000000
float值:6.88
----------
监控项的数据类型:8
监控项的详细位置:Channel01.Device02.TAG7
监控项的详细信息:Value: [[[Type: 1 , [world]]]], Timestamp: 星期五 四月 02 15:21:31 CST 2021, Quality: 192, ErrorCode: 00000000
String类型值:world
----------------
监控项的数据类型:11
监控项的详细位置:Channel01.Device02.TAG6
监控项的详细信息:Value: [[true]], Timestamp: 星期五 四月 02 15:21:31 CST 2021, Quality: 192, ErrorCode: 00000000
boolean值:true
------------
JIVariant类对应数据类型

       JIVariant类中将数据类型以int形式表示

数据类型JIVariant属性表示数值
shortVT_I22
intVT_I43
longVT_I820
floatVT_R44
doubleVT_R85
StringVT_BSTR8
DateVT_DATE7
booleanVT_BOOL11
2-byte unsigned integer
(KEPServer中word类型)
VT_UI218
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值