1. 程式人生 > >java opc client 開源Utgard學習及踩坑

java opc client 開源Utgard學習及踩坑

最近的專案中需要運用到opc client。需要從opc server中獲取資料,或修改引數。主要運用的語言為java。

準備知識: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協議不相容,客戶端沒法通用。

com:Component Object Model物件元件模型,是微軟定義的一套軟體的二進位制介面,可以實現跨程式語言的程序間通訊,進而實現複用。

dcom:Microsoft Distributed Component Object Model,網路傳輸資料的COM協議,客戶端也可以通過網際網路分佈在各個角落。

然後準備開工,一番查詢後,發現存在兩個開源類庫

JEasy是java呼叫動態連線庫,底層用的是jni,dll庫比較老。

Utgard則是純java編寫,就是不支援opc 3.0協議。

JEasy在呼叫的時候,jni總是報錯,無力解決。

最終使用了Utgard的庫,在其官網上學習使用方法http://openscada.org/projects/utgard/,最終還是跑起來了,其中opc服務端的配置,一般工程人員會配置好,不需要操心dcom配置之類的問題。最終專案是maven配置完成。最終執行的時候,發現一個group死活發現不了,跟我說是新增加的,但是opc 服務端卻沒有重新配置,導致無法發現,最後還是研究了手冊,自己手動配置完成了。

關鍵API示例

  • 列舉某Server下的所有OPC連線
ServerList serverList = new ServerList("10.1.5.123", "freud",
		"password", "");

Collection<ClassDetails> classDetails = serverList
		.listServersWithDetails(new Category[] {
				Categories.OPCDAServer10, Categories.OPCDAServer20,
				Categories.OPCDAServer30 }, new Category[] {});

for (ClassDetails cds : classDetails) {
	System.out.println(cds.getProgId() + "=" + cds.getDescription());
}
  • 列舉連線下的所有Group和Item
public static void main(String[] args) throws Exception {
	ConnectionInformation ci = new ConnectionInformation();
	ci.setHost("10.1.5.123");
	ci.setDomain("");
	ci.setUser("freud");
	ci.setPassword("password");
	ci.setClsid("F8582CF2-88FB-11D0-B850-00C0F0104305");

	Server server = new Server(ci, Executors.newSingleThreadScheduledExecutor());

	server.connect();

	dumpTree(server.getTreeBrowser().browse(), 0);
	dumpFlat(server.getFlatBrowser());

	server.disconnect();
}

private static void dumpFlat(final FlatBrowser browser)
		throws IllegalArgumentException, UnknownHostException, JIException {
	for (String name : browser.browse()) {
		System.out.println(name);
	}
}

private static void dumpTree(final Branch branch, final int level) {

	for (final Leaf leaf : branch.getLeaves()) {
		dumpLeaf(leaf, level);
	}
	for (final Branch subBranch : branch.getBranches()) {
		dumpBranch(subBranch, level);
		dumpTree(subBranch, level + 1);
	}
}

private static String printTab(int level) {
	StringBuilder sb = new StringBuilder();
	for (int i = 0; i < level; i++) {
		sb.append("\t");
	}
	return sb.toString();
}

private static void dumpLeaf(final Leaf leaf, final int level) {
	System.out.println(printTab(level) + "Leaf: " + leaf.getName() + ":"
			+ leaf.getItemId());
}

private static void dumpBranch(final Branch branch, final int level) {
	System.out.println(printTab(level) + "Branch: " + branch.getName());
}
  • Item的同步查詢
public static void main(String[] args) throws Exception {

	ConnectionInformation ci = new ConnectionInformation();
	ci.setHost("10.1.5.123");
	ci.setDomain("");
	ci.setUser("freud");
	ci.setPassword("password");
	ci.setClsid("F8582CF2-88FB-11D0-B850-00C0F0104305");

	Server server = new Server(ci,
			Executors.newSingleThreadScheduledExecutor());

	server.connect();

	Group group = server.addGroup();
	Item item = group.addItem("Random.Real5");

	Map<String, Item> items = group.addItems("Random.Real1",
			"Random.Real2", "Random.Real3", "Random.Real4");

	dumpItem(item);

	for (Entry<String, Item> temp : items.entrySet()) {
		dumpItem(temp.getValue());
	}

	server.dispose();
}

private static void dumpItem(Item item) throws JIException {
	System.out.println("[" + (++count) + "],ItemName:[" + item.getId()
			+ "],value:" + item.read(false).getValue());
}

private static int count;
  • Item的非同步查詢
private static final int PERIOD = 100;

private static final int SLEEP = 2000;

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

	ConnectionInformation ci = new ConnectionInformation();
	ci.setHost("10.1.5.123");
	ci.setDomain("");
	ci.setUser("freud");
	ci.setPassword("password");
	ci.setClsid("F8582CF2-88FB-11D0-B850-00C0F0104305");

	Server server = new Server(ci,
			Executors.newSingleThreadScheduledExecutor());

	server.connect();

	AccessBase access = new SyncAccess(server, PERIOD);

	access.addItem("Random.Real5", new DataCallback() {
		private int i;

		public void changed(Item item, ItemState itemstate) {
			System.out.println("[" + (++i) + "],ItemName:[" + item.getId()
					+ "],value:" + itemstate.getValue());
		}
	});

	access.bind();
	Thread.sleep(SLEEP);
	access.unbind();
	server.dispose();
}
  • Item的釋出訂閱查詢
private static final int PERIOD = 100;

private static final int SLEEP = 2000;

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

	ConnectionInformation ci = new ConnectionInformation();
	ci.setHost("10.1.5.123");
	ci.setDomain("");
	ci.setUser("freud");
	ci.setPassword("password");
	ci.setClsid("F8582CF2-88FB-11D0-B850-00C0F0104305");

	Server server = new Server(ci,
			Executors.newSingleThreadScheduledExecutor());

	server.connect();

	AccessBase access = new Async20Access(server, PERIOD, false);

	access.addItem("Random.Real5", new DataCallback() {

		private int count;

		public void changed(Item item, ItemState itemstate) {
			System.out.println("[" + (++count) + "],ItemName:["
					+ item.getId() + "],value:" + itemstate.getValue());
		}
	});

	access.bind();
	Thread.sleep(SLEEP);
	access.unbind();
	server.dispose();
}
  • 自動重連Item非同步讀取
private static final int PERIOD = 100;

private static final int SLEEP = 2000;

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

	ConnectionInformation ci = new ConnectionInformation();
	ci.setHost("10.1.5.123");
	ci.setDomain("");
	ci.setUser("freud");
	ci.setPassword("password");
	ci.setClsid("F8582CF2-88FB-11D0-B850-00C0F0104305");

	Server server = new Server(ci,
			Executors.newSingleThreadScheduledExecutor());

	AutoReconnectController controller = new AutoReconnectController(server);

	controller.connect();

	AccessBase access = new SyncAccess(server, PERIOD);

	access.addItem("Random.Real5", new DataCallback() {
		private int i;

		public void changed(Item item, ItemState itemstate) {
			System.out.println("[" + (++i) + "],ItemName:[" + item.getId()
					+ "],value:" + itemstate.getValue());
		}
	});

	access.bind();
	Thread.sleep(SLEEP);
	access.unbind();
	controller.disconnect();
}
  • Item同步寫入
public static void main(String[] args) throws Exception {

	ConnectionInformation ci = new ConnectionInformation();
	ci.setHost("10.1.5.123");
	ci.setDomain("");
	ci.setUser("freud");
	ci.setPassword("password");
	ci.setClsid("F8582CF2-88FB-11D0-B850-00C0F0104305");

	Server server = new Server(ci,
			Executors.newSingleThreadScheduledExecutor());

	server.connect();

	Group group = server.addGroup();
	Item item = group.addItem("Square Waves.Real4");

	final Float[] integerData = new Float[] { 1202f, 1203f, 1204f };
	final JIArray array = new JIArray(integerData, false);
	final JIVariant value = new JIVariant(array);

	item.write(value);
	Thread.sleep(2000);

	dumpItem(item);

	server.dispose();

}

private static void dumpItem(Item item) throws JIException {
	System.out.println("[" + (++count) + "],ItemName:[" + item.getId()
			+ "],value:" + item.read(true).getValue());
}

private static int count;
  • Item非同步寫入

參考資料