pointConfig, IotConnect device, IotConfig point, WValue wValue) {
+ Server server = getConnector(device.getId().toString(), driverConfig);
+ return writeValue(server, pointConfig, wValue);
+ }
+
+ /**
+ * 获取 OPC DA 服务器连接
+ *
+ * 根据设备ID和驱动配置获取对应的 OPC DA 服务器连接。如果连接不存在, 则创建新的连接并缓存。
+ *
+ * @param deviceId 设备ID, 用于标识设备对应的 OPC DA 服务器连接
+ * @param driverConfig 驱动配置, 包含 OPC DA 服务器的连接信息(如主机地址, CLSID, 用户名, 密码等)
+ * @return Server 返回与设备ID对应的 OPC DA 服务器连接
+ * @throws ConnectorException 如果连接 OPC DA 服务器时发生异常, 则抛出此异常
+ */
+ private Server getConnector(String deviceId, Map driverConfig) {
+ log.debug("Opc Da Server Connection Info {}", driverConfig);
+ Server server = connectMap.get(deviceId);
+ if (Objects.isNull(server)) {
+ String host = driverConfig.get("host").getValueByClass(String.class);
+ String clsId = driverConfig.get("clsId").getValueByClass(String.class);
+ String user = driverConfig.get("username").getValueByClass(String.class);
+ String password = driverConfig.get("password").getValueByClass(String.class);
+ ConnectionInformation connectionInformation = new ConnectionInformation(host, clsId, user, password);
+ server = new Server(connectionInformation, Executors.newSingleThreadScheduledExecutor());
+ try {
+ server.connect();
+ connectMap.put(deviceId, server);
+ } catch (AlreadyConnectedException | UnknownHostException | JIException e) {
+ connectMap.entrySet().removeIf(next -> next.getKey().equals(deviceId));
+ log.error("Connect opc da server error: {}", e.getMessage(), e);
+ throw new CommonException(e.getMessage());
+ }
+ }
+ return server;
+ }
+
+ /**
+ * 从 OPC DA 服务器读取位号值
+ *
+ * 该方法通过给定的 OPC DA 服务器和位号配置, 获取对应的 Item 对象, 并读取其值。
+ * 如果在读取过程中发生异常, 将断开服务器连接并抛出 {@link ReadPointException}。
+ *
+ * @param server 已连接的 OPC DA 服务器实例
+ * @param pointConfig 位号配置, 包含组名和标签名等信息
+ * @return String 返回读取到的位号值
+ * @throws ReadPointException 如果读取位号值时发生异常, 则抛出此异常
+ */
+ private String readValue(Server server, Map pointConfig) {
+ try {
+ Item item = getItem(server, pointConfig);
+ return readItem(item);
+ } catch (NotConnectedException | JIException | AddFailedException | DuplicateGroupException |
+ UnknownHostException e) {
+ server.dispose();
+ log.error("Read opc da value error: {}", e.getMessage(), e);
+ throw new CommonException(e.getMessage());
+ }
+ }
+
+ /**
+ * 获取 OPC DA 服务器中的 Item 对象
+ *
+ * 根据位号配置中的组名和标签名, 从指定的 OPC DA 服务器中获取对应的 Item 对象。
+ * 如果组不存在, 则创建新的组;如果组已存在, 则直接使用该组。
+ *
+ * @param server 已连接的 OPC DA 服务器实例
+ * @param pointConfig 位号配置, 包含组名和标签名等信息
+ * @return Item 返回与位号配置对应的 Item 对象
+ * @throws NotConnectedException 如果 OPC DA 服务器未连接, 则抛出此异常
+ * @throws JIException 如果与 OPC DA 服务器通信时发生错误, 则抛出此异常
+ * @throws UnknownHostException 如果无法解析 OPC DA 服务器的主机地址, 则抛出此异常
+ * @throws DuplicateGroupException 如果尝试添加已存在的组, 则抛出此异常
+ * @throws AddFailedException 如果添加组或 Item 失败, 则抛出此异常
+ */
+ public Item getItem(Server server, Map pointConfig) throws NotConnectedException, JIException, UnknownHostException, DuplicateGroupException, AddFailedException {
+ Group group;
+ String groupName = pointConfig.get("group").getValueByClass(String.class);
+ try {
+ group = server.findGroup(groupName);
+ } catch (UnknownGroupException e) {
+ group = server.addGroup(groupName);
+ }
+ return group.addItem(pointConfig.get("tag").getValueByClass(String.class));
+ }
+
+ /**
+ * 读取 OPC DA 位号值
+ *
+ * 该方法通过给定的 OPC DA Item 对象, 读取其值并根据数据类型进行转换。
+ * 支持的数据类型包括:短整型 (VT_I2), 整型 (VT_I4), 长整型 (VT_I8), 浮点型 (VT_R4), 双精度浮点型 (VT_R8), 布尔型 (VT_BOOL), 字符串型 (VT_BSTR)。
+ * 如果数据类型不在上述范围内, 则返回对象的字符串表示。
+ *
+ * @param item OPC DA Item 对象, 包含要读取的位号值
+ * @return String 返回读取到的位号值的字符串表示
+ * @throws JIException 如果与 OPC DA 服务器通信时发生错误, 则抛出此异常
+ */
+ public String readItem(Item item) throws JIException {
+ JIVariant jiVariant = item.read(false).getValue();
+ switch (jiVariant.getType()) {
+ case JIVariant.VT_I2:
+ short shortValue = jiVariant.getObjectAsShort();
+ return String.valueOf(shortValue);
+ case JIVariant.VT_I4:
+ int intValue = jiVariant.getObjectAsInt();
+ return String.valueOf(intValue);
+ case JIVariant.VT_I8:
+ long longValue = jiVariant.getObjectAsLong();
+ return String.valueOf(longValue);
+ case JIVariant.VT_R4:
+ float floatValue = jiVariant.getObjectAsFloat();
+ return String.valueOf(floatValue);
+ case JIVariant.VT_R8:
+ double doubleValue = jiVariant.getObjectAsDouble();
+ return String.valueOf(doubleValue);
+ case JIVariant.VT_BOOL:
+ boolean boolValue = jiVariant.getObjectAsBoolean();
+ return String.valueOf(boolValue);
+ case JIVariant.VT_BSTR:
+ return jiVariant.getObjectAsString2();
+ default:
+ return jiVariant.getObject().toString();
+ }
+ }
+
+ /**
+ * 向 OPC DA 服务器写入位号值
+ *
+ * 该方法通过给定的 OPC DA 服务器, 位号配置和写入值, 获取对应的 Item 对象, 并将值写入该 Item。
+ * 如果在写入过程中发生异常, 将断开服务器连接并抛出 {@link WritePointException}。
+ *
+ * @param server 已连接的 OPC DA 服务器实例
+ * @param pointConfig 位号配置, 包含组名和标签名等信息
+ * @param wValue 写入值, 包含要写入的数据类型和值
+ * @return boolean 返回写入操作是否成功
+ * @throws WritePointException 如果写入位号值时发生异常, 则抛出此异常
+ */
+ private boolean writeValue(Server server, Map pointConfig, WValue wValue) {
+ try {
+ Item item = getItem(server, pointConfig);
+ return writeItem(item, wValue);
+ } catch (NotConnectedException | AddFailedException | DuplicateGroupException | UnknownHostException |
+ JIException e) {
+ server.dispose();
+ log.error("Write opc da value error: {}", e.getMessage(), e);
+ throw new CommonException(e.getMessage());
+ }
+ }
+
+ /**
+ * 向 OPC DA Item 写入值
+ *
+ * 该方法根据写入值的数据类型, 将值转换为相应的 JIVariant 对象, 并写入到指定的 OPC DA Item 中。
+ * 支持的数据类型包括:短整型 (SHORT), 整型 (INT), 长整型 (LONG), 浮点型 (FLOAT), 双精度浮点型 (DOUBLE), 布尔型 (BOOLEAN), 字符串型 (STRING)。
+ * 如果数据类型不支持, 将抛出 {@link UnSupportException} 异常。
+ *
+ * @param item OPC DA Item 对象, 表示要写入的目标位号
+ * @param wValue 写入值对象, 包含要写入的数据类型和值
+ * @return boolean 返回写入操作是否成功, 成功返回 true, 失败返回 false
+ * @throws JIException 如果与 OPC DA 服务器通信时发生错误, 则抛出此异常
+ * @throws UnSupportException 如果写入值的数据类型不支持, 则抛出此异常
+ */
+ private boolean writeItem(Item item, WValue wValue) throws JIException {
+ PointTypeFlagEnum valueType = PointTypeFlagEnum.ofCode(wValue.getType());
+ if (Objects.isNull(valueType)) {
+ throw new CommonException("Unsupported type of " + wValue.getType());
+ }
+
+ int writeResult = 0;
+ switch (valueType) {
+ case SHORT:
+ short shortValue = wValue.getValueByClass(Short.class);
+ writeResult = item.write(new JIVariant(shortValue, false));
+ break;
+ case INT:
+ int intValue = wValue.getValueByClass(Integer.class);
+ writeResult = item.write(new JIVariant(intValue, false));
+ break;
+ case LONG:
+ long longValue = wValue.getValueByClass(Long.class);
+ writeResult = item.write(new JIVariant(longValue, false));
+ break;
+ case FLOAT:
+ float floatValue = wValue.getValueByClass(Float.class);
+ writeResult = item.write(new JIVariant(floatValue, false));
+ break;
+ case DOUBLE:
+ double doubleValue = wValue.getValueByClass(Double.class);
+ writeResult = item.write(new JIVariant(doubleValue, false));
+ break;
+ case BOOLEAN:
+ boolean booleanValue = wValue.getValueByClass(Boolean.class);
+ writeResult = item.write(new JIVariant(booleanValue, false));
+ break;
+ case STRING:
+ writeResult = item.write(new JIVariant(wValue.getValue(), false));
+ break;
+ default:
+ break;
+ }
+ return writeResult > 0;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/Categories.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/Categories.java
new file mode 100644
index 0000000..b4f6d98
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/Categories.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common;
+
+public interface Categories {
+ public static final String OPCDAServer10 = "63D5F430-CFE4-11d1-B2C8-0060083BA1FB";
+
+ public static final String OPCDAServer20 = "63D5F432-CFE4-11d1-B2C8-0060083BA1FB";
+
+ public static final String OPCDAServer30 = "CC603642-66D7-48f1-B69A-B625E73652D7";
+
+ public static final String XMLDAServer10 = "3098EDA4-A006-48b2-A27F-247453959408";
+
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/Constants.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/Constants.java
new file mode 100644
index 0000000..e25a0f6
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/Constants.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common;
+
+public interface Constants {
+ public static final String IConnectionPointContainer_IID = "B196B284-BAB4-101A-B69C-00AA00341D07";
+
+ public static final String IConnectionPoint_IID = "B196B286-BAB4-101A-B69C-00AA00341D07";
+
+ public static final String IOPCCommon_IID = "F31DFDE2-07B6-11D2-B2D8-0060083BA1FB";
+
+ public static final String IEnumString_IID = "00000101-0000-0000-C000-000000000046";
+
+ public static final String IEnumGUID_IID = "0002E000-0000-0000-C000-000000000046";
+
+ public static final int S_OK = 0;
+
+ public static final int S_FALSE = 1;
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/EventHandler.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/EventHandler.java
new file mode 100644
index 0000000..4c62295
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/EventHandler.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.IJIComObject;
+
+public interface EventHandler {
+ public String getIdentifier();
+
+ public IJIComObject getObject();
+
+ public void detach() throws JIException;
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/FILETIME.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/FILETIME.java
new file mode 100644
index 0000000..9ddeda2
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/FILETIME.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.JIStruct;
+
+import java.math.BigDecimal;
+import java.util.Calendar;
+
+public class FILETIME {
+ private int high = 0;
+
+ private int low = 0;
+
+ public FILETIME() {
+ }
+
+ public FILETIME(final FILETIME arg0) {
+ this.high = arg0.high;
+ this.low = arg0.low;
+ }
+
+ public FILETIME(final int high, final int low) {
+ this.high = high;
+ this.low = low;
+ }
+
+ public static JIStruct getStruct() throws JIException {
+ final JIStruct struct = new JIStruct();
+
+ struct.addMember(Integer.class);
+ struct.addMember(Integer.class);
+
+ return struct;
+ }
+
+ public static FILETIME fromStruct(final JIStruct struct) {
+ final FILETIME ft = new FILETIME();
+
+ ft.setLow((Integer) struct.getMember(0));
+ ft.setHigh((Integer) struct.getMember(1));
+
+ return ft;
+ }
+
+ public int getHigh() {
+ return this.high;
+ }
+
+ public void setHigh(final int high) {
+ this.high = high;
+ }
+
+ public int getLow() {
+ return this.low;
+ }
+
+ public void setLow(final int low) {
+ this.low = low;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ result = PRIME * result + this.high;
+ result = PRIME * result + this.low;
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final FILETIME other = (FILETIME) obj;
+ if (this.high != other.high) {
+ return false;
+ }
+ if (this.low != other.low) {
+ return false;
+ }
+ return true;
+ }
+
+ public Calendar asCalendar() {
+ final Calendar c = Calendar.getInstance();
+
+ /*
+ * The following "strange" stuff is needed since we miss a ulong type
+ */
+ long i = 0xFFFFFFFFL & this.high;
+ i = i << 32;
+ long j = 0xFFFFFFFFFFFFFFFFL & i;
+
+ i = 0xFFFFFFFFL & this.low;
+ j += i;
+ j /= 10000L;
+ j -= 11644473600000L;
+
+ c.setTimeInMillis(j);
+
+ return c;
+ }
+
+ public Calendar asBigDecimalCalendar() {
+ final Calendar c = Calendar.getInstance();
+
+ /*
+ * The following "strange" stuff is needed since we miss a ulong type
+ */
+ long i = 0xFFFFFFFFL & this.high;
+ i = i << 32;
+ BigDecimal d1 = new BigDecimal(0xFFFFFFFFFFFFFFFFL & i);
+
+ i = 0xFFFFFFFFL & this.low;
+ d1 = d1.add(new BigDecimal(i));
+ d1 = d1.divide(new BigDecimal(10000L));
+ d1 = d1.subtract(new BigDecimal(11644473600000L));
+
+ c.setTimeInMillis(d1.longValue());
+
+ return c;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s/%s", this.high, this.low);
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/KeyedResult.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/KeyedResult.java
new file mode 100644
index 0000000..c671db8
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/KeyedResult.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common;
+
+public class KeyedResult extends Result {
+ private K key;
+
+ public KeyedResult() {
+ super();
+ }
+
+ public KeyedResult(final K key, final V value, final int errorCode) {
+ super(value, errorCode);
+ this.key = key;
+ }
+
+ public K getKey() {
+ return this.key;
+ }
+
+ public void setKey(final K key) {
+ this.key = key;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/KeyedResultSet.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/KeyedResultSet.java
new file mode 100644
index 0000000..c31f308
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/KeyedResultSet.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common;
+
+import java.io.Serial;
+import java.util.ArrayList;
+
+public class KeyedResultSet extends ArrayList> {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ public KeyedResultSet() {
+ super();
+ }
+
+ public KeyedResultSet(final int size) {
+ super(size); // me
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/Result.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/Result.java
new file mode 100644
index 0000000..be405ff
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/Result.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common;
+
+public class Result {
+ private T value;
+
+ private int errorCode;
+
+ public Result() {
+ }
+
+ public Result(final T value, final int errorCode) {
+ this.value = value;
+ this.errorCode = errorCode;
+ }
+
+ public int getErrorCode() {
+ return this.errorCode;
+ }
+
+ public void setErrorCode(final int errorCode) {
+ this.errorCode = errorCode;
+ }
+
+ public T getValue() {
+ return this.value;
+ }
+
+ public void setValue(final T value) {
+ this.value = value;
+ }
+
+ public boolean isFailed() {
+ return this.errorCode != 0;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/ResultSet.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/ResultSet.java
new file mode 100644
index 0000000..6c0b9db
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/ResultSet.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common;
+
+import java.util.ArrayList;
+
+public class ResultSet extends ArrayList> {
+
+ private static final long serialVersionUID = 1L;
+
+ public ResultSet() {
+ super();
+ }
+
+ public ResultSet(final int size) {
+ super(size); // me
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/impl/BaseCOMObject.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/impl/BaseCOMObject.java
new file mode 100644
index 0000000..d7dfaa9
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/impl/BaseCOMObject.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl;
+
+import org.jinterop.dcom.core.IJIComObject;
+
+public class BaseCOMObject {
+ private IJIComObject comObject = null;
+
+ /**
+ * Create a new base COM object
+ *
+ * @param comObject The COM object to wrap but be addRef'ed
+ */
+ public BaseCOMObject(final IJIComObject comObject) {
+ this.comObject = comObject;
+ }
+
+ protected synchronized IJIComObject getCOMObject() {
+ return this.comObject;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/impl/EnumGUID.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/impl/EnumGUID.java
new file mode 100644
index 0000000..f62800d
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/impl/EnumGUID.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.IJIComObject;
+import org.jinterop.dcom.core.JIArray;
+import org.jinterop.dcom.core.JICallBuilder;
+import org.jinterop.dcom.core.JIFlags;
+import rpc.core.UUID;
+
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class EnumGUID extends BaseCOMObject {
+ public static final int DEFAULT_BATCH_SIZE = Integer.getInteger("openscada.dcom.enum-batch-size", 10);
+
+ public EnumGUID(final IJIComObject enumStringObject) throws IllegalArgumentException, UnknownHostException, JIException {
+ super(enumStringObject.queryInterface(org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Constants.IEnumGUID_IID));
+ }
+
+ public int next(final List list, final int num) throws JIException {
+ if (num <= 0) {
+ return 0;
+ }
+
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(0);
+
+ callObject.addInParamAsInt(num, JIFlags.FLAG_NULL);
+ callObject.addInParamAsInt(num, JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIArray(UUID.class, null, 1, true, true), JIFlags.FLAG_NULL);
+ callObject.addOutParamAsType(Integer.class, JIFlags.FLAG_NULL);
+
+ Object[] result = Helper.callRespectSFALSE(getCOMObject(), callObject);
+
+ UUID[] resultData = (UUID[]) ((JIArray) result[0]).getArrayInstance();
+ Integer cnt = (Integer) result[1];
+
+ for (int i = 0; i < cnt; i++) {
+ list.add(resultData[i]);
+ }
+ return cnt;
+ }
+
+ public Collection next(final int num) throws JIException {
+ List list = new ArrayList(num);
+ next(list, num);
+ return list;
+ }
+
+ public void skip(final int num) throws JIException {
+ if (num <= 0) {
+ return;
+ }
+
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(1);
+
+ callObject.addInParamAsInt(num, JIFlags.FLAG_NULL);
+
+ getCOMObject().call(callObject);
+ }
+
+ public void reset() throws JIException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(2);
+
+ getCOMObject().call(callObject);
+ }
+
+ public EnumGUID cloneObject() throws JIException, IllegalArgumentException, UnknownHostException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(3);
+
+ callObject.addOutParamAsType(IJIComObject.class, JIFlags.FLAG_NULL);
+
+ Object[] result = getCOMObject().call(callObject);
+
+ IJIComObject object = (IJIComObject) result[0];
+
+ return new EnumGUID(object);
+ }
+
+ public Collection asCollection(final int batchSize) throws JIException {
+ reset();
+
+ List data = new ArrayList();
+ int i = 0;
+ do {
+ i = next(data, batchSize);
+ } while (i == batchSize);
+
+ return data;
+ }
+
+ public Collection asCollection() throws JIException {
+ return asCollection(DEFAULT_BATCH_SIZE);
+ }
+
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/impl/EnumString.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/impl/EnumString.java
new file mode 100644
index 0000000..26c4f99
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/impl/EnumString.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.*;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Constants;
+
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class EnumString extends BaseCOMObject {
+ public static final int DEFAULT_BATCH_SIZE = Integer.getInteger("openscada.dcom.enum-batch-size", 10);
+
+ public EnumString(final IJIComObject enumStringObject) throws IllegalArgumentException, UnknownHostException, JIException {
+ super(enumStringObject.queryInterface(Constants.IEnumString_IID));
+ }
+
+ public int next(final List list, final int num) throws JIException {
+ if (num <= 0) {
+ return 0;
+ }
+
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(0);
+
+ callObject.addInParamAsInt(num, JIFlags.FLAG_NULL);
+ //callObject.addInParamAsInt ( num, JIFlags.FLAG_NULL );
+ //callObject.addOutParamAsObject ( new JIArray ( new JIPointer ( new JIString (
+ // JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR ) ), null, 1, true, true ), JIFlags.FLAG_NULL );
+ callObject.addOutParamAsObject(new JIArray(new JIString(JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR), null, 1, true, true), JIFlags.FLAG_NULL);
+ callObject.addOutParamAsType(Integer.class, JIFlags.FLAG_NULL);
+
+ Object[] result = Helper.callRespectSFALSE(getCOMObject(), callObject);
+
+ //JIPointer[] resultData = (JIPointer[]) ( (JIArray) ( result[0] ) ).getArrayInstance ();
+ JIString[] resultData = (JIString[]) ((JIArray) result[0]).getArrayInstance();
+ Integer cnt = (Integer) result[1];
+
+ for (int i = 0; i < cnt; i++) {
+ //list.add ( ( (JIString)resultData[i].getReferent () ).getString () );
+ list.add(resultData[i].getString());
+ }
+ return cnt;
+ }
+
+ public Collection next(final int num) throws JIException {
+ List list = new ArrayList(num);
+ next(list, num);
+ return list;
+ }
+
+ public void skip(final int num) throws JIException {
+ if (num <= 0) {
+ return;
+ }
+
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(1);
+
+ callObject.addInParamAsInt(num, JIFlags.FLAG_NULL);
+
+ getCOMObject().call(callObject);
+ }
+
+ public void reset() throws JIException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(2);
+
+ getCOMObject().call(callObject);
+ }
+
+ public EnumString cloneObject() throws JIException, IllegalArgumentException, UnknownHostException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(3);
+
+ callObject.addOutParamAsType(IJIComObject.class, JIFlags.FLAG_NULL);
+
+ Object[] result = getCOMObject().call(callObject);
+
+ IJIComObject object = (IJIComObject) result[0];
+ return new EnumString(object);
+ }
+
+ public Collection asCollection(final int batchSize) throws JIException {
+ reset();
+
+ List data = new ArrayList();
+ int i = 0;
+ do {
+ i = next(data, batchSize);
+ } while (i == batchSize);
+
+ return data;
+ }
+
+ public Collection asCollection() throws JIException {
+ return asCollection(DEFAULT_BATCH_SIZE);
+ }
+
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/impl/EventHandlerImpl.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/impl/EventHandlerImpl.java
new file mode 100644
index 0000000..6772ddb
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/impl/EventHandlerImpl.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.IJIComObject;
+import org.jinterop.dcom.core.JIFrameworkHelper;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.EventHandler;
+
+public class EventHandlerImpl implements EventHandler {
+ private String identifier = null;
+
+ private IJIComObject object = null;
+
+ public String getIdentifier() {
+ return this.identifier;
+ }
+
+ public synchronized IJIComObject getObject() {
+ return this.object;
+ }
+
+ public synchronized void setInfo(final IJIComObject object, final String identifier) {
+ this.object = object;
+ this.identifier = identifier;
+ }
+
+ public synchronized void detach() throws JIException {
+ if (this.object != null && this.identifier != null) {
+ try {
+ JIFrameworkHelper.detachEventHandler(this.object, this.identifier);
+ } finally {
+ this.object = null;
+ this.identifier = null;
+ }
+ }
+ }
+
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/impl/Helper.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/impl/Helper.java
new file mode 100644
index 0000000..0065e1a
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/impl/Helper.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.IJIComObject;
+import org.jinterop.dcom.core.JICallBuilder;
+import org.jinterop.dcom.core.JIFlags;
+import org.jinterop.dcom.core.JIVariant;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Constants;
+
+public class Helper {
+ /**
+ * Make the COM call but do not treat S_FALSE as error condition for the whole call
+ *
+ * @param object the object to make to call on
+ * @param callObject the call object
+ * @return the result of the call
+ * @throws JIException JIException
+ */
+ public static Object[] callRespectSFALSE(final IJIComObject object, final JICallBuilder callObject) throws JIException {
+ try {
+ return object.call(callObject);
+ } catch (JIException e) {
+ if (e.getErrorCode() != Constants.S_FALSE) {
+ throw e;
+ }
+ return callObject.getResultsInCaseOfException();
+ }
+ }
+
+ /**
+ * Perform some fixes on the variant when writing it to OPC items. This method
+ * only changes control information on the variant and not the value itself!
+ *
+ * @param value the value to fix
+ * @return the fixed value
+ * @throws JIException JIException
+ */
+ public static JIVariant fixVariant(final JIVariant value) throws JIException {
+ if (value.isArray()) {
+ if (value.getObjectAsArray().getArrayInstance() instanceof Boolean[]) {
+ value.setFlag(JIFlags.FLAG_REPRESENTATION_VARIANT_BOOL);
+ }
+ }
+ return value;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/impl/OPCCommon.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/impl/OPCCommon.java
new file mode 100644
index 0000000..6e17116
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/common/impl/OPCCommon.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.*;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Constants;
+
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Collection;
+
+public class OPCCommon extends BaseCOMObject {
+ public OPCCommon(final IJIComObject opcObject) throws IllegalArgumentException, UnknownHostException, JIException {
+ super(opcObject.queryInterface(Constants.IOPCCommon_IID));
+ }
+
+ public int getLocaleID() throws JIException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(1);
+
+ callObject.addOutParamAsObject(Integer.class, JIFlags.FLAG_NULL);
+
+ Object[] result = getCOMObject().call(callObject);
+ return (Integer) result[0];
+ }
+
+ public void setLocaleID(final int localeID) throws JIException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(0);
+
+ callObject.addInParamAsInt(localeID, JIFlags.FLAG_NULL);
+
+ getCOMObject().call(callObject);
+ }
+
+ public String getErrorString(final int errorCode, final int localeID) throws JIException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(3);
+
+ callObject.addInParamAsInt(errorCode, JIFlags.FLAG_NULL);
+ callObject.addInParamAsInt(localeID, JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIPointer(new JIString(JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR)), JIFlags.FLAG_NULL);
+
+ Object[] result = getCOMObject().call(callObject);
+ return ((JIString) ((JIPointer) result[0]).getReferent()).getString();
+ }
+
+ public void setClientName(final String clientName) throws JIException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(4);
+
+ callObject.addInParamAsString(clientName, JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR);
+
+ getCOMObject().call(callObject);
+ }
+
+ public Collection queryAvailableLocaleIDs() throws JIException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(2);
+
+ callObject.addOutParamAsType(Integer.class, JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(Integer.class, null, 1, true)), JIFlags.FLAG_NULL);
+
+ Object[] result = getCOMObject().call(callObject);
+
+ JIArray resultArray = (JIArray) ((JIPointer) result[1]).getReferent();
+ Integer[] intArray = (Integer[]) resultArray.getArrayInstance();
+
+ return Arrays.asList(intArray);
+ }
+
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/Constants.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/Constants.java
new file mode 100644
index 0000000..ecf5be8
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/Constants.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da;
+
+public interface Constants extends org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Constants {
+ public static final String IOPCServer_IID = "39C13A4D-011E-11D0-9675-0020AFD8ADB3";
+
+ public static final String IOPCGroupStateMgt_IID = "39C13A50-011E-11D0-9675-0020AFD8ADB3";
+
+ public static final String IOPCBrowse_IID = "39227004-A18F-4B57-8B0A-5235670F4468";
+
+ public static final String IOPCBrowseServerAddressSpace_IID = "39C13A4F-011E-11D0-9675-0020AFD8ADB3";
+
+ public static final String IOPCItemMgt_IID = "39C13A54-011E-11D0-9675-0020AFD8ADB3";
+
+ public static final String IOPCItemProperties_IID = "39C13A72-011E-11D0-9675-0020AFD8ADB3";
+
+ public static final String IOPCItemIO_IID = "85C0B427-2893-4CBC-BD78-E5FC5146F08F";
+
+ public static final String IOPCDataCallback_IID = "39C13A70-011E-11D0-9675-0020AFD8ADB3";
+
+ public static final String IOPCAsyncIO2_IID = "39C13A71-011E-11D0-9675-0020AFD8ADB3";
+
+ public static final String IOPCSyncIO_IID = "39C13A52-011E-11D0-9675-0020AFD8ADB3";
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/IOPCDataCallback.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/IOPCDataCallback.java
new file mode 100644
index 0000000..d03305b
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/IOPCDataCallback.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da;
+
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.KeyedResultSet;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.ResultSet;
+
+public interface IOPCDataCallback {
+ public void dataChange(int transactionId, int serverGroupHandle, int masterQuality, int masterErrorCode, KeyedResultSet result);
+
+ public void readComplete(int transactionId, int serverGroupHandle, int masterQuality, int masterErrorCode, KeyedResultSet result);
+
+ public void writeComplete(int transactionId, int serverGroupHandle, int masterErrorCode, ResultSet result);
+
+ public void cancelComplete(int transactionId, int serverGroupHandle);
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/IORequest.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/IORequest.java
new file mode 100644
index 0000000..9768845
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/IORequest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da;
+
+public class IORequest {
+ private String itemID;
+
+ private int maxAge;
+
+ public IORequest(final String itemID, final int maxAge) {
+ this.itemID = itemID;
+ this.maxAge = maxAge;
+ }
+
+ public String getItemID() {
+ return this.itemID;
+ }
+
+ public void setItemID(final String itemID) {
+ this.itemID = itemID;
+ }
+
+ public int getMaxAge() {
+ return this.maxAge;
+ }
+
+ public void setMaxAge(final int maxAge) {
+ this.maxAge = maxAge;
+ }
+
+}
\ No newline at end of file
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCBROWSEDIRECTION.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCBROWSEDIRECTION.java
new file mode 100644
index 0000000..0b11db5
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCBROWSEDIRECTION.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da;
+
+public enum OPCBROWSEDIRECTION {
+ OPC_BROWSE_UP(1),
+ OPC_BROWSE_DOWN(2),
+ OPC_BROWSE_TO(3),
+ OPC_BROWSE_UNKNOWN(0);
+
+ private int _id;
+
+ private OPCBROWSEDIRECTION(final int id) {
+ this._id = id;
+ }
+
+ public static OPCBROWSEDIRECTION fromID(final int id) {
+ switch (id) {
+ case 1:
+ return OPC_BROWSE_UP;
+ case 2:
+ return OPC_BROWSE_DOWN;
+ case 3:
+ return OPC_BROWSE_TO;
+ default:
+ return OPC_BROWSE_UNKNOWN;
+ }
+ }
+
+ public int id() {
+ return this._id;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCBROWSETYPE.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCBROWSETYPE.java
new file mode 100644
index 0000000..18c459e
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCBROWSETYPE.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da;
+
+public enum OPCBROWSETYPE {
+ OPC_BRANCH(1),
+ OPC_LEAF(2),
+ OPC_FLAT(3),
+ OPC_UNKNOWN(0);
+
+ private int _id;
+
+ private OPCBROWSETYPE(final int id) {
+ this._id = id;
+ }
+
+ public static OPCBROWSETYPE fromID(final int id) {
+ switch (id) {
+ case 1:
+ return OPC_BRANCH;
+ case 2:
+ return OPC_LEAF;
+ case 3:
+ return OPC_FLAT;
+ default:
+ return OPC_UNKNOWN;
+ }
+ }
+
+ public int id() {
+ return this._id;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCDATASOURCE.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCDATASOURCE.java
new file mode 100644
index 0000000..2b983ab
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCDATASOURCE.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da;
+
+public enum OPCDATASOURCE {
+ OPC_DS_CACHE(1),
+ OPC_DS_DEVICE(2),
+ OPC_DS_UNKNOWN(0);
+
+ private int _id;
+
+ private OPCDATASOURCE(final int id) {
+ this._id = id;
+ }
+
+ public static OPCDATASOURCE fromID(final int id) {
+ switch (id) {
+ case 1:
+ return OPC_DS_CACHE;
+ case 2:
+ return OPC_DS_DEVICE;
+ default:
+ return OPC_DS_UNKNOWN;
+ }
+ }
+
+ public int id() {
+ return this._id;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCENUMSCOPE.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCENUMSCOPE.java
new file mode 100644
index 0000000..72349c4
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCENUMSCOPE.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da;
+
+public enum OPCENUMSCOPE {
+ OPC_ENUM_PRIVATE_CONNECTIONS(1),
+ OPC_ENUM_PUBLIC_CONNECTIONS(2),
+ OPC_ENUM_ALL_CONNECTIONS(3),
+ OPC_ENUM_PRIVATE(4),
+ OPC_ENUM_PUBLIC(5),
+ OPC_ENUM_ALL(6),
+ OPC_ENUM_UNKNOWN(0);
+
+ private int _id;
+
+ private OPCENUMSCOPE(final int id) {
+ this._id = id;
+ }
+
+ public static OPCENUMSCOPE fromID(final int id) {
+ switch (id) {
+ case 1:
+ return OPC_ENUM_PRIVATE_CONNECTIONS;
+ case 2:
+ return OPC_ENUM_PUBLIC_CONNECTIONS;
+ case 3:
+ return OPC_ENUM_ALL_CONNECTIONS;
+ case 4:
+ return OPC_ENUM_PRIVATE;
+ case 5:
+ return OPC_ENUM_PUBLIC;
+ case 6:
+ return OPC_ENUM_ALL;
+ default:
+ return OPC_ENUM_UNKNOWN;
+ }
+ }
+
+ public int id() {
+ return this._id;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCGroupState.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCGroupState.java
new file mode 100644
index 0000000..597e029
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCGroupState.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da;
+
+public class OPCGroupState {
+ private int _updateRate = 1000;
+
+ private boolean _active = true;
+
+ private String _name = "";
+
+ private int _timeBias = 0;
+
+ private float _percentDeadband = 0.0f;
+
+ private int _localeID = 0;
+
+ private int _clientHandle = 0;
+
+ private int _serverHandle = 0;
+
+ public boolean isActive() {
+ return this._active;
+ }
+
+ public void setActive(final boolean active) {
+ this._active = active;
+ }
+
+ public int getClientHandle() {
+ return this._clientHandle;
+ }
+
+ public void setClientHandle(final int clientHandle) {
+ this._clientHandle = clientHandle;
+ }
+
+ public int getLocaleID() {
+ return this._localeID;
+ }
+
+ public void setLocaleID(final int localeID) {
+ this._localeID = localeID;
+ }
+
+ public String getName() {
+ return this._name;
+ }
+
+ public void setName(final String name) {
+ this._name = name;
+ }
+
+ public float getPercentDeadband() {
+ return this._percentDeadband;
+ }
+
+ public void setPercentDeadband(final float percentDeadband) {
+ this._percentDeadband = percentDeadband;
+ }
+
+ public int getServerHandle() {
+ return this._serverHandle;
+ }
+
+ public void setServerHandle(final int serverHandle) {
+ this._serverHandle = serverHandle;
+ }
+
+ public int getTimeBias() {
+ return this._timeBias;
+ }
+
+ public void setTimeBias(final int timeBias) {
+ this._timeBias = timeBias;
+ }
+
+ public int getUpdateRate() {
+ return this._updateRate;
+ }
+
+ public void setUpdateRate(final int updateRate) {
+ this._updateRate = updateRate;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCITEMDEF.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCITEMDEF.java
new file mode 100644
index 0000000..3507ac0
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCITEMDEF.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.*;
+
+public class OPCITEMDEF {
+ private String accessPath = "";
+
+ private String itemID = "";
+
+ private boolean active = true;
+
+ private int clientHandle;
+
+ private short requestedDataType = JIVariant.VT_EMPTY;
+
+ private short reserved;
+
+ public String getAccessPath() {
+ return this.accessPath;
+ }
+
+ public void setAccessPath(final String accessPath) {
+ this.accessPath = accessPath;
+ }
+
+ public int getClientHandle() {
+ return this.clientHandle;
+ }
+
+ public void setClientHandle(final int clientHandle) {
+ this.clientHandle = clientHandle;
+ }
+
+ public boolean isActive() {
+ return this.active;
+ }
+
+ public void setActive(final boolean active) {
+ this.active = active;
+ }
+
+ public String getItemID() {
+ return this.itemID;
+ }
+
+ public void setItemID(final String itemID) {
+ this.itemID = itemID;
+ }
+
+ public short getRequestedDataType() {
+ return this.requestedDataType;
+ }
+
+ public void setRequestedDataType(final short requestedDataType) {
+ this.requestedDataType = requestedDataType;
+ }
+
+ public short getReserved() {
+ return this.reserved;
+ }
+
+ public void setReserved(final short reserved) {
+ this.reserved = reserved;
+ }
+
+ /**
+ * Convert to structure to a J-Interop structure
+ *
+ * @return the j-interop structe
+ * @throws JIException JIException
+ */
+ public JIStruct toStruct() throws JIException {
+ final JIStruct struct = new JIStruct();
+ struct.addMember(new JIString(getAccessPath(), JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR));
+ struct.addMember(new JIString(getItemID(), JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR));
+ struct.addMember(isActive() ? 1 : 0);
+ struct.addMember(getClientHandle());
+
+ struct.addMember(0); // blob size
+ struct.addMember(new JIPointer(null)); // blob
+
+ struct.addMember(getRequestedDataType());
+ struct.addMember(getReserved());
+ return struct;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCITEMRESULT.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCITEMRESULT.java
new file mode 100644
index 0000000..1d20464
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCITEMRESULT.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.JIArray;
+import org.jinterop.dcom.core.JIPointer;
+import org.jinterop.dcom.core.JIStruct;
+import org.jinterop.dcom.core.JIVariant;
+
+public class OPCITEMRESULT {
+ private int _serverHandle = 0;
+
+ private short _canonicalDataType = JIVariant.VT_EMPTY;
+
+ private short _reserved = 0;
+
+ private int _accessRights = 0;
+
+ public static JIStruct getStruct() throws JIException {
+ JIStruct struct = new JIStruct();
+
+ struct.addMember(Integer.class); // Server handle
+ struct.addMember(Short.class); // data type
+ struct.addMember(Short.class); // reserved
+ struct.addMember(Integer.class); // access rights
+ struct.addMember(Integer.class); // blob size
+ // grab the normally unused byte array
+ struct.addMember(new JIPointer(new JIArray(Byte.class, null, 1, true, false)));
+
+ return struct;
+ }
+
+ public static OPCITEMRESULT fromStruct(final JIStruct struct) {
+ OPCITEMRESULT result = new OPCITEMRESULT();
+
+ result.setServerHandle((Integer) struct.getMember(0));
+ result.setCanonicalDataType((Short) struct.getMember(1));
+ result.setReserved((Short) struct.getMember(2));
+ result.setAccessRights((Integer) struct.getMember(3));
+
+ return result;
+ }
+
+ public int getAccessRights() {
+ return this._accessRights;
+ }
+
+ public void setAccessRights(final int accessRights) {
+ this._accessRights = accessRights;
+ }
+
+ public short getCanonicalDataType() {
+ return this._canonicalDataType;
+ }
+
+ public void setCanonicalDataType(final short canonicalDataType) {
+ this._canonicalDataType = canonicalDataType;
+ }
+
+ public short getReserved() {
+ return this._reserved;
+ }
+
+ public void setReserved(final short reserved) {
+ this._reserved = reserved;
+ }
+
+ public int getServerHandle() {
+ return this._serverHandle;
+ }
+
+ public void setServerHandle(final int serverHandle) {
+ this._serverHandle = serverHandle;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCITEMSTATE.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCITEMSTATE.java
new file mode 100644
index 0000000..fbe56c9
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCITEMSTATE.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.JIStruct;
+import org.jinterop.dcom.core.JIVariant;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.FILETIME;
+
+public class OPCITEMSTATE {
+ private int _clientHandle = 0;
+
+ private FILETIME _timestamp = null;
+
+ private short _quality = 0;
+
+ private short _reserved = 0;
+
+ private JIVariant _value = null;
+
+ public static JIStruct getStruct() throws JIException {
+ JIStruct struct = new JIStruct();
+
+ struct.addMember(Integer.class);
+ struct.addMember(FILETIME.getStruct());
+ struct.addMember(Short.class);
+ struct.addMember(Short.class);
+ struct.addMember(JIVariant.class);
+
+ return struct;
+ }
+
+ public static OPCITEMSTATE fromStruct(final JIStruct struct) {
+ OPCITEMSTATE itemState = new OPCITEMSTATE();
+
+ itemState.setClientHandle((Integer) struct.getMember(0));
+ itemState.setTimestamp(FILETIME.fromStruct((JIStruct) struct.getMember(1)));
+ itemState.setQuality((Short) struct.getMember(2));
+ itemState.setReserved((Short) struct.getMember(3));
+ itemState.setValue((JIVariant) struct.getMember(4));
+
+ return itemState;
+ }
+
+ public int getClientHandle() {
+ return this._clientHandle;
+ }
+
+ public void setClientHandle(final int clientHandle) {
+ this._clientHandle = clientHandle;
+ }
+
+ public short getQuality() {
+ return this._quality;
+ }
+
+ public void setQuality(final short quality) {
+ this._quality = quality;
+ }
+
+ public short getReserved() {
+ return this._reserved;
+ }
+
+ public void setReserved(final short reserved) {
+ this._reserved = reserved;
+ }
+
+ public FILETIME getTimestamp() {
+ return this._timestamp;
+ }
+
+ public void setTimestamp(final FILETIME timestamp) {
+ this._timestamp = timestamp;
+ }
+
+ public JIVariant getValue() {
+ return this._value;
+ }
+
+ public void setValue(final JIVariant value) {
+ this._value = value;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCNAMESPACETYPE.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCNAMESPACETYPE.java
new file mode 100644
index 0000000..cfb4502
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCNAMESPACETYPE.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da;
+
+public enum OPCNAMESPACETYPE {
+ OPC_NS_HIERARCHIAL(1),
+ OPC_NS_FLAT(2),
+ OPC_NS_UNKNOWN(0);
+
+ private int _id;
+
+ private OPCNAMESPACETYPE(final int id) {
+ this._id = id;
+ }
+
+ public static OPCNAMESPACETYPE fromID(final int id) {
+ switch (id) {
+ case 1:
+ return OPC_NS_HIERARCHIAL;
+ case 2:
+ return OPC_NS_FLAT;
+ default:
+ return OPC_NS_UNKNOWN;
+ }
+ }
+
+ public int id() {
+ return this._id;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCSERVERSTATE.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCSERVERSTATE.java
new file mode 100644
index 0000000..cb4a216
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCSERVERSTATE.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da;
+
+public enum OPCSERVERSTATE {
+ OPC_STATUS_RUNNING(1),
+ OPC_STATUS_FAILED(2),
+ OPC_STATUS_NOCONFIG(3),
+ OPC_STATUS_SUSPENDED(4),
+ OPC_STATUS_TEST(5),
+ OPC_STATUS_COMM_FAULT(6),
+ OPC_STATUS_UNKNOWN(0);
+
+ private int _id;
+
+ private OPCSERVERSTATE(final int id) {
+ this._id = id;
+ }
+
+ public static OPCSERVERSTATE fromID(final int id) {
+ switch (id) {
+ case 1:
+ return OPC_STATUS_RUNNING;
+ case 2:
+ return OPC_STATUS_FAILED;
+ case 3:
+ return OPC_STATUS_NOCONFIG;
+ case 4:
+ return OPC_STATUS_SUSPENDED;
+ case 5:
+ return OPC_STATUS_TEST;
+ case 6:
+ return OPC_STATUS_COMM_FAULT;
+ default:
+ return OPC_STATUS_UNKNOWN;
+ }
+ }
+
+ public int id() {
+ return this._id;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCSERVERSTATUS.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCSERVERSTATUS.java
new file mode 100644
index 0000000..7cb7837
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/OPCSERVERSTATUS.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.JIFlags;
+import org.jinterop.dcom.core.JIPointer;
+import org.jinterop.dcom.core.JIString;
+import org.jinterop.dcom.core.JIStruct;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.FILETIME;
+
+public class OPCSERVERSTATUS {
+ private FILETIME _startTime = null;
+
+ private FILETIME _currentTime = null;
+
+ private FILETIME _lastUpdateTime = null;
+
+ private OPCSERVERSTATE _serverState = null;
+
+ private int _groupCount = -1;
+
+ private int _bandWidth = -1;
+
+ private short _majorVersion = -1;
+
+ private short _minorVersion = -1;
+
+ private short _buildNumber = -1;
+
+ private short _reserved = 0;
+
+ private String _vendorInfo = null;
+
+ public static JIStruct getStruct() throws JIException {
+ JIStruct struct = new JIStruct();
+
+ struct.addMember(FILETIME.getStruct());
+ struct.addMember(FILETIME.getStruct());
+ struct.addMember(FILETIME.getStruct());
+ struct.addMember(Short.class); // enum: OPCSERVERSTATE
+ struct.addMember(Integer.class);
+ struct.addMember(Integer.class);
+ struct.addMember(Short.class);
+ struct.addMember(Short.class);
+ struct.addMember(Short.class);
+ struct.addMember(Short.class);
+ struct.addMember(new JIPointer(new JIString(JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR)));
+
+ return struct;
+ }
+
+ public static OPCSERVERSTATUS fromStruct(final JIStruct struct) {
+ OPCSERVERSTATUS status = new OPCSERVERSTATUS();
+
+ status._startTime = FILETIME.fromStruct((JIStruct) struct.getMember(0));
+ status._currentTime = FILETIME.fromStruct((JIStruct) struct.getMember(1));
+ status._lastUpdateTime = FILETIME.fromStruct((JIStruct) struct.getMember(2));
+
+ status._serverState = OPCSERVERSTATE.fromID((Short) struct.getMember(3));
+ status._groupCount = (Integer) struct.getMember(4);
+ status._bandWidth = (Integer) struct.getMember(5);
+ status._majorVersion = (Short) struct.getMember(6);
+ status._minorVersion = (Short) struct.getMember(7);
+ status._buildNumber = (Short) struct.getMember(8);
+ status._reserved = (Short) struct.getMember(9);
+ status._vendorInfo = ((JIString) ((JIPointer) struct.getMember(10)).getReferent()).getString();
+
+ return status;
+ }
+
+ public int getBandWidth() {
+ return this._bandWidth;
+ }
+
+ public void setBandWidth(final int bandWidth) {
+ this._bandWidth = bandWidth;
+ }
+
+ public short getBuildNumber() {
+ return this._buildNumber;
+ }
+
+ public void setBuildNumber(final short buildNumber) {
+ this._buildNumber = buildNumber;
+ }
+
+ public FILETIME getCurrentTime() {
+ return this._currentTime;
+ }
+
+ public void setCurrentTime(final FILETIME currentTime) {
+ this._currentTime = currentTime;
+ }
+
+ public int getGroupCount() {
+ return this._groupCount;
+ }
+
+ public void setGroupCount(final int groupCount) {
+ this._groupCount = groupCount;
+ }
+
+ public FILETIME getLastUpdateTime() {
+ return this._lastUpdateTime;
+ }
+
+ public void setLastUpdateTime(final FILETIME lastUpdateTime) {
+ this._lastUpdateTime = lastUpdateTime;
+ }
+
+ public short getMajorVersion() {
+ return this._majorVersion;
+ }
+
+ public void setMajorVersion(final short majorVersion) {
+ this._majorVersion = majorVersion;
+ }
+
+ public short getMinorVersion() {
+ return this._minorVersion;
+ }
+
+ public void setMinorVersion(final short minorVersion) {
+ this._minorVersion = minorVersion;
+ }
+
+ public short getReserved() {
+ return this._reserved;
+ }
+
+ public void setReserved(final short reserved) {
+ this._reserved = reserved;
+ }
+
+ public FILETIME getStartTime() {
+ return this._startTime;
+ }
+
+ public void setStartTime(final FILETIME startTime) {
+ this._startTime = startTime;
+ }
+
+ public String getVendorInfo() {
+ return this._vendorInfo;
+ }
+
+ public void setVendorInfo(final String vendorInfo) {
+ this._vendorInfo = vendorInfo;
+ }
+
+ public OPCSERVERSTATE getServerState() {
+ return this._serverState;
+ }
+
+ public void setServerState(final OPCSERVERSTATE dwServerState) {
+ this._serverState = dwServerState;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/PropertyDescription.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/PropertyDescription.java
new file mode 100644
index 0000000..e04e8b2
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/PropertyDescription.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da;
+
+public class PropertyDescription {
+ private int _id = -1;
+
+ private String _description = "";
+
+ private short _varType = 0;
+
+ public String getDescription() {
+ return this._description;
+ }
+
+ public void setDescription(final String description) {
+ this._description = description;
+ }
+
+ public int getId() {
+ return this._id;
+ }
+
+ public void setId(final int id) {
+ this._id = id;
+ }
+
+ public short getVarType() {
+ return this._varType;
+ }
+
+ public void setVarType(final short varType) {
+ this._varType = varType;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/ValueData.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/ValueData.java
new file mode 100644
index 0000000..37160c5
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/ValueData.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da;
+
+import org.jinterop.dcom.core.JIVariant;
+
+import java.util.Calendar;
+
+public class ValueData {
+ private JIVariant value;
+
+ private short quality;
+
+ private Calendar timestamp;
+
+ public short getQuality() {
+ return this.quality;
+ }
+
+ public void setQuality(final short quality) {
+ this.quality = quality;
+ }
+
+ public Calendar getTimestamp() {
+ return this.timestamp;
+ }
+
+ public void setTimestamp(final Calendar timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public JIVariant getValue() {
+ return this.value;
+ }
+
+ public void setValue(final JIVariant value) {
+ this.value = value;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/WriteRequest.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/WriteRequest.java
new file mode 100644
index 0000000..79bdea1
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/WriteRequest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da;
+
+import org.jinterop.dcom.core.JIVariant;
+
+/**
+ * Data for a write request to the server
+ *
+ * @author Jens Reimann jens.reimann@th4-systems.com
+ */
+public class WriteRequest {
+ private int serverHandle = 0;
+
+ private JIVariant value = JIVariant.EMPTY();
+
+ public WriteRequest() {
+ }
+
+ public WriteRequest(final WriteRequest request) {
+ this.serverHandle = request.serverHandle;
+ this.value = request.value;
+ }
+
+ /**
+ * Create a new write request with pre-fille data
+ *
+ * @param serverHandle the server handle of the item to write to
+ * @param value the value to write.
+ */
+ public WriteRequest(final int serverHandle, final JIVariant value) {
+ this.serverHandle = serverHandle;
+ this.value = value;
+ }
+
+ public int getServerHandle() {
+ return this.serverHandle;
+ }
+
+ public void setServerHandle(final int serverHandle) {
+ this.serverHandle = serverHandle;
+ }
+
+ public JIVariant getValue() {
+ return this.value;
+ }
+
+ public void setValue(final JIVariant value) {
+ this.value = value;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCAsyncIO2.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCAsyncIO2.java
new file mode 100644
index 0000000..dba8b6b
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCAsyncIO2.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.*;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Result;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.ResultSet;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.BaseCOMObject;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.Constants;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCDATASOURCE;
+
+import java.net.UnknownHostException;
+
+public class OPCAsyncIO2 extends BaseCOMObject {
+ public OPCAsyncIO2(final IJIComObject opcAsyncIO2) throws IllegalArgumentException, UnknownHostException, JIException {
+ super(opcAsyncIO2.queryInterface(Constants.IOPCAsyncIO2_IID));
+ }
+
+ public void setEnable(final boolean state) throws JIException {
+ final JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(4);
+
+ callObject.addInParamAsInt(state ? 1 : 0, JIFlags.FLAG_NULL);
+
+ getCOMObject().call(callObject);
+ }
+
+ public int refresh(final OPCDATASOURCE dataSource, final int transactionID) throws JIException {
+ final JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(2);
+
+ callObject.addInParamAsShort((short) dataSource.id(), JIFlags.FLAG_NULL);
+ callObject.addInParamAsInt(transactionID, JIFlags.FLAG_NULL);
+ callObject.addOutParamAsType(Integer.class, JIFlags.FLAG_NULL);
+
+ final Object result[] = getCOMObject().call(callObject);
+
+ return (Integer) result[0];
+ }
+
+ public void cancel(final int cancelId) throws JIException {
+ final JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(3);
+
+ callObject.addInParamAsInt(cancelId, JIFlags.FLAG_NULL);
+
+ getCOMObject().call(callObject);
+ }
+
+ public AsyncResult read(final int transactionId, final Integer... serverHandles) throws JIException {
+ if (serverHandles == null || serverHandles.length == 0) {
+ return new AsyncResult();
+ }
+
+ final JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(0);
+
+ callObject.addInParamAsInt(serverHandles.length, JIFlags.FLAG_NULL);
+ callObject.addInParamAsArray(new JIArray(serverHandles, true), JIFlags.FLAG_NULL);
+ callObject.addInParamAsInt(transactionId, JIFlags.FLAG_NULL);
+
+ callObject.addOutParamAsType(Integer.class, JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(Integer.class, null, 1, true)), JIFlags.FLAG_NULL);
+
+ final Object[] result = getCOMObject().call(callObject);
+
+ final Integer cancelId = (Integer) result[0];
+ final Integer[] errorCodes = (Integer[]) ((JIArray) ((JIPointer) result[1]).getReferent()).getArrayInstance();
+
+ final ResultSet resultSet = new ResultSet();
+
+ for (int i = 0; i < serverHandles.length; i++) {
+ resultSet.add(new Result(serverHandles[i], errorCodes[i]));
+ }
+
+ return new AsyncResult(resultSet, cancelId);
+ }
+
+ public class AsyncResult {
+ private final ResultSet result;
+
+ private final Integer cancelId;
+
+ public AsyncResult() {
+ super();
+ this.result = new ResultSet();
+ this.cancelId = null;
+ }
+
+ public AsyncResult(final ResultSet result, final Integer cancelId) {
+ super();
+ this.result = result;
+ this.cancelId = cancelId;
+ }
+
+ public Integer getCancelId() {
+ return this.cancelId;
+ }
+
+ public ResultSet getResult() {
+ return this.result;
+ }
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCBrowse.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCBrowse.java
new file mode 100644
index 0000000..06e9e32
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCBrowse.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.IJIComObject;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.BaseCOMObject;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.Constants;
+
+import java.net.UnknownHostException;
+
+public class OPCBrowse extends BaseCOMObject {
+ public OPCBrowse(final IJIComObject opcServer) throws IllegalArgumentException, UnknownHostException, JIException {
+ super(opcServer.queryInterface(Constants.IOPCBrowse_IID));
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCBrowseServerAddressSpace.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCBrowseServerAddressSpace.java
new file mode 100644
index 0000000..4613184
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCBrowseServerAddressSpace.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.*;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.BaseCOMObject;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.EnumString;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.Helper;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.Constants;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCBROWSEDIRECTION;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCBROWSETYPE;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCNAMESPACETYPE;
+
+import java.net.UnknownHostException;
+
+/**
+ * Implementation for IOPCBrowseServerAddressSpace
+ *
+ * @author Jens Reimann jens.reimann@th4-systems.com
+ */
+public class OPCBrowseServerAddressSpace extends BaseCOMObject {
+ public OPCBrowseServerAddressSpace(final IJIComObject opcServer) throws IllegalArgumentException, UnknownHostException, JIException {
+ super(opcServer.queryInterface(Constants.IOPCBrowseServerAddressSpace_IID));
+ }
+
+ /**
+ * Get the information how the namespace is organized
+ *
+ * @return the organization of the namespace
+ * @throws JIException JIException
+ */
+ public OPCNAMESPACETYPE queryOrganization() throws JIException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(0);
+
+ callObject.addOutParamAsType(Short.class, JIFlags.FLAG_NULL);
+
+ Object result[] = getCOMObject().call(callObject);
+
+ return OPCNAMESPACETYPE.fromID((Short) result[0]);
+ }
+
+ /**
+ * Direct the browser to another position
+ *
+ * Depending on the direction the new position will be set based on the provided
+ * position information. If the direction is {@link OPCBROWSEDIRECTION#OPC_BROWSE_TO} then
+ * the position is the item to go to. If the direction is {@link OPCBROWSEDIRECTION#OPC_BROWSE_DOWN}
+ * the browser will descent into the tree down (not to) the branch item in position.
+ * Passing {@link OPCBROWSEDIRECTION#OPC_BROWSE_UP} won't need a position (pass null)
+ * and will ascent in the tree one level.
+ *
+ * Passing {@link OPCBROWSEDIRECTION#OPC_BROWSE_TO} and null as position will
+ * go to the first root entry of the namespace.
+ *
+ * @param position The item position reference for the direction
+ * @param direction The direction to go based on the position
+ * @throws JIException JIException
+ */
+ public void changePosition(final String position, final OPCBROWSEDIRECTION direction) throws JIException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(1);
+
+ callObject.addInParamAsShort((short) direction.id(), JIFlags.FLAG_NULL);
+ callObject.addInParamAsString(position, JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR);
+
+ getCOMObject().call(callObject);
+
+ }
+
+ public EnumString browse(final OPCBROWSETYPE browseType, final String filterCriteria, final int accessRights, final int dataType) throws JIException, IllegalArgumentException, UnknownHostException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(2);
+
+ callObject.addInParamAsShort((short) browseType.id(), JIFlags.FLAG_NULL);
+ callObject.addInParamAsString(filterCriteria, JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR);
+ callObject.addInParamAsShort((short) dataType, JIFlags.FLAG_NULL);
+ callObject.addInParamAsInt(accessRights, JIFlags.FLAG_NULL);
+ callObject.addOutParamAsType(IJIComObject.class, JIFlags.FLAG_NULL);
+
+ Object result[] = Helper.callRespectSFALSE(getCOMObject(), callObject);
+
+ return new EnumString((IJIComObject) result[0]);
+ }
+
+ /**
+ * Return the possible access paths for an item
+ *
+ * @param itemID the item to query
+ * @return A string enumerator for the possible access paths
+ * @throws JIException JIException
+ * @throws IllegalArgumentException IllegalArgumentException
+ * @throws UnknownHostException UnknownHostException
+ */
+ public EnumString browseAccessPaths(final String itemID) throws JIException, IllegalArgumentException, UnknownHostException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(4);
+
+ callObject.addInParamAsString(itemID, JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR);
+ callObject.addOutParamAsType(IJIComObject.class, JIFlags.FLAG_NULL);
+
+ Object[] result = Helper.callRespectSFALSE(getCOMObject(), callObject);
+
+ return new EnumString((IJIComObject) result[0]);
+ }
+
+ /**
+ * Get the complete item id from an item at the local position.
+ *
+ * Browsing a hierarchical namespace the browse method will return items based on the
+ * local level in the namespace. So actually only the last part of the item ID hierarchy
+ * is returned. In order to convert this to the full item ID one can use this method. It
+ * will only work if the browser is still at the position in question.
+ *
+ * @param item the local item
+ * @return the complete item ID
+ * @throws JIException JIException
+ */
+ public String getItemID(final String item) throws JIException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(3);
+
+ callObject.addInParamAsString(item, JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR);
+ callObject.addOutParamAsObject(new JIPointer(new JIString(JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR)), JIFlags.FLAG_NULL);
+
+ Object[] result = getCOMObject().call(callObject);
+
+ return ((JIString) ((JIPointer) result[0]).getReferent()).getString();
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCDataCallback.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCDataCallback.java
new file mode 100644
index 0000000..fefd676
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCDataCallback.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.*;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.*;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.EventHandlerImpl;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.Constants;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.IOPCDataCallback;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.ValueData;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class OPCDataCallback extends EventHandlerImpl {
+ private IOPCDataCallback callback = null;
+
+ private JILocalCoClass coClass = null;
+
+ public OPCDataCallback() {
+ super();
+ }
+
+ public Object[] OnDataChange(final int transactionId, final int serverGroupHandle, final int masterQuality, final int masterErrorCode, final int count, final JIArray clientHandles, final JIArray values, final JIArray qualities, final JIArray timestamps, final JIArray errors) {
+ final IOPCDataCallback callback = this.callback;
+ if (callback == null) {
+ return new Object[]{org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Constants.S_OK};
+ }
+
+ // get arrays for more readable code later ;-)
+ final Integer[] errorCodes = (Integer[]) errors.getArrayInstance();
+ final Integer[] itemHandles = (Integer[]) clientHandles.getArrayInstance();
+ final Short[] qualitiesArray = (Short[]) qualities.getArrayInstance();
+ final JIVariant[] valuesArray = (JIVariant[]) values.getArrayInstance();
+ final JIStruct[] timestampArray = (JIStruct[]) timestamps.getArrayInstance();
+
+ // create result data
+ final KeyedResultSet result = new KeyedResultSet();
+ for (int i = 0; i < count; i++) {
+ final ValueData vd = new ValueData();
+ vd.setQuality(qualitiesArray[i]);
+ vd.setTimestamp(FILETIME.fromStruct(timestampArray[i]).asCalendar());
+ vd.setValue(valuesArray[i]);
+ result.add(new KeyedResult(itemHandles[i], vd, errorCodes[i]));
+ }
+
+ // fire event
+ try {
+ callback.dataChange(transactionId, serverGroupHandle, masterQuality, masterErrorCode, result);
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ }
+
+ // The client must always return S_OK
+ return new Object[]{org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Constants.S_OK};
+ }
+
+ public synchronized Object[] OnReadComplete(final int transactionId, final int serverGroupHandle, final int masterQuality, final int masterErrorCode, final int count, final JIArray clientHandles, final JIArray values, final JIArray qualities, final JIArray timestamps, final JIArray errors) {
+ if (this.callback == null) {
+ return new Object[]{org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Constants.S_OK};
+ }
+
+ // get arrays for more readable code later ;-)
+ final Integer[] errorCodes = (Integer[]) errors.getArrayInstance();
+ final Integer[] itemHandles = (Integer[]) clientHandles.getArrayInstance();
+ final Short[] qualitiesArray = (Short[]) qualities.getArrayInstance();
+ final JIVariant[] valuesArray = (JIVariant[]) values.getArrayInstance();
+ final JIStruct[] timestampArray = (JIStruct[]) timestamps.getArrayInstance();
+
+ // create result data
+ final KeyedResultSet result = new KeyedResultSet();
+ for (int i = 0; i < count; i++) {
+ final ValueData vd = new ValueData();
+ vd.setQuality(qualitiesArray[i]);
+ vd.setTimestamp(FILETIME.fromStruct(timestampArray[i]).asCalendar());
+ vd.setValue(valuesArray[i]);
+ result.add(new KeyedResult(itemHandles[i], vd, errorCodes[i]));
+ }
+
+ // fire event
+ try {
+ this.callback.readComplete(transactionId, serverGroupHandle, masterQuality, masterErrorCode, result);
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ }
+
+ // The client must always return S_OK
+ return new Object[]{org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Constants.S_OK};
+ }
+
+ public synchronized Object[] OnWriteComplete(final int transactionId, final int serverGroupHandle, final int masterErrorCode, final int count, final JIArray clientHandles, final JIArray errors) {
+ if (this.callback == null) {
+ return new Object[]{org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Constants.S_OK};
+ }
+
+ // get arrays for more readable code later ;-)
+ final Integer[] errorCodes = (Integer[]) errors.getArrayInstance();
+ final Integer[] itemHandles = (Integer[]) clientHandles.getArrayInstance();
+
+ // create result data
+ final ResultSet result = new ResultSet();
+ for (int i = 0; i < count; i++) {
+ result.add(new Result(itemHandles[i], errorCodes[i]));
+ }
+
+ // fire event
+ try {
+ this.callback.writeComplete(transactionId, serverGroupHandle, masterErrorCode, result);
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ }
+
+ // The client must always return S_OK
+ return new Object[]{org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Constants.S_OK};
+ }
+
+ public synchronized Object[] OnCancelComplete(final int transactionId, final int serverGroupHandle) {
+ if (this.callback == null) {
+ return new Object[]{org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Constants.S_OK};
+ }
+
+ this.callback.cancelComplete(transactionId, serverGroupHandle);
+
+ // The client must always return S_OK
+ return new Object[]{org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Constants.S_OK};
+ }
+
+ public synchronized JILocalCoClass getCoClass() throws JIException {
+ if (this.coClass != null) {
+ return this.coClass;
+ }
+
+ this.coClass = new JILocalCoClass(new JILocalInterfaceDefinition(Constants.IOPCDataCallback_IID, false), this, false);
+
+ JILocalParamsDescriptor params;
+ JILocalMethodDescriptor method;
+
+ // OnDataChange
+ params = new JILocalParamsDescriptor();
+ params.addInParamAsType(Integer.class, JIFlags.FLAG_NULL); // trans id
+ params.addInParamAsType(Integer.class, JIFlags.FLAG_NULL); // group handle
+ params.addInParamAsType(Integer.class, JIFlags.FLAG_NULL); // master quality
+ params.addInParamAsType(Integer.class, JIFlags.FLAG_NULL); // master error
+ params.addInParamAsType(Integer.class, JIFlags.FLAG_NULL); // count
+ params.addInParamAsObject(new JIArray(Integer.class, null, 1, true), JIFlags.FLAG_NULL); // item handles
+ params.addInParamAsObject(new JIArray(JIVariant.class, null, 1, true), JIFlags.FLAG_NULL); // values
+ params.addInParamAsObject(new JIArray(Short.class, null, 1, true), JIFlags.FLAG_NULL); // qualities
+ params.addInParamAsObject(new JIArray(FILETIME.getStruct(), null, 1, true), JIFlags.FLAG_NULL); // timestamps
+ params.addInParamAsObject(new JIArray(Integer.class, null, 1, true), JIFlags.FLAG_NULL); // errors
+
+ method = new JILocalMethodDescriptor("OnDataChange", params);
+ this.coClass.getInterfaceDefinition().addMethodDescriptor(method);
+
+ // OnReadComplete
+ params = new JILocalParamsDescriptor();
+ params.addInParamAsType(Integer.class, JIFlags.FLAG_NULL);
+ params.addInParamAsType(Integer.class, JIFlags.FLAG_NULL);
+ params.addInParamAsType(Integer.class, JIFlags.FLAG_NULL);
+ params.addInParamAsType(Integer.class, JIFlags.FLAG_NULL);
+ params.addInParamAsType(Integer.class, JIFlags.FLAG_NULL);
+ params.addInParamAsObject(new JIArray(Integer.class, null, 1, true), JIFlags.FLAG_NULL);
+ params.addInParamAsObject(new JIArray(JIVariant.class, null, 1, true), JIFlags.FLAG_NULL);
+ params.addInParamAsObject(new JIArray(Short.class, null, 1, true), JIFlags.FLAG_NULL);
+ params.addInParamAsObject(new JIArray(FILETIME.getStruct(), null, 1, true), JIFlags.FLAG_NULL);
+ params.addInParamAsObject(new JIArray(Integer.class, null, 1, true), JIFlags.FLAG_NULL);
+ method = new JILocalMethodDescriptor("OnReadComplete", params);
+ this.coClass.getInterfaceDefinition().addMethodDescriptor(method);
+
+ // OnWriteComplete
+ params = new JILocalParamsDescriptor();
+ params.addInParamAsType(Integer.class, JIFlags.FLAG_NULL);
+ params.addInParamAsType(Integer.class, JIFlags.FLAG_NULL);
+ params.addInParamAsType(Integer.class, JIFlags.FLAG_NULL);
+ params.addInParamAsType(Integer.class, JIFlags.FLAG_NULL);
+ params.addInParamAsObject(new JIArray(Integer.class, null, 1, true), JIFlags.FLAG_NULL);
+ params.addInParamAsObject(new JIArray(Integer.class, null, 1, true), JIFlags.FLAG_NULL);
+ method = new JILocalMethodDescriptor("OnWriteComplete", params);
+ this.coClass.getInterfaceDefinition().addMethodDescriptor(method);
+
+ // OnCancelComplete
+ params = new JILocalParamsDescriptor();
+ params.addInParamAsType(Integer.class, JIFlags.FLAG_NULL);
+ params.addInParamAsType(Integer.class, JIFlags.FLAG_NULL);
+ method = new JILocalMethodDescriptor("OnCancelComplete", params);
+ this.coClass.getInterfaceDefinition().addMethodDescriptor(method);
+
+ // Add supported event interfaces
+ final List eventInterfaces = new LinkedList();
+ eventInterfaces.add(Constants.IOPCDataCallback_IID);
+ this.coClass.setSupportedEventInterfaces(eventInterfaces);
+
+ return this.coClass;
+ }
+
+ public IOPCDataCallback getCallback() {
+ return this.callback;
+ }
+
+ public void setCallback(final IOPCDataCallback callback) {
+ this.callback = callback;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCGroupStateMgt.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCGroupStateMgt.java
new file mode 100644
index 0000000..abf480d
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCGroupStateMgt.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.*;
+import org.jinterop.dcom.impls.JIObjectFactory;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.EventHandler;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.BaseCOMObject;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.Constants;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.IOPCDataCallback;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCGroupState;
+
+import java.net.UnknownHostException;
+
+/**
+ * Implementation of IOPCGroupStateMgt
+ *
+ * @author Jens Reimann jens.reimann@th4-systems.com
+ */
+public class OPCGroupStateMgt extends BaseCOMObject {
+ public OPCGroupStateMgt(final IJIComObject opcGroup) throws IllegalArgumentException, UnknownHostException, JIException {
+ super(opcGroup.queryInterface(Constants.IOPCGroupStateMgt_IID));
+ }
+
+ public OPCGroupState getState() throws JIException {
+ final JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(0);
+
+ callObject.addOutParamAsType(Integer.class, JIFlags.FLAG_NULL);
+ callObject.addOutParamAsType(Boolean.class, JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIPointer(new JIString(JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR)), JIFlags.FLAG_NULL);
+ callObject.addOutParamAsType(Integer.class, JIFlags.FLAG_NULL);
+ callObject.addOutParamAsType(Float.class, JIFlags.FLAG_NULL);
+ callObject.addOutParamAsType(Integer.class, JIFlags.FLAG_NULL);
+ callObject.addOutParamAsType(Integer.class, JIFlags.FLAG_NULL);
+ callObject.addOutParamAsType(Integer.class, JIFlags.FLAG_NULL);
+
+ final Object result[] = getCOMObject().call(callObject);
+
+ final OPCGroupState state = new OPCGroupState();
+ state.setUpdateRate((Integer) result[0]);
+ state.setActive((Boolean) result[1]);
+ state.setName(((JIString) ((JIPointer) result[2]).getReferent()).getString());
+ state.setTimeBias((Integer) result[3]);
+ state.setPercentDeadband((Float) result[4]);
+ state.setLocaleID((Integer) result[5]);
+ state.setClientHandle((Integer) result[6]);
+ state.setServerHandle((Integer) result[7]);
+
+ return state;
+ }
+
+ /**
+ * Set the group state Leaving any of the parameters null will keep the current value untouched.
+ *
+ * @param requestedUpdateRate the requested update rate
+ * @param active Flag if the group is active or not
+ * @param timeBias The time bias
+ * @param percentDeadband the deadband percent
+ * @param localeID the locale ID
+ * @param clientHandle the client handle
+ * @return the granted update rate
+ * @throws JIException JIException
+ */
+ public int setState(final Integer requestedUpdateRate, final Boolean active, final Integer timeBias, final Float percentDeadband, final Integer localeID, final Integer clientHandle) throws JIException {
+ final JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(1);
+
+ callObject.addInParamAsPointer(new JIPointer(requestedUpdateRate), JIFlags.FLAG_NULL);
+ if (active != null) {
+ callObject.addInParamAsPointer(new JIPointer(Integer.valueOf(active.booleanValue() ? 1 : 0)), JIFlags.FLAG_NULL);
+ } else {
+ callObject.addInParamAsPointer(new JIPointer(null), JIFlags.FLAG_NULL);
+ }
+ callObject.addInParamAsPointer(new JIPointer(timeBias), JIFlags.FLAG_NULL);
+ callObject.addInParamAsPointer(new JIPointer(percentDeadband), JIFlags.FLAG_NULL);
+ callObject.addInParamAsPointer(new JIPointer(localeID), JIFlags.FLAG_NULL);
+ callObject.addInParamAsPointer(new JIPointer(clientHandle), JIFlags.FLAG_NULL);
+
+ callObject.addOutParamAsType(Integer.class, JIFlags.FLAG_NULL);
+
+ final Object[] result = getCOMObject().call(callObject);
+
+ return (Integer) result[0];
+ }
+
+ public OPCItemMgt getItemManagement() throws JIException {
+ return new OPCItemMgt(getCOMObject());
+ }
+
+ /**
+ * Rename to group
+ *
+ * @param name the new name
+ * @throws JIException JIException
+ */
+ public void setName(final String name) throws JIException {
+ final JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(2);
+
+ callObject.addInParamAsString(name, JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR);
+
+ getCOMObject().call(callObject);
+ }
+
+ /**
+ * Clone the group
+ *
+ * @param name the name of the cloned group
+ * @return The cloned group
+ * @throws JIException JIException
+ * @throws UnknownHostException UnknownHostException
+ * @throws IllegalArgumentException IllegalArgumentException
+ */
+ public OPCGroupStateMgt clone(final String name) throws JIException, IllegalArgumentException, UnknownHostException {
+ final JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(3);
+
+ callObject.addInParamAsString(name, JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR);
+ callObject.addInParamAsUUID(Constants.IOPCGroupStateMgt_IID, JIFlags.FLAG_NULL);
+ callObject.addOutParamAsType(IJIComObject.class, JIFlags.FLAG_NULL);
+
+ final Object[] result = getCOMObject().call(callObject);
+ return new OPCGroupStateMgt((IJIComObject) result[0]);
+ }
+
+ /**
+ * Attach a new callback to the group
+ *
+ * @param callback The callback to attach
+ * @return The event handler information
+ * @throws JIException JIException
+ */
+ public EventHandler attach(final IOPCDataCallback callback) throws JIException {
+ final OPCDataCallback callbackObject = new OPCDataCallback();
+
+ callbackObject.setCallback(callback);
+
+ // sync the callback object so that no calls get through the callback
+ // until the callback information is set
+ // If happens in some cases that the callback is triggered before
+ // the method attachEventHandler returns.
+ synchronized (callbackObject) {
+ final String id = JIFrameworkHelper.attachEventHandler(getCOMObject(), Constants.IOPCDataCallback_IID, JIObjectFactory.buildObject(getCOMObject().getAssociatedSession(), callbackObject.getCoClass()));
+
+ callbackObject.setInfo(getCOMObject(), id);
+ }
+ return callbackObject;
+ }
+
+ public OPCAsyncIO2 getAsyncIO2() {
+ try {
+ return new OPCAsyncIO2(getCOMObject());
+ } catch (final Exception e) {
+ return null;
+ }
+ }
+
+ public OPCSyncIO getSyncIO() {
+ try {
+ return new OPCSyncIO(getCOMObject());
+ } catch (final Exception e) {
+ return null;
+ }
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCItemIO.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCItemIO.java
new file mode 100644
index 0000000..acb186b
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCItemIO.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.*;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.FILETIME;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.BaseCOMObject;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.Constants;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.IORequest;
+
+import java.net.UnknownHostException;
+
+public class OPCItemIO extends BaseCOMObject {
+ public OPCItemIO(final IJIComObject opcItemIO) throws IllegalArgumentException, UnknownHostException, JIException {
+ super(opcItemIO.queryInterface(Constants.IOPCItemIO_IID));
+ }
+
+ public void read(final IORequest[] requests) throws JIException {
+ if (requests.length == 0) {
+ return;
+ }
+
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(0);
+
+ JIString itemIDs[] = new JIString[requests.length];
+ Integer maxAges[] = new Integer[requests.length];
+ for (int i = 0; i < requests.length; i++) {
+ itemIDs[i] = new JIString(requests[i].getItemID(), JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR);
+ maxAges[i] = requests[i].getMaxAge();
+ }
+
+ callObject.addInParamAsInt(requests.length, JIFlags.FLAG_NULL);
+ callObject.addInParamAsArray(new JIArray(itemIDs, true), JIFlags.FLAG_NULL);
+ callObject.addInParamAsArray(new JIArray(maxAges, true), JIFlags.FLAG_NULL);
+
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(JIVariant.class, null, 1, true)), JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(Integer.class, null, 1, true)), JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(FILETIME.getStruct(), null, 1, true)), JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(Integer.class, null, 1, true)), JIFlags.FLAG_NULL);
+
+ getCOMObject().call(callObject);
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCItemMgt.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCItemMgt.java
new file mode 100644
index 0000000..6383ccb
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCItemMgt.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.*;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.KeyedResult;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.KeyedResultSet;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Result;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.ResultSet;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.BaseCOMObject;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.Helper;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.Constants;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCITEMDEF;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCITEMRESULT;
+
+public class OPCItemMgt extends BaseCOMObject {
+ public OPCItemMgt(final IJIComObject opcGroup) throws JIException {
+ super(opcGroup.queryInterface(Constants.IOPCItemMgt_IID));
+ }
+
+ public KeyedResultSet validate(final OPCITEMDEF... items) throws JIException {
+ if (items.length == 0) {
+ return new KeyedResultSet();
+ }
+
+ final JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(1);
+
+ final JIStruct struct[] = new JIStruct[items.length];
+ for (int i = 0; i < items.length; i++) {
+ struct[i] = items[i].toStruct();
+ }
+ final JIArray itemArray = new JIArray(struct, true);
+
+ callObject.addInParamAsInt(items.length, JIFlags.FLAG_NULL);
+ callObject.addInParamAsArray(itemArray, JIFlags.FLAG_NULL);
+ callObject.addInParamAsInt(0, JIFlags.FLAG_NULL); // don't update blobs
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(OPCITEMRESULT.getStruct(), null, 1, true)), JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(Integer.class, null, 1, true)), JIFlags.FLAG_NULL);
+
+ final Object result[] = Helper.callRespectSFALSE(getCOMObject(), callObject);
+
+ final JIStruct[] results = (JIStruct[]) ((JIArray) ((JIPointer) result[0]).getReferent()).getArrayInstance();
+ final Integer[] errorCodes = (Integer[]) ((JIArray) ((JIPointer) result[1]).getReferent()).getArrayInstance();
+
+ final KeyedResultSet resultList = new KeyedResultSet(items.length);
+ for (int i = 0; i < items.length; i++) {
+ final OPCITEMRESULT itemResult = OPCITEMRESULT.fromStruct(results[i]);
+ final KeyedResult resultEntry = new KeyedResult(items[i], itemResult, errorCodes[i]);
+ resultList.add(resultEntry);
+ }
+
+ return resultList;
+ }
+
+ public KeyedResultSet add(final OPCITEMDEF... items) throws JIException {
+ if (items.length == 0) {
+ return new KeyedResultSet();
+ }
+
+ final JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(0);
+
+ final JIStruct struct[] = new JIStruct[items.length];
+ for (int i = 0; i < items.length; i++) {
+ struct[i] = items[i].toStruct();
+ }
+ final JIArray itemArray = new JIArray(struct, true);
+
+ callObject.addInParamAsInt(items.length, JIFlags.FLAG_NULL);
+ callObject.addInParamAsArray(itemArray, JIFlags.FLAG_NULL);
+
+ /*
+ callObject.addOutParamAsObject ( new JIPointer ( new JIArray ( OPCITEMRESULT.getStruct (), null, 1, true ) ),
+ JIFlags.FLAG_NULL );
+ callObject.addOutParamAsObject ( new JIPointer ( new JIArray ( Integer.class, null, 1, true ) ),
+ JIFlags.FLAG_NULL );
+ */
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(OPCITEMRESULT.getStruct(), null, 1, true)), JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(Integer.class, null, 1, true)), JIFlags.FLAG_NULL);
+
+ final Object result[] = Helper.callRespectSFALSE(getCOMObject(), callObject);
+
+ final JIStruct[] results = (JIStruct[]) ((JIArray) ((JIPointer) result[0]).getReferent()).getArrayInstance();
+ final Integer[] errorCodes = (Integer[]) ((JIArray) ((JIPointer) result[1]).getReferent()).getArrayInstance();
+
+ final KeyedResultSet resultList = new KeyedResultSet(items.length);
+ for (int i = 0; i < items.length; i++) {
+ final OPCITEMRESULT itemResult = OPCITEMRESULT.fromStruct(results[i]);
+ final KeyedResult resultEntry = new KeyedResult(items[i], itemResult, errorCodes[i]);
+ resultList.add(resultEntry);
+ }
+
+ return resultList;
+ }
+
+ public ResultSet remove(final Integer... serverHandles) throws JIException {
+ if (serverHandles.length == 0) {
+ return new ResultSet();
+ }
+
+ final JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(2);
+
+ callObject.addInParamAsInt(serverHandles.length, JIFlags.FLAG_NULL);
+ callObject.addInParamAsArray(new JIArray(serverHandles, true), JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(Integer.class, null, 1, true)), JIFlags.FLAG_NULL);
+
+ final Object result[] = Helper.callRespectSFALSE(getCOMObject(), callObject);
+
+ final Integer[] errorCodes = (Integer[]) ((JIArray) ((JIPointer) result[0]).getReferent()).getArrayInstance();
+ final ResultSet results = new ResultSet(serverHandles.length);
+ for (int i = 0; i < serverHandles.length; i++) {
+ results.add(new Result(serverHandles[i], errorCodes[i]));
+ }
+ return results;
+ }
+
+ public ResultSet setActiveState(final boolean state, final Integer... items) throws JIException {
+ if (items.length == 0) {
+ return new ResultSet();
+ }
+
+ final JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(3);
+
+ callObject.addInParamAsInt(items.length, JIFlags.FLAG_NULL);
+ callObject.addInParamAsArray(new JIArray(items, true), JIFlags.FLAG_NULL);
+ callObject.addInParamAsInt(state ? 1 : 0, JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(Integer.class, null, 1, true)), JIFlags.FLAG_NULL);
+
+ final Object[] result = Helper.callRespectSFALSE(getCOMObject(), callObject);
+
+ final Integer[] errorCodes = (Integer[]) ((JIArray) ((JIPointer) result[0]).getReferent()).getArrayInstance();
+ final ResultSet results = new ResultSet(items.length);
+ for (int i = 0; i < items.length; i++) {
+ results.add(new Result(items[i], errorCodes[i]));
+ }
+ return results;
+ }
+
+ public ResultSet setClientHandles(final Integer[] serverHandles, final Integer[] clientHandles) throws JIException {
+ if (serverHandles.length != clientHandles.length) {
+ throw new JIException(0, "Array sizes don't match");
+ }
+ if (serverHandles.length == 0) {
+ return new ResultSet();
+ }
+
+ final JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(4);
+
+ callObject.addInParamAsInt(serverHandles.length, JIFlags.FLAG_NULL);
+ callObject.addInParamAsArray(new JIArray(serverHandles, true), JIFlags.FLAG_NULL);
+ callObject.addInParamAsArray(new JIArray(clientHandles, true), JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(Integer.class, null, 1, true)), JIFlags.FLAG_NULL);
+
+ final Object[] result = Helper.callRespectSFALSE(getCOMObject(), callObject);
+
+ final Integer[] errorCodes = (Integer[]) ((JIArray) ((JIPointer) result[0]).getReferent()).getArrayInstance();
+ final ResultSet results = new ResultSet(serverHandles.length);
+ for (int i = 0; i < serverHandles.length; i++) {
+ results.add(new Result(serverHandles[i], errorCodes[i]));
+ }
+ return results;
+ }
+
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCItemProperties.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCItemProperties.java
new file mode 100644
index 0000000..0d44790
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCItemProperties.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.*;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.KeyedResult;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.KeyedResultSet;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.BaseCOMObject;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.Helper;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.Constants;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.PropertyDescription;
+
+import java.net.UnknownHostException;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+public class OPCItemProperties extends BaseCOMObject {
+ public OPCItemProperties(final IJIComObject opcItemProperties) throws IllegalArgumentException, UnknownHostException, JIException {
+ super(opcItemProperties.queryInterface(Constants.IOPCItemProperties_IID));
+ }
+
+ public Collection queryAvailableProperties(final String itemID) throws JIException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(0);
+
+ callObject.addInParamAsString(itemID, JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR);
+
+ callObject.addOutParamAsType(Integer.class, JIFlags.FLAG_NULL);
+
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(Integer.class, null, 1, true)), JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(new JIString(JIFlags.FLAG_REPRESENTATION_STRING_BSTR), null, 1, true)), JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(Short.class, null, 1, true)), JIFlags.FLAG_NULL);
+
+ Object result[] = getCOMObject().call(callObject);
+
+ List properties = new LinkedList();
+
+ int len = (Integer) result[0];
+ Integer[] ids = (Integer[]) ((JIArray) ((JIPointer) result[1]).getReferent()).getArrayInstance();
+ JIString[] descriptions = (JIString[]) ((JIArray) ((JIPointer) result[2]).getReferent()).getArrayInstance();
+ Short[] variableTypes = (Short[]) ((JIArray) ((JIPointer) result[3]).getReferent()).getArrayInstance();
+
+ for (int i = 0; i < len; i++) {
+ PropertyDescription pd = new PropertyDescription();
+ pd.setId(ids[i]);
+ pd.setDescription(descriptions[i].getString());
+ pd.setVarType(variableTypes[i]);
+ properties.add(pd);
+ }
+ return properties;
+ }
+
+ public KeyedResultSet getItemProperties(final String itemID, final int... properties) throws JIException {
+ if (properties.length == 0) {
+ return new KeyedResultSet();
+ }
+
+ Integer[] ids = new Integer[properties.length];
+ for (int i = 0; i < properties.length; i++) {
+ ids[i] = properties[i];
+ }
+
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(1);
+
+ callObject.addInParamAsString(itemID, JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR);
+ callObject.addInParamAsInt(properties.length, JIFlags.FLAG_NULL);
+ callObject.addInParamAsArray(new JIArray(ids, true), JIFlags.FLAG_NULL);
+
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(JIVariant.class, null, 1, true)), JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(Integer.class, null, 1, true)), JIFlags.FLAG_NULL);
+
+ Object result[] = Helper.callRespectSFALSE(getCOMObject(), callObject);
+
+ JIVariant[] values = (JIVariant[]) ((JIArray) ((JIPointer) result[0]).getReferent()).getArrayInstance();
+ Integer[] errorCodes = (Integer[]) ((JIArray) ((JIPointer) result[1]).getReferent()).getArrayInstance();
+
+ KeyedResultSet results = new KeyedResultSet();
+ for (int i = 0; i < properties.length; i++) {
+ results.add(new KeyedResult(properties[i], values[i], errorCodes[i]));
+ }
+ return results;
+ }
+
+ public KeyedResultSet lookupItemIDs(final String itemID, final int... properties) throws JIException {
+ if (properties.length == 0) {
+ return new KeyedResultSet();
+ }
+
+ Integer[] ids = new Integer[properties.length];
+ for (int i = 0; i < properties.length; i++) {
+ ids[i] = properties[i];
+ }
+
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(2);
+
+ callObject.addInParamAsString(itemID, JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR);
+ callObject.addInParamAsInt(properties.length, JIFlags.FLAG_NULL);
+ callObject.addInParamAsArray(new JIArray(ids, true), JIFlags.FLAG_NULL);
+
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(new JIPointer(new JIString(JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR)), null, 1, true)), JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(Integer.class, null, 1, true)), JIFlags.FLAG_NULL);
+
+ Object result[] = Helper.callRespectSFALSE(getCOMObject(), callObject);
+
+ JIPointer[] itemIDs = (JIPointer[]) ((JIArray) ((JIPointer) result[0]).getReferent()).getArrayInstance();
+ Integer[] errorCodes = (Integer[]) ((JIArray) ((JIPointer) result[1]).getReferent()).getArrayInstance();
+
+ KeyedResultSet results = new KeyedResultSet();
+
+ for (int i = 0; i < properties.length; i++) {
+ results.add(new KeyedResult(properties[i], ((JIString) itemIDs[i].getReferent()).getString(), errorCodes[i]));
+ }
+ return results;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCServer.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCServer.java
new file mode 100644
index 0000000..74cf031
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCServer.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.*;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.BaseCOMObject;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.EnumString;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.Helper;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.OPCCommon;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.Constants;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCENUMSCOPE;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCSERVERSTATUS;
+
+import java.net.UnknownHostException;
+
+public class OPCServer extends BaseCOMObject {
+ public OPCServer(final IJIComObject opcServer) throws IllegalArgumentException, UnknownHostException, JIException {
+ super(opcServer.queryInterface(Constants.IOPCServer_IID));
+ }
+
+ /**
+ * Retrieve the current server status
+ *
+ * @return the current server status
+ * @throws JIException JIException
+ */
+ public OPCSERVERSTATUS getStatus() throws JIException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(3);
+
+ callObject.addOutParamAsObject(new JIPointer(OPCSERVERSTATUS.getStruct()), JIFlags.FLAG_NULL);
+
+ Object[] result = getCOMObject().call(callObject);
+
+ return OPCSERVERSTATUS.fromStruct((JIStruct) ((JIPointer) result[0]).getReferent());
+ }
+
+ public OPCGroupStateMgt addGroup(final String name, final boolean active, final int updateRate, final int clientHandle, final Integer timeBias, final Float percentDeadband, final int localeID) throws JIException, IllegalArgumentException, UnknownHostException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(0);
+
+ callObject.addInParamAsString(name, JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR);
+ callObject.addInParamAsInt(active ? 1 : 0, JIFlags.FLAG_NULL);
+ callObject.addInParamAsInt(updateRate, JIFlags.FLAG_NULL);
+ callObject.addInParamAsInt(clientHandle, JIFlags.FLAG_NULL);
+ callObject.addInParamAsPointer(new JIPointer(timeBias), JIFlags.FLAG_NULL);
+ callObject.addInParamAsPointer(new JIPointer(percentDeadband), JIFlags.FLAG_NULL);
+ callObject.addInParamAsInt(localeID, JIFlags.FLAG_NULL);
+ callObject.addOutParamAsType(Integer.class, JIFlags.FLAG_NULL);
+ callObject.addOutParamAsType(Integer.class, JIFlags.FLAG_NULL);
+ callObject.addInParamAsUUID(Constants.IOPCGroupStateMgt_IID, JIFlags.FLAG_NULL);
+ callObject.addOutParamAsType(IJIComObject.class, JIFlags.FLAG_NULL);
+
+ Object[] result = getCOMObject().call(callObject);
+
+ return new OPCGroupStateMgt((IJIComObject) result[2]);
+ }
+
+ public void removeGroup(final int serverHandle, final boolean force) throws JIException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(4);
+
+ callObject.addInParamAsInt(serverHandle, JIFlags.FLAG_NULL);
+ callObject.addInParamAsInt(force ? 1 : 0, JIFlags.FLAG_NULL);
+
+ getCOMObject().call(callObject);
+ }
+
+ public void removeGroup(final OPCGroupStateMgt group, final boolean force) throws JIException {
+ removeGroup(group.getState().getServerHandle(), force);
+ }
+
+ public OPCGroupStateMgt getGroupByName(final String name) throws JIException, IllegalArgumentException, UnknownHostException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(2);
+
+ callObject.addInParamAsString(name, JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR);
+ callObject.addInParamAsUUID(Constants.IOPCGroupStateMgt_IID, JIFlags.FLAG_NULL);
+ callObject.addOutParamAsType(IJIComObject.class, JIFlags.FLAG_NULL);
+
+ Object[] result = getCOMObject().call(callObject);
+
+ return new OPCGroupStateMgt((IJIComObject) result[0]);
+ }
+
+ /**
+ * Get the groups
+ *
+ * @param scope The scope to get
+ * @return A string enumerator with the groups
+ * @throws JIException JIException
+ * @throws IllegalArgumentException IllegalArgumentException
+ * @throws UnknownHostException UnknownHostException
+ */
+ public EnumString getGroups(final OPCENUMSCOPE scope) throws JIException, IllegalArgumentException, UnknownHostException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(5);
+
+ callObject.addInParamAsShort((short) scope.id(), JIFlags.FLAG_NULL);
+ callObject.addInParamAsUUID(org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Constants.IEnumString_IID, JIFlags.FLAG_NULL);
+ callObject.addOutParamAsType(IJIComObject.class, JIFlags.FLAG_NULL);
+
+ Object[] result = Helper.callRespectSFALSE(getCOMObject(), callObject);
+
+ return new EnumString((IJIComObject) result[0]);
+ }
+
+ public OPCItemProperties getItemPropertiesService() {
+ try {
+ return new OPCItemProperties(getCOMObject());
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ public OPCItemIO getItemIOService() {
+ try {
+ return new OPCItemIO(getCOMObject());
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Get the browser object (IOPCBrowseServerAddressSpace) from the server instance
+ *
+ * @return the browser object
+ */
+ public OPCBrowseServerAddressSpace getBrowser() {
+ try {
+ return new OPCBrowseServerAddressSpace(getCOMObject());
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Get the common interface if supported
+ *
+ * @return the common interface or null if it is not supported
+ */
+ public OPCCommon getCommon() {
+ try {
+ return new OPCCommon(getCOMObject());
+ } catch (Exception e) {
+ return null;
+ }
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCSyncIO.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCSyncIO.java
new file mode 100644
index 0000000..2e669d5
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/da/impl/OPCSyncIO.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.*;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.KeyedResult;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.KeyedResultSet;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Result;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.ResultSet;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.BaseCOMObject;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.Helper;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.Constants;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCDATASOURCE;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCITEMSTATE;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.WriteRequest;
+
+public class OPCSyncIO extends BaseCOMObject {
+ public OPCSyncIO(final IJIComObject opcSyncIO) throws JIException {
+ super(opcSyncIO.queryInterface(Constants.IOPCSyncIO_IID));
+ }
+
+ public KeyedResultSet read(final OPCDATASOURCE source, final Integer... serverHandles) throws JIException {
+ if (serverHandles == null || serverHandles.length == 0) {
+ return new KeyedResultSet();
+ }
+
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(0);
+
+ callObject.addInParamAsShort((short) source.id(), JIFlags.FLAG_NULL);
+ callObject.addInParamAsInt(serverHandles.length, JIFlags.FLAG_NULL);
+ callObject.addInParamAsArray(new JIArray(serverHandles, true), JIFlags.FLAG_NULL);
+
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(OPCITEMSTATE.getStruct(), null, 1, true)), JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(Integer.class, null, 1, true)), JIFlags.FLAG_NULL);
+
+ Object result[] = Helper.callRespectSFALSE(getCOMObject(), callObject);
+
+ KeyedResultSet results = new KeyedResultSet();
+ JIStruct[] states = (JIStruct[]) ((JIArray) ((JIPointer) result[0]).getReferent()).getArrayInstance();
+ Integer[] errorCodes = (Integer[]) ((JIArray) ((JIPointer) result[1]).getReferent()).getArrayInstance();
+
+ for (int i = 0; i < serverHandles.length; i++) {
+ results.add(new KeyedResult(serverHandles[i], OPCITEMSTATE.fromStruct(states[i]), errorCodes[i]));
+ }
+
+ return results;
+ }
+
+ public ResultSet write(final WriteRequest... requests) throws JIException {
+ if (requests.length == 0) {
+ return new ResultSet();
+ }
+
+ Integer[] items = new Integer[requests.length];
+ JIVariant[] values = new JIVariant[requests.length];
+ for (int i = 0; i < requests.length; i++) {
+ items[i] = requests[i].getServerHandle();
+ values[i] = Helper.fixVariant(requests[i].getValue());
+ }
+
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(1);
+
+ callObject.addInParamAsInt(requests.length, JIFlags.FLAG_NULL);
+ callObject.addInParamAsArray(new JIArray(items, true), JIFlags.FLAG_NULL);
+ callObject.addInParamAsArray(new JIArray(values, true), JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIPointer(new JIArray(Integer.class, null, 1, true)), JIFlags.FLAG_NULL);
+
+ Object result[] = Helper.callRespectSFALSE(getCOMObject(), callObject);
+
+ Integer[] errorCodes = (Integer[]) ((JIArray) ((JIPointer) result[0]).getReferent()).getArrayInstance();
+
+ ResultSet results = new ResultSet();
+ for (int i = 0; i < requests.length; i++) {
+ results.add(new Result(requests[i], errorCodes[i]));
+ }
+ return results;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/list/ClassDetails.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/list/ClassDetails.java
new file mode 100644
index 0000000..8211dfa
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/list/ClassDetails.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.list;
+
+/**
+ * Details about an OPC server class
+ *
+ * @author Jens Reimann <jens.reimann@th4-systems.com>
+ * @since 0.1.8
+ */
+public class ClassDetails {
+ private String _clsId;
+
+ private String _progId;
+
+ private String _description;
+
+ public String getClsId() {
+ return this._clsId;
+ }
+
+ public void setClsId(final String clsId) {
+ this._clsId = clsId;
+ }
+
+ public String getDescription() {
+ return this._description;
+ }
+
+ public void setDescription(final String description) {
+ this._description = description;
+ }
+
+ public String getProgId() {
+ return this._progId;
+ }
+
+ public void setProgId(final String progId) {
+ this._progId = progId;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/list/Constants.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/list/Constants.java
new file mode 100644
index 0000000..fbba66e
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/list/Constants.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.list;
+
+public interface Constants extends org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Constants {
+ public static final String IOPCServerList_IID = "13486D50-4821-11D2-A494-3CB306C10000";
+
+ public static final String OPCServerList_CLSID = "13486D51-4821-11D2-A494-3CB306C10000";
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/list/impl/OPCServerList.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/list/impl/OPCServerList.java
new file mode 100644
index 0000000..b11e616
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/dcom/list/impl/OPCServerList.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.list.impl;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.*;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.BaseCOMObject;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.EnumGUID;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.Helper;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.list.ClassDetails;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.list.Constants;
+import rpc.core.UUID;
+
+import java.net.UnknownHostException;
+
+/**
+ * This class implements the IOPCServerList (aka OPCEnum) service.
+ *
+ * @author Jens Reimann <jens.reimann@th4-systems.com>
+ */
+public class OPCServerList extends BaseCOMObject {
+ public OPCServerList(final IJIComObject listObject) throws JIException {
+ super(listObject.queryInterface(Constants.IOPCServerList_IID));
+ }
+
+ public JIClsid getCLSIDFromProgID(final String progId) throws JIException {
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(2);
+
+ callObject.addInParamAsString(progId, JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR);
+ callObject.addOutParamAsType(UUID.class, JIFlags.FLAG_NULL);
+
+ try {
+ Object[] result = getCOMObject().call(callObject);
+ return JIClsid.valueOf(((UUID) result[0]).toString());
+ } catch (JIException e) {
+ if (e.getErrorCode() == 0x800401F3) {
+ return null;
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Return details about a serve class
+ *
+ * @param clsId A server class
+ * @return ClassDetails
+ * @throws JIException JIException
+ */
+ public ClassDetails getClassDetails(final JIClsid clsId) throws JIException {
+ if (clsId == null) {
+ return null;
+ }
+
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(1);
+
+ callObject.addInParamAsUUID(clsId.getCLSID(), JIFlags.FLAG_NULL);
+
+ callObject.addOutParamAsObject(new JIPointer(new JIString(JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR)), JIFlags.FLAG_NULL);
+ callObject.addOutParamAsObject(new JIPointer(new JIString(JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR)), JIFlags.FLAG_NULL);
+
+ Object[] result = Helper.callRespectSFALSE(getCOMObject(), callObject);
+
+ ClassDetails cd = new ClassDetails();
+ cd.setClsId(clsId.getCLSID());
+ cd.setProgId(((JIString) ((JIPointer) result[0]).getReferent()).getString());
+ cd.setDescription(((JIString) ((JIPointer) result[1]).getReferent()).getString());
+
+ return cd;
+ }
+
+ /*
+ HRESULT EnumClassesOfCategories(
+ [in] ULONG cImplemented,
+ [in,size_is(cImplemented)] CATID rgcatidImpl[],
+ [in] ULONG cRequired,
+ [in,size_is(cRequired)] CATID rgcatidReq[],
+ [out] IEnumGUID ** ppenumClsid
+ );
+ */
+
+ public EnumGUID enumClassesOfCategories(final String[] implemented, final String[] required) throws IllegalArgumentException, UnknownHostException, JIException {
+ UUID[] u1 = new UUID[implemented.length];
+ UUID[] u2 = new UUID[required.length];
+
+ for (int i = 0; i < implemented.length; i++) {
+ u1[i] = new UUID(implemented[i]);
+ }
+
+ for (int i = 0; i < required.length; i++) {
+ u2[i] = new UUID(required[i]);
+ }
+
+ return enumClassesOfCategories(u1, u2);
+ }
+
+ public EnumGUID enumClassesOfCategories(final UUID[] implemented, final UUID[] required) throws IllegalArgumentException, UnknownHostException, JIException {
+ // ** CALL
+ JICallBuilder callObject = new JICallBuilder(true);
+ callObject.setOpnum(0);
+
+ // ** IN
+ callObject.addInParamAsInt(implemented.length, JIFlags.FLAG_NULL);
+ if (implemented.length == 0) {
+ callObject.addInParamAsPointer(new JIPointer(null), JIFlags.FLAG_NULL);
+ } else {
+ callObject.addInParamAsArray(new JIArray(implemented, true), JIFlags.FLAG_NULL);
+ }
+
+ callObject.addInParamAsInt(required.length, JIFlags.FLAG_NULL);
+ if (required.length == 0) {
+ callObject.addInParamAsPointer(new JIPointer(null), JIFlags.FLAG_NULL);
+ } else {
+ callObject.addInParamAsArray(new JIArray(required, true), JIFlags.FLAG_NULL);
+ }
+
+ // ** OUT
+ callObject.addOutParamAsType(IJIComObject.class, JIFlags.FLAG_NULL);
+
+ // ** RESULT
+ Object result[] = Helper.callRespectSFALSE(getCOMObject(), callObject);
+
+ return new EnumGUID((IJIComObject) result[0]);
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/common/AlreadyConnectedException.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/common/AlreadyConnectedException.java
new file mode 100644
index 0000000..c88b2c1
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/common/AlreadyConnectedException.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.common;
+
+public class AlreadyConnectedException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/common/ConnectionInformation.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/common/ConnectionInformation.java
new file mode 100644
index 0000000..ca1201c
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/common/ConnectionInformation.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.common;
+
+/**
+ * Holds the connection information
+ *
+ * @author Jens Reimann jens.reimann@th4-systems.com
+ *
+ * If both clsId and progId are set then clsId
+ * has priority!
+ */
+public class ConnectionInformation {
+ private String _host = "localhost";
+
+ private String _domain = "localhost";
+
+ private String _user = "";
+
+ private String _password = "";
+
+ private String _clsid = null;
+
+ private String _progId = null;
+
+ public ConnectionInformation() {
+ super();
+ }
+
+ public ConnectionInformation(String host, String clsid, String user, String password) {
+ this._host = host;
+ this._clsid = clsid;
+ this._user = user;
+ this._password = password;
+ }
+
+ public ConnectionInformation(final String user, final String password) {
+ super();
+ this._user = user;
+ this._password = password;
+ }
+
+ public ConnectionInformation(final ConnectionInformation arg0) {
+ super();
+ this._user = arg0._user;
+ this._password = arg0._password;
+ this._domain = arg0._domain;
+ this._host = arg0._host;
+ this._progId = arg0._progId;
+ this._clsid = arg0._clsid;
+ }
+
+ public String getDomain() {
+ return this._domain;
+ }
+
+ /**
+ * Set the domain of the user used for logging on
+ *
+ * @param domain Domain
+ */
+ public void setDomain(final String domain) {
+ this._domain = domain;
+ }
+
+ public String getHost() {
+ return this._host;
+ }
+
+ /**
+ * Set the host on which the server is located
+ *
+ * @param host The host to use, either an IP address oder hostname
+ */
+ public void setHost(final String host) {
+ this._host = host;
+ }
+
+ public String getPassword() {
+ return this._password;
+ }
+
+ public void setPassword(final String password) {
+ this._password = password;
+ }
+
+ public String getUser() {
+ return this._user;
+ }
+
+ public void setUser(final String user) {
+ this._user = user;
+ }
+
+ public String getClsid() {
+ return this._clsid;
+ }
+
+ public void setClsid(final String clsid) {
+ this._clsid = clsid;
+ }
+
+ public String getProgId() {
+ return this._progId;
+ }
+
+ public void setProgId(final String progId) {
+ this._progId = progId;
+ }
+
+ public String getClsOrProgId() {
+ if (this._clsid != null) {
+ return this._clsid;
+ } else if (this._progId != null) {
+ return this._progId;
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/common/NotConnectedException.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/common/NotConnectedException.java
new file mode 100644
index 0000000..ac0090f
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/common/NotConnectedException.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.common;
+
+public class NotConnectedException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/AccessBase.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/AccessBase.java
new file mode 100644
index 0000000..44fb7c0
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/AccessBase.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jinterop.dcom.common.JIException;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.common.NotConnectedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+@Slf4j
+public abstract class AccessBase implements ServerConnectionStateListener {
+
+ private final List stateListeners = new CopyOnWriteArrayList();
+ protected Server server = null;
+ protected Group group = null;
+ protected boolean active = false;
+ /**
+ * Holds the item to callback assignment
+ */
+ protected Map- items = new HashMap
- ();
+ protected Map itemMap = new HashMap();
+ protected Map
- itemCache = new HashMap
- ();
+ protected Map itemSet = new HashMap();
+ protected String logTag = null;
+ protected Logger dataLogger = null;
+ private boolean bound = false;
+ private int period = 0;
+
+ public AccessBase(final Server server, final int period) throws IllegalArgumentException, UnknownHostException, NotConnectedException, JIException, DuplicateGroupException {
+ super();
+ this.server = server;
+ this.period = period;
+ }
+
+ public AccessBase(final Server server, final int period, final String logTag) {
+ super();
+ this.server = server;
+ this.period = period;
+ this.logTag = logTag;
+ if (this.logTag != null) {
+ this.dataLogger = LoggerFactory.getLogger("opc.data." + logTag);
+ }
+ }
+
+ public boolean isBound() {
+ return this.bound;
+ }
+
+ public synchronized void bind() {
+ if (isBound()) {
+ return;
+ }
+
+ this.server.addStateListener(this);
+ this.bound = true;
+ }
+
+ public synchronized void unbind() throws JIException {
+ if (!isBound()) {
+ return;
+ }
+
+ this.server.removeStateListener(this);
+ this.bound = false;
+
+ stop();
+ }
+
+ public boolean isActive() {
+ return this.active;
+ }
+
+ public void addStateListener(final AccessStateListener listener) {
+ this.stateListeners.add(listener);
+ listener.stateChanged(isActive());
+ }
+
+ public void removeStateListener(final AccessStateListener listener) {
+ this.stateListeners.remove(listener);
+ }
+
+ protected void notifyStateListenersState(final boolean state) {
+ final List list = new ArrayList(this.stateListeners);
+
+ for (final AccessStateListener listener : list) {
+ listener.stateChanged(state);
+ }
+ }
+
+ protected void notifyStateListenersError(final Throwable t) {
+ final List list = new ArrayList(this.stateListeners);
+
+ for (final AccessStateListener listener : list) {
+ listener.errorOccured(t);
+ }
+ }
+
+ public int getPeriod() {
+ return this.period;
+ }
+
+ public synchronized void addItem(final String itemId, final DataCallback dataCallback) throws JIException, AddFailedException {
+ if (this.itemSet.containsKey(itemId)) {
+ return;
+ }
+
+ this.itemSet.put(itemId, dataCallback);
+
+ if (isActive()) {
+ realizeItem(itemId);
+ }
+ }
+
+ public synchronized void removeItem(final String itemId) {
+ if (!this.itemSet.containsKey(itemId)) {
+ return;
+ }
+
+ this.itemSet.remove(itemId);
+
+ if (isActive()) {
+ unrealizeItem(itemId);
+ }
+ }
+
+ public void connectionStateChanged(final boolean connected) {
+ try {
+ if (connected) {
+ start();
+ } else {
+ stop();
+ }
+ } catch (final Exception e) {
+ log.error(String.format("Failed to change state (%s)", connected), e);
+ }
+ }
+
+ protected synchronized void start() throws JIException, IllegalArgumentException, UnknownHostException, NotConnectedException, DuplicateGroupException {
+ if (isActive()) {
+ return;
+ }
+
+ log.debug("Create a new group");
+ this.group = this.server.addGroup();
+ this.group.setActive(true);
+ this.active = true;
+
+ notifyStateListenersState(true);
+
+ realizeAll();
+ }
+
+ protected void realizeItem(final String itemId) throws JIException, AddFailedException {
+ log.debug("Realizing item: {}", itemId);
+
+ final DataCallback dataCallback = this.itemSet.get(itemId);
+ if (dataCallback == null) {
+ return;
+ }
+
+ final Item item = this.group.addItem(itemId);
+ this.items.put(item, dataCallback);
+ this.itemMap.put(itemId, item);
+ }
+
+ protected void unrealizeItem(final String itemId) {
+ final Item item = this.itemMap.remove(itemId);
+ this.items.remove(item);
+ this.itemCache.remove(item);
+
+ try {
+ this.group.removeItem(itemId);
+ } catch (final Throwable e) {
+ log.error(String.format("Failed to unrealize item '%s'", itemId), e);
+ }
+ }
+
+ /*
+ * FIXME: need some perfomance boost: subscribe all in one call
+ */
+ protected void realizeAll() {
+ for (final String itemId : this.itemSet.keySet()) {
+ try {
+ realizeItem(itemId);
+ } catch (final AddFailedException e) {
+ Integer rc = e.getErrors().get(itemId);
+ if (rc == null) {
+ rc = -1;
+ }
+ log.warn(String.format("Failed to add item: %s (%08X)", itemId, rc));
+
+ } catch (final Exception e) {
+ log.warn("Failed to realize item: " + itemId, e);
+ }
+ }
+ }
+
+ protected void unrealizeAll() {
+ this.items.clear();
+ this.itemCache.clear();
+ try {
+ this.group.clear();
+ } catch (final JIException e) {
+ log.info("Failed to clear group. No problem if we already lost the connection", e);
+ }
+ }
+
+ protected synchronized void stop() throws JIException {
+ if (!isActive()) {
+ return;
+ }
+
+ unrealizeAll();
+
+ this.active = false;
+ notifyStateListenersState(false);
+
+ try {
+ this.group.remove();
+ } catch (final Throwable t) {
+ log.warn("Failed to disable group. No problem if we already lost connection");
+ }
+ this.group = null;
+ }
+
+ public synchronized void clear() {
+ this.itemSet.clear();
+ this.items.clear();
+ this.itemMap.clear();
+ this.itemCache.clear();
+ }
+
+ protected void updateItem(final Item item, final ItemState itemState) {
+ if (this.dataLogger != null) {
+ this.dataLogger.debug("Update item: {}, {}", item.getId(), itemState);
+ }
+
+ final DataCallback dataCallback = this.items.get(item);
+ if (dataCallback == null) {
+ return;
+ }
+
+ final ItemState cachedState = this.itemCache.get(item);
+ if (cachedState == null) {
+ this.itemCache.put(item, itemState);
+ dataCallback.changed(item, itemState);
+ } else {
+ if (!cachedState.equals(itemState)) {
+ this.itemCache.put(item, itemState);
+ dataCallback.changed(item, itemState);
+ }
+ }
+ }
+
+ protected void handleError(final Throwable e) {
+ notifyStateListenersError(e);
+ this.server.dispose();
+ }
+
+}
\ No newline at end of file
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/AccessStateListener.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/AccessStateListener.java
new file mode 100644
index 0000000..622eeb7
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/AccessStateListener.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+public interface AccessStateListener {
+ public abstract void stateChanged(boolean state);
+
+ public abstract void errorOccured(Throwable t);
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/AddFailedException.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/AddFailedException.java
new file mode 100644
index 0000000..29894c4
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/AddFailedException.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class AddFailedException extends Exception {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ private Map _errors = new HashMap();
+
+ private Map _items = new HashMap();
+
+ public AddFailedException(final Map errors, final Map items) {
+ super();
+ this._errors = errors;
+ this._items = items;
+ }
+
+ /**
+ * Get the map of item id to error code
+ *
+ * @return the result map containing the failed items
+ */
+ public Map getErrors() {
+ return this._errors;
+ }
+
+ /**
+ * Get the map of item it to item object
+ *
+ * @return the result map containing the succeeded items
+ */
+ public Map getItems() {
+ return this._items;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/Async20Access.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/Async20Access.java
new file mode 100644
index 0000000..5df8b10
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/Async20Access.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jinterop.dcom.common.JIException;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.EventHandler;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.KeyedResult;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.KeyedResultSet;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.ResultSet;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.IOPCDataCallback;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCDATASOURCE;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.ValueData;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl.OPCAsyncIO2;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.common.NotConnectedException;
+
+import java.net.UnknownHostException;
+
+@Slf4j
+public class Async20Access extends AccessBase implements IOPCDataCallback {
+
+ private EventHandler eventHandler = null;
+
+ private boolean initialRefresh = false;
+
+ public Async20Access(final Server server, final int period, final boolean initialRefresh) throws IllegalArgumentException, UnknownHostException, NotConnectedException, JIException, DuplicateGroupException {
+ super(server, period);
+ this.initialRefresh = initialRefresh;
+ }
+
+ public Async20Access(final Server server, final int period, final boolean initialRefresh, final String logTag) throws IllegalArgumentException, UnknownHostException, NotConnectedException, JIException, DuplicateGroupException {
+ super(server, period, logTag);
+ this.initialRefresh = initialRefresh;
+ }
+
+ @Override
+ protected synchronized void start() throws JIException, IllegalArgumentException, UnknownHostException, NotConnectedException, DuplicateGroupException {
+ if (isActive()) {
+ return;
+ }
+
+ super.start();
+
+ this.eventHandler = this.group.attach(this);
+ if (!this.items.isEmpty() && this.initialRefresh) {
+ final OPCAsyncIO2 async20 = this.group.getAsyncIO20();
+ if (async20 == null) {
+ throw new NotConnectedException();
+ }
+
+ this.group.getAsyncIO20().refresh(OPCDATASOURCE.OPC_DS_CACHE, 0);
+ }
+ }
+
+ @Override
+ protected synchronized void stop() throws JIException {
+ if (!isActive()) {
+ return;
+ }
+
+ if (this.eventHandler != null) {
+ try {
+ this.eventHandler.detach();
+ } catch (final Throwable e) {
+ log.warn("Failed to detach group", e);
+ }
+
+ this.eventHandler = null;
+ }
+
+ super.stop();
+ }
+
+ public void cancelComplete(final int transactionId, final int serverGroupHandle) {
+ }
+
+ public void dataChange(final int transactionId, final int serverGroupHandle, final int masterQuality, final int masterErrorCode, final KeyedResultSet result) {
+ log.debug("dataChange - transId {}, items: {}", transactionId, result.size());
+
+ final Group group = this.group;
+ if (group == null) {
+ return;
+ }
+
+ for (final KeyedResult entry : result) {
+ final Item item = group.findItemByClientHandle(entry.getKey());
+ log.debug("Update for '{}'", item.getId());
+ updateItem(item, new ItemState(entry.getErrorCode(), entry.getValue().getValue(), entry.getValue().getTimestamp(), entry.getValue().getQuality()));
+ }
+ }
+
+ public void readComplete(final int transactionId, final int serverGroupHandle, final int masterQuality, final int masterErrorCode, final KeyedResultSet result) {
+ log.debug("readComplete - transId {}", transactionId);
+ }
+
+ public void writeComplete(final int transactionId, final int serverGroupHandle, final int masterErrorCode, final ResultSet result) {
+ log.debug("writeComplete - transId {}", transactionId);
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/AutoReconnectController.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/AutoReconnectController.java
new file mode 100644
index 0000000..c30e2a6
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/AutoReconnectController.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+@Slf4j
+public class AutoReconnectController implements ServerConnectionStateListener {
+
+ private static final int DEFAULT_DELAY = 5 * 1000;
+ private final Server _server;
+ private final Set _listeners = new CopyOnWriteArraySet();
+ private int _delay;
+ private AutoReconnectState _state = AutoReconnectState.DISABLED;
+
+ private Thread _connectTask = null;
+
+ public AutoReconnectController(final Server server) {
+ this(server, DEFAULT_DELAY);
+ }
+
+ public AutoReconnectController(final Server server, final int delay) {
+ super();
+ setDelay(delay);
+
+ this._server = server;
+ this._server.addStateListener(this);
+ }
+
+ public void addListener(final AutoReconnectListener listener) {
+ if (listener != null) {
+ this._listeners.add(listener);
+ listener.stateChanged(this._state);
+ }
+ }
+
+ public void removeListener(final AutoReconnectListener listener) {
+ this._listeners.remove(listener);
+ }
+
+ protected void notifyStateChange(final AutoReconnectState state) {
+ this._state = state;
+ for (AutoReconnectListener listener : this._listeners) {
+ listener.stateChanged(state);
+ }
+ }
+
+ public int getDelay() {
+ return this._delay;
+ }
+
+ /**
+ * Set the reconnect delay. If the delay less than or equal to zero it will be
+ * the default delay time.
+ *
+ * @param delay The delay to use
+ */
+ public void setDelay(int delay) {
+ if (delay <= 0) {
+ delay = DEFAULT_DELAY;
+ }
+ this._delay = delay;
+ }
+
+ public synchronized void connect() {
+ if (isRequested()) {
+ return;
+ }
+
+ log.debug("Requesting connection");
+ notifyStateChange(AutoReconnectState.DISCONNECTED);
+
+ triggerReconnect(false);
+ }
+
+ public synchronized void disconnect() {
+ if (!isRequested()) {
+ return;
+ }
+
+ log.debug("Un-Requesting connection");
+
+ notifyStateChange(AutoReconnectState.DISABLED);
+ this._server.disconnect();
+ }
+
+ public boolean isRequested() {
+ return this._state != AutoReconnectState.DISABLED;
+ }
+
+ public synchronized void connectionStateChanged(final boolean connected) {
+ log.debug("Connection state changed: " + connected);
+
+ if (!connected) {
+ if (isRequested()) {
+ notifyStateChange(AutoReconnectState.DISCONNECTED);
+ triggerReconnect(true);
+ }
+ } else {
+ if (!isRequested()) {
+ this._server.disconnect();
+ } else {
+ notifyStateChange(AutoReconnectState.CONNECTED);
+ }
+ }
+ }
+
+ private synchronized void triggerReconnect(final boolean wait) {
+ if (this._connectTask != null) {
+ log.info("Connect thread already running");
+ return;
+ }
+
+ log.debug("Trigger reconnect");
+
+ this._connectTask = new Thread(new Runnable() {
+
+ public void run() {
+ boolean result = false;
+ try {
+ result = performReconnect(wait);
+ } finally {
+ AutoReconnectController.this._connectTask = null;
+ log.debug(String.format("performReconnect completed : %s", result));
+ if (!result) {
+ triggerReconnect(true);
+ }
+ }
+ }
+ }, "OPCReconnectThread");
+ this._connectTask.setDaemon(true);
+ this._connectTask.start();
+ }
+
+ private boolean performReconnect(final boolean wait) {
+ try {
+ if (wait) {
+ notifyStateChange(AutoReconnectState.WAITING);
+ log.debug(String.format("Delaying (%s)...", this._delay));
+ Thread.sleep(this._delay);
+ }
+ } catch (InterruptedException e) {
+ }
+
+ if (!isRequested()) {
+ log.debug("Request canceled during delay");
+ return true;
+ }
+
+ try {
+ log.debug("Connecting to server");
+ notifyStateChange(AutoReconnectState.CONNECTING);
+ synchronized (this) {
+ this._server.connect();
+ return true;
+ }
+ // CONNECTED state will be set by server callback
+ } catch (Throwable e) {
+ log.info("Re-connect failed", e);
+ notifyStateChange(AutoReconnectState.DISCONNECTED);
+ return false;
+ }
+ }
+
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/AutoReconnectListener.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/AutoReconnectListener.java
new file mode 100644
index 0000000..67eceee
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/AutoReconnectListener.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+public interface AutoReconnectListener {
+ public abstract void stateChanged(AutoReconnectState state);
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/AutoReconnectState.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/AutoReconnectState.java
new file mode 100644
index 0000000..05e18c5
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/AutoReconnectState.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+/**
+ * A state for the auto-reconnect controller
+ *
+ * @author Jens Reimann
+ */
+public enum AutoReconnectState {
+ /**
+ * Auto reconnect is disabled.
+ */
+ DISABLED,
+ /**
+ * Auto reconnect is enabled, but the connection is currently not established.
+ */
+ DISCONNECTED,
+ /**
+ * Auto reconnect is enabled, the connection is not established and the controller
+ * is currently waiting the delay until it will reconnect.
+ */
+ WAITING,
+ /**
+ * Auto reconnect is enabled, the connection is not established but the controller
+ * currently tries to establish the connection.
+ */
+ CONNECTING,
+ /**
+ * Auto reconnect is enabled and the connection is established.
+ */
+ CONNECTED
+}
\ No newline at end of file
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/DataCallback.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/DataCallback.java
new file mode 100644
index 0000000..d56bced
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/DataCallback.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+public interface DataCallback {
+ void changed(Item item, ItemState itemState);
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/DuplicateGroupException.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/DuplicateGroupException.java
new file mode 100644
index 0000000..0e90299
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/DuplicateGroupException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+public class DuplicateGroupException extends Exception {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/ErrorMessageResolver.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/ErrorMessageResolver.java
new file mode 100644
index 0000000..2e675c6
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/ErrorMessageResolver.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jinterop.dcom.common.JIException;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.OPCCommon;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An error message resolver that will lookup the error code using the
+ * server interface and will cache the result locally.
+ *
+ * @author Jens Reimann
+ */
+@Slf4j
+public class ErrorMessageResolver {
+
+ private final Map _messageCache = new HashMap();
+ private OPCCommon _opcCommon = null;
+ private int _localeId = 0;
+
+ public ErrorMessageResolver(final OPCCommon opcCommon, final int localeId) {
+ super();
+ this._opcCommon = opcCommon;
+ this._localeId = localeId;
+ }
+
+ /**
+ * Get an error message from an error code
+ *
+ * @param errorCode The error code to look up
+ * @return the error message or
null if no message could be looked up
+ */
+ public synchronized String getMessage(final int errorCode) {
+ String message = this._messageCache.get(Integer.valueOf(errorCode));
+
+ if (message == null) {
+ try {
+ message = this._opcCommon.getErrorString(errorCode, this._localeId);
+ log.info(String.format("Resolved %08X to '%s'", errorCode, message));
+ } catch (JIException e) {
+ log.warn(String.format("Failed to resolve error code for %08X", errorCode), e);
+ }
+ if (message != null) {
+ this._messageCache.put(errorCode, message);
+ }
+ }
+ return message;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/Group.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/Group.java
new file mode 100644
index 0000000..5dd55cb
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/Group.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jinterop.dcom.common.JIException;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.*;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.*;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl.OPCAsyncIO2;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl.OPCGroupStateMgt;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl.OPCItemMgt;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl.OPCSyncIO;
+
+import java.net.UnknownHostException;
+import java.util.*;
+
+@Slf4j
+public class Group {
+
+ private static Random _random = new Random();
+ private final int _serverHandle;
+ private final Map _itemHandleMap = new HashMap();
+ private final Map _itemMap = new HashMap();
+ private final Map _itemClientMap = new HashMap();
+ private Server _server = null;
+ private OPCGroupStateMgt _group = null;
+ private OPCItemMgt _items = null;
+ private OPCSyncIO _syncIO = null;
+
+ Group(final Server server, final int serverHandle, final OPCGroupStateMgt group) throws IllegalArgumentException, UnknownHostException, JIException {
+ log.debug("Creating new group instance with COM group " + group);
+ this._server = server;
+ this._serverHandle = serverHandle;
+ this._group = group;
+ this._items = group.getItemManagement();
+ this._syncIO = group.getSyncIO();
+ }
+
+ /**
+ * remove the group from the server
+ *
+ * @throws JIException JIException
+ */
+ public void remove() throws JIException {
+ this._server.removeGroup(this, true);
+ }
+
+ public boolean isActive() throws JIException {
+ return this._group.getState().isActive();
+ }
+
+ public void setActive(final boolean state) throws JIException {
+ this._group.setState(null, state, null, null, null, null);
+ }
+
+ /**
+ * Get the group name from the server
+ *
+ * @return The group name fetched from the server
+ * @throws JIException JIException
+ */
+ public String getName() throws JIException {
+ return this._group.getState().getName();
+ }
+
+ /**
+ * Change the group name
+ *
+ * @param name the new name of the group
+ * @throws JIException JIException
+ */
+ public void setName(final String name) throws JIException {
+ this._group.setName(name);
+ }
+
+ /**
+ * Add a single item. Actually calls {@link #addItems(String[])} with only
+ * one paraemter
+ *
+ * @param item The item to add
+ * @return The added item
+ * @throws JIException The add operation failed
+ * @throws AddFailedException The item was not added due to an error
+ */
+ public Item addItem(final String item) throws JIException, AddFailedException {
+ Map items = addItems(item);
+ return items.get(item);
+ }
+
+ /**
+ * Validate item ids and get additional information to them.
+ *
+ * According to the OPC specification you should first validate
+ * the items and the add them. The spec also says that when a server
+ * lets the item pass validation it must also let them pass the add operation.
+ *
+ * @param items The items to validate
+ * @return A result map of item id to result information (including error code).
+ * @throws JIException JIException
+ */
+ public synchronized Map> validateItems(final String... items) throws JIException {
+ OPCITEMDEF[] defs = new OPCITEMDEF[items.length];
+ for (int i = 0; i < items.length; i++) {
+ defs[i] = new OPCITEMDEF();
+ defs[i].setItemID(items[i]);
+ }
+
+ KeyedResultSet result = this._items.validate(defs);
+
+ Map> resultMap = new HashMap>();
+ for (KeyedResult resultEntry : result) {
+ resultMap.put(resultEntry.getKey().getItemID(), new Result(resultEntry.getValue(), resultEntry.getErrorCode()));
+ }
+
+ return resultMap;
+ }
+
+ /**
+ * Add new items to the group
+ *
+ * @param items The items (by string id) to add
+ * @return A result map of id to item object
+ * @throws JIException The add operation completely failed. No item was added.
+ * @throws AddFailedException If one or more item could not be added. Item without error where added.
+ */
+ public synchronized Map addItems(final String... items) throws JIException, AddFailedException {
+ // Find which items we already have
+ Map handles = findItems(items);
+
+ List foundItems = new ArrayList(items.length);
+ List missingItems = new ArrayList();
+
+ // separate missing items from the found ones
+ for (Map.Entry entry : handles.entrySet()) {
+ if (entry.getValue() == null) {
+ missingItems.add(entry.getKey());
+ } else {
+ foundItems.add(entry.getValue());
+ }
+ }
+
+ // now fetch missing items from OPC server
+ Set newClientHandles = new HashSet();
+ OPCITEMDEF[] itemDef = new OPCITEMDEF[missingItems.size()];
+ for (int i = 0; i < missingItems.size(); i++) {
+ OPCITEMDEF def = new OPCITEMDEF();
+ def.setItemID(missingItems.get(i));
+ def.setActive(true);
+
+ Integer clientHandle;
+ do {
+ clientHandle = _random.nextInt();
+ } while (this._itemClientMap.containsKey(clientHandle) || newClientHandles.contains(clientHandle));
+ newClientHandles.add(clientHandle);
+ def.setClientHandle(clientHandle);
+
+ itemDef[i] = def;
+ }
+
+ // check the result and add new items
+ Map failedItems = new HashMap();
+ KeyedResultSet result = this._items.add(itemDef);
+ int i = 0;
+ for (KeyedResult entry : result) {
+ if (entry.getErrorCode() == 0) {
+ Item item = new Item(this, entry.getValue().getServerHandle(), itemDef[i].getClientHandle(), entry.getKey().getItemID());
+ addItem(item);
+ foundItems.add(item.getServerHandle());
+ } else {
+ failedItems.put(entry.getKey().getItemID(), entry.getErrorCode());
+ }
+ i++;
+ }
+
+ // if we have failed items then throw an exception with the result
+ if (failedItems.size() != 0) {
+ throw new AddFailedException(failedItems, findItems(foundItems));
+ }
+
+ // simply return the result in case of success
+ return findItems(foundItems);
+ }
+
+ private synchronized void addItem(final Item item) {
+ log.debug(String.format("Adding item: '%s', %d", item.getId(), item.getServerHandle()));
+
+ this._itemHandleMap.put(item.getId(), item.getServerHandle());
+ this._itemMap.put(item.getServerHandle(), item);
+ this._itemClientMap.put(item.getClientHandle(), item);
+ }
+
+ private synchronized void removeItem(final Item item) {
+ this._itemHandleMap.remove(item.getId());
+ this._itemMap.remove(item.getServerHandle());
+ this._itemClientMap.remove(item.getClientHandle());
+ }
+
+ protected Item getItemByOPCItemId(final String opcItemId) {
+ Integer serverHandle = this._itemHandleMap.get(opcItemId);
+ if (serverHandle == null) {
+ log.debug(String.format("Failed to locate item with id '%s'", opcItemId));
+ return null;
+ }
+ log.debug(String.format("Item '%s' has server id '%d'", opcItemId, serverHandle));
+ return this._itemMap.get(serverHandle);
+ }
+
+ private synchronized Map findItems(final String[] items) {
+ Map data = new HashMap();
+
+ for (int i = 0; i < items.length; i++) {
+ data.put(items[i], this._itemHandleMap.get(items[i]));
+ }
+
+ return data;
+ }
+
+ private synchronized Map findItems(final Collection handles) {
+ Map itemMap = new HashMap();
+ for (Integer i : handles) {
+ Item item = this._itemMap.get(i);
+ if (item != null) {
+ itemMap.put(item.getId(), item);
+ }
+ }
+ return itemMap;
+ }
+
+ protected void checkItems(final Item[] items) {
+ for (Item item : items) {
+ if (item.getGroup() != this) {
+ throw new IllegalArgumentException("Item doesn't belong to this group");
+ }
+ }
+ }
+
+ public void setActive(final boolean state, final Item... items) throws JIException {
+ checkItems(items);
+
+ Integer[] handles = new Integer[items.length];
+ for (int i = 0; i < items.length; i++) {
+ handles[i] = items[i].getServerHandle();
+ }
+
+ this._items.setActiveState(state, handles);
+ }
+
+ protected Integer[] getServerHandles(final Item[] items) {
+ checkItems(items);
+
+ Integer[] handles = new Integer[items.length];
+
+ for (int i = 0; i < items.length; i++) {
+ handles[i] = items[i].getServerHandle();
+ }
+
+ return handles;
+ }
+
+ public synchronized Map- write(final WriteRequest... requests) throws JIException {
+ Item[] items = new Item[requests.length];
+
+ for (int i = 0; i < requests.length; i++) {
+ items[i] = requests[i].getItem();
+ }
+
+ Integer[] handles = getServerHandles(items);
+
+ org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.WriteRequest[] wr = new org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.WriteRequest[items.length];
+ for (int i = 0; i < items.length; i++) {
+ wr[i] = new org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.WriteRequest(handles[i], requests[i].getValue());
+ }
+
+ ResultSet resultSet = this._syncIO.write(wr);
+
+ Map
- result = new HashMap
- ();
+ for (int i = 0; i < requests.length; i++) {
+ Result entry = resultSet.get(i);
+ result.put(requests[i].getItem(), entry.getErrorCode());
+ }
+
+ return result;
+ }
+
+ public synchronized Map
- read(final boolean device, final Item... items) throws JIException {
+ Integer[] handles = getServerHandles(items);
+
+ KeyedResultSet states = this._syncIO.read(device ? OPCDATASOURCE.OPC_DS_DEVICE : OPCDATASOURCE.OPC_DS_CACHE, handles);
+
+ Map
- data = new HashMap
- ();
+ for (KeyedResult entry : states) {
+ Item item = this._itemMap.get(entry.getKey());
+ ItemState state = new ItemState(entry.getErrorCode(), entry.getValue().getValue(), entry.getValue().getTimestamp().asCalendar(), entry.getValue().getQuality());
+ data.put(item, state);
+ }
+ return data;
+ }
+
+ public Server getServer() {
+ return this._server;
+ }
+
+ public synchronized void clear() throws JIException {
+ Integer[] handles = this._itemMap.keySet().toArray(new Integer[0]);
+ try {
+ this._items.remove(handles);
+ } finally {
+ // in any case clear our maps
+ this._itemHandleMap.clear();
+ this._itemMap.clear();
+ this._itemClientMap.clear();
+ }
+ }
+
+ public synchronized OPCAsyncIO2 getAsyncIO20() {
+ return this._group.getAsyncIO2();
+ }
+
+ public synchronized EventHandler attach(final IOPCDataCallback dataCallback) throws JIException {
+ return this._group.attach(dataCallback);
+ }
+
+ public Item findItemByClientHandle(final int clientHandle) {
+ return this._itemClientMap.get(clientHandle);
+ }
+
+ public int getServerHandle() {
+ return this._serverHandle;
+ }
+
+ public synchronized void removeItem(final String opcItemId) throws IllegalArgumentException, UnknownHostException, JIException {
+ log.debug(String.format("Removing item '%s'", opcItemId));
+ Item item = getItemByOPCItemId(opcItemId);
+ if (item != null) {
+ this._group.getItemManagement().remove(item.getServerHandle());
+ removeItem(item);
+ log.debug(String.format("Removed item '%s'", opcItemId));
+ } else {
+ log.warn(String.format("Unable to find item '%s'", opcItemId));
+ }
+ }
+
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/Item.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/Item.java
new file mode 100644
index 0000000..ba90b21
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/Item.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.JIVariant;
+
+@Slf4j
+public class Item {
+
+ private Group _group = null;
+
+ private int _serverHandle = 0;
+
+ private int _clientHandle = 0;
+
+ private String _id = null;
+
+ Item(final Group group, final int serverHandle, final int clientHandle, final String id) {
+ super();
+ log.debug(String.format("Adding new item '%s' (0x%08X) for group %s", id, serverHandle, group.toString()));
+ this._group = group;
+ this._serverHandle = serverHandle;
+ this._clientHandle = clientHandle;
+ this._id = id;
+ }
+
+ public Group getGroup() {
+ return this._group;
+ }
+
+ public int getServerHandle() {
+ return this._serverHandle;
+ }
+
+ public int getClientHandle() {
+ return this._clientHandle;
+ }
+
+ public String getId() {
+ return this._id;
+ }
+
+ public void setActive(final boolean state) throws JIException {
+ this._group.setActive(state, this);
+ }
+
+ public ItemState read(final boolean device) throws JIException {
+ return this._group.read(device, this).get(this);
+ }
+
+ public Integer write(final JIVariant value) throws JIException {
+ return this._group.write(new WriteRequest[]{new WriteRequest(this, value)}).get(this);
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/ItemState.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/ItemState.java
new file mode 100644
index 0000000..5aa277d
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/ItemState.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+import org.jinterop.dcom.core.JIVariant;
+
+import java.util.Calendar;
+
+public class ItemState {
+ private int _errorCode = 0;
+
+ private JIVariant _value = null;
+
+ private Calendar _timestamp = null;
+
+ private Short _quality = null;
+
+ public ItemState(final int errorCode, final JIVariant value, final Calendar timestamp, final Short quality) {
+ super();
+ this._errorCode = errorCode;
+ this._value = value;
+ this._timestamp = timestamp;
+ this._quality = quality;
+ }
+
+ public ItemState() {
+ super();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Value: %s, Timestamp: %tc, Quality: %s, ErrorCode: %08x", this._value, this._timestamp, this._quality, this._errorCode);
+ }
+
+ public Short getQuality() {
+ return this._quality;
+ }
+
+ public void setQuality(final Short quality) {
+ this._quality = quality;
+ }
+
+ public Calendar getTimestamp() {
+ return this._timestamp;
+ }
+
+ public void setTimestamp(final Calendar timestamp) {
+ this._timestamp = timestamp;
+ }
+
+ public JIVariant getValue() {
+ return this._value;
+ }
+
+ public void setValue(final JIVariant value) {
+ this._value = value;
+ }
+
+ public int getErrorCode() {
+ return this._errorCode;
+ }
+
+ public void setErrorCode(final int errorCode) {
+ this._errorCode = errorCode;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ result = PRIME * result + this._errorCode;
+ result = PRIME * result + (this._quality == null ? 0 : this._quality.hashCode());
+ result = PRIME * result + (this._timestamp == null ? 0 : this._timestamp.hashCode());
+ result = PRIME * result + (this._value == null ? 0 : this._value.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final ItemState other = (ItemState) obj;
+ if (this._errorCode != other._errorCode) {
+ return false;
+ }
+ if (this._quality == null) {
+ if (other._quality != null) {
+ return false;
+ }
+ } else if (!this._quality.equals(other._quality)) {
+ return false;
+ }
+ if (this._timestamp == null) {
+ if (other._timestamp != null) {
+ return false;
+ }
+ } else if (!this._timestamp.equals(other._timestamp)) {
+ return false;
+ }
+ if (this._value == null) {
+ if (other._value != null) {
+ return false;
+ }
+ } else if (!this._value.equals(other._value)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/Server.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/Server.java
new file mode 100644
index 0000000..2434450
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/Server.java
@@ -0,0 +1,413 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.JIClsid;
+import org.jinterop.dcom.core.JIComServer;
+import org.jinterop.dcom.core.JIProgId;
+import org.jinterop.dcom.core.JISession;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCNAMESPACETYPE;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCSERVERSTATUS;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl.OPCBrowseServerAddressSpace;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl.OPCGroupStateMgt;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl.OPCServer;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.common.AlreadyConnectedException;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.common.ConnectionInformation;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.common.NotConnectedException;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da.browser.FlatBrowser;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da.browser.TreeBrowser;
+
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ScheduledExecutorService;
+
+@Slf4j
+public class Server {
+
+ private final ConnectionInformation connectionInformation;
+ private final Map groups = new HashMap<>(16);
+ private final List stateListeners = new CopyOnWriteArrayList<>();
+ private final ScheduledExecutorService scheduler;
+ private JISession session;
+ private JIComServer comServer;
+ private OPCServer server;
+ private boolean defaultActive = true;
+ private int defaultUpdateRate = 1000;
+ private Integer defaultTimeBias;
+ private Float defaultPercentDeadband;
+ private int defaultLocaleID = 0;
+ private ErrorMessageResolver errorMessageResolver;
+
+ public Server(final ConnectionInformation connectionInformation,
+ final ScheduledExecutorService scheduler) {
+ super();
+ this.connectionInformation = connectionInformation;
+ this.scheduler = scheduler;
+ }
+
+ /**
+ * Gets the scheduler for the server. Note that this scheduler might get
+ * blocked for a short time if the connection breaks. It should not be used
+ * for time critical operations.
+ *
+ * @return the scheduler for the server
+ */
+ public ScheduledExecutorService getScheduler() {
+ return this.scheduler;
+ }
+
+ protected synchronized boolean isConnected() {
+ return this.session != null;
+ }
+
+ public synchronized void connect() throws IllegalArgumentException, UnknownHostException, JIException, AlreadyConnectedException {
+ if (isConnected()) {
+ throw new AlreadyConnectedException();
+ }
+
+ final int socketTimeout = Integer.getInteger("rpc.socketTimeout", 0);
+ log.debug(String.format("Socket timeout: %s ", socketTimeout));
+
+ try {
+ if (this.connectionInformation.getClsid() != null) {
+ this.session = JISession.createSession(
+ this.connectionInformation.getDomain(),
+ this.connectionInformation.getUser(),
+ this.connectionInformation.getPassword());
+ this.session.setGlobalSocketTimeout(socketTimeout);
+ this.session.useSessionSecurity(true);
+ this.comServer = new JIComServer(
+ JIClsid.valueOf(this.connectionInformation.getClsid()),
+ this.connectionInformation.getHost(), this.session);
+ } else if (this.connectionInformation.getProgId() != null) {
+ this.session = JISession.createSession(
+ this.connectionInformation.getDomain(),
+ this.connectionInformation.getUser(),
+ this.connectionInformation.getPassword());
+ this.session.setGlobalSocketTimeout(socketTimeout);
+ this.comServer = new JIComServer(
+ JIProgId.valueOf(this.connectionInformation.getProgId()),
+ this.connectionInformation.getHost(), this.session);
+ } else {
+ throw new IllegalArgumentException("Neither clsid nor progid is valid!");
+ }
+
+ this.server = new OPCServer(this.comServer.createInstance());
+ this.errorMessageResolver = new ErrorMessageResolver(
+ this.server.getCommon(), this.defaultLocaleID);
+ } catch (final UnknownHostException e) {
+ log.error("Unknown host when connecting to server", e);
+ cleanup();
+ throw e;
+ } catch (final JIException e) {
+ log.error("Failed to connect to server", e);
+ cleanup();
+ throw e;
+ } catch (final Throwable e) {
+ log.error("Unknown error", e);
+ cleanup();
+ throw new RuntimeException(e);
+ }
+
+ notifyConnectionStateChange(true);
+ }
+
+ /**
+ * cleanup after the connection is closed
+ */
+ protected void cleanup() {
+ log.debug("Destroying DCOM session...");
+ final JISession destructSession = this.session;
+ final Thread destructor = new Thread(new Runnable() {
+
+ public void run() {
+ final long ts = System.currentTimeMillis();
+ try {
+ log.debug("Starting destruction of DCOM session");
+ JISession.destroySession(destructSession);
+ log.debug("Destructed DCOM session");
+ } catch (final Throwable e) {
+ log.error("Failed to destruct DCOM session", e);
+ }
+ }
+ }, "UtgardSessionDestructor");
+ destructor.setName("OPCSessionDestructor");
+ destructor.setDaemon(true);
+ destructor.start();
+ log.debug("Destroying DCOM session... forked");
+
+ this.errorMessageResolver = null;
+ this.session = null;
+ this.comServer = null;
+ this.server = null;
+
+ this.groups.clear();
+ }
+
+ /**
+ * Disconnect the connection if it is connected
+ */
+ public synchronized void disconnect() {
+ if (!isConnected()) {
+ return;
+ }
+
+ try {
+ notifyConnectionStateChange(false);
+ } catch (final Throwable t) {
+ }
+
+ cleanup();
+ }
+
+ /**
+ * Dispose the connection in the case of an error
+ */
+ public void dispose() {
+ disconnect();
+ }
+
+ protected synchronized Group getGroup(final OPCGroupStateMgt groupMgt) throws JIException, IllegalArgumentException, UnknownHostException {
+ final Integer serverHandle = groupMgt.getState().getServerHandle();
+ if (this.groups.containsKey(serverHandle)) {
+ return this.groups.get(serverHandle);
+ } else {
+ final Group group = new Group(this, serverHandle, groupMgt);
+ this.groups.put(serverHandle, group);
+ return group;
+ }
+ }
+
+ /**
+ * Add a new named group to the server
+ *
+ * @param name The name of the group to use. Must be unique or
+ *
null so that the server creates a unique name.
+ * @return The new group
+ * @throws NotConnectedException If the server is not connected using {@link Server#connect()}
+ * @throws IllegalArgumentException IllegalArgumentException
+ * @throws UnknownHostException UnknownHostException
+ * @throws JIException JIException
+ * @throws DuplicateGroupException If a group with this name already exists
+ */
+ public synchronized Group addGroup(final String name) throws NotConnectedException, IllegalArgumentException, UnknownHostException, JIException, DuplicateGroupException {
+ if (!isConnected()) {
+ throw new NotConnectedException();
+ }
+
+ try {
+ final OPCGroupStateMgt groupMgt = this.server.addGroup(name,
+ this.defaultActive, this.defaultUpdateRate, 0,
+ this.defaultTimeBias, this.defaultPercentDeadband,
+ this.defaultLocaleID);
+ return getGroup(groupMgt);
+ } catch (final JIException e) {
+ if (e.getErrorCode() == 0xC004000C) {
+ throw new DuplicateGroupException();
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Add a new group and let the server generate a group name
+ *
+ * Actually this method only calls {@link Server#addGroup(String)} with
+ * null as parameter.
+ *
+ * @return the new group
+ * @throws IllegalArgumentException IllegalArgumentException
+ * @throws UnknownHostException UnknownHostException
+ * @throws NotConnectedException NotConnectedException
+ * @throws JIException JIException
+ * @throws DuplicateGroupException DuplicateGroupException
+ */
+ public Group addGroup() throws IllegalArgumentException,
+ UnknownHostException, NotConnectedException, JIException,
+ DuplicateGroupException {
+ return addGroup(null);
+ }
+
+ /**
+ * Find a group by its name
+ *
+ * @param name The name to look for
+ * @return The group
+ * @throws IllegalArgumentException IllegalArgumentException
+ * @throws UnknownHostException UnknownHostException
+ * @throws JIException JIException
+ * @throws UnknownGroupException If the group was not found
+ * @throws NotConnectedException If the server is not connected
+ */
+ public Group findGroup(final String name) throws IllegalArgumentException, UnknownHostException, JIException, UnknownGroupException, NotConnectedException {
+ if (!isConnected()) {
+ throw new NotConnectedException();
+ }
+
+ try {
+ final OPCGroupStateMgt groupMgt = this.server.getGroupByName(name);
+ return getGroup(groupMgt);
+ } catch (final JIException e) {
+ switch (e.getErrorCode()) {
+ case 0x80070057:
+ throw new UnknownGroupException(name);
+ default:
+ throw e;
+ }
+ }
+ }
+
+ public int getDefaultLocaleID() {
+ return this.defaultLocaleID;
+ }
+
+ public void setDefaultLocaleID(final int defaultLocaleID) {
+ this.defaultLocaleID = defaultLocaleID;
+ }
+
+ public Float getDefaultPercentDeadband() {
+ return this.defaultPercentDeadband;
+ }
+
+ public void setDefaultPercentDeadband(final Float defaultPercentDeadband) {
+ this.defaultPercentDeadband = defaultPercentDeadband;
+ }
+
+ public Integer getDefaultTimeBias() {
+ return this.defaultTimeBias;
+ }
+
+ public void setDefaultTimeBias(final Integer defaultTimeBias) {
+ this.defaultTimeBias = defaultTimeBias;
+ }
+
+ public int getDefaultUpdateRate() {
+ return this.defaultUpdateRate;
+ }
+
+ public void setDefaultUpdateRate(final int defaultUpdateRate) {
+ this.defaultUpdateRate = defaultUpdateRate;
+ }
+
+ public boolean isDefaultActive() {
+ return this.defaultActive;
+ }
+
+ public void setDefaultActive(final boolean defaultActive) {
+ this.defaultActive = defaultActive;
+ }
+
+ /**
+ * Get the flat browser
+ *
+ * @return The flat browser or null if the functionality is not
+ * supported
+ */
+ public FlatBrowser getFlatBrowser() {
+ final OPCBrowseServerAddressSpace browser = this.server.getBrowser();
+ if (browser == null) {
+ return null;
+ }
+
+ return new FlatBrowser(browser);
+ }
+
+ /**
+ * Get the tree browser
+ *
+ * @return The tree browser or null if the functionality is not
+ * supported
+ * @throws JIException JIException
+ */
+ public TreeBrowser getTreeBrowser() throws JIException {
+ final OPCBrowseServerAddressSpace browser = this.server.getBrowser();
+ if (browser == null) {
+ return null;
+ }
+
+ if (browser.queryOrganization() != OPCNAMESPACETYPE.OPC_NS_HIERARCHIAL) {
+ return null;
+ }
+
+ return new TreeBrowser(browser);
+ }
+
+ public synchronized String getErrorMessage(final int errorCode) {
+ if (this.errorMessageResolver == null) {
+ return String.format("Unknown error (%08X)", errorCode);
+ }
+
+ // resolve message
+ final String message = this.errorMessageResolver.getMessage(errorCode);
+
+ // and return if successfull
+ if (message != null) {
+ return message;
+ }
+
+ // return default message
+ return String.format("Unknown error (%08X)", errorCode);
+ }
+
+ public synchronized void addStateListener(
+ final ServerConnectionStateListener listener) {
+ this.stateListeners.add(listener);
+ listener.connectionStateChanged(isConnected());
+ }
+
+ public synchronized void removeStateListener(
+ final ServerConnectionStateListener listener) {
+ this.stateListeners.remove(listener);
+ }
+
+ protected void notifyConnectionStateChange(final boolean connected) {
+ final List list = new ArrayList(
+ this.stateListeners);
+ for (final ServerConnectionStateListener listener : list) {
+ listener.connectionStateChanged(connected);
+ }
+ }
+
+ public OPCSERVERSTATUS getServerState(final int timeout) throws Throwable {
+ return new ServerStateOperation(this.server).getServerState(timeout);
+ }
+
+ public OPCSERVERSTATUS getServerState() {
+ try {
+ return getServerState(2500);
+ } catch (final Throwable e) {
+ log.error("Server connection failed", e);
+ dispose();
+ return null;
+ }
+ }
+
+ public void removeGroup(final Group group, final boolean force)
+ throws JIException {
+ if (this.groups.containsKey(group.getServerHandle())) {
+ this.server.removeGroup(group.getServerHandle(), force);
+ this.groups.remove(group.getServerHandle());
+ }
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/ServerConnectionStateListener.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/ServerConnectionStateListener.java
new file mode 100644
index 0000000..766ad43
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/ServerConnectionStateListener.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+public interface ServerConnectionStateListener {
+ public abstract void connectionStateChanged(boolean connected);
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/ServerStateListener.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/ServerStateListener.java
new file mode 100644
index 0000000..886cc13
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/ServerStateListener.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCSERVERSTATUS;
+
+public interface ServerStateListener {
+ public void stateUpdate(OPCSERVERSTATUS state);
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/ServerStateOperation.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/ServerStateOperation.java
new file mode 100644
index 0000000..fe421e0
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/ServerStateOperation.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+import lombok.extern.slf4j.Slf4j;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCSERVERSTATUS;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl.OPCServer;
+
+/**
+ * A server state operation which can be interruped
+ *
+ * @author Jens Reimann
+ */
+@Slf4j
+public class ServerStateOperation implements Runnable {
+
+ public OPCSERVERSTATUS _serverStatus = null;
+
+ public OPCServer _server;
+
+ public Throwable _error;
+
+ public Object _lock = new Object();
+
+ public boolean _running = false;
+
+ public ServerStateOperation(final OPCServer server) {
+ super();
+ this._server = server;
+ }
+
+ /**
+ * Perform the operation.
+ *
+ * This method will block until either the serve state has been aquired or the
+ * timeout triggers cancels the call.
+ *
+ */
+ public void run() {
+ synchronized (this._lock) {
+ this._running = true;
+ }
+ try {
+ this._serverStatus = this._server.getStatus();
+ synchronized (this._lock) {
+ this._running = false;
+ this._lock.notify();
+ }
+ } catch (Throwable e) {
+ log.info("Failed to get server state", e);
+ this._error = e;
+ this._running = false;
+ synchronized (this._lock) {
+ this._lock.notify();
+ }
+ }
+
+ }
+
+ /**
+ * Get the server state with a timeout.
+ *
+ * @param timeout the timeout in ms
+ * @return the server state or null if the server is not set.
+ * @throws Throwable any error that occurred
+ */
+ public OPCSERVERSTATUS getServerState(final int timeout) throws Throwable {
+ if (this._server == null) {
+ log.debug("No connection to server. Skipping...");
+ return null;
+ }
+
+ Thread t = new Thread(this, "OPCServerStateReader");
+
+ synchronized (this._lock) {
+ t.start();
+ this._lock.wait(timeout);
+ if (this._running) {
+ log.warn("State operation still running. Interrupting...");
+ t.interrupt();
+ throw new InterruptedException("Interrupted getting server state");
+ }
+ }
+ if (this._error != null) {
+ log.warn("An error occurred while getting server state", this._error);
+ throw this._error;
+ }
+
+ return this._serverStatus;
+ }
+
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/ServerStateReader.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/ServerStateReader.java
new file mode 100644
index 0000000..76a9049
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/ServerStateReader.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+import lombok.extern.slf4j.Slf4j;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCSERVERSTATUS;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+public class ServerStateReader {
+
+ private final List _listeners = new CopyOnWriteArrayList();
+ private Server _server = null;
+ private ScheduledExecutorService _scheduler = null;
+ private ScheduledFuture> _job = null;
+
+ public ServerStateReader(final Server server) {
+ super();
+ this._server = server;
+ this._scheduler = this._server.getScheduler();
+ }
+
+ /**
+ * Create a new server state reader. Please note that the scheduler might get
+ * blocked for a short period of time in case of a connection failure!
+ *
+ * @param server the server to check
+ * @param scheduler the scheduler to use
+ */
+ public ServerStateReader(final Server server, final ScheduledExecutorService scheduler) {
+ super();
+ this._server = server;
+ this._scheduler = scheduler;
+ }
+
+ public synchronized void start() {
+ if (this._job != null) {
+ return;
+ }
+
+ this._job = this._scheduler.scheduleAtFixedRate(new Runnable() {
+
+ public void run() {
+ once();
+ }
+ }, 1000, 1000, TimeUnit.MILLISECONDS);
+ }
+
+ public synchronized void stop() {
+ this._job.cancel(false);
+ this._job = null;
+ }
+
+ protected void once() {
+ log.debug("Reading server state");
+
+ final OPCSERVERSTATUS state = this._server.getServerState();
+
+ for (final ServerStateListener listener : new ArrayList(this._listeners)) {
+ listener.stateUpdate(state);
+ }
+ }
+
+ public void addListener(final ServerStateListener listener) {
+ this._listeners.add(listener);
+ }
+
+ public void removeListener(final ServerStateListener listener) {
+ this._listeners.remove(listener);
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/SyncAccess.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/SyncAccess.java
new file mode 100644
index 0000000..4bbe561
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/SyncAccess.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jinterop.dcom.common.JIException;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.common.NotConnectedException;
+
+import java.net.UnknownHostException;
+import java.util.Map;
+
+@Slf4j
+public class SyncAccess extends AccessBase implements Runnable {
+
+ private Thread runner = null;
+
+ private Throwable lastError = null;
+
+ public SyncAccess(final Server server, final int period) throws IllegalArgumentException, UnknownHostException, NotConnectedException, JIException, DuplicateGroupException {
+ super(server, period);
+ }
+
+ public SyncAccess(final Server server, final int period, final String logTag) throws IllegalArgumentException, UnknownHostException, NotConnectedException, JIException, DuplicateGroupException {
+ super(server, period, logTag);
+ }
+
+ public void run() {
+ while (this.active) {
+ try {
+ runOnce();
+ if (this.lastError != null) {
+ this.lastError = null;
+ handleError(null);
+ }
+ } catch (Throwable e) {
+ log.error("Sync read failed", e);
+ handleError(e);
+ this.server.disconnect();
+ }
+
+ try {
+ Thread.sleep(getPeriod());
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ protected void runOnce() throws JIException {
+ if (!this.active || this.group == null) {
+ return;
+ }
+
+ Map- result;
+
+ // lock only this section since we could get into a deadlock otherwise
+ // calling updateItem
+ synchronized (this) {
+ Item[] items = this.items.keySet().toArray(new Item[this.items.size()]);
+ result = this.group.read(false, items);
+ }
+
+ for (Map.Entry
- entry : result.entrySet()) {
+ updateItem(entry.getKey(), entry.getValue());
+ }
+
+ }
+
+ @Override
+ protected synchronized void start() throws JIException, IllegalArgumentException, UnknownHostException, NotConnectedException, DuplicateGroupException {
+ super.start();
+
+ this.runner = new Thread(this, "UtgardSyncReader");
+ this.runner.setDaemon(true);
+ this.runner.start();
+ }
+
+ @Override
+ protected synchronized void stop() throws JIException {
+ super.stop();
+
+ this.runner = null;
+ this.items.clear();
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/UnknownGroupException.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/UnknownGroupException.java
new file mode 100644
index 0000000..988012a
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/UnknownGroupException.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+public class UnknownGroupException extends Exception {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ private String _name = null;
+
+ public UnknownGroupException(final String name) {
+ super();
+ this._name = name;
+ }
+
+ public String getName() {
+ return this._name;
+ }
+
+ public void setName(final String name) {
+ this._name = name;
+ }
+
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/WriteRequest.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/WriteRequest.java
new file mode 100644
index 0000000..c6fc703
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/WriteRequest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da;
+
+import org.jinterop.dcom.core.JIVariant;
+
+public class WriteRequest {
+ private Item _item = null;
+
+ private JIVariant _value = null;
+
+ public WriteRequest(final Item item, final JIVariant value) {
+ super();
+ this._item = item;
+ this._value = value;
+ }
+
+ public Item getItem() {
+ return this._item;
+ }
+
+ public JIVariant getValue() {
+ return this._value;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/browser/Access.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/browser/Access.java
new file mode 100644
index 0000000..8ad63bc
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/browser/Access.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da.browser;
+
+public enum Access {
+ READ(1),
+ WRITE(2);
+
+ private int _code = 0;
+
+ private Access(final int code) {
+ this._code = code;
+ }
+
+ public int getCode() {
+ return this._code;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/browser/BaseBrowser.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/browser/BaseBrowser.java
new file mode 100644
index 0000000..2f7f073
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/browser/BaseBrowser.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da.browser;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jinterop.dcom.common.JIException;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.impl.EnumString;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCBROWSETYPE;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl.OPCBrowseServerAddressSpace;
+
+import java.net.UnknownHostException;
+import java.util.Collection;
+import java.util.EnumSet;
+
+/**
+ * A class implementing base browsing
+ *
+ * @author Jens Reimann
+ */
+@Slf4j
+public class BaseBrowser {
+
+ protected OPCBrowseServerAddressSpace _browser;
+
+ /**
+ * The batch size is the number of entries that will be requested with one call
+ * from the server. Sometimes too big batch sizes will cause an exception. And
+ * smaller batch sizes degrade perfomance. The default is set by {@link EnumString#DEFAULT_BATCH_SIZE}
+ * and can be overridden by the java property openscada.dcom.enum-batch-size.
+ */
+ protected int _batchSize;
+
+ public BaseBrowser(final OPCBrowseServerAddressSpace browser) {
+ this(browser, EnumString.DEFAULT_BATCH_SIZE);
+ }
+
+ public BaseBrowser(final OPCBrowseServerAddressSpace browser, final int batchSize) {
+ super();
+ this._browser = browser;
+ this._batchSize = batchSize;
+ }
+
+ /**
+ * Get the batch size
+ *
+ * @return the current batch size
+ */
+ public int getBatchSize() {
+ return this._batchSize;
+ }
+
+ /**
+ * Set the batch size
+ *
+ * @param batchSize The new batch size
+ */
+ public void setBatchSize(final int batchSize) {
+ this._batchSize = batchSize;
+ }
+
+ /**
+ * Perform the browse operation.
+ *
+ * @param type OPCBROWSETYPE
+ * @param filterCriteria Filter Criteria
+ * @param accessMask Access Mask
+ * @param variantType Variant Type
+ * @return The browse result
+ * @throws IllegalArgumentException IllegalArgumentException
+ * @throws UnknownHostException UnknownHostException
+ * @throws JIException JIException
+ */
+ protected Collection browse(final OPCBROWSETYPE type, final String filterCriteria, final EnumSet accessMask, final int variantType) throws IllegalArgumentException, UnknownHostException, JIException {
+ int accessMaskValue = 0;
+
+ if (accessMask.contains(Access.READ)) {
+ accessMaskValue |= Access.READ.getCode();
+ }
+ if (accessMask.contains(Access.WRITE)) {
+ accessMaskValue |= Access.WRITE.getCode();
+ }
+
+ log.debug("Browsing with a batch size of " + this._batchSize);
+
+ return this._browser.browse(type, filterCriteria, accessMaskValue, variantType).asCollection(this._batchSize);
+ }
+
+ /**
+ * Browse the access paths for one item.
+ *
+ * @param itemId The item ID to look up the access paths
+ * @return The collection of the access paths
+ * @throws JIException JIException
+ * @throws UnknownHostException UnknownHostException
+ * @throws IllegalArgumentException IllegalArgumentException
+ */
+ public Collection getAccessPaths(final String itemId) throws IllegalArgumentException, UnknownHostException, JIException {
+ return this._browser.browseAccessPaths(itemId).asCollection(this._batchSize);
+ }
+
+}
\ No newline at end of file
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/browser/Branch.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/browser/Branch.java
new file mode 100644
index 0000000..450b800
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/browser/Branch.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da.browser;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+
+public class Branch {
+ private Branch _parent = null;
+
+ private String _name = null;
+
+ private Collection _branches = new LinkedList();
+
+ private Collection _leaves = new LinkedList();
+
+ /**
+ * Create a branch to the virtual root folder
+ */
+ public Branch() {
+ super();
+ }
+
+ /**
+ * Create a branch with a parent branch and a name of this branch.
+ *
+ * @param parent The parent of this branch
+ * @param name The name of this branch
+ */
+ public Branch(final Branch parent, final String name) {
+ super();
+ this._name = name;
+ this._parent = parent;
+ }
+
+ /**
+ * Get all branches.
+ *
+ * They must be filled first with a fill method from the {@link TreeBrowser}
+ *
+ * @return The list of branches
+ */
+ public Collection getBranches() {
+ return this._branches;
+ }
+
+ public void setBranches(final Collection branches) {
+ this._branches = branches;
+ }
+
+ /**
+ * Get all leaves.
+ *
+ * They must be filled first with a fill method from the {@link TreeBrowser}
+ *
+ * @return The list of leaves
+ */
+ public Collection getLeaves() {
+ return this._leaves;
+ }
+
+ public void setLeaves(final Collection leaves) {
+ this._leaves = leaves;
+ }
+
+ public String getName() {
+ return this._name;
+ }
+
+ public void setName(final String name) {
+ this._name = name;
+ }
+
+ public Branch getParent() {
+ return this._parent;
+ }
+
+ /**
+ * Get the list of names from the parent up to this branch
+ *
+ * @return The stack of branch names from the parent up this one
+ */
+ public Collection getBranchStack() {
+ LinkedList branches = new LinkedList();
+
+ Branch currentBranch = this;
+ while (currentBranch.getParent() != null) {
+ branches.add(currentBranch.getName());
+ currentBranch = currentBranch.getParent();
+ }
+
+ Collections.reverse(branches);
+ return branches;
+ }
+
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/browser/FlatBrowser.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/browser/FlatBrowser.java
new file mode 100644
index 0000000..cbdd8b4
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/browser/FlatBrowser.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da.browser;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.JIVariant;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCBROWSETYPE;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl.OPCBrowseServerAddressSpace;
+
+import java.net.UnknownHostException;
+import java.util.Collection;
+import java.util.EnumSet;
+
+/**
+ * Browse through the flat server namespace
+ *
+ * @author Jens Reimann jens.reimann@th4-systems.com
+ */
+public class FlatBrowser extends BaseBrowser {
+ public FlatBrowser(final OPCBrowseServerAddressSpace browser) {
+ super(browser);
+ }
+
+ public FlatBrowser(final OPCBrowseServerAddressSpace browser, final int batchSize) {
+ super(browser, batchSize);
+ }
+
+ /**
+ * Perform a flat browse operation
+ *
+ * @param filterCriteria The filter criteria. Use an empty string if you don't need one.
+ * @param accessMask The access mask. An empty set will search for all.
+ * @param variantType The variant type. Must be one of the VT_ constants of {@link JIVariant}. Use {@link JIVariant#VT_EMPTY} if you want to browse for all.
+ * @return The list of entries
+ * @throws IllegalArgumentException IllegalArgumentException
+ * @throws UnknownHostException UnknownHostException
+ * @throws JIException JIException
+ */
+ public Collection browse(final String filterCriteria, final EnumSet accessMask, final int variantType) throws IllegalArgumentException, UnknownHostException, JIException {
+ return browse(OPCBROWSETYPE.OPC_FLAT, filterCriteria, accessMask, variantType);
+ }
+
+ public Collection browse(final String filterCriteria) throws IllegalArgumentException, UnknownHostException, JIException {
+ return browse(filterCriteria, EnumSet.noneOf(Access.class), JIVariant.VT_EMPTY);
+ }
+
+ public Collection browse() throws IllegalArgumentException, UnknownHostException, JIException {
+ return browse("", EnumSet.noneOf(Access.class), JIVariant.VT_EMPTY);
+ }
+
+ public Collection browse(final EnumSet accessMask) throws IllegalArgumentException, UnknownHostException, JIException {
+ return browse("", accessMask, JIVariant.VT_EMPTY);
+ }
+
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/browser/Leaf.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/browser/Leaf.java
new file mode 100644
index 0000000..2acf230
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/browser/Leaf.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da.browser;
+
+public class Leaf {
+ private Branch _parent = null;
+
+ private String _name = "";
+
+ private String _itemId = null;
+
+ public Leaf(final Branch parent, final String name) {
+ this._parent = parent;
+ this._name = name;
+ }
+
+ public Leaf(final Branch parent, final String name, final String itemId) {
+ this._parent = parent;
+ this._name = name;
+ this._itemId = itemId;
+ }
+
+ public String getItemId() {
+ return this._itemId;
+ }
+
+ public void setItemId(final String itemId) {
+ this._itemId = itemId;
+ }
+
+ public String getName() {
+ return this._name;
+ }
+
+ public void setName(final String name) {
+ this._name = name;
+ }
+
+ public Branch getParent() {
+ return this._parent;
+ }
+
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/browser/TreeBrowser.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/browser/TreeBrowser.java
new file mode 100644
index 0000000..2c6fccf
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/da/browser/TreeBrowser.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da.browser;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.JIVariant;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCBROWSEDIRECTION;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.OPCBROWSETYPE;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.da.impl.OPCBrowseServerAddressSpace;
+
+import java.net.UnknownHostException;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.LinkedList;
+
+/**
+ * Browse through the hierarchical server namespace.
+ *
+ * The operations on the address space browser browser are not synchronized
+ * as is the TreeBrowser itself. The user must take care of preventing
+ * simultanious access to this instance and the server address space browser.
+ *
+ * @author Jens Reimann jens.reimann@th4-systems.com
+ */
+public class TreeBrowser extends BaseBrowser {
+
+ private String _filterCriteria = "";
+
+ private EnumSet _accessMask = EnumSet.noneOf(Access.class);
+
+ private int _variantType = JIVariant.VT_EMPTY;
+
+ /**
+ * Browse for all items without search parameters.
+ *
+ * This will actually call:
+ *
+ * TreeBrowser ( browser, "", EnumSet.noneOf ( Access.class ), JIVariant.VT_EMPTY );
+ *
+ *
+ * @param browser The browser to use for browsing
+ */
+ public TreeBrowser(final OPCBrowseServerAddressSpace browser) {
+ super(browser);
+ }
+
+ /**
+ * Browse for items with search parameters.
+ *
+ * @param browser The browser to use
+ * @param filterCriteria The filter criteria. It is specific to the server you use.
+ * @param accessMask The access mask (use EnumSet.noneOf ( Access.class ) for all)
+ * @param variantType The variant type (use JIVariant.VT_EMPTY for all)
+ */
+ public TreeBrowser(final OPCBrowseServerAddressSpace browser, final String filterCriteria, final EnumSet accessMask, final int variantType) {
+ super(browser);
+ this._filterCriteria = filterCriteria;
+ this._accessMask = accessMask;
+ this._variantType = variantType;
+ }
+
+ /**
+ * Move the tree browser to the root folder
+ *
+ * @throws JIException JIException
+ */
+ protected void moveToRoot() throws JIException {
+ this._browser.changePosition(null, OPCBROWSEDIRECTION.OPC_BROWSE_TO);
+ }
+
+ /**
+ * Move the tree browser to a branch
+ *
+ * @param branch The branch to move to
+ * @throws JIException JIException
+ */
+ protected void moveToBranch(final Branch branch) throws JIException {
+ Collection branchStack = branch.getBranchStack();
+
+ moveToRoot();
+ for (String branchName : branchStack) {
+ this._browser.changePosition(branchName, OPCBROWSEDIRECTION.OPC_BROWSE_DOWN);
+ }
+ }
+
+ /**
+ * Browse the root branch for its sub-branches.
+ *
+ * @return The list of sub-branches
+ * @throws JIException JIException
+ * @throws IllegalArgumentException IllegalArgumentException
+ * @throws UnknownHostException UnknownHostException
+ */
+ public Branch browseBranches() throws JIException, IllegalArgumentException, UnknownHostException {
+ Branch branch = new Branch();
+ fillBranches(branch);
+ return branch;
+ }
+
+ /**
+ * Browse the root branch for this leaves.
+ *
+ * @return The list of leaves
+ * @throws IllegalArgumentException IllegalArgumentException
+ * @throws UnknownHostException UnknownHostException
+ * @throws JIException JIException
+ */
+ public Branch browseLeaves() throws IllegalArgumentException, UnknownHostException, JIException {
+ Branch branch = new Branch();
+ fillLeaves(branch);
+ return branch;
+ }
+
+ /**
+ * Fill the branch list of the provided branch.
+ *
+ * @param branch The branch to fill.
+ * @throws JIException JIException
+ * @throws IllegalArgumentException IllegalArgumentException
+ * @throws UnknownHostException UnknownHostException
+ */
+ public void fillBranches(final Branch branch) throws JIException, IllegalArgumentException, UnknownHostException {
+ moveToBranch(branch);
+ browse(branch, false, true, false);
+ }
+
+ /**
+ * Fill the leaf list of the provided branch.
+ *
+ * @param branch The branch to fill.
+ * @throws IllegalArgumentException IllegalArgumentException
+ * @throws UnknownHostException UnknownHostException
+ * @throws JIException JIException
+ */
+ public void fillLeaves(final Branch branch) throws IllegalArgumentException, UnknownHostException, JIException {
+ moveToBranch(branch);
+ browse(branch, true, false, false);
+ }
+
+ /**
+ * Browse through all levels of the tree browser.
+ *
+ * @return The whole expanded server address space
+ * @throws JIException JIException
+ * @throws IllegalArgumentException IllegalArgumentException
+ * @throws UnknownHostException UnknownHostException
+ */
+ public Branch browse() throws JIException, IllegalArgumentException, UnknownHostException {
+ Branch branch = new Branch();
+ fill(branch);
+ return branch;
+ }
+
+ /**
+ * Fill the leaves and branches of the branch provided branches including
+ * alls sub-branches.
+ *
+ * @param branch The branch to fill.
+ * @throws IllegalArgumentException IllegalArgumentException
+ * @throws UnknownHostException UnknownHostException
+ * @throws JIException JIException
+ */
+ public void fill(final Branch branch) throws IllegalArgumentException, UnknownHostException, JIException {
+ moveToBranch(branch);
+ browse(branch, true, true, true);
+ }
+
+ /**
+ * Fill the branch object with the leaves of this currently selected branch.
+ *
+ * The server object is not located to the branch before browsing!
+ *
+ * @param branch The branch to fill
+ * @throws IllegalArgumentException IllegalArgumentException
+ * @throws UnknownHostException UnknownHostException
+ * @throws JIException JIException
+ */
+ protected void browseLeaves(final Branch branch) throws IllegalArgumentException, UnknownHostException, JIException {
+ branch.setLeaves(new LinkedList());
+
+ for (String item : browse(OPCBROWSETYPE.OPC_LEAF, this._filterCriteria, this._accessMask, this._variantType)) {
+ Leaf leaf = new Leaf(branch, item, this._browser.getItemID(item));
+ branch.getLeaves().add(leaf);
+ }
+ }
+
+ protected void browseBranches(final Branch branch, final boolean leaves, final boolean descend) throws IllegalArgumentException, UnknownHostException, JIException {
+ branch.setBranches(new LinkedList());
+
+ for (String item : browse(OPCBROWSETYPE.OPC_BRANCH, this._filterCriteria, this._accessMask, this._variantType)) {
+ Branch subBranch = new Branch(branch, item);
+ // descend only if we should
+ if (descend) {
+ this._browser.changePosition(item, OPCBROWSEDIRECTION.OPC_BROWSE_DOWN);
+ browse(subBranch, leaves, true, true);
+ this._browser.changePosition(null, OPCBROWSEDIRECTION.OPC_BROWSE_UP);
+ }
+ branch.getBranches().add(subBranch);
+ }
+ }
+
+ protected void browse(final Branch branch, final boolean leaves, final boolean branches, final boolean descend) throws IllegalArgumentException, UnknownHostException, JIException {
+ // process leaves
+ if (leaves) {
+ browseLeaves(branch);
+ }
+
+ // process branches
+ if (branches) {
+ browseBranches(branch, leaves, descend);
+ }
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/list/Categories.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/list/Categories.java
new file mode 100644
index 0000000..cdc8a4a
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/list/Categories.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.list;
+
+public interface Categories {
+ /**
+ * Category of the OPC DA 1.0 Servers
+ */
+ public final static Category OPCDAServer10 = new Category(org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Categories.OPCDAServer10);
+
+ /**
+ * Category of the OPC DA 2.0 Servers
+ */
+ public final static Category OPCDAServer20 = new Category(org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Categories.OPCDAServer20);
+
+ /**
+ * Category of the OPC DA 3.0 Servers
+ */
+ public final static Category OPCDAServer30 = new Category(org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Categories.OPCDAServer30);
+
+ /**
+ * Category of the XML DA 1.0 Servers
+ */
+ public final static Category XMLDAServer10 = new Category(org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.common.Categories.XMLDAServer10);
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/list/Category.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/list/Category.java
new file mode 100644
index 0000000..c118fda
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/list/Category.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.list;
+
+public class Category {
+ private String _catId = null;
+
+ public Category(final String catId) {
+ this._catId = catId;
+ }
+
+ @Override
+ public String toString() {
+ return this._catId;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ result = PRIME * result + (this._catId == null ? 0 : this._catId.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final Category other = (Category) obj;
+ if (this._catId == null) {
+ if (other._catId != null) {
+ return false;
+ }
+ } else if (!this._catId.equals(other._catId)) {
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/list/ServerList.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/list/ServerList.java
new file mode 100644
index 0000000..6fb5a58
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/org/openscada/opc/lib/list/ServerList.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.list;
+
+import org.jinterop.dcom.common.JIException;
+import org.jinterop.dcom.core.JIClsid;
+import org.jinterop.dcom.core.JIComServer;
+import org.jinterop.dcom.core.JISession;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.list.ClassDetails;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.list.Constants;
+import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.dcom.list.impl.OPCServerList;
+import rpc.core.UUID;
+
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * A wrapper around the {@link OPCServerList} class which makes the handling somewhat easier.
+ *
+ * @author Jens Reimann <jens.reimann@th4-systems.com>
+ * @since 0.1.8
+ */
+public class ServerList {
+ private final JISession _session;
+
+ private final OPCServerList _serverList;
+
+ /**
+ * Create a new instance with an already existing session
+ *
+ * @param session the DCOM session
+ * @param host the host to query
+ * @throws IllegalArgumentException IllegalArgumentException
+ * @throws UnknownHostException UnknownHostException
+ * @throws JIException JIException
+ */
+ public ServerList(final JISession session, final String host) throws IllegalArgumentException, UnknownHostException, JIException {
+ this._session = session;
+ JIComServer comServer = new JIComServer(JIClsid.valueOf(Constants.OPCServerList_CLSID), host, this._session);
+ this._serverList = new OPCServerList(comServer.createInstance());
+ }
+
+ /**
+ * Create a new instance and a new DCOM session
+ *
+ * @param host the host to contact
+ * @param user the user to use for authentication
+ * @param password the password to use for authentication
+ * @throws IllegalArgumentException IllegalArgumentException
+ * @throws UnknownHostException UnknownHostException
+ * @throws JIException JIException
+ */
+ public ServerList(final String host, final String user, final String password) throws IllegalArgumentException, UnknownHostException, JIException {
+ this(host, user, password, null);
+ }
+
+ /**
+ * Create a new instance and a new DCOM session
+ *
+ * @param host the host to contact
+ * @param user the user to use for authentication
+ * @param password the password to use for authentication
+ * @param domain The domain to use for authentication
+ * @throws IllegalArgumentException IllegalArgumentException
+ * @throws UnknownHostException UnknownHostException
+ * @throws JIException JIException
+ */
+ public ServerList(final String host, final String user, final String password, final String domain) throws IllegalArgumentException, UnknownHostException, JIException {
+ this(JISession.createSession(domain, user, password), host);
+ }
+
+ /**
+ * Get the details of a opc class
+ *
+ * @param clsId the class to request details for
+ * @return The class details
+ * @throws JIException JIException
+ */
+ public ClassDetails getDetails(final String clsId) throws JIException {
+ return this._serverList.getClassDetails(JIClsid.valueOf(clsId));
+ }
+
+ /**
+ * Fetch the class id of a prog id
+ *
+ * @param progId The prog id to look up
+ * @return the class id or null if none could be found.
+ * @throws JIException JIException
+ */
+ public String getClsIdFromProgId(final String progId) throws JIException {
+ JIClsid cls = this._serverList.getCLSIDFromProgID(progId);
+ if (cls == null) {
+ return null;
+ }
+ return cls.getCLSID();
+ }
+
+ /**
+ * List all servers that match the requirements
+ *
+ * @param implemented All implemented categories
+ * @param required All required categories
+ * @return A collection of class ids
+ * @throws IllegalArgumentException IllegalArgumentException
+ * @throws UnknownHostException UnknownHostException
+ * @throws JIException JIException
+ */
+ public Collection listServers(final Category[] implemented, final Category[] required) throws IllegalArgumentException, UnknownHostException, JIException {
+ // convert the type safe categories to plain UUIDs
+ UUID[] u1 = new UUID[implemented.length];
+ UUID[] u2 = new UUID[required.length];
+
+ for (int i = 0; i < implemented.length; i++) {
+ u1[i] = new UUID(implemented[i].toString());
+ }
+
+ for (int i = 0; i < required.length; i++) {
+ u2[i] = new UUID(required[i].toString());
+ }
+
+ // get them as UUIDs
+ Collection resultU = this._serverList.enumClassesOfCategories(u1, u2).asCollection();
+
+ // and convert to easier usable strings
+ Collection result = new ArrayList(resultU.size());
+ for (UUID uuid : resultU) {
+ result.add(uuid.toString());
+ }
+ return result;
+ }
+
+ /**
+ * List all servers that match the requirements and return the class details
+ *
+ * @param implemented All implemented categories
+ * @param required All required categories
+ * @return a collection of matching server and their class information
+ * @throws IllegalArgumentException IllegalArgumentException
+ * @throws UnknownHostException UnknownHostException
+ * @throws JIException JIException
+ */
+ public Collection listServersWithDetails(final Category[] implemented, final Category[] required) throws IllegalArgumentException, UnknownHostException, JIException {
+ Collection resultString = listServers(implemented, required);
+
+ List result = new ArrayList(resultString.size());
+
+ for (String clsId : resultString) {
+ result.add(getDetails(clsId));
+ }
+
+ return result;
+ }
+}
diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcua/OpcUaProtocolDriverImpl.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcua/OpcUaProtocolDriverImpl.java
new file mode 100644
index 0000000..648cbc1
--- /dev/null
+++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcua/OpcUaProtocolDriverImpl.java
@@ -0,0 +1,225 @@
+package org.nl.iot.core.driver.protocol.opcua;
+
+import jakarta.annotation.PostConstruct;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
+import org.eclipse.milo.opcua.sdk.client.api.identity.AnonymousProvider;
+import org.eclipse.milo.opcua.stack.core.UaException;
+import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
+import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
+import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode;
+import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
+import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned;
+import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
+import org.nl.common.exception.CommonException;
+import org.nl.iot.core.driver.bo.AttributeBO;
+import org.nl.iot.core.driver.entity.RValue;
+import org.nl.iot.core.driver.entity.WValue;
+import org.nl.iot.core.driver.enums.PointTypeFlagEnum;
+import org.nl.iot.core.driver.service.DriverCustomService;
+import org.nl.iot.modular.iot.entity.IotConfig;
+import org.nl.iot.modular.iot.entity.IotConnect;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.*;
+
+@Slf4j
+@Service
+public class OpcUaProtocolDriverImpl implements DriverCustomService {
+ private Map connectMap;
+
+ @PostConstruct
+ @Override
+ public void initial() {
+ /*
+ * 驱动初始化逻辑
+ *
+ * 提示: 此处逻辑仅供参考, 请务必结合实际应用场景进行修改。
+ * 驱动启动时会自动执行该方法, 您可以在此处执行特定的初始化操作。
+ *
+ */
+ connectMap = new ConcurrentHashMap<>(16);
+ }
+
+ @Override
+ public void schedule() {
+
+ }
+
+
+
+ @Override
+ public RValue read(Map driverConfig, Map pointConfig, IotConnect connect, IotConfig config) {
+ return new RValue(config, connect, readValue(getConnector(connect.getId().toString(), driverConfig), pointConfig));
+ }
+
+ /**
+ * 获取 OPC UA 客户端连接
+ *
+ * @param deviceId 设备ID, 用于标识唯一的设备连接
+ * @param driverConfig 驱动配置信息, 包含连接 OPC UA 服务器所需的参数
+ * @return OpcUaClient 返回与指定设备关联的 OPC UA 客户端实例
+ * @throws CommonException 如果连接 OPC UA 服务器失败, 抛出此异常
+ */
+ private OpcUaClient getConnector(String deviceId, Map driverConfig) {
+ log.debug("OPC UA server connection info: {}", driverConfig);
+ OpcUaClient opcUaClient = connectMap.get(deviceId);
+ if (Objects.isNull(opcUaClient)) {
+ String host = driverConfig.get("host").getValueByClass(String.class);
+ int port = driverConfig.get("port").getValueByClass(Integer.class);
+ String path = driverConfig.get("path").getValueByClass(String.class);
+ String url = String.format("opc.tcp://%s:%s%s", host, port, path);
+ try {
+ opcUaClient = OpcUaClient.create(
+ url,
+ endpoints -> endpoints.stream().findFirst(),
+ configBuilder -> configBuilder
+ .setIdentityProvider(new AnonymousProvider()) // 使用匿名身份验证
+ .setRequestTimeout(Unsigned.uint(5000)) // 设置请求超时时间为 5000 毫秒
+ .build()
+ );
+ connectMap.put(deviceId, opcUaClient);
+ } catch (UaException e) {
+ connectMap.entrySet().removeIf(next -> next.getKey().equals(deviceId));
+ log.error("Failed to connect OPC UA client: {}", e.getMessage(), e);
+ throw new CommonException(e.getMessage());
+ }
+ }
+ return opcUaClient;
+ }
+
+ /**
+ * 读取 OPC UA 节点的值
+ *
+ * @param client OPC UA 客户端实例
+ * @param pointConfig 点位配置信息
+ * @return 读取到的节点值
+ * @throws CommonException 如果读取操作失败, 抛出此异常
+ */
+ private String readValue(OpcUaClient client, Map pointConfig) {
+ try {
+ NodeId nodeId = getNode(pointConfig);
+ // 确保客户端已连接
+ client.connect().get(10, TimeUnit.SECONDS);
+
+ // 直接使用同步方式读取值,设置更长的超时时间
+ DataValue dataValue = client.readValue(0.0, TimestampsToReturn.Both, nodeId).get(10, TimeUnit.SECONDS);
+
+ // 检查读取状态
+ if (dataValue.getStatusCode().isGood()) {
+ Object value = dataValue.getValue().getValue();
+ return value != null ? value.toString() : null;
+ } else {
+ log.error("Read opc ua value failed with status: {}", dataValue.getStatusCode());
+ throw new CommonException("Read failed with status: " + dataValue.getStatusCode());
+ }
+ } catch (InterruptedException e) {
+ log.error("Read opc ua value error: {}", e.getMessage(), e);
+ Thread.currentThread().interrupt();
+ throw new CommonException(e.getMessage());
+ } catch (ExecutionException | TimeoutException e) {
+ log.error("Read opc ua value error: {}", e.getMessage(), e);
+ throw new CommonException(e.getMessage());
+ }
+ }
+
+ @Override
+ public Boolean write(Map driverConfig, Map pointConfig, IotConnect device, IotConfig point, WValue wValue) {
+ OpcUaClient client = getConnector(device.getId().toString(), driverConfig);
+ return writeValue(client, pointConfig, wValue);
+ }
+
+ /**
+ * 写入 OPC UA 节点的值
+ *
+ * @param client OPC UA 客户端实例
+ * @param pointConfig 点位配置信息
+ * @param wValue 写入值
+ * @return 写入操作是否成功
+ * @throws CommonException 如果写入操作失败, 抛出此异常
+ */
+ private boolean writeValue(OpcUaClient client, Map pointConfig, WValue wValue) {
+ try {
+ NodeId nodeId = getNode(pointConfig);
+ // 确保客户端已连接,设置超时时间
+ client.connect().get(10, TimeUnit.SECONDS);
+ return writeNode(client, nodeId, wValue);
+ } catch (InterruptedException e) {
+ log.error("Write opc ua value error: {}", e.getMessage(), e);
+ Thread.currentThread().interrupt();
+ throw new CommonException(e.getMessage());
+ } catch (ExecutionException | TimeoutException e) {
+ log.error("Write opc ua value error: {}", e.getMessage(), e);
+ throw new CommonException(e.getMessage());
+ }
+ }
+
+ /**
+ * 根据点位配置获取 OPC UA 节点
+ *
+ * @param pointConfig 点位配置信息, 包含命名空间和标签
+ * @return OPC UA 节点标识
+ */
+ private NodeId getNode(Map pointConfig) {
+ int namespace = pointConfig.get("namespace").getValueByClass(Integer.class);
+ String tag = pointConfig.get("tag").getValueByClass(String.class);
+ return new NodeId(namespace, tag);
+ }
+
+ /**
+ * 将值写入 OPC UA 节点
+ *
+ * @param client OPC UA 客户端实例
+ * @param nodeId OPC UA 节点标识
+ * @param wValue 待写入的值
+ * @return 写入操作是否成功
+ * @throws ExecutionException 写入操作失败时抛出
+ * @throws InterruptedException 写入操作被中断时抛出
+ * @throws TimeoutException 写入操作超时时抛出
+ */
+ private boolean writeNode(OpcUaClient client, NodeId nodeId, WValue wValue) throws ExecutionException, InterruptedException, TimeoutException {
+ PointTypeFlagEnum valueType = PointTypeFlagEnum.ofCode(wValue.getType());
+ if (Objects.isNull(valueType)) {
+ throw new CommonException("Unsupported type of " + wValue.getType());
+ }
+
+ CompletableFuture status = null;
+ switch (valueType) {
+ case INT:
+ int intValue = wValue.getValueByClass(Integer.class);
+ status = client.writeValue(nodeId, new DataValue(new Variant(intValue)));
+ break;
+ case LONG:
+ long longValue = wValue.getValueByClass(Long.class);
+ status = client.writeValue(nodeId, new DataValue(new Variant(longValue)));
+ break;
+ case FLOAT:
+ float floatValue = wValue.getValueByClass(Float.class);
+ status = client.writeValue(nodeId, new DataValue(new Variant(floatValue)));
+ break;
+ case DOUBLE:
+ double doubleValue = wValue.getValueByClass(Double.class);
+ status = client.writeValue(nodeId, new DataValue(new Variant(doubleValue)));
+ break;
+ case BOOLEAN:
+ boolean booleanValue = wValue.getValueByClass(Boolean.class);
+ status = client.writeValue(nodeId, new DataValue(new Variant(booleanValue)));
+ break;
+ case STRING:
+ status = client.writeValue(nodeId, new DataValue(new Variant(wValue.getValue())));
+ break;
+ default:
+ break;
+ }
+
+ if (Objects.nonNull(status)) {
+ StatusCode statusCode = status.get(10, TimeUnit.SECONDS);
+ if (Objects.nonNull(statusCode)) {
+ return statusCode.isGood();
+ }
+ }
+ return false;
+ }
+}