摘要:目前常見的協議版本號為和,兩個協議不完全兼容。第一坑基于開發的非常少,大部分是商業的,售價不菲。端存儲的格式如下這個就比較有意思了,是可以自己維護一個存儲層級。不但配置繁瑣復雜,還會受到各種權限以及防火墻規則的影響。
最近一個項目中需要用到OPC client,從OPC Server中獲取數據。主要的編程語言使用Java實現。實際開發中遇到了各種坑,其實也和自己沒有這方面的經驗有關,現在寫一篇文章分享下整個項目中遇到的一些問題。
準備知識開發OPC Client之前需要一些準備知識,需要一些知識儲備,否則根本搞不清楚里面的門道。現在對一些預先準備的知識點做一概述。OPC是什么就不說了。
OPC Server端的協議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,極個別可能有OPC DA 3.0。當時找到的很多類庫實現的都是OPC UA的。
OPC存儲格式第一坑: 基于JAVA開發的OPC Client非常少,大部分是商業的,售價不菲。現場環境又是OPC DA的Server,開源client只有兩個可選,找工具和評估就花了不少時間。
OPC存儲和傳統的關系型數據庫存儲格式有很大的不同,不同于關系型數據庫的表存儲,OPC存儲格式是樹形結構,Server端的存儲格式如下:
host `-- OPC Server Name `-- tag1: value, type, timestamp, ..., `-- tag2: value, type, timestamp, ..., `-- tag3: ... ...
每個主機上可能存在多個OPC Server,每個Server下面有若干個tag,就是各個數據收集點當前的值,會定期更新。每個tag包含的內容大致有當前值,值類型,時間戳等等數據。是一種樹形結構。所以客戶端連接的時候需要指明服務器的ip或主機名,需要連接的OPC服務名,以及監聽哪些tag的數據。
Client端存儲的格式如下:
Group1 `-- tag1 `-- tag2 `-- tag3 Group2 `-- tag4 `-- tag5 ...
這個就比較有意思了,Client是可以自己維護一個存儲層級Group。也就是服務端存儲的都是一個個tag,客戶端可以自己維護一個個Group,分類存放這些tag。所以OPC的Client就和傳統的關系型數據庫有很大的不同。客戶端除了指明上述Server端的信息之外,還需要創建一個個Group,將Server端的tag一個個放到這些Group中,然后對應的tag才能持續的獲得數據。
COM第二坑: 這種存儲格式在其他數據庫十分罕見,當時這里就迷茫了好一陣子,通過了解協議的人講解,才明白原來客戶端還可以維護一套存儲結構。當時沒理清楚Group和tag的關系,從服務端看不到Group,客戶端卻要填一個Group,不知道這個Group從哪來。后來才搞清楚。
Component Object Model對象組件模型,是微軟定義的一套軟件的二進制接口,可以實現跨編程語言的進程間通信,進而實現復用。
DCOMMicrosoft Distributed Component Object Model,坑最多的一個玩意。字面意思看起來是分布式的COM,簡單理解就是可以利用網絡傳輸數據的COM協議,客戶端也可以通過互聯網分布在各個角落,不再限制在同一臺主機上了。
上面描述來看這玩意好像挺美好是吧?實際操作開發中才發現,這玩意簡直是坑王之王,對于不熟悉的人來說充滿了坑,十分折騰。配置過程可以參考一些文章
DCOM是windows上的服務,使用前需要啟用
DCOM是遠程連接的協議,需要配置相關的權限,以及防火墻規則放行
特別注意這一點,前兩項配置在網上都能找到,這一條是我在經歷無數次痛之后才意識到的。DCOM遠程連接和http不同,是通過本地用戶認證的,需要以本地用戶身份登錄服務器,拿到相應的權限,才能使用DCOM。有點繞是吧?你可以類比Windows的遠程桌面登錄,需要拿到服務器的用戶名密碼才能登錄并操作系統,權限受到登錄用戶的權限所限制。而DCOM就是用的這種方式。關于各種錯誤網上能找出一大堆解決方案,可能還沒一個能解決你的問題的。甚至可能progID無論無何也通不了,始終報錯,不得不改用CLSID這種方法,十分坑。
開發過程神坑: DCOM。從配置開始就充滿了陷阱和坑。不但配置繁瑣復雜,還會受到各種權限以及防火墻規則的影響。最惡心的是這玩意隨時可能報各種奇葩的錯誤,由于缺乏足夠的錯誤信息,很難解決,基本憑借經驗解決DCOM的故障。
收集到足夠的準備知識后,就可以開工了。OPC Server是DA 2.0的,因此找到了以下兩個開源類庫。
JEasyOPC Client
底層依賴JNI,只能跑在windows環境,不能跨平臺
整個類庫比較古老,使用的dll是32位的,整個項目只能使用32位的JRE運行
同時支持DA 2.0與3.0協議,算是亮點
Utgard
OpenSCADA項目底下的子項目
純Java編寫,具有跨平臺特性
全部基于DCOM實現(劃重點)
目前只支持DA 2.0協議,3.0協議的支持還在開發中
這兩個類庫都試過,JEasyOPC底層用了JNI,調用代碼量倒不是很大,使用也足夠簡單,坑也遇到了點,就是64位的JRE運行會報錯,說dll是ia32架構的,不能運行于AMD64平臺下,換了32位版本的JRE之后運行起來了,但是一直報錯Unknown Error,從JNI報出來的,不明所以,實在無力解決,只能放棄。
只剩下Utgard一種選擇了,也慶幸目標Server是DA 2.0的,用這個類庫完全夠用。這個類庫全部使用DCOM協議連接OPC Server,所以對于本地連接OPC Server,理論上不需要COM口,但是這個類庫全部使用DCOM協議連接,所以依舊需要配置主機名,以及登錄的用戶名密碼。使用之前必須先配置DCOM,其中痛苦不足為外人道也,在上面準備知識部分已經寫道了。
經過一番折騰,總算將項目跑起來了,最終參考的工程代碼如下(項目實用Gradle構建,代碼使用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(); } } }
最終項目運行輸出如下:
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 信息:
總算跑起來了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67311.html
摘要:有著最全的協議支持,同時有各種非阻塞拓展,可以說是最符合要求的,但是異步需要對做很大的改動。的計劃將基于開發,同時也提供一些無法提供的功能和特性。 一點小遺憾 對于 Notadd 我們本來期望它實現更多... 盡管我們也嘗試做了很多努力,但是由于 PHP 本身的局限,以及考慮到開發環境配置的復雜程度,最終使用了折中方案。接下來,我們談談整個技術選型歷程,也供今后相關開發者做借鑒和參考:...
摘要:最近有個需求是要生成分享海報,讓用戶可以將圖片保存到本地然后分享到朋友圈。本來以為是一個很簡單的需求,可是萬萬沒想到,微信會這么坑。 最近有個需求是要生成分享海報,讓用戶可以將圖片保存到本地然后分享到朋友圈。本來以為是一個很簡單的需求,可是萬萬沒想到,微信會這么坑。剛開始的思路是這樣的: 后臺根據小程序傳過來的參數獲取對應的小程序碼,然后與背景圖合成之后將base64格式的圖片傳給小程...
摘要:前言都到了,所以是時候玩轉一下的新特性了。安裝的包名稱由改成了。方法一原因的配置改變了,導致正確的不能用。打開終端,切換到根路徑文件里面修改為方法二是默認路徑修改了路徑會出現錯誤。按上面的方法修改完,再全局卸載果然就成功了。 showImg(https://segmentfault.com/img/remote/1460000016423946); 前言 vue-cli 都到 3.0....
閱讀 1251·2021-11-08 13:25
閱讀 1445·2021-10-13 09:40
閱讀 2778·2021-09-28 09:35
閱讀 742·2021-09-23 11:54
閱讀 1133·2021-09-02 15:11
閱讀 2437·2019-08-30 13:18
閱讀 1672·2019-08-30 12:51
閱讀 2689·2019-08-29 18:39