成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Java OPC client開發(fā)踩坑記

URLOS / 1262人閱讀

摘要:目前常見的協(xié)議版本號(hào)為和,兩個(gè)協(xié)議不完全兼容。第一坑基于開發(fā)的非常少,大部分是商業(yè)的,售價(jià)不菲。端存儲(chǔ)的格式如下這個(gè)就比較有意思了,是可以自己維護(hù)一個(gè)存儲(chǔ)層級(jí)。不但配置繁瑣復(fù)雜,還會(huì)受到各種權(quán)限以及防火墻規(guī)則的影響。

最近一個(gè)項(xiàng)目中需要用到OPC client,從OPC Server中獲取數(shù)據(jù)。主要的編程語言使用Java實(shí)現(xiàn)。實(shí)際開發(fā)中遇到了各種坑,其實(shí)也和自己沒有這方面的經(jīng)驗(yàn)有關(guān),現(xiàn)在寫一篇文章分享下整個(gè)項(xiàng)目中遇到的一些問題。

準(zhǔn)備知識(shí)

開發(fā)OPC Client之前需要一些準(zhǔn)備知識(shí),需要一些知識(shí)儲(chǔ)備,否則根本搞不清楚里面的門道?,F(xiàn)在對(duì)一些預(yù)先準(zhǔn)備的知識(shí)點(diǎn)做一概述。OPC是什么就不說了。

OPC Server端的協(xié)議

OPC Server端目前常見的有以下幾種協(xié)議:

OPC DA: Data Access協(xié)議,是最基本的OPC協(xié)議。OPC DA服務(wù)器本身不存儲(chǔ)數(shù)據(jù),只負(fù)責(zé)顯示數(shù)據(jù)收集點(diǎn)的當(dāng)前值??蛻舳丝梢栽O(shè)置一個(gè)refresh interval,定期刷新這個(gè)值。目前常見的協(xié)議版本號(hào)為2.0和3.0,兩個(gè)協(xié)議不完全兼容。也就是用OPC DA 2.0協(xié)議的客戶端連不上OPC DA 3.0的Server

OPC HDA: Historical Data Access協(xié)議。前面說過DA只顯示當(dāng)前狀態(tài)值,不存儲(chǔ)數(shù)據(jù)。而HDA協(xié)議是由數(shù)據(jù)庫(kù)提供,提供了歷史數(shù)據(jù)訪問的能力。比如價(jià)格昂貴的Historian數(shù)據(jù)庫(kù),就是提供HDA協(xié)議接口訪問OPC的歷史數(shù)據(jù)。HDA的Java客戶端目前我沒找到免費(fèi)的。

OPC UA: Unified Architecture統(tǒng)一架構(gòu)協(xié)議。誕生于2008年,摒棄了前面老的OPC協(xié)議繁雜,互不兼容等劣勢(shì),并且不再需要COM口訪問,大大簡(jiǎn)化了編程的難度?;贠PC UA的開源客戶端非常多。不過由于誕生時(shí)間較晚,目前在國(guó)內(nèi)工業(yè)上未大規(guī)模應(yīng)用,并且這個(gè)協(xié)議本身就跟舊的DA協(xié)議不兼容,客戶端沒法通用。

我們的目標(biāo)環(huán)境絕大多數(shù)是OPC DA 2.0的Server,極個(gè)別可能有OPC DA 3.0。當(dāng)時(shí)找到的很多類庫(kù)實(shí)現(xiàn)的都是OPC UA的。

第一坑: 基于JAVA開發(fā)的OPC Client非常少,大部分是商業(yè)的,售價(jià)不菲?,F(xiàn)場(chǎng)環(huán)境又是OPC DA的Server,開源client只有兩個(gè)可選,找工具和評(píng)估就花了不少時(shí)間。

OPC存儲(chǔ)格式

OPC存儲(chǔ)和傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)存儲(chǔ)格式有很大的不同,不同于關(guān)系型數(shù)據(jù)庫(kù)的表存儲(chǔ),OPC存儲(chǔ)格式是樹形結(jié)構(gòu),Server端的存儲(chǔ)格式如下:

host
`-- OPC Server Name
    `-- tag1: value, type, timestamp, ...,
    `-- tag2: value, type, timestamp, ...,
    `-- tag3: ...
    ...

每個(gè)主機(jī)上可能存在多個(gè)OPC Server,每個(gè)Server下面有若干個(gè)tag,就是各個(gè)數(shù)據(jù)收集點(diǎn)當(dāng)前的值,會(huì)定期更新。每個(gè)tag包含的內(nèi)容大致有當(dāng)前值,值類型,時(shí)間戳等等數(shù)據(jù)。是一種樹形結(jié)構(gòu)。所以客戶端連接的時(shí)候需要指明服務(wù)器的ip或主機(jī)名,需要連接的OPC服務(wù)名,以及監(jiān)聽哪些tag的數(shù)據(jù)。

Client端存儲(chǔ)的格式如下:

Group1
`-- tag1
`-- tag2
`-- tag3
Group2
`-- tag4
`-- tag5
...

這個(gè)就比較有意思了,Client是可以自己維護(hù)一個(gè)存儲(chǔ)層級(jí)Group。也就是服務(wù)端存儲(chǔ)的都是一個(gè)個(gè)tag,客戶端可以自己維護(hù)一個(gè)個(gè)Group,分類存放這些tag。所以O(shè)PC的Client就和傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)有很大的不同??蛻舳顺酥该魃鲜鯯erver端的信息之外,還需要?jiǎng)?chuàng)建一個(gè)個(gè)Group,將Server端的tag一個(gè)個(gè)放到這些Group中,然后對(duì)應(yīng)的tag才能持續(xù)的獲得數(shù)據(jù)。

第二坑: 這種存儲(chǔ)格式在其他數(shù)據(jù)庫(kù)十分罕見,當(dāng)時(shí)這里就迷茫了好一陣子,通過了解協(xié)議的人講解,才明白原來客戶端還可以維護(hù)一套存儲(chǔ)結(jié)構(gòu)。當(dāng)時(shí)沒理清楚Group和tag的關(guān)系,從服務(wù)端看不到Group,客戶端卻要填一個(gè)Group,不知道這個(gè)Group從哪來。后來才搞清楚。

COM

Component Object Model對(duì)象組件模型,是微軟定義的一套軟件的二進(jìn)制接口,可以實(shí)現(xiàn)跨編程語言的進(jìn)程間通信,進(jìn)而實(shí)現(xiàn)復(fù)用。

DCOM

Microsoft Distributed Component Object Model,坑最多的一個(gè)玩意。字面意思看起來是分布式的COM,簡(jiǎn)單理解就是可以利用網(wǎng)絡(luò)傳輸數(shù)據(jù)的COM協(xié)議,客戶端也可以通過互聯(lián)網(wǎng)分布在各個(gè)角落,不再限制在同一臺(tái)主機(jī)上了。

上面描述來看這玩意好像挺美好是吧?實(shí)際操作開發(fā)中才發(fā)現(xiàn),這玩意簡(jiǎn)直是坑王之王,對(duì)于不熟悉的人來說充滿了坑,十分折騰。配置過程可以參考一些文章

DCOM是windows上的服務(wù),使用前需要啟用

DCOM是遠(yuǎn)程連接的協(xié)議,需要配置相關(guān)的權(quán)限,以及防火墻規(guī)則放行

特別注意這一點(diǎn),前兩項(xiàng)配置在網(wǎng)上都能找到,這一條是我在經(jīng)歷無數(shù)次痛之后才意識(shí)到的。DCOM遠(yuǎn)程連接和http不同,是通過本地用戶認(rèn)證的,需要以本地用戶身份登錄服務(wù)器,拿到相應(yīng)的權(quán)限,才能使用DCOM。有點(diǎn)繞是吧?你可以類比Windows的遠(yuǎn)程桌面登錄,需要拿到服務(wù)器的用戶名密碼才能登錄并操作系統(tǒng),權(quán)限受到登錄用戶的權(quán)限所限制。而DCOM就是用的這種方式。關(guān)于各種錯(cuò)誤網(wǎng)上能找出一大堆解決方案,可能還沒一個(gè)能解決你的問題的。甚至可能progID無論無何也通不了,始終報(bào)錯(cuò),不得不改用CLSID這種方法,十分坑。

神坑: DCOM。從配置開始就充滿了陷阱和坑。不但配置繁瑣復(fù)雜,還會(huì)受到各種權(quán)限以及防火墻規(guī)則的影響。最惡心的是這玩意隨時(shí)可能報(bào)各種奇葩的錯(cuò)誤,由于缺乏足夠的錯(cuò)誤信息,很難解決,基本憑借經(jīng)驗(yàn)解決DCOM的故障。

開發(fā)過程

收集到足夠的準(zhǔn)備知識(shí)后,就可以開工了。OPC Server是DA 2.0的,因此找到了以下兩個(gè)開源類庫(kù)。
JEasyOPC Client

底層依賴JNI,只能跑在windows環(huán)境,不能跨平臺(tái)

整個(gè)類庫(kù)比較古老,使用的dll是32位的,整個(gè)項(xiàng)目只能使用32位的JRE運(yùn)行

同時(shí)支持DA 2.0與3.0協(xié)議,算是亮點(diǎn)

Utgard

OpenSCADA項(xiàng)目底下的子項(xiàng)目

純Java編寫,具有跨平臺(tái)特性

全部基于DCOM實(shí)現(xiàn)(劃重點(diǎn))

目前只支持DA 2.0協(xié)議,3.0協(xié)議的支持還在開發(fā)中

這兩個(gè)類庫(kù)都試過,JEasyOPC底層用了JNI,調(diào)用代碼量倒不是很大,使用也足夠簡(jiǎn)單,坑也遇到了點(diǎn),就是64位的JRE運(yùn)行會(huì)報(bào)錯(cuò),說dll是ia32架構(gòu)的,不能運(yùn)行于AMD64平臺(tái)下,換了32位版本的JRE之后運(yùn)行起來了,但是一直報(bào)錯(cuò)Unknown Error,從JNI報(bào)出來的,不明所以,實(shí)在無力解決,只能放棄。

只剩下Utgard一種選擇了,也慶幸目標(biāo)Server是DA 2.0的,用這個(gè)類庫(kù)完全夠用。這個(gè)類庫(kù)全部使用DCOM協(xié)議連接OPC Server,所以對(duì)于本地連接OPC Server,理論上不需要COM口,但是這個(gè)類庫(kù)全部使用DCOM協(xié)議連接,所以依舊需要配置主機(jī)名,以及登錄的用戶名密碼。使用之前必須先配置DCOM,其中痛苦不足為外人道也,在上面準(zhǔn)備知識(shí)部分已經(jīng)寫道了。

經(jīng)過一番折騰,總算將項(xiàng)目跑起來了,最終參考的工程代碼如下(項(xiàng)目實(shí)用Gradle構(gòu)建,代碼使用Utgard官方的tutorial范例):
build.gradle:

apply plugin: "java"
apply plugin: "application"

repositories {
    maven { url "http://maven.aliyun.com/nexus/content/groups/public/" }
    jcenter()
    maven { url "http://neutronium.openscada.org/maven/" }
}

dependencies {
    compile "org.openscada.utgard:org.openscada.opc.lib:1.3.0-SNAPSHOT"
    compile "org.openscada.utgard:org.openscada.opc.dcom:1.2.0-SNAPSHOT"
    compile "org.jinterop:j-interop:2.0.4"
    compile "ch.qos.logback:logback-core:1.2.3"
    compile "org.slf4j:slf4j-api:1.7.25"
}

mainClassName = "UtgardTutorial1"

src/main/java/UtgardTutorial1.java:

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.Server;
import org.openscada.opc.lib.da.SyncAccess;

import java.util.concurrent.Executors;

public class UtgardTutorial1 {

    public static void main(String[] args) throws Exception {
        // create connection information
        final ConnectionInformation ci = new ConnectionInformation();
        ci.setHost("localhost");
        ci.setUser("Administrator");
        ci.setPassword("mypassword");
        ci.setProgId("TLSvrRDK.OPCTOOLKIT.DEMO");
//        ci.setClsid("08a3cc25-5953-47c1-9f81-efe3046f2d8c"); // if ProgId is not working, try it using the Clsid instead
        final String itemId = "tag1";
        // 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, (item, state) ->
                    System.out.println("Resut: " + state.toString()));
            // 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())));
            e.printStackTrace();
        }
    }
}

最終項(xiàng)目運(yùn)行輸出如下:

 Recieved RESPONSE
Resut: Value: [[]]], Timestamp: 星期三 七月 05 00:32:29 CST 2017, Quality: 192, ErrorCode: 00000000
七月 05, 2017 12:32:27 上午 rpc.DefaultConnection processOutgoing
信息:
 Sending REQUEST
七月 05, 2017 12:32:27 上午 rpc.DefaultConnection processIncoming
信息:
 Recieved RESPONSE
Resut: Value: [[]]], Timestamp: 星期三 七月 05 00:32:29 CST 2017, Quality: 192, ErrorCode: 00000000
七月 05, 2017 12:32:28 上午 rpc.DefaultConnection processOutgoing
信息:
 Sending REQUEST
七月 05, 2017 12:32:28 上午 rpc.DefaultConnection processIncoming
信息:
 Recieved RESPONSE
Resut: Value: [[U]], Timestamp: 星期三 七月 05 00:32:30 CST 2017, Quality: 192, ErrorCode: 00000000
七月 05, 2017 12:32:28 上午 rpc.DefaultConnection processOutgoing
信息:
 Sending REQUEST
七月 05, 2017 12:32:28 上午 rpc.DefaultConnection processIncoming
信息:
 Recieved RESPONSE
Resut: Value: [[U]], Timestamp: 星期三 七月 05 00:32:30 CST 2017, Quality: 192, ErrorCode: 00000000
七月 05, 2017 12:32:29 上午 rpc.DefaultConnection processOutgoing
信息:

總算跑起來了。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/67311.html

相關(guān)文章

  • Notadd 2.0 全新 Node.js 版本~ (開發(fā)中) [從 PHP 到 node 的坑記

    摘要:有著最全的協(xié)議支持,同時(shí)有各種非阻塞拓展,可以說是最符合要求的,但是異步需要對(duì)做很大的改動(dòng)。的計(jì)劃將基于開發(fā),同時(shí)也提供一些無法提供的功能和特性。 一點(diǎn)小遺憾 對(duì)于 Notadd 我們本來期望它實(shí)現(xiàn)更多... 盡管我們也嘗試做了很多努力,但是由于 PHP 本身的局限,以及考慮到開發(fā)環(huán)境配置的復(fù)雜程度,最終使用了折中方案。接下來,我們談?wù)務(wù)麄€(gè)技術(shù)選型歷程,也供今后相關(guān)開發(fā)者做借鑒和參考:...

    Tonny 評(píng)論0 收藏0
  • 微信小程序 海報(bào)生成坑記

    摘要:最近有個(gè)需求是要生成分享海報(bào),讓用戶可以將圖片保存到本地然后分享到朋友圈。本來以為是一個(gè)很簡(jiǎn)單的需求,可是萬萬沒想到,微信會(huì)這么坑。 最近有個(gè)需求是要生成分享海報(bào),讓用戶可以將圖片保存到本地然后分享到朋友圈。本來以為是一個(gè)很簡(jiǎn)單的需求,可是萬萬沒想到,微信會(huì)這么坑。剛開始的思路是這樣的: 后臺(tái)根據(jù)小程序傳過來的參數(shù)獲取對(duì)應(yīng)的小程序碼,然后與背景圖合成之后將base64格式的圖片傳給小程...

    lidashuang 評(píng)論0 收藏0
  • vue-cli3.x 新特性及坑記

    摘要:前言都到了,所以是時(shí)候玩轉(zhuǎn)一下的新特性了。安裝的包名稱由改成了。方法一原因的配置改變了,導(dǎo)致正確的不能用。打開終端,切換到根路徑文件里面修改為方法二是默認(rèn)路徑修改了路徑會(huì)出現(xiàn)錯(cuò)誤。按上面的方法修改完,再全局卸載果然就成功了。 showImg(https://segmentfault.com/img/remote/1460000016423946); 前言 vue-cli 都到 3.0....

    xiaoqibTn 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<