初识opc

前期的协议知识了解

opc server协议了解

常见三种的协议

OPC DA: Data Access协议,是最基本的OPC协议。OPC DA服务器本身不存储数据,只负责显示数据收集点的当前值。客户端可以设置一个refresh interval,定期刷新这个值。目前常见的协议版本号为2.0和3.0,两个协议不完全兼容。也就是用OPC DA 2.0协议的客户端连不上OPC DA 3.0的Server

OPC HDA: Historical Data Access协议。前面说过DA只显示当前状态值,不存储数据。而HDA协议是由数据库提供,提供了历史数据访问的能力。比如价格昂贵的Historian数据库,就是提供HDA协议接口访问OPC的历史数据。HDA的Java客户端目前我没找到免费的。

OPC UA: Unified Architecture统一架构协议。诞生于2008年,摒弃了前面老的OPC协议繁杂,互不兼容等劣势,并且不再需要COM口访问,大大简化了编程的难度。基于OPC UA的开源客户端非常多。不过由于诞生时间较晚,目前在国内工业上未大规模应用,并且这个协议本身就跟旧的DA协议不兼容,客户端没法通用。

实际情况

目标环境是 OPC DA 2.0的server,基于JAVA开发的OPC Client非常少,大部分是商业的,现场环境又是OPC DA的Server,开源client只有两个可选。

开发过程

JEasyOPC Client

底层依赖JNI,只能跑在windows环境,不能跨平台
整个类库比较古老,使用的dll是32位的,整个项目只能使用32位的JRE运行
同时支持DA 2.0与3.0协议,算是亮点

Utgard

OpenSCADA项目底下的子项目
纯Java编写,具有跨平台特性
全部基于DCOM实现(划重点)
目前只支持DA 2.0协议,3.0协议的支持还在开发中

综合考虑一下决定选用Utgard进行开发,参考了一下网上的一个表格

对比项UtgardJeasyopc
Linux下支持(纯Java编写的)不支持()
Windows 64位下支持不支持
用户名密码需要不需要
组查询不支持支持
压力测试(单线程同步)略快7W点大约在4224ms略慢7W点大约在22540ms
压力测试(单线程异步)略快略慢
压力测试(单线程发布订阅)

环境准备

需要配置OPCserver。

服务端准备

使用Matrikon OPC Server Simulation

代码开发

读取数据
package com.skytech.opc;

import java.util.concurrent.Executors;

import org.jinterop.dcom.common.JIException;
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;


/**
  * Created by michloas on 2018/9/6.
  */
public class UtgardTutorialRead {

public static void main(String[] args) throws Exception {
    // 连接信息
    final ConnectionInformation ci = new ConnectionInformation();
    ci.setHost("localhost");
    ci.setDomain("");
    ci.setUser("opc");
    ci.setPassword("123456");
    //ci.setProgId("Matrikon.OPC.Simulation.1");

    // MatrikonOPC Server
    ci.setClsid("F8582CF2-88FB-11D0-B850-00C0F0104305");
     final String itemId = "uu.u";//项的名字按实际

    // KEPServer
    // ci.setClsid("7BC0CC8E-482C-47CA-ABDC-0FE7F9C6E729");
    // final String itemId = "u.u.u";// 项的名字按实际
    // final String itemId = "通道 1.设备 1.标记 1";

    // create a new server
    final Server server = new Server(ci, Executors.newSingleThreadScheduledExecutor());

    try {
        // connect to server
        server.connect();
        // add sync access, poll every 500 ms
        final AccessBase access = new SyncAccess(server, 500);
        access.addItem(itemId, new DataCallback() {
            @Override
            public void changed(Item item, ItemState state) {
                System.out.println("-----" + state);
            }
        });
        // start reading
        access.bind();
        // wait a little bit
        Thread.sleep(10 * 1000);
        // stop reading
        access.unbind();
    } catch (final JIException e) {
        System.out.println(String.format("%08X: %s", e.getErrorCode(), server.getErrorMessage(e.getErrorCode())));
    }
  }
}
写入数据
package com.skytech.opc;


import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.jinterop.dcom.common.JIException;
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.Group;
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 UtgardTutorialWrite {

public static void main(String[] args) throws Exception {

    // 连接信息
    final ConnectionInformation ci = new ConnectionInformation();

    ci.setHost("127.0.0.1");
    ci.setDomain("");
    ci.setUser("OPCUser");
    ci.setPassword("123456");

    // MatrikonOPC Server
     ci.setClsid("F8582CF2-88FB-11D0-B850-00C0F0104305");
     final String itemId = "uu.u";//项的名字按实际

    // KEPServer
    //ci.setClsid("7BC0CC8E-482C-47CA-ABDC-0FE7F9C6E729");
    //final String itemId = "u.u.u";// 项的名字按实际
    // final String itemId = "通道 1.设备 1.标记 1";

    // create a new server
    final Server server = new Server(ci, Executors.newSingleThreadScheduledExecutor());
    try {
        // connect to server
        server.connect();
        // add sync access, poll every 500 ms
        final AccessBase access = new SyncAccess(server, 500);
        access.addItem(itemId, new DataCallback() {
            @Override
            public void changed(Item item, ItemState state) {
                // also dump value
                try {
                    if (state.getValue().getType() == JIVariant.VT_UI4) {
                        System.out.println("<<< " + state + " / value = " + state.getValue().getObjectAsUnsigned().getValue());
                    } else {
                        System.out.println("<<< " + state + " / value = " + state.getValue().getObject());
                    }
                } catch (JIException e) {
                    e.printStackTrace();
                }
            }
        });

        // Add a new group
        final Group group = server.addGroup("test");
        // Add a new item to the group
        final Item item = group.addItem(itemId);

        // start reading
        access.bind();

        // add a thread for writing a value every 3 seconds
        //写入线程,至于为什么要用线程,要一直写,,如果只写一次就不用写线程了
        ScheduledExecutorService writeThread = Executors.newSingleThreadScheduledExecutor();
        writeThread.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                final JIVariant value = new JIVariant("24");//一直写入24
                try {
                    System.out.println(">>> " + "writing value " + "24");
                    item.write(value);
                } catch (JIException e) {
                    e.printStackTrace();
                }
            }
        }, 5, 3, TimeUnit.SECONDS);

        // wait a little bit
        Thread.sleep(20 * 1000);
        writeThread.shutdownNow();
        // stop reading
        access.unbind();
    } catch (final JIException e) {
        System.out.println(String.format("%08X: %s", e.getErrorCode(), server.getErrorMessage(e.getErrorCode())));
    }
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值