From 6d72a9b91e00c5d26fef17ff09ec7e4e979d6fef Mon Sep 17 00:00:00 2001 From: luoyan35714 Date: Wed, 10 Dec 2014 17:42:02 +0800 Subject: [PATCH] Add the Utgard resources --- OPC_Client_Utgard/.classpath | 19 +- OPC_Client_Utgard/pom.xml | 33 +- .../com/freud/opc/utgard/cases/OPCTest4.java | 1 - .../com/freud/opc/utgard/cases/OPCTest5.java | 1 - .../com/freud/opc/utgard/cases/OPCTest6.java | 1 - OPC_Client_Utgard_Performance/.classpath | 19 +- OPC_Client_Utgard_Performance/.project | 2 + ...org.openscada.opc.dcom_1.1.0.v20130529.jar | Bin 58208 -> 0 bytes .../org.openscada.opc.lib_1.1.0.v20130529.jar | Bin 53137 -> 0 bytes OPC_Client_Utgard_Performance/pom.xml | 32 +- .../com/freud/opc/dcom/perf/SyncPerfTest.java | 11 +- .../opc/utgard/perf/AsyncMultiThreadTest.java | 118 +++++ .../opc/utgard/perf/AsyncOPCPerfTest.java | 81 ++- .../opc/utgard/perf/PublishOPCPerfTest.java | 11 +- .../java/com/freud/opc/utgard/perf/Test.java | 67 --- .../perf/common/DataCallBackListener.java | 38 -- .../utgard_result.log | 113 ++++- .../utgard_result.log.2014-12-09 | 12 + org.openscada.opc.dcom.test/.classpath | 16 + org.openscada.opc.dcom.test/.gitignore | 1 + org.openscada.opc.dcom.test/.project | 46 ++ org.openscada.opc.dcom.test/LICENSE | 165 ++++++ org.openscada.opc.dcom.test/pom.xml | 31 ++ .../opc/dcom/common/FiletimeTest1.java | 86 ++++ .../opc/dcom/da/DumpDataCallback.java | 59 +++ .../IconicsSimulationServerConfiguration.java | 46 ++ ...MatrikonSimulationServerConfiguration.java | 47 ++ .../da/SoftingDemoServerConfiguration.java | 45 ++ .../src/org/openscada/opc/dcom/da/Test1.java | 475 ++++++++++++++++++ .../src/org/openscada/opc/dcom/da/Test2.java | 214 ++++++++ .../opc/dcom/da/TestConfiguration.java | 31 ++ .../org/openscada/opc/dcom/da/WriteTest.java | 56 +++ org.openscada.opc.dcom/.classpath | 16 + org.openscada.opc.dcom/.gitignore | 2 + org.openscada.opc.dcom/.project | 46 ++ org.openscada.opc.dcom/build.properties | 1 + org.openscada.opc.dcom/pom.xml | 26 + .../openscada/opc/dcom/common/Categories.java | 32 ++ .../openscada/opc/dcom/common/Constants.java | 37 ++ .../opc/dcom/common/EventHandler.java | 32 ++ .../openscada/opc/dcom/common/FILETIME.java | 174 +++++++ .../opc/dcom/common/KeyedResult.java | 46 ++ .../opc/dcom/common/KeyedResultSet.java | 37 ++ .../org/openscada/opc/dcom/common/Result.java | 64 +++ .../openscada/opc/dcom/common/ResultSet.java | 38 ++ .../opc/dcom/common/impl/BaseCOMObject.java | 45 ++ .../opc/dcom/common/impl/EnumGUID.java | 134 +++++ .../opc/dcom/common/impl/EnumString.java | 136 +++++ .../dcom/common/impl/EventHandlerImpl.java | 65 +++ .../opc/dcom/common/impl/Helper.java | 71 +++ .../opc/dcom/common/impl/OPCCommon.java | 101 ++++ .../org/openscada/opc/dcom/da/Constants.java | 43 ++ .../opc/dcom/da/IOPCDataCallback.java | 34 ++ .../org/openscada/opc/dcom/da/IORequest.java | 56 +++ .../opc/dcom/da/OPCBROWSEDIRECTION.java | 55 ++ .../openscada/opc/dcom/da/OPCBROWSETYPE.java | 55 ++ .../openscada/opc/dcom/da/OPCDATASOURCE.java | 52 ++ .../openscada/opc/dcom/da/OPCENUMSCOPE.java | 64 +++ .../openscada/opc/dcom/da/OPCGroupState.java | 119 +++++ .../org/openscada/opc/dcom/da/OPCITEMDEF.java | 124 +++++ .../openscada/opc/dcom/da/OPCITEMRESULT.java | 104 ++++ .../openscada/opc/dcom/da/OPCITEMSTATE.java | 114 +++++ .../opc/dcom/da/OPCNAMESPACETYPE.java | 52 ++ .../openscada/opc/dcom/da/OPCSERVERSTATE.java | 64 +++ .../opc/dcom/da/OPCSERVERSTATUS.java | 201 ++++++++ .../opc/dcom/da/PropertyDescription.java | 59 +++ .../org/openscada/opc/dcom/da/ValueData.java | 65 +++ .../openscada/opc/dcom/da/WriteRequest.java | 80 +++ .../opc/dcom/da/impl/OPCAsyncIO2.java | 141 ++++++ .../openscada/opc/dcom/da/impl/OPCBrowse.java | 35 ++ .../da/impl/OPCBrowseServerAddressSpace.java | 157 ++++++ .../opc/dcom/da/impl/OPCDataCallback.java | 259 ++++++++++ .../opc/dcom/da/impl/OPCGroupStateMgt.java | 218 ++++++++ .../openscada/opc/dcom/da/impl/OPCItemIO.java | 73 +++ .../opc/dcom/da/impl/OPCItemMgt.java | 211 ++++++++ .../opc/dcom/da/impl/OPCItemProperties.java | 154 ++++++ .../openscada/opc/dcom/da/impl/OPCServer.java | 191 +++++++ .../openscada/opc/dcom/da/impl/OPCSyncIO.java | 113 +++++ .../openscada/opc/dcom/list/ClassDetails.java | 64 +++ .../openscada/opc/dcom/list/Constants.java | 27 + .../opc/dcom/list/impl/OPCServerList.java | 168 +++++++ .../src/progIdVsClsidDB.properties | 13 + org.openscada.opc.lib.test/.classpath | 15 + org.openscada.opc.lib.test/.gitignore | 1 + org.openscada.opc.lib.test/.project | 46 ++ org.openscada.opc.lib.test/LICENSE | 165 ++++++ org.openscada.opc.lib.test/pom.xml | 37 ++ .../openscada/opc/lib/DataCallbackDumper.java | 45 ++ .../src/org/openscada/opc/lib/OPCTest1.java | 112 +++++ .../src/org/openscada/opc/lib/OPCTest2.java | 79 +++ .../src/org/openscada/opc/lib/OPCTest3.java | 130 +++++ .../src/org/openscada/opc/lib/OPCTest4.java | 93 ++++ .../src/org/openscada/opc/lib/OPCTest5.java | 80 +++ .../src/org/openscada/opc/lib/OPCTest6.java | 92 ++++ .../src/org/openscada/opc/lib/OPCTest7.java | 110 ++++ .../src/org/openscada/opc/lib/OPCTest8.java | 67 +++ .../org/openscada/opc/lib/OPCTestInfo.java | 35 ++ .../org/openscada/opc/lib/VariantDumper.java | 134 +++++ org.openscada.opc.lib/.classpath | 16 + org.openscada.opc.lib/.gitignore | 2 + org.openscada.opc.lib/.project | 46 ++ org.openscada.opc.lib/LICENSE | 165 ++++++ org.openscada.opc.lib/build.properties | 1 + org.openscada.opc.lib/pom.xml | 33 ++ .../lib/common/AlreadyConnectedException.java | 30 ++ .../opc/lib/common/ConnectionInformation.java | 149 ++++++ .../opc/lib/common/NotConnectedException.java | 30 ++ .../org/openscada/opc/lib/da/AccessBase.java | 359 +++++++++++++ .../opc/lib/da/AccessStateListener.java | 27 + .../opc/lib/da/AddFailedException.java | 61 +++ .../openscada/opc/lib/da/Async20Access.java | 136 +++++ .../opc/lib/da/AutoReconnectController.java | 230 +++++++++ .../opc/lib/da/AutoReconnectListener.java | 25 + .../opc/lib/da/AutoReconnectState.java | 51 ++ .../openscada/opc/lib/da/DataCallback.java | 25 + .../opc/lib/da/DuplicateGroupException.java | 30 ++ .../opc/lib/da/ErrorMessageResolver.java | 80 +++ .../src/org/openscada/opc/lib/da/Group.java | 437 ++++++++++++++++ .../src/org/openscada/opc/lib/da/Item.java | 83 +++ .../org/openscada/opc/lib/da/ItemState.java | 163 ++++++ .../src/org/openscada/opc/lib/da/Server.java | 445 ++++++++++++++++ .../lib/da/ServerConnectionStateListener.java | 25 + .../opc/lib/da/ServerStateListener.java | 27 + .../opc/lib/da/ServerStateOperation.java | 123 +++++ .../opc/lib/da/ServerStateReader.java | 108 ++++ .../org/openscada/opc/lib/da/SyncAccess.java | 120 +++++ .../opc/lib/da/UnknownGroupException.java | 47 ++ .../openscada/opc/lib/da/WriteRequest.java | 46 ++ .../openscada/opc/lib/da/browser/Access.java | 38 ++ .../opc/lib/da/browser/BaseBrowser.java | 124 +++++ .../openscada/opc/lib/da/browser/Branch.java | 123 +++++ .../opc/lib/da/browser/FlatBrowser.java | 78 +++ .../openscada/opc/lib/da/browser/Leaf.java | 68 +++ .../opc/lib/da/browser/TreeBrowser.java | 240 +++++++++ .../openscada/opc/lib/list/Categories.java | 43 ++ .../org/openscada/opc/lib/list/Category.java | 76 +++ .../openscada/opc/lib/list/ServerList.java | 179 +++++++ 137 files changed, 11245 insertions(+), 230 deletions(-) delete mode 100644 OPC_Client_Utgard_Performance/lib/org.openscada.opc.dcom_1.1.0.v20130529.jar delete mode 100644 OPC_Client_Utgard_Performance/lib/org.openscada.opc.lib_1.1.0.v20130529.jar create mode 100644 OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/AsyncMultiThreadTest.java delete mode 100644 OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/Test.java delete mode 100644 OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/common/DataCallBackListener.java create mode 100644 OPC_Client_Utgard_Performance/utgard_result.log.2014-12-09 create mode 100644 org.openscada.opc.dcom.test/.classpath create mode 100644 org.openscada.opc.dcom.test/.gitignore create mode 100644 org.openscada.opc.dcom.test/.project create mode 100644 org.openscada.opc.dcom.test/LICENSE create mode 100644 org.openscada.opc.dcom.test/pom.xml create mode 100644 org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/common/FiletimeTest1.java create mode 100644 org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/DumpDataCallback.java create mode 100644 org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/IconicsSimulationServerConfiguration.java create mode 100644 org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/MatrikonSimulationServerConfiguration.java create mode 100644 org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/SoftingDemoServerConfiguration.java create mode 100644 org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/Test1.java create mode 100644 org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/Test2.java create mode 100644 org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/TestConfiguration.java create mode 100644 org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/WriteTest.java create mode 100644 org.openscada.opc.dcom/.classpath create mode 100644 org.openscada.opc.dcom/.gitignore create mode 100644 org.openscada.opc.dcom/.project create mode 100644 org.openscada.opc.dcom/build.properties create mode 100644 org.openscada.opc.dcom/pom.xml create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/Categories.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/Constants.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/EventHandler.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/FILETIME.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/KeyedResult.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/KeyedResultSet.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/Result.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/ResultSet.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/BaseCOMObject.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/EnumGUID.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/EnumString.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/EventHandlerImpl.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/Helper.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/OPCCommon.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/Constants.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/IOPCDataCallback.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/IORequest.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCBROWSEDIRECTION.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCBROWSETYPE.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCDATASOURCE.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCENUMSCOPE.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCGroupState.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCITEMDEF.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCITEMRESULT.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCITEMSTATE.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCNAMESPACETYPE.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCSERVERSTATE.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCSERVERSTATUS.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/PropertyDescription.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/ValueData.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/WriteRequest.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCAsyncIO2.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCBrowse.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCBrowseServerAddressSpace.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCDataCallback.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCGroupStateMgt.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCItemIO.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCItemMgt.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCItemProperties.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCServer.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCSyncIO.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/list/ClassDetails.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/list/Constants.java create mode 100644 org.openscada.opc.dcom/src/org/openscada/opc/dcom/list/impl/OPCServerList.java create mode 100644 org.openscada.opc.dcom/src/progIdVsClsidDB.properties create mode 100644 org.openscada.opc.lib.test/.classpath create mode 100644 org.openscada.opc.lib.test/.gitignore create mode 100644 org.openscada.opc.lib.test/.project create mode 100644 org.openscada.opc.lib.test/LICENSE create mode 100644 org.openscada.opc.lib.test/pom.xml create mode 100644 org.openscada.opc.lib.test/src/org/openscada/opc/lib/DataCallbackDumper.java create mode 100644 org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest1.java create mode 100644 org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest2.java create mode 100644 org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest3.java create mode 100644 org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest4.java create mode 100644 org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest5.java create mode 100644 org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest6.java create mode 100644 org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest7.java create mode 100644 org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest8.java create mode 100644 org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTestInfo.java create mode 100644 org.openscada.opc.lib.test/src/org/openscada/opc/lib/VariantDumper.java create mode 100644 org.openscada.opc.lib/.classpath create mode 100644 org.openscada.opc.lib/.gitignore create mode 100644 org.openscada.opc.lib/.project create mode 100644 org.openscada.opc.lib/LICENSE create mode 100644 org.openscada.opc.lib/build.properties create mode 100644 org.openscada.opc.lib/pom.xml create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/common/AlreadyConnectedException.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/common/ConnectionInformation.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/common/NotConnectedException.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/AccessBase.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/AccessStateListener.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/AddFailedException.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/Async20Access.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/AutoReconnectController.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/AutoReconnectListener.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/AutoReconnectState.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/DataCallback.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/DuplicateGroupException.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/ErrorMessageResolver.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/Group.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/Item.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/ItemState.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/Server.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/ServerConnectionStateListener.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/ServerStateListener.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/ServerStateOperation.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/ServerStateReader.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/SyncAccess.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/UnknownGroupException.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/WriteRequest.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/Access.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/BaseBrowser.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/Branch.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/FlatBrowser.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/Leaf.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/TreeBrowser.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/list/Categories.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/list/Category.java create mode 100644 org.openscada.opc.lib/src/org/openscada/opc/lib/list/ServerList.java diff --git a/OPC_Client_Utgard/.classpath b/OPC_Client_Utgard/.classpath index 7136062..850c2f7 100644 --- a/OPC_Client_Utgard/.classpath +++ b/OPC_Client_Utgard/.classpath @@ -1,10 +1,19 @@ - - - - - + + + + + + + + + + + + + + diff --git a/OPC_Client_Utgard/pom.xml b/OPC_Client_Utgard/pom.xml index e23bae9..25e983b 100644 --- a/OPC_Client_Utgard/pom.xml +++ b/OPC_Client_Utgard/pom.xml @@ -4,27 +4,6 @@ com.freud.opc OPC_Client_Utgard 0.0.1-SNAPSHOT - - src - - - src - - **/*.java - - - - - - maven-compiler-plugin - 3.0 - - 1.6 - 1.6 - - - - @@ -37,6 +16,18 @@ j-interop 2.0.5 + + + org.openscada.opc.dcom + org.openscada.opc.dcom + 0.0.1-SNAPSHOT + + + + org.openscada.utgard + org.openscada.opc.lib + 1.1.0.v20130529 + \ No newline at end of file diff --git a/OPC_Client_Utgard/src/main/java/com/freud/opc/utgard/cases/OPCTest4.java b/OPC_Client_Utgard/src/main/java/com/freud/opc/utgard/cases/OPCTest4.java index 390f9be..ebb3a93 100644 --- a/OPC_Client_Utgard/src/main/java/com/freud/opc/utgard/cases/OPCTest4.java +++ b/OPC_Client_Utgard/src/main/java/com/freud/opc/utgard/cases/OPCTest4.java @@ -44,7 +44,6 @@ public class OPCTest4 { access.addItem("Random.Real5", new DataCallback() { private int i; - @Override public void changed(Item item, ItemState itemstate) { System.out.println("[" + (++i) + "],ItemName:[" + item.getId() + "],value:" + itemstate.getValue()); diff --git a/OPC_Client_Utgard/src/main/java/com/freud/opc/utgard/cases/OPCTest5.java b/OPC_Client_Utgard/src/main/java/com/freud/opc/utgard/cases/OPCTest5.java index d37edba..0a6c705 100644 --- a/OPC_Client_Utgard/src/main/java/com/freud/opc/utgard/cases/OPCTest5.java +++ b/OPC_Client_Utgard/src/main/java/com/freud/opc/utgard/cases/OPCTest5.java @@ -45,7 +45,6 @@ public class OPCTest5 { private int count; - @Override public void changed(Item item, ItemState itemstate) { System.out.println("[" + (++count) + "],ItemName:[" + item.getId() + "],value:" + itemstate.getValue()); diff --git a/OPC_Client_Utgard/src/main/java/com/freud/opc/utgard/cases/OPCTest6.java b/OPC_Client_Utgard/src/main/java/com/freud/opc/utgard/cases/OPCTest6.java index 3685d6b..593877d 100644 --- a/OPC_Client_Utgard/src/main/java/com/freud/opc/utgard/cases/OPCTest6.java +++ b/OPC_Client_Utgard/src/main/java/com/freud/opc/utgard/cases/OPCTest6.java @@ -47,7 +47,6 @@ public class OPCTest6 { access.addItem("Random.Real5", new DataCallback() { private int i; - @Override public void changed(Item item, ItemState itemstate) { System.out.println("[" + (++i) + "],ItemName:[" + item.getId() + "],value:" + itemstate.getValue()); diff --git a/OPC_Client_Utgard_Performance/.classpath b/OPC_Client_Utgard_Performance/.classpath index 015fbd2..7ed68b1 100644 --- a/OPC_Client_Utgard_Performance/.classpath +++ b/OPC_Client_Utgard_Performance/.classpath @@ -1,10 +1,19 @@ - - - - - + + + + + + + + + + + + + + diff --git a/OPC_Client_Utgard_Performance/.project b/OPC_Client_Utgard_Performance/.project index c6b1fe4..f83eb1b 100644 --- a/OPC_Client_Utgard_Performance/.project +++ b/OPC_Client_Utgard_Performance/.project @@ -3,6 +3,8 @@ OPC_Client_Utgard_Performance + org.openscada.opc.dcom + org.openscada.opc.lib diff --git a/OPC_Client_Utgard_Performance/lib/org.openscada.opc.dcom_1.1.0.v20130529.jar b/OPC_Client_Utgard_Performance/lib/org.openscada.opc.dcom_1.1.0.v20130529.jar deleted file mode 100644 index 11bbdf74cfe6e84215a9b8d336a37b771f890d35..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58208 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 aGhRvz1W>c*#S1FHPaRcom.freud.opc OPC_Client_Utgard_Performance 0.0.1-SNAPSHOT - - src - - - maven-compiler-plugin - 3.0 - - 1.6 - 1.6 - - - - - org.slf4j slf4j-log4j12 @@ -42,6 +22,18 @@ j-interop 2.0.5 + + + org.openscada.opc.dcom + org.openscada.opc.dcom + 0.0.1-SNAPSHOT + + + + org.openscada.utgard + org.openscada.opc.lib + 1.1.0.v20130529 + \ No newline at end of file diff --git a/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/dcom/perf/SyncPerfTest.java b/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/dcom/perf/SyncPerfTest.java index 8554936..ea0f5a7 100644 --- a/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/dcom/perf/SyncPerfTest.java +++ b/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/dcom/perf/SyncPerfTest.java @@ -47,8 +47,8 @@ public class SyncPerfTest { OPCGroupStateMgt group = server.addGroup("", true, 1000, 1234, 0, 0.0f, 1033); - //group.getSyncIO().read(source, serverHandles) - + // group.getSyncIO().read(source, serverHandles) + OPCITEMDEF item = new OPCITEMDEF(); item.setActive(true); @@ -57,18 +57,13 @@ public class SyncPerfTest { group.attach(new IOPCDataCallback() { - @Override public void writeComplete(int arg0, int arg1, int arg2, ResultSet arg3) { } - - @Override public void readComplete(int arg0, int arg1, int arg2, int arg3, KeyedResultSet arg4) { } - - @Override public void dataChange(int arg0, int arg1, int arg2, int arg3, KeyedResultSet items) { for (KeyedResult item : items) { @@ -76,8 +71,6 @@ public class SyncPerfTest { + item.getValue()); } } - - @Override public void cancelComplete(int arg0, int arg1) { } diff --git a/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/AsyncMultiThreadTest.java b/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/AsyncMultiThreadTest.java new file mode 100644 index 0000000..9c72ab8 --- /dev/null +++ b/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/AsyncMultiThreadTest.java @@ -0,0 +1,118 @@ +package com.freud.opc.utgard.perf; + +import static com.freud.opc.utgard.perf.config.ConfigReader.config; + +import java.util.Date; +import java.util.concurrent.Executors; + +import org.apache.commons.logging.Log; +import org.apache.log4j.Logger; +import org.openscada.opc.dcom.common.KeyedResult; +import org.openscada.opc.dcom.common.KeyedResultSet; +import org.openscada.opc.dcom.common.ResultSet; +import org.openscada.opc.dcom.da.IOPCDataCallback; +import org.openscada.opc.dcom.da.OPCDATASOURCE; +import org.openscada.opc.dcom.da.ValueData; +import org.openscada.opc.dcom.da.impl.OPCAsyncIO2; +import org.openscada.opc.lib.common.NotConnectedException; +import org.openscada.opc.lib.da.Group; +import org.openscada.opc.lib.da.Item; +import org.openscada.opc.lib.da.Server; + +public class AsyncMultiThreadTest { + + private static final int count = 50; + + public static void main(String[] args) throws Exception { + for (int i = 1; i <= count; i++) { + new Thread(new AsyncMulti(i)).start(); + } + } +} + +class AsyncMulti implements Runnable { + + private static Logger LOGGER = Logger.getLogger(AsyncMulti.class); + + private static final int NUMBER = 4000; + private int count_number; + + private static long start; + private static long read; + private static long end; + + public AsyncMulti(int count_number) { + this.count_number = count_number; + } + + public void run() { + try { + start = System.currentTimeMillis(); + + LOGGER.info("Step-" + count_number * NUMBER + " site:"); + LOGGER.info("StartDate[" + new Date() + "],CurrentMillis:" + start); + + Server server = new Server(config(), + Executors.newScheduledThreadPool(4)); + + server.connect(); + + final Group group = server.addGroup("Group-" + count_number); + group.setActive(true); + Item[] items = new Item[NUMBER]; + + for (int i = (count_number - 1) * NUMBER; i < count_number * NUMBER; i++) { + items[i % NUMBER] = group.addItem("Random.Int" + i); + } + + read = System.currentTimeMillis(); + group.attach(new IOPCDataCallback() { + + public void writeComplete(int arg0, int arg1, int arg2, + ResultSet arg3) { + } + + public void readComplete(int arg0, int arg1, int arg2, + int arg3, KeyedResultSet arg4) { + } + + public void dataChange(int arg0, int arg1, int arg2, int arg3, + KeyedResultSet result) { + int i = 0; + for (final KeyedResult entry : result) { + i++; + } + + if (i == NUMBER) { + end = System.currentTimeMillis(); + LOGGER.info("Total Use[" + (end - start) + + "] and Async Read[" + (end - read) + "]"); + } + + } + + public void cancelComplete(int arg0, int arg1) { + } + }); + + final OPCAsyncIO2 async20 = group.getAsyncIO20(); + if (async20 == null) { + throw new NotConnectedException(); + } + + group.getAsyncIO20().refresh(OPCDATASOURCE.OPC_DS_CACHE, 0); + + group.setActive(false); + + Thread.sleep(3000); + + group.clear(); + group.remove(); + + server.disconnect(); + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/AsyncOPCPerfTest.java b/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/AsyncOPCPerfTest.java index c46dc91..f035278 100644 --- a/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/AsyncOPCPerfTest.java +++ b/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/AsyncOPCPerfTest.java @@ -3,7 +3,6 @@ package com.freud.opc.utgard.perf; import static com.freud.opc.utgard.perf.config.ConfigReader.config; import java.util.Date; -import java.util.Iterator; import java.util.concurrent.Executors; import org.apache.log4j.Logger; @@ -16,18 +15,19 @@ import org.openscada.opc.dcom.da.ValueData; import org.openscada.opc.dcom.da.impl.OPCAsyncIO2; import org.openscada.opc.lib.common.NotConnectedException; import org.openscada.opc.lib.da.Group; +import org.openscada.opc.lib.da.Item; import org.openscada.opc.lib.da.Server; public class AsyncOPCPerfTest { private static Logger LOGGER = Logger.getLogger(AsyncOPCPerfTest.class); - private static final int NUMBER = 10; - private static final int count = 1; - public static long start; - - public static void main(String[] args) throws Exception { + private static final int count = 4000; + private static long start; + private static long read; + private static long end; + private static void testSteps(final int count) throws Exception { start = System.currentTimeMillis(); LOGGER.info("Step-" + count + "W:"); @@ -38,62 +38,61 @@ public class AsyncOPCPerfTest { server.connect(); - Group group = server.addGroup("Freud"); - // AsyncResult result = group.getAsyncIO20().read(transactionId, - // serverHandles); + Group group = server.addGroup("Group"); + group.setActive(true); + Item[] items = new Item[count]; - group.addItem("Read Error.Int1"); - group.addItem("Random.Int1"); - - OPCAsyncIO2 async20 = group.getAsyncIO20(); - - if (async20 == null) - throw new NotConnectedException(); - async20.refresh(OPCDATASOURCE.OPC_DS_DEVICE, 0); + for (int i = 1; i <= count; i++) { + items[i - 1] = group.addItem("Random.Int" + i); + } + read = System.currentTimeMillis(); group.attach(new IOPCDataCallback() { - @Override public void writeComplete(int arg0, int arg1, int arg2, ResultSet arg3) { - System.out.println("Write Complete"); } - @Override public void readComplete(int arg0, int arg1, int arg2, int arg3, KeyedResultSet arg4) { - System.out.println("Read Complete"); } - @Override public void dataChange(int arg0, int arg1, int arg2, int arg3, - KeyedResultSet arg4) { - Iterator> ite = arg4.iterator(); + KeyedResultSet result) { int i = 0; - while (ite.hasNext()) { - KeyedResult value = ite.next(); - System.out.println((++i) + "-" + value.getKey() + "--" - + value.getValue().getValue()); + for (final KeyedResult entry : result) { + i++; + } + + if (i == count) { + end = System.currentTimeMillis(); + LOGGER.info("Total Use[" + (end - start) + + "] and Async Read[" + (end - read) + "]"); } } - @Override public void cancelComplete(int arg0, int arg1) { - System.out.println("Cancel Complete"); } }); - // AccessBase access = new SyncAccess(server, 1000); - // - // int limit = count * NUMBER; - // for (int i = 0; i < limit; i++) { - // access.addItem("Random.Real" + i, new DataCallBackListener(limit)); - // } - // - // access.bind(); - Thread.sleep(100000); - // access.unbind(); + final OPCAsyncIO2 async20 = group.getAsyncIO20(); + if (async20 == null) { + throw new NotConnectedException(); + } - server.dispose(); + group.getAsyncIO20().refresh(OPCDATASOURCE.OPC_DS_CACHE, 0); + + group.setActive(false); + + Thread.sleep(3000); + + group.clear(); + group.remove(); + + server.disconnect(); + } + + public static void main(String[] args) throws Exception { + testSteps(count); } } diff --git a/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/PublishOPCPerfTest.java b/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/PublishOPCPerfTest.java index 843cda7..5c50267 100644 --- a/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/PublishOPCPerfTest.java +++ b/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/PublishOPCPerfTest.java @@ -3,7 +3,6 @@ package com.freud.opc.utgard.perf; import static com.freud.opc.utgard.perf.config.ConfigReader.config; import java.util.Date; -import java.util.Map; import java.util.concurrent.Executors; import org.apache.log4j.Logger; @@ -11,16 +10,14 @@ import org.openscada.opc.dcom.common.KeyedResultSet; import org.openscada.opc.dcom.common.ResultSet; import org.openscada.opc.dcom.da.IOPCDataCallback; import org.openscada.opc.dcom.da.ValueData; -import org.openscada.opc.lib.da.Async20Access; import org.openscada.opc.lib.da.Group; -import org.openscada.opc.lib.da.Item; import org.openscada.opc.lib.da.Server; public class PublishOPCPerfTest { private static Logger LOGGER = Logger.getLogger(PublishOPCPerfTest.class); - private static final int NUMBER = 10000; + //private static final int NUMBER = 10000; private static final int WAN_NUMBER = 1; public static void main(String[] args) throws Exception { @@ -42,27 +39,23 @@ public class PublishOPCPerfTest { Group group = server.addGroup("Group"); - Map map = group.addItems("Random.int" + 1); + //Map map = group.addItems("Random.int" + 1); group.attach(new IOPCDataCallback() { - @Override public void writeComplete(int i, int j, int k, ResultSet resultset) { } - @Override public void readComplete(int i, int j, int k, int l, KeyedResultSet keyedresultset) { } - @Override public void dataChange(int i, int j, int k, int l, KeyedResultSet keyedresultset) { System.out.println("DataChanged"); } - @Override public void cancelComplete(int i, int j) { } }); diff --git a/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/Test.java b/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/Test.java deleted file mode 100644 index 9af9079..0000000 --- a/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/Test.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.freud.opc.utgard.perf; - -import static com.freud.opc.utgard.perf.config.ConfigReader.config; - -import java.util.Date; -import java.util.concurrent.Executors; - -import org.apache.log4j.Logger; -import org.openscada.opc.dcom.common.KeyedResultSet; -import org.openscada.opc.dcom.common.ResultSet; -import org.openscada.opc.dcom.da.IOPCDataCallback; -import org.openscada.opc.dcom.da.ValueData; -import org.openscada.opc.lib.da.Group; -import org.openscada.opc.lib.da.Item; -import org.openscada.opc.lib.da.Server; - -public class Test { - private static Logger LOGGER = Logger.getLogger(Test.class); - - public static void main(String[] args) throws Exception { - long start = System.currentTimeMillis(); - - LOGGER.info("Step-" + 10 + "W:"); - LOGGER.info("StartDate[" + new Date() + "],CurrentMillis:" + start); - - Server server = new Server(config(), - Executors.newSingleThreadScheduledExecutor()); - - server.connect(); - - Group group = server.addGroup(); - Item item = group.addItem("Random.int4"); - - group.setActive(true); - group.setActive(true, item); - - group.attach(new IOPCDataCallback() { - - @Override - public void writeComplete(int i, int j, int k, - ResultSet resultset) { - System.out.println("Write Complete"); - } - - @Override - public void readComplete(int i, int j, int k, int l, - KeyedResultSet keyedresultset) { - System.out.println("Read Complete"); - } - - @Override - public void dataChange(int i, int j, int k, int l, - KeyedResultSet keyedresultset) { - System.out.println("Data change"); - } - - @Override - public void cancelComplete(int i, int j) { - System.out.println("Cancel Complete"); - } - }); - - Thread.sleep(10 * 1000); - - server.disconnect(); - } -} diff --git a/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/common/DataCallBackListener.java b/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/common/DataCallBackListener.java deleted file mode 100644 index de43598..0000000 --- a/OPC_Client_Utgard_Performance/src/main/java/com/freud/opc/utgard/perf/common/DataCallBackListener.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.freud.opc.utgard.perf.common; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import org.apache.log4j.Logger; -import org.openscada.opc.lib.da.DataCallback; -import org.openscada.opc.lib.da.Item; -import org.openscada.opc.lib.da.ItemState; - -import com.freud.opc.utgard.perf.AsyncOPCPerfTest; - -public class DataCallBackListener implements DataCallback { - - private static Logger LOGGER = Logger.getLogger(DataCallBackListener.class); - - private static List items = new ArrayList(); - - private int size; - - public DataCallBackListener(int size) { - this.size = size; - } - - @Override - public void changed(Item item, ItemState is) { - LOGGER.info("Item:[" + item.getId() + "], Value:[" + is.getValue() - + "]"); - items.add(item.getId()); - if (items.size() == size) { - long end = System.currentTimeMillis(); - LOGGER.info("EndDate[" + new Date() + "],CurrentMillis:" + end); - LOGGER.info("Total Spend:[" + (end - AsyncOPCPerfTest.start) + "]"); - System.exit(0); - } - } -} diff --git a/OPC_Client_Utgard_Performance/utgard_result.log b/OPC_Client_Utgard_Performance/utgard_result.log index 932497f..0d0b535 100644 --- a/OPC_Client_Utgard_Performance/utgard_result.log +++ b/OPC_Client_Utgard_Performance/utgard_result.log @@ -1,12 +1,101 @@ -2014-12-09 10:18:20,882-[TS] INFO main com.freud.opc.utgard.perf.Test - Step-10W: -2014-12-09 10:18:20,885-[TS] INFO main com.freud.opc.utgard.perf.Test - StartDate[Tue Dec 09 10:18:20 CST 2014],CurrentMillis:1418091500881 -2014-12-09 10:18:38,649-[TS] INFO main com.freud.opc.utgard.perf.Test - Step-10W: -2014-12-09 10:18:38,651-[TS] INFO main com.freud.opc.utgard.perf.Test - StartDate[Tue Dec 09 10:18:38 CST 2014],CurrentMillis:1418091518648 -2014-12-09 10:19:05,380-[TS] INFO main com.freud.opc.utgard.perf.Test - Step-10W: -2014-12-09 10:19:05,382-[TS] INFO main com.freud.opc.utgard.perf.Test - StartDate[Tue Dec 09 10:19:05 CST 2014],CurrentMillis:1418091545379 -2014-12-09 10:21:34,695-[TS] INFO main com.freud.opc.utgard.perf.Test - Step-10W: -2014-12-09 10:21:34,698-[TS] INFO main com.freud.opc.utgard.perf.Test - StartDate[Tue Dec 09 10:21:34 CST 2014],CurrentMillis:1418091694695 -2014-12-09 10:23:48,822-[TS] INFO main com.freud.opc.utgard.perf.Test - Step-10W: -2014-12-09 10:23:48,824-[TS] INFO main com.freud.opc.utgard.perf.Test - StartDate[Tue Dec 09 10:23:48 CST 2014],CurrentMillis:1418091828821 -2014-12-09 10:27:05,952-[TS] INFO main com.freud.opc.utgard.perf.Test - Step-10W: -2014-12-09 10:27:05,955-[TS] INFO main com.freud.opc.utgard.perf.Test - StartDate[Tue Dec 09 10:27:05 CST 2014],CurrentMillis:1418092025952 +2014-12-10 17:39:43,078-[TS] INFO Thread-0 com.freud.opc.utgard.perf.AsyncMulti - Step-4000 site: +2014-12-10 17:39:43,078-[TS] INFO Thread-1 com.freud.opc.utgard.perf.AsyncMulti - Step-8000 site: +2014-12-10 17:39:43,079-[TS] INFO Thread-3 com.freud.opc.utgard.perf.AsyncMulti - Step-16000 site: +2014-12-10 17:39:43,081-[TS] INFO Thread-0 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383078 +2014-12-10 17:39:43,082-[TS] INFO Thread-1 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383078 +2014-12-10 17:39:43,085-[TS] INFO Thread-3 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383078 +2014-12-10 17:39:43,086-[TS] INFO Thread-4 com.freud.opc.utgard.perf.AsyncMulti - Step-20000 site: +2014-12-10 17:39:43,086-[TS] INFO Thread-4 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383086 +2014-12-10 17:39:43,086-[TS] INFO Thread-5 com.freud.opc.utgard.perf.AsyncMulti - Step-24000 site: +2014-12-10 17:39:43,087-[TS] INFO Thread-5 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383086 +2014-12-10 17:39:43,087-[TS] INFO Thread-7 com.freud.opc.utgard.perf.AsyncMulti - Step-32000 site: +2014-12-10 17:39:43,087-[TS] INFO Thread-7 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383087 +2014-12-10 17:39:43,088-[TS] INFO Thread-8 com.freud.opc.utgard.perf.AsyncMulti - Step-36000 site: +2014-12-10 17:39:43,088-[TS] INFO Thread-8 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383088 +2014-12-10 17:39:43,088-[TS] INFO Thread-9 com.freud.opc.utgard.perf.AsyncMulti - Step-40000 site: +2014-12-10 17:39:43,088-[TS] INFO Thread-9 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383088 +2014-12-10 17:39:43,089-[TS] INFO Thread-2 com.freud.opc.utgard.perf.AsyncMulti - Step-12000 site: +2014-12-10 17:39:43,089-[TS] INFO Thread-6 com.freud.opc.utgard.perf.AsyncMulti - Step-28000 site: +2014-12-10 17:39:43,089-[TS] INFO Thread-10 com.freud.opc.utgard.perf.AsyncMulti - Step-44000 site: +2014-12-10 17:39:43,089-[TS] INFO Thread-14 com.freud.opc.utgard.perf.AsyncMulti - Step-60000 site: +2014-12-10 17:39:43,090-[TS] INFO Thread-18 com.freud.opc.utgard.perf.AsyncMulti - Step-76000 site: +2014-12-10 17:39:43,090-[TS] INFO Thread-22 com.freud.opc.utgard.perf.AsyncMulti - Step-92000 site: +2014-12-10 17:39:43,090-[TS] INFO Thread-26 com.freud.opc.utgard.perf.AsyncMulti - Step-108000 site: +2014-12-10 17:39:43,090-[TS] INFO Thread-30 com.freud.opc.utgard.perf.AsyncMulti - Step-124000 site: +2014-12-10 17:39:43,090-[TS] INFO Thread-34 com.freud.opc.utgard.perf.AsyncMulti - Step-140000 site: +2014-12-10 17:39:43,090-[TS] INFO Thread-38 com.freud.opc.utgard.perf.AsyncMulti - Step-156000 site: +2014-12-10 17:39:43,090-[TS] INFO Thread-42 com.freud.opc.utgard.perf.AsyncMulti - Step-172000 site: +2014-12-10 17:39:43,090-[TS] INFO Thread-46 com.freud.opc.utgard.perf.AsyncMulti - Step-188000 site: +2014-12-10 17:39:43,092-[TS] INFO Thread-46 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383090 +2014-12-10 17:39:43,092-[TS] INFO Thread-42 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383090 +2014-12-10 17:39:43,092-[TS] INFO Thread-38 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383090 +2014-12-10 17:39:43,092-[TS] INFO Thread-34 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383090 +2014-12-10 17:39:43,092-[TS] INFO Thread-30 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383090 +2014-12-10 17:39:43,092-[TS] INFO Thread-26 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383090 +2014-12-10 17:39:43,093-[TS] INFO Thread-22 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383090 +2014-12-10 17:39:43,093-[TS] INFO Thread-18 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383090 +2014-12-10 17:39:43,093-[TS] INFO Thread-14 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383090 +2014-12-10 17:39:43,093-[TS] INFO Thread-11 com.freud.opc.utgard.perf.AsyncMulti - Step-48000 site: +2014-12-10 17:39:43,093-[TS] INFO Thread-10 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383093 +2014-12-10 17:39:43,093-[TS] INFO Thread-11 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383093 +2014-12-10 17:39:43,096-[TS] INFO Thread-12 com.freud.opc.utgard.perf.AsyncMulti - Step-52000 site: +2014-12-10 17:39:43,096-[TS] INFO Thread-49 com.freud.opc.utgard.perf.AsyncMulti - Step-200000 site: +2014-12-10 17:39:43,096-[TS] INFO Thread-47 com.freud.opc.utgard.perf.AsyncMulti - Step-192000 site: +2014-12-10 17:39:43,096-[TS] INFO Thread-44 com.freud.opc.utgard.perf.AsyncMulti - Step-180000 site: +2014-12-10 17:39:43,096-[TS] INFO Thread-41 com.freud.opc.utgard.perf.AsyncMulti - Step-168000 site: +2014-12-10 17:39:43,096-[TS] INFO Thread-39 com.freud.opc.utgard.perf.AsyncMulti - Step-160000 site: +2014-12-10 17:39:43,096-[TS] INFO Thread-36 com.freud.opc.utgard.perf.AsyncMulti - Step-148000 site: +2014-12-10 17:39:43,096-[TS] INFO Thread-33 com.freud.opc.utgard.perf.AsyncMulti - Step-136000 site: +2014-12-10 17:39:43,096-[TS] INFO Thread-31 com.freud.opc.utgard.perf.AsyncMulti - Step-128000 site: +2014-12-10 17:39:43,096-[TS] INFO Thread-28 com.freud.opc.utgard.perf.AsyncMulti - Step-116000 site: +2014-12-10 17:39:43,096-[TS] INFO Thread-25 com.freud.opc.utgard.perf.AsyncMulti - Step-104000 site: +2014-12-10 17:39:43,096-[TS] INFO Thread-23 com.freud.opc.utgard.perf.AsyncMulti - Step-96000 site: +2014-12-10 17:39:43,096-[TS] INFO Thread-20 com.freud.opc.utgard.perf.AsyncMulti - Step-84000 site: +2014-12-10 17:39:43,096-[TS] INFO Thread-17 com.freud.opc.utgard.perf.AsyncMulti - Step-72000 site: +2014-12-10 17:39:43,096-[TS] INFO Thread-15 com.freud.opc.utgard.perf.AsyncMulti - Step-64000 site: +2014-12-10 17:39:43,096-[TS] INFO Thread-45 com.freud.opc.utgard.perf.AsyncMulti - Step-184000 site: +2014-12-10 17:39:43,096-[TS] INFO Thread-48 com.freud.opc.utgard.perf.AsyncMulti - Step-196000 site: +2014-12-10 17:39:43,096-[TS] INFO Thread-13 com.freud.opc.utgard.perf.AsyncMulti - Step-56000 site: +2014-12-10 17:39:43,096-[TS] INFO Thread-6 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,097-[TS] INFO Thread-13 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,096-[TS] INFO Thread-16 com.freud.opc.utgard.perf.AsyncMulti - Step-68000 site: +2014-12-10 17:39:43,097-[TS] INFO Thread-16 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,096-[TS] INFO Thread-19 com.freud.opc.utgard.perf.AsyncMulti - Step-80000 site: +2014-12-10 17:39:43,097-[TS] INFO Thread-19 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,096-[TS] INFO Thread-21 com.freud.opc.utgard.perf.AsyncMulti - Step-88000 site: +2014-12-10 17:39:43,097-[TS] INFO Thread-21 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,096-[TS] INFO Thread-24 com.freud.opc.utgard.perf.AsyncMulti - Step-100000 site: +2014-12-10 17:39:43,097-[TS] INFO Thread-24 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,096-[TS] INFO Thread-27 com.freud.opc.utgard.perf.AsyncMulti - Step-112000 site: +2014-12-10 17:39:43,098-[TS] INFO Thread-27 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,096-[TS] INFO Thread-29 com.freud.opc.utgard.perf.AsyncMulti - Step-120000 site: +2014-12-10 17:39:43,098-[TS] INFO Thread-29 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,096-[TS] INFO Thread-32 com.freud.opc.utgard.perf.AsyncMulti - Step-132000 site: +2014-12-10 17:39:43,098-[TS] INFO Thread-32 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,099-[TS] INFO Thread-48 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,096-[TS] INFO Thread-35 com.freud.opc.utgard.perf.AsyncMulti - Step-144000 site: +2014-12-10 17:39:43,099-[TS] INFO Thread-35 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,096-[TS] INFO Thread-37 com.freud.opc.utgard.perf.AsyncMulti - Step-152000 site: +2014-12-10 17:39:43,099-[TS] INFO Thread-37 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,099-[TS] INFO Thread-45 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,096-[TS] INFO Thread-40 com.freud.opc.utgard.perf.AsyncMulti - Step-164000 site: +2014-12-10 17:39:43,100-[TS] INFO Thread-40 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,100-[TS] INFO Thread-15 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,096-[TS] INFO Thread-43 com.freud.opc.utgard.perf.AsyncMulti - Step-176000 site: +2014-12-10 17:39:43,100-[TS] INFO Thread-43 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,101-[TS] INFO Thread-17 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,101-[TS] INFO Thread-20 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,101-[TS] INFO Thread-23 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,102-[TS] INFO Thread-25 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,102-[TS] INFO Thread-28 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,098-[TS] INFO Thread-2 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,102-[TS] INFO Thread-31 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,102-[TS] INFO Thread-33 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,103-[TS] INFO Thread-36 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,103-[TS] INFO Thread-39 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,104-[TS] INFO Thread-41 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,104-[TS] INFO Thread-44 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,104-[TS] INFO Thread-47 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,105-[TS] INFO Thread-49 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 +2014-12-10 17:39:43,106-[TS] INFO Thread-12 com.freud.opc.utgard.perf.AsyncMulti - StartDate[Wed Dec 10 17:39:43 CST 2014],CurrentMillis:1418204383096 + diff --git a/OPC_Client_Utgard_Performance/utgard_result.log.2014-12-09 b/OPC_Client_Utgard_Performance/utgard_result.log.2014-12-09 new file mode 100644 index 0000000..932497f --- /dev/null +++ b/OPC_Client_Utgard_Performance/utgard_result.log.2014-12-09 @@ -0,0 +1,12 @@ +2014-12-09 10:18:20,882-[TS] INFO main com.freud.opc.utgard.perf.Test - Step-10W: +2014-12-09 10:18:20,885-[TS] INFO main com.freud.opc.utgard.perf.Test - StartDate[Tue Dec 09 10:18:20 CST 2014],CurrentMillis:1418091500881 +2014-12-09 10:18:38,649-[TS] INFO main com.freud.opc.utgard.perf.Test - Step-10W: +2014-12-09 10:18:38,651-[TS] INFO main com.freud.opc.utgard.perf.Test - StartDate[Tue Dec 09 10:18:38 CST 2014],CurrentMillis:1418091518648 +2014-12-09 10:19:05,380-[TS] INFO main com.freud.opc.utgard.perf.Test - Step-10W: +2014-12-09 10:19:05,382-[TS] INFO main com.freud.opc.utgard.perf.Test - StartDate[Tue Dec 09 10:19:05 CST 2014],CurrentMillis:1418091545379 +2014-12-09 10:21:34,695-[TS] INFO main com.freud.opc.utgard.perf.Test - Step-10W: +2014-12-09 10:21:34,698-[TS] INFO main com.freud.opc.utgard.perf.Test - StartDate[Tue Dec 09 10:21:34 CST 2014],CurrentMillis:1418091694695 +2014-12-09 10:23:48,822-[TS] INFO main com.freud.opc.utgard.perf.Test - Step-10W: +2014-12-09 10:23:48,824-[TS] INFO main com.freud.opc.utgard.perf.Test - StartDate[Tue Dec 09 10:23:48 CST 2014],CurrentMillis:1418091828821 +2014-12-09 10:27:05,952-[TS] INFO main com.freud.opc.utgard.perf.Test - Step-10W: +2014-12-09 10:27:05,955-[TS] INFO main com.freud.opc.utgard.perf.Test - StartDate[Tue Dec 09 10:27:05 CST 2014],CurrentMillis:1418092025952 diff --git a/org.openscada.opc.dcom.test/.classpath b/org.openscada.opc.dcom.test/.classpath new file mode 100644 index 0000000..8f89c61 --- /dev/null +++ b/org.openscada.opc.dcom.test/.classpath @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/org.openscada.opc.dcom.test/.gitignore b/org.openscada.opc.dcom.test/.gitignore new file mode 100644 index 0000000..e660fd9 --- /dev/null +++ b/org.openscada.opc.dcom.test/.gitignore @@ -0,0 +1 @@ +bin/ diff --git a/org.openscada.opc.dcom.test/.project b/org.openscada.opc.dcom.test/.project new file mode 100644 index 0000000..d63364b --- /dev/null +++ b/org.openscada.opc.dcom.test/.project @@ -0,0 +1,46 @@ + + + org.openscada.opc.dcom.test + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.babel.editor.rbeBuilder + + + + + org.eclipse.pde.api.tools.apiAnalysisBuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.m2e.core.maven2Nature + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + org.eclipse.babel.editor.rbeNature + org.eclipse.pde.api.tools.apiAnalysisNature + + diff --git a/org.openscada.opc.dcom.test/LICENSE b/org.openscada.opc.dcom.test/LICENSE new file mode 100644 index 0000000..0836c46 --- /dev/null +++ b/org.openscada.opc.dcom.test/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/org.openscada.opc.dcom.test/pom.xml b/org.openscada.opc.dcom.test/pom.xml new file mode 100644 index 0000000..aa05f51 --- /dev/null +++ b/org.openscada.opc.dcom.test/pom.xml @@ -0,0 +1,31 @@ + + 4.0.0 + org.openscada.opc.dcom.test + org.openscada.opc.dcom.test + 0.0.1-SNAPSHOT + + + + org.slf4j + slf4j-log4j12 + 1.7.2 + + + commons-logging + commons-logging + 1.2 + + + + org.kohsuke.jinterop + j-interop + 2.0.5 + + + org.openscada.opc.dcom + org.openscada.opc.dcom + 0.0.1-SNAPSHOT + + + \ No newline at end of file diff --git a/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/common/FiletimeTest1.java b/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/common/FiletimeTest1.java new file mode 100644 index 0000000..78b648e --- /dev/null +++ b/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/common/FiletimeTest1.java @@ -0,0 +1,86 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.dcom.common; + +import junit.framework.Assert; + +import org.junit.Test; + +public class FiletimeTest1 +{ + public static void main ( final String[] args ) + { + FILETIME ft = new FILETIME ( 29949427, 2139800608 ); + System.out.println ( String.format ( "%s, %tc", ft, ft.asCalendar () ) ); + + ft = new FILETIME ( 29949427, -2145016688 ); + System.out.println ( String.format ( "%s, %tc", ft, ft.asCalendar () ) ); + } + + @Test + public void test () + { + assertEquals ( "Thu Aug 14 11:52:43 CEST 2008", new FILETIME ( 29949427, 2139800608 ) ); + assertEquals ( "Thu Aug 14 11:52:44 CEST 2008", new FILETIME ( 29949427, -2145016688 ) ); + } + + protected void assertEquals ( final String expected, final FILETIME actual ) + { + Assert.assertEquals ( expected, String.format ( "%tc", actual.asCalendar () ) ); + } + + @Test + public void test2 () + { + FILETIME last = null; + for ( int i = 0; i < 10000; i++ ) + { + final FILETIME ft = new FILETIME ( 29949427 + i, 2139800608 + i ); + Assert.assertEquals ( ft.asBigDecimalCalendar (), ft.asCalendar () ); + + if ( last != null ) + { + Assert.assertTrue ( last.asCalendar ().before ( ft.asCalendar () ) ); + } + + last = ft; + } + } + + @Test + public void test3a () + { + for ( int i = 0; i < 10000; i++ ) + { + final FILETIME ft = new FILETIME ( 29949427 + i, 2139800608 + i ); + ft.asCalendar (); + } + } + + @Test + public void test3b () + { + for ( int i = 0; i < 10000; i++ ) + { + final FILETIME ft = new FILETIME ( 29949427 + i, 2139800608 + i ); + ft.asBigDecimalCalendar (); + } + } +} diff --git a/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/DumpDataCallback.java b/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/DumpDataCallback.java new file mode 100644 index 0000000..6bd8b6a --- /dev/null +++ b/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/DumpDataCallback.java @@ -0,0 +1,59 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.dcom.da; + +import org.openscada.opc.dcom.common.KeyedResult; +import org.openscada.opc.dcom.common.KeyedResultSet; +import org.openscada.opc.dcom.common.ResultSet; + +public class DumpDataCallback implements IOPCDataCallback +{ + + public void cancelComplete ( final int transactionId, final int serverGroupHandle ) + { + System.out.println ( String.format ( "cancelComplete: %08X, Group: %08X", transactionId, serverGroupHandle ) ); + } + + public void dataChange ( final int transactionId, final int serverGroupHandle, final int masterQuality, final int masterErrorCode, final KeyedResultSet result ) + { + System.out.println ( String.format ( "dataChange: %d, Group: %08X, MasterQ: %d, Error: %d", transactionId, serverGroupHandle, masterQuality, masterErrorCode ) ); + + for ( final KeyedResult entry : result ) + { + System.out.println ( String.format ( "%08X - Error: %08X, Quality: %d, %Tc - %s", entry.getKey (), entry.getErrorCode (), entry.getValue ().getQuality (), entry.getValue ().getTimestamp (), entry.getValue ().getValue ().toString () ) ); + } + } + + public void readComplete ( final int transactionId, final int serverGroupHandle, final int masterQuality, final int masterErrorCode, final KeyedResultSet result ) + { + System.out.println ( String.format ( "readComplete: %d, Group: %08X, MasterQ: %d, Error: %d", transactionId, serverGroupHandle, masterQuality, masterErrorCode ) ); + + for ( final KeyedResult entry : result ) + { + System.out.println ( String.format ( "%08X - Error: %08X, Quality: %d, %Tc - %s", entry.getKey (), entry.getErrorCode (), entry.getValue ().getQuality (), entry.getValue ().getTimestamp (), entry.getValue ().getValue ().toString () ) ); + } + } + + public void writeComplete ( final int transactionId, final int serverGroupHandle, final int masterErrorCode, final ResultSet result ) + { + // TODO Auto-generated method stub + } + +} diff --git a/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/IconicsSimulationServerConfiguration.java b/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/IconicsSimulationServerConfiguration.java new file mode 100644 index 0000000..fbc1e6b --- /dev/null +++ b/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/IconicsSimulationServerConfiguration.java @@ -0,0 +1,46 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.dcom.da; + + +public class IconicsSimulationServerConfiguration implements TestConfiguration +{ + + public String getCLSID () + { + return "A879768A-7387-11D4-B0D8-009027242C59"; + } + + public String getProgId () + { + return "ICONICS.SimulatorOPCDA.2"; + } + + public String[] getReadItems () + { + return new String[] { "Logical._BOOL" }; + } + + public WriteTest[] getWriteItems () + { + return new WriteTest[]{}; + } + +} diff --git a/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/MatrikonSimulationServerConfiguration.java b/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/MatrikonSimulationServerConfiguration.java new file mode 100644 index 0000000..9df7b45 --- /dev/null +++ b/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/MatrikonSimulationServerConfiguration.java @@ -0,0 +1,47 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.dcom.da; + +import org.jinterop.dcom.core.JIVariant; + +public class MatrikonSimulationServerConfiguration implements TestConfiguration +{ + + public String getCLSID () + { + return "F8582CF2-88FB-11D0-B850-00C0F0104305"; + } + + public String getProgId () + { + return "Matrikon.OPC.Simulation.1"; + } + + public String[] getReadItems () + { + return new String[] { "Saw-toothed Waves.Int2", "Saw-toothed Waves.Int4" }; + } + + public WriteTest[] getWriteItems () + { + return new WriteTest[] { new WriteTest ( "Write Only.Int2", new JIVariant ( (short)1202, false ) ), new WriteTest ( "Write Only.Int4", new JIVariant ( 1202, false ) ) }; + } + +} diff --git a/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/SoftingDemoServerConfiguration.java b/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/SoftingDemoServerConfiguration.java new file mode 100644 index 0000000..8daf109 --- /dev/null +++ b/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/SoftingDemoServerConfiguration.java @@ -0,0 +1,45 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.dcom.da; + +public class SoftingDemoServerConfiguration implements TestConfiguration +{ + + public String getCLSID () + { + return "2E565242-B238-11D3-842D-0008C779D775"; + } + + public String getProgId () + { + return "Softing.OPCToolboxDemo_ServerDA.1"; + } + + public String[] getReadItems () + { + return new String[] { "increment.I2", "increment.I4" }; + } + + public WriteTest[] getWriteItems () + { + return null; + } + +} diff --git a/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/Test1.java b/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/Test1.java new file mode 100644 index 0000000..df0f56c --- /dev/null +++ b/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/Test1.java @@ -0,0 +1,475 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.dcom.da; + +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; + +import org.jinterop.dcom.common.JIException; +import org.jinterop.dcom.common.JISystem; +import org.jinterop.dcom.core.IJIComObject; +import org.jinterop.dcom.core.JIComServer; +import org.jinterop.dcom.core.JIProgId; +import org.jinterop.dcom.core.JISession; +import org.jinterop.dcom.core.JIVariant; +import org.openscada.opc.dcom.common.EventHandler; +import org.openscada.opc.dcom.common.KeyedResult; +import org.openscada.opc.dcom.common.KeyedResultSet; +import org.openscada.opc.dcom.common.Result; +import org.openscada.opc.dcom.common.ResultSet; +import org.openscada.opc.dcom.common.impl.OPCCommon; +import org.openscada.opc.dcom.da.impl.OPCBrowseServerAddressSpace; +import org.openscada.opc.dcom.da.impl.OPCGroupStateMgt; +import org.openscada.opc.dcom.da.impl.OPCItemIO; +import org.openscada.opc.dcom.da.impl.OPCItemMgt; +import org.openscada.opc.dcom.da.impl.OPCItemProperties; +import org.openscada.opc.dcom.da.impl.OPCServer; +import org.openscada.opc.dcom.da.impl.OPCSyncIO; + +public class Test1 +{ + private static JISession _session = null; + + public static void showError ( final OPCCommon common, final int errorCode ) throws JIException + { + System.out.println ( String.format ( "Error (%X): '%s'", errorCode, common.getErrorString ( errorCode, 1033 ) ) ); + } + + public static void showError ( final OPCServer server, final int errorCode ) throws JIException + { + showError ( server.getCommon (), errorCode ); + } + + public static void showAccessPaths ( final OPCBrowseServerAddressSpace browser, final String id ) throws IllegalArgumentException, UnknownHostException, JIException + { + for ( final String i : browser.browseAccessPaths ( id ).asCollection () ) + { + System.out.println ( "AccessPath Entry: " + i ); + } + } + + public static void browseTree ( final OPCBrowseServerAddressSpace browser ) throws IllegalArgumentException, UnknownHostException, JIException + { + System.out.println ( "Showing hierarchial address space" ); + System.out.println ( String.format ( "Organization: %s", browser.queryOrganization () ) ); + + if ( !browser.queryOrganization ().equals ( OPCNAMESPACETYPE.OPC_NS_HIERARCHIAL ) ) + { + return; + } + + browser.changePosition ( null, OPCBROWSEDIRECTION.OPC_BROWSE_TO ); + browseTree ( browser, 0 ); + } + + protected static void browseTree ( final OPCBrowseServerAddressSpace browser, final int level ) throws JIException, IllegalArgumentException, UnknownHostException + { + final StringBuilder indent = new StringBuilder ( level ); + for ( int i = 0; i < level; i++ ) + { + indent.append ( '\t' ); + } + for ( final String item : browser.browse ( OPCBROWSETYPE.OPC_LEAF, "", 0, JIVariant.VT_EMPTY ).asCollection () ) + { + System.out.println ( indent + "Leaf: " + item ); + System.out.println ( indent + "\tName: " + browser.getItemID ( item ) ); + } + + for ( final String item : browser.browse ( OPCBROWSETYPE.OPC_BRANCH, "", 0, JIVariant.VT_EMPTY ).asCollection () ) + { + System.out.println ( indent + "Branch: " + item ); + browser.changePosition ( item, OPCBROWSEDIRECTION.OPC_BROWSE_DOWN ); + browseTree ( browser, level + 1 ); + browser.changePosition ( null, OPCBROWSEDIRECTION.OPC_BROWSE_UP ); + } + } + + public static void browseFlat ( final OPCBrowseServerAddressSpace browser ) throws JIException, IllegalArgumentException, UnknownHostException + { + System.out.println ( String.format ( "Organization: %s", browser.queryOrganization () ) ); + browser.changePosition ( null, OPCBROWSEDIRECTION.OPC_BROWSE_TO ); + + System.out.println ( "Showing flat address space" ); + for ( final String id : browser.browse ( OPCBROWSETYPE.OPC_FLAT, "", 0, JIVariant.VT_EMPTY ).asCollection () ) + { + System.out.println ( "Item: " + id ); + //showAccessPaths ( browser, id ); + } + } + + public static void dumpGroupState ( final OPCGroupStateMgt group ) throws JIException + { + final OPCGroupState state = group.getState (); + + System.out.println ( "Name: " + state.getName () ); + System.out.println ( "Active: " + state.isActive () ); + System.out.println ( "Update Rate: " + state.getUpdateRate () ); + System.out.println ( "Time Bias: " + state.getTimeBias () ); + System.out.println ( "Percent Deadband: " + state.getPercentDeadband () ); + System.out.println ( "Locale ID: " + state.getLocaleID () ); + System.out.println ( "Client Handle: " + state.getClientHandle () ); + System.out.println ( "Server Handle: " + state.getServerHandle () ); + } + + public static void dumpItemProperties2 ( final OPCItemProperties itemProperties, final String itemID, final int... ids ) throws JIException + { + final KeyedResultSet values = itemProperties.getItemProperties ( itemID, ids ); + for ( final KeyedResult entry : values ) + { + System.out.println ( String.format ( "ID: %d, Value: %s, Error Code: %08x", entry.getKey (), entry.getValue ().toString (), entry.getErrorCode () ) ); + } + } + + public static void dumpItemPropertiesLookup ( final OPCItemProperties itemProperties, final String itemID, final int... ids ) throws JIException + { + final KeyedResultSet values = itemProperties.lookupItemIDs ( itemID, ids ); + for ( final KeyedResult entry : values ) + { + System.out.println ( String.format ( "ID: %d, Item ID: %s, Error Code: %08x", entry.getKey (), entry.getValue (), entry.getErrorCode () ) ); + } + } + + public static void dumpItemProperties ( final OPCItemProperties itemProperties, final String itemID ) throws JIException + { + final Collection properties = itemProperties.queryAvailableProperties ( itemID ); + final int[] ids = new int[properties.size ()]; + + System.out.println ( String.format ( "Item Properties for '%s' (count:%d)", itemID, properties.size () ) ); + int i = 0; + for ( final PropertyDescription pd : properties ) + { + ids[i] = pd.getId (); + System.out.println ( "ID: " + pd.getId () ); + System.out.println ( "Description: " + pd.getDescription () ); + System.out.println ( "Variable Type: " + pd.getVarType () ); + i++; + } + + System.out.println ( "Lookup" ); + dumpItemPropertiesLookup ( itemProperties, itemID, ids ); + + System.out.println ( "Query" ); + dumpItemProperties2 ( itemProperties, itemID, ids ); + } + + public static void queryItems ( final OPCItemIO itemIO, final String... items ) throws JIException + { + final List requests = new LinkedList (); + for ( final String item : items ) + { + requests.add ( new IORequest ( item, 0 ) ); + } + itemIO.read ( requests.toArray ( new IORequest[0] ) ); + } + + public static boolean dumpOPCITEMRESULT ( final KeyedResultSet result ) + { + int failed = 0; + for ( final KeyedResult resultEntry : result ) + { + System.out.println ( "==================================" ); + System.out.println ( String.format ( "Item: '%s' ", resultEntry.getKey ().getItemID () ) ); + + System.out.println ( String.format ( "Error Code: %08x", resultEntry.getErrorCode () ) ); + if ( !resultEntry.isFailed () ) + { + System.out.println ( String.format ( "Server Handle: %08X", resultEntry.getValue ().getServerHandle () ) ); + System.out.println ( String.format ( "Data Type: %d", resultEntry.getValue ().getCanonicalDataType () ) ); + System.out.println ( String.format ( "Access Rights: %d", resultEntry.getValue ().getAccessRights () ) ); + System.out.println ( String.format ( "Reserved: %d", resultEntry.getValue ().getReserved () ) ); + } + else + { + failed++; + } + } + return failed == 0; + } + + public static void writeItems ( final OPCServer server, final OPCGroupStateMgt group, final WriteTest... writeTests ) throws IllegalArgumentException, UnknownHostException, JIException + { + System.out.println ( "Write items" ); + + final OPCItemMgt itemManagement = group.getItemManagement (); + final OPCITEMDEF[] defs = new OPCITEMDEF[writeTests.length]; + for ( int i = 0; i < writeTests.length; i++ ) + { + final OPCITEMDEF def = new OPCITEMDEF (); + def.setActive ( true ); + def.setItemID ( writeTests[i].getItemID () ); + //def.setRequestedDataType ( (short)writeTests[i].getValue ().getType () ); + defs[i] = def; + + System.out.println ( String.format ( "%s <= (%d) %s", writeTests[i].getItemID (), writeTests[i].getValue ().getType (), writeTests[i].getValue ().toString () ) ); + } + + System.out.println ( "Add to group" ); + final KeyedResultSet result = itemManagement.add ( defs ); + final WriteRequest[] writeRequests = new WriteRequest[writeTests.length]; + final Integer[] serverHandles = new Integer[writeTests.length]; + for ( int i = 0; i < writeTests.length; i++ ) + { + if ( result.get ( i ).getErrorCode () != 0 ) + { + throw new JIException ( result.get ( i ).getErrorCode () ); + } + + writeRequests[i] = new WriteRequest ( result.get ( i ).getValue ().getServerHandle (), writeTests[i].getValue () ); + serverHandles[i] = writeRequests[i].getServerHandle (); + + System.out.println ( String.format ( "Item: %s, VT: %d", writeTests[i].getItemID (), result.get ( i ).getValue ().getCanonicalDataType () ) ); + } + + System.out.println ( "Perform write" ); + final OPCSyncIO syncIO = group.getSyncIO (); + final ResultSet writeResults = syncIO.write ( writeRequests ); + for ( int i = 0; i < writeTests.length; i++ ) + { + final Result writeResult = writeResults.get ( i ); + System.out.println ( String.format ( "ItemID: %s, ErrorCode: %08X", writeTests[i].getItemID (), writeResult.getErrorCode () ) ); + if ( writeResult.getErrorCode () != 0 ) + { + showError ( server, writeResult.getErrorCode () ); + } + } + + // finally remove them again + System.out.println ( "Remove from group" ); + itemManagement.remove ( serverHandles ); + + System.out.println ( "Write items...complete" ); + } + + public static void testItems ( final OPCServer server, final OPCGroupStateMgt group, final String... itemIDs ) throws IllegalArgumentException, UnknownHostException, JIException + { + final OPCItemMgt itemManagement = group.getItemManagement (); + final List items = new ArrayList ( itemIDs.length ); + for ( final String id : itemIDs ) + { + final OPCITEMDEF item = new OPCITEMDEF (); + item.setItemID ( id ); + item.setClientHandle ( new Random ().nextInt () ); + items.add ( item ); + } + + final OPCITEMDEF[] itemArray = items.toArray ( new OPCITEMDEF[0] ); + + System.out.println ( "Validate" ); + KeyedResultSet result = itemManagement.validate ( itemArray ); + if ( !dumpOPCITEMRESULT ( result ) ) + { + return; + } + + // now add them to the group + System.out.println ( "Add" ); + result = itemManagement.add ( itemArray ); + if ( !dumpOPCITEMRESULT ( result ) ) + { + return; + } + + // get the server handle array + final Integer[] serverHandles = new Integer[itemArray.length]; + for ( int i = 0; i < itemArray.length; i++ ) + { + serverHandles[i] = new Integer ( result.get ( i ).getValue ().getServerHandle () ); + } + + // set them active + System.out.println ( "Activate" ); + final ResultSet resultSet = itemManagement.setActiveState ( true, serverHandles ); + for ( final Result resultEntry : resultSet ) + { + System.out.println ( String.format ( "Item: %08X, Error: %08X", resultEntry.getValue (), resultEntry.getErrorCode () ) ); + } + + // set client handles + System.out.println ( "Set client handles" ); + final Integer[] clientHandles = new Integer[serverHandles.length]; + for ( int i = 0; i < serverHandles.length; i++ ) + { + clientHandles[i] = i; + } + itemManagement.setClientHandles ( serverHandles, clientHandles ); + + System.out.println ( "Create async IO 2.0 object" ); + // OPCAsyncIO2 asyncIO2 = group.getAsyncIO2 (); + // connect handler + + System.out.println ( "attach" ); + final EventHandler eventHandler = group.attach ( new DumpDataCallback () ); + /* + System.out.println ( "attach..enable" ); + asyncIO2.setEnable ( true ); + System.out.println ( "attach..refresh" ); + asyncIO2.refresh ( (short)1, 1 ); + */ + // sleep + try + { + System.out.println ( "Waiting..." ); + Thread.sleep ( 10 * 1000 ); + } + catch ( final InterruptedException e ) + { + // TODO Auto-generated catch block + e.printStackTrace (); + } + + eventHandler.detach (); + + // sync IO - read + final OPCSyncIO syncIO = group.getSyncIO (); + final KeyedResultSet itemState = syncIO.read ( OPCDATASOURCE.OPC_DS_DEVICE, serverHandles ); + for ( final KeyedResult itemStateEntry : itemState ) + { + final int errorCode = itemStateEntry.getErrorCode (); + System.out.println ( String.format ( "Server ID: %08X, Value: %s, Timestamp: %d/%d (%Tc), Quality: %d, Error: %08X", itemStateEntry.getKey (), itemStateEntry.getValue ().getValue (), itemStateEntry.getValue ().getTimestamp ().getHigh (), itemStateEntry.getValue ().getTimestamp ().getLow (), itemStateEntry.getValue ().getTimestamp ().asCalendar (), itemStateEntry.getValue ().getQuality (), errorCode ) ); + if ( errorCode != 0 ) + { + showError ( server, errorCode ); + } + } + + // set them inactive + System.out.println ( "In-Active" ); + itemManagement.setActiveState ( false, serverHandles ); + + // finally remove them again + System.out.println ( "Remove" ); + itemManagement.remove ( serverHandles ); + } + + public static void dumpServerStatus ( final OPCServer server ) throws JIException + { + final OPCSERVERSTATUS status = server.getStatus (); + + System.out.println ( "===== SERVER STATUS ======" ); + System.out.println ( "State: " + status.getServerState ().toString () ); + System.out.println ( "Vendor: " + status.getVendorInfo () ); + System.out.println ( String.format ( "Version: %d.%d.%d", status.getMajorVersion (), status.getMinorVersion (), status.getBuildNumber () ) ); + System.out.println ( "Groups: " + status.getGroupCount () ); + System.out.println ( "Bandwidth: " + status.getBandWidth () ); + System.out.println ( String.format ( "Start Time: %tc", status.getStartTime ().asCalendar () ) ); + System.out.println ( String.format ( "Current Time: %tc", status.getCurrentTime ().asCalendar () ) ); + System.out.println ( String.format ( "Last Update Time: %tc", status.getLastUpdateTime ().asCalendar () ) ); + System.out.println ( "===== SERVER STATUS ======" ); + } + + public static void enumerateGroups ( final OPCServer server, final OPCENUMSCOPE scope ) throws IllegalArgumentException, UnknownHostException, JIException + { + System.out.println ( "Enum Groups: " + scope.toString () ); + + for ( final String group : server.getGroups ( scope ).asCollection () ) + { + System.out.println ( "Group: " + group ); + } + } + + @SuppressWarnings ( "unused" ) + public static void main ( final String[] args ) throws IllegalArgumentException, UnknownHostException, JIException + { + final TestConfiguration configuration = new MatrikonSimulationServerConfiguration (); + + OPCServer server = null; + try + { + JISystem.setAutoRegisteration ( true ); + + _session = JISession.createSession ( args[1], args[2], args[3] ); + // OPCServer server = new OPCServer ( "127.0.0.1", JIProgId.valueOf + // ( session, "Matrikon.OPC.Simulation.1" ), + // session ); + + //JIComServer comServer = new JIComServer ( JIClsid.valueOf ( configuration.getCLSID () ), args[0], _session ); + final JIComServer comServer = new JIComServer ( JIProgId.valueOf ( configuration.getProgId () ), args[0], _session ); + + final IJIComObject serverObject = comServer.createInstance (); + server = new OPCServer ( serverObject ); + dumpServerStatus ( server ); + + /* + OPCCommon common = server.getCommon (); + common.setLocaleID ( 1033 ); + System.out.println ( String.format ( "LCID: %d", common.getLocaleID () ) ); + common.setClientName ( "test" ); + for ( Integer i : common.queryAvailableLocaleIDs () ) + { + System.out.println ( String.format ( "Available LCID: %d", i ) ); + } + */ + + final OPCBrowseServerAddressSpace serverBrowser = server.getBrowser (); + browseFlat ( serverBrowser ); + /* + browseTree ( serverBrowser ); + */ + + final OPCGroupStateMgt group = server.addGroup ( "test", true, 100, 1234, 60, 0.0f, 1033 ); + /* + group.setName ( "test2" ); + OPCGroupStateMgt group2 = group.clone ( "test" ); + group = server.getGroupByName ( "test2" ); + group.setState ( null, false, null, null, null, null ); + group.setState ( null, true, null, null, null, null ); + dumpGroupState ( group ); + dumpGroupState ( group2 ); + */ + testItems ( server, group, configuration.getReadItems () ); + if ( configuration.getWriteItems () != null ) + { + writeItems ( server, group, configuration.getWriteItems () ); + } + + final OPCItemProperties itemProperties = server.getItemPropertiesService (); + //dumpItemProperties ( itemProperties, "Saw-toothed Waves.Int" ); + + final OPCItemIO itemIO = server.getItemIOService (); + //queryItems ( itemIO, "Saw-toothed Waves.Int" ); + + enumerateGroups ( server, OPCENUMSCOPE.OPC_ENUM_PUBLIC ); + enumerateGroups ( server, OPCENUMSCOPE.OPC_ENUM_PRIVATE ); + enumerateGroups ( server, OPCENUMSCOPE.OPC_ENUM_ALL ); + + // clean up + server.removeGroup ( group, true ); + //server.removeGroup ( group2, true ); + + } + catch ( final JIException e ) + { + e.printStackTrace (); + showError ( server, e.getErrorCode () ); + } + finally + { + if ( _session != null ) + { + JISession.destroySession ( _session ); + } + _session = null; + } + } +} diff --git a/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/Test2.java b/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/Test2.java new file mode 100644 index 0000000..9223866 --- /dev/null +++ b/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/Test2.java @@ -0,0 +1,214 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.dcom.da; + +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import org.jinterop.dcom.common.JIException; +import org.jinterop.dcom.common.JISystem; +import org.jinterop.dcom.core.IJIComObject; +import org.jinterop.dcom.core.JIComServer; +import org.jinterop.dcom.core.JIProgId; +import org.jinterop.dcom.core.JISession; +import org.openscada.opc.dcom.common.EventHandler; +import org.openscada.opc.dcom.common.KeyedResult; +import org.openscada.opc.dcom.common.KeyedResultSet; +import org.openscada.opc.dcom.common.Result; +import org.openscada.opc.dcom.common.ResultSet; +import org.openscada.opc.dcom.common.impl.OPCCommon; +import org.openscada.opc.dcom.da.impl.OPCAsyncIO2; +import org.openscada.opc.dcom.da.impl.OPCGroupStateMgt; +import org.openscada.opc.dcom.da.impl.OPCItemMgt; +import org.openscada.opc.dcom.da.impl.OPCServer; +import org.openscada.opc.dcom.da.impl.OPCAsyncIO2.AsyncResult; + +public class Test2 +{ + private static JISession _session = null; + + public static void showError ( final OPCCommon common, final int errorCode ) throws JIException + { + System.out.println ( String.format ( "Error (%X): '%s'", errorCode, common.getErrorString ( errorCode, 1033 ) ) ); + } + + public static void showError ( final OPCServer server, final int errorCode ) throws JIException + { + showError ( server.getCommon (), errorCode ); + } + + public static boolean dumpOPCITEMRESULT ( final KeyedResultSet result ) + { + int failed = 0; + for ( final KeyedResult resultEntry : result ) + { + System.out.println ( "==================================" ); + System.out.println ( String.format ( "Item: '%s' ", resultEntry.getKey ().getItemID () ) ); + + System.out.println ( String.format ( "Error Code: %08x", resultEntry.getErrorCode () ) ); + if ( !resultEntry.isFailed () ) + { + System.out.println ( String.format ( "Server Handle: %08X", resultEntry.getValue ().getServerHandle () ) ); + System.out.println ( String.format ( "Data Type: %d", resultEntry.getValue ().getCanonicalDataType () ) ); + System.out.println ( String.format ( "Access Rights: %d", resultEntry.getValue ().getAccessRights () ) ); + System.out.println ( String.format ( "Reserved: %d", resultEntry.getValue ().getReserved () ) ); + } + else + { + failed++; + } + } + return failed == 0; + } + + public static void testItems ( final OPCServer server, final OPCGroupStateMgt group, final String... itemIDs ) throws IllegalArgumentException, UnknownHostException, JIException + { + final OPCItemMgt itemManagement = group.getItemManagement (); + final List items = new ArrayList ( itemIDs.length ); + for ( final String id : itemIDs ) + { + final OPCITEMDEF item = new OPCITEMDEF (); + item.setItemID ( id ); + item.setClientHandle ( new Random ().nextInt () ); + items.add ( item ); + } + + final OPCITEMDEF[] itemArray = items.toArray ( new OPCITEMDEF[0] ); + + System.out.println ( "Validate" ); + KeyedResultSet result = itemManagement.validate ( itemArray ); + if ( !dumpOPCITEMRESULT ( result ) ) + { + return; + } + + // now add them to the group + System.out.println ( "Add" ); + result = itemManagement.add ( itemArray ); + if ( !dumpOPCITEMRESULT ( result ) ) + { + return; + } + + // get the server handle array + final Integer[] serverHandles = new Integer[itemArray.length]; + for ( int i = 0; i < itemArray.length; i++ ) + { + serverHandles[i] = new Integer ( result.get ( i ).getValue ().getServerHandle () ); + } + + // set them active + System.out.println ( "Activate" ); + final ResultSet resultSet = itemManagement.setActiveState ( true, serverHandles ); + for ( final Result resultEntry : resultSet ) + { + System.out.println ( String.format ( "Item: %08X, Error: %08X", resultEntry.getValue (), resultEntry.getErrorCode () ) ); + } + + // set client handles + System.out.println ( "Set client handles" ); + final Integer[] clientHandles = new Integer[serverHandles.length]; + for ( int i = 0; i < serverHandles.length; i++ ) + { + clientHandles[i] = i; + } + itemManagement.setClientHandles ( serverHandles, clientHandles ); + + System.out.println ( "Create async IO 2.0 object" ); + final OPCAsyncIO2 asyncIO2 = group.getAsyncIO2 (); + + // connect handler + System.out.println ( "attach" ); + final EventHandler eventHandler = group.attach ( new DumpDataCallback () ); + + System.out.println ( "attach..enable" ); + asyncIO2.setEnable ( true ); + + System.out.println ( "attach..refresh" ); + final int cancelId = asyncIO2.refresh ( OPCDATASOURCE.OPC_DS_DEVICE, 1 ); + System.out.println ( "Cancel ID: " + cancelId ); + + System.out.println ( "attach..read" ); + final AsyncResult asyncResult = asyncIO2.read ( 2, serverHandles ); + System.out.println ( String.format ( "attach..read..cancelId: %08X", asyncResult.getCancelId () ) ); + + // sleep + try + { + System.out.println ( "Waiting..." ); + Thread.sleep ( 10 * 1000 ); + } + catch ( final InterruptedException e ) + { + // TODO Auto-generated catch block + e.printStackTrace (); + } + + System.out.println ( "Detaching" ); + eventHandler.detach (); + + // set them inactive + System.out.println ( "In-Active" ); + itemManagement.setActiveState ( false, serverHandles ); + + // finally remove them again + System.out.println ( "Remove" ); + itemManagement.remove ( serverHandles ); + } + + public static void main ( final String[] args ) throws IllegalArgumentException, UnknownHostException, JIException + { + final TestConfiguration configuration = new MatrikonSimulationServerConfiguration (); + + OPCServer server = null; + try + { + JISystem.setAutoRegisteration ( true ); + + _session = JISession.createSession ( args[1], args[2], args[3] ); + + //JIComServer comServer = new JIComServer ( JIClsid.valueOf ( configuration.getCLSID () ), args[0], _session ); + final JIComServer comServer = new JIComServer ( JIProgId.valueOf ( configuration.getProgId () ), args[0], _session ); + + final IJIComObject serverObject = comServer.createInstance (); + server = new OPCServer ( serverObject ); + + final OPCGroupStateMgt group = server.addGroup ( "test", true, 100, 1234, 60, 0.0f, 1033 ); + + testItems ( server, group, configuration.getReadItems () ); + server.removeGroup ( group, true ); + } + catch ( final JIException e ) + { + e.printStackTrace (); + showError ( server, e.getErrorCode () ); + } + finally + { + if ( _session != null ) + { + JISession.destroySession ( _session ); + } + _session = null; + } + } +} diff --git a/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/TestConfiguration.java b/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/TestConfiguration.java new file mode 100644 index 0000000..8a00255 --- /dev/null +++ b/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/TestConfiguration.java @@ -0,0 +1,31 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.dcom.da; + +public interface TestConfiguration +{ + public String getCLSID (); + + public String getProgId (); + + public String[] getReadItems (); + + public WriteTest[] getWriteItems (); +} diff --git a/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/WriteTest.java b/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/WriteTest.java new file mode 100644 index 0000000..092a992 --- /dev/null +++ b/org.openscada.opc.dcom.test/src/org/openscada/opc/dcom/da/WriteTest.java @@ -0,0 +1,56 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.dcom.da; + +import org.jinterop.dcom.core.JIVariant; + +public class WriteTest +{ + private String _itemID = ""; + + private JIVariant _value = JIVariant.EMPTY (); + + public WriteTest ( final String itemID, final JIVariant value ) + { + super (); + this._itemID = itemID; + this._value = value; + } + + public String getItemID () + { + return this._itemID; + } + + public void setItemID ( final String itemID ) + { + this._itemID = itemID; + } + + public JIVariant getValue () + { + return this._value; + } + + public void setValue ( final JIVariant value ) + { + this._value = value; + } +} diff --git a/org.openscada.opc.dcom/.classpath b/org.openscada.opc.dcom/.classpath new file mode 100644 index 0000000..2911ff5 --- /dev/null +++ b/org.openscada.opc.dcom/.classpath @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/org.openscada.opc.dcom/.gitignore b/org.openscada.opc.dcom/.gitignore new file mode 100644 index 0000000..4cc0812 --- /dev/null +++ b/org.openscada.opc.dcom/.gitignore @@ -0,0 +1,2 @@ +bin/ +/target diff --git a/org.openscada.opc.dcom/.project b/org.openscada.opc.dcom/.project new file mode 100644 index 0000000..1274dfa --- /dev/null +++ b/org.openscada.opc.dcom/.project @@ -0,0 +1,46 @@ + + + org.openscada.opc.dcom + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.babel.editor.rbeBuilder + + + + + org.eclipse.pde.api.tools.apiAnalysisBuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.m2e.core.maven2Nature + org.eclipse.jdt.core.javanature + org.eclipse.pde.PluginNature + org.eclipse.babel.editor.rbeNature + org.eclipse.pde.api.tools.apiAnalysisNature + + diff --git a/org.openscada.opc.dcom/build.properties b/org.openscada.opc.dcom/build.properties new file mode 100644 index 0000000..4394f20 --- /dev/null +++ b/org.openscada.opc.dcom/build.properties @@ -0,0 +1 @@ +source.. = src diff --git a/org.openscada.opc.dcom/pom.xml b/org.openscada.opc.dcom/pom.xml new file mode 100644 index 0000000..4d6dcb7 --- /dev/null +++ b/org.openscada.opc.dcom/pom.xml @@ -0,0 +1,26 @@ + + 4.0.0 + org.openscada.opc.dcom + org.openscada.opc.dcom + 0.0.1-SNAPSHOT + + + org.slf4j + slf4j-log4j12 + 1.7.2 + + + commons-logging + commons-logging + 1.2 + + + + org.kohsuke.jinterop + j-interop + 2.0.5 + + + + \ No newline at end of file diff --git a/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/Categories.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/Categories.java new file mode 100644 index 0000000..32cc414 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/Categories.java @@ -0,0 +1,32 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/Constants.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/Constants.java new file mode 100644 index 0000000..1bb75fa --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/Constants.java @@ -0,0 +1,37 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/EventHandler.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/EventHandler.java new file mode 100644 index 0000000..37cab5c --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/EventHandler.java @@ -0,0 +1,32 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/FILETIME.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/FILETIME.java new file mode 100644 index 0000000..57fe74e --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/FILETIME.java @@ -0,0 +1,174 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.common; + +import java.math.BigDecimal; +import java.util.Calendar; + +import org.jinterop.dcom.common.JIException; +import org.jinterop.dcom.core.JIStruct; + +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 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 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 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/KeyedResult.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/KeyedResult.java new file mode 100644 index 0000000..e65c30b --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/KeyedResult.java @@ -0,0 +1,46 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/KeyedResultSet.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/KeyedResultSet.java new file mode 100644 index 0000000..5780a56 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/KeyedResultSet.java @@ -0,0 +1,37 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2012 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.common; + +import java.util.ArrayList; + +public class KeyedResultSet extends ArrayList> +{ + private static final long serialVersionUID = 1L; + + public KeyedResultSet () + { + super (); + } + + public KeyedResultSet ( final int size ) + { + super ( size ); // me + } +} diff --git a/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/Result.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/Result.java new file mode 100644 index 0000000..d76b6f7 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/Result.java @@ -0,0 +1,64 @@ +/* + * This file is part of the OpenSCADA project + * + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * Copyright (C) 2013 Jens Reimann (ctron@dentrassi.de) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/ResultSet.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/ResultSet.java new file mode 100644 index 0000000..8573e3a --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/ResultSet.java @@ -0,0 +1,38 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.common; + +import java.util.ArrayList; + +public class ResultSet extends ArrayList> +{ + + private static final long serialVersionUID = 6392417310208978252L; + + public ResultSet () + { + super (); + } + + public ResultSet ( final int size ) + { + super ( size ); // me + } +} diff --git a/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/BaseCOMObject.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/BaseCOMObject.java new file mode 100644 index 0000000..e64f707 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/BaseCOMObject.java @@ -0,0 +1,45 @@ +/* + * This file is part of the OpenSCADA project + * + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * Copyright (C) 2013 Jens Reimann (ctron@dentrassi.de) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/EnumGUID.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/EnumGUID.java new file mode 100644 index 0000000..37ccad9 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/EnumGUID.java @@ -0,0 +1,134 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.common.impl; + +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +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; + +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.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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/EnumString.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/EnumString.java new file mode 100644 index 0000000..431060f --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/EnumString.java @@ -0,0 +1,136 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.common.impl; + +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +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 org.jinterop.dcom.core.JIString; + +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 ( org.openscada.opc.dcom.common.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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/EventHandlerImpl.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/EventHandlerImpl.java new file mode 100644 index 0000000..cad1e84 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/EventHandlerImpl.java @@ -0,0 +1,65 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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.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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/Helper.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/Helper.java new file mode 100644 index 0000000..17e2381 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/Helper.java @@ -0,0 +1,71 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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; + +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 + */ + public static Object[] callRespectSFALSE ( final IJIComObject object, final JICallBuilder callObject ) throws JIException + { + try + { + return object.call ( callObject ); + } + catch ( JIException e ) + { + if ( e.getErrorCode () != org.openscada.opc.dcom.common.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 In case something goes wrong + */ + 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/OPCCommon.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/OPCCommon.java new file mode 100644 index 0000000..a65372e --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/common/impl/OPCCommon.java @@ -0,0 +1,101 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.common.impl; + +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.Collection; + +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 org.jinterop.dcom.core.JIPointer; +import org.jinterop.dcom.core.JIString; + +public class OPCCommon extends BaseCOMObject +{ + public OPCCommon ( final IJIComObject opcObject ) throws IllegalArgumentException, UnknownHostException, JIException + { + super ( opcObject.queryInterface ( org.openscada.opc.dcom.common.Constants.IOPCCommon_IID ) ); + } + + 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 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 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/Constants.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/Constants.java new file mode 100644 index 0000000..0c11e45 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/Constants.java @@ -0,0 +1,43 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.da; + +public interface Constants extends 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/IOPCDataCallback.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/IOPCDataCallback.java new file mode 100644 index 0000000..84f1286 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/IOPCDataCallback.java @@ -0,0 +1,34 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.da; + +import org.openscada.opc.dcom.common.KeyedResultSet; +import 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/IORequest.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/IORequest.java new file mode 100644 index 0000000..7d43435 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/IORequest.java @@ -0,0 +1,56 @@ +/* + * This file is part of the OpenSCADA project + * + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * Copyright (C) 2013 Jens Reimann (ctron@dentrassi.de) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCBROWSEDIRECTION.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCBROWSEDIRECTION.java new file mode 100644 index 0000000..1ba83e6 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCBROWSEDIRECTION.java @@ -0,0 +1,55 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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 int id () + { + return this._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; + } + } +} diff --git a/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCBROWSETYPE.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCBROWSETYPE.java new file mode 100644 index 0000000..500adfa --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCBROWSETYPE.java @@ -0,0 +1,55 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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 int id () + { + return this._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; + } + } +} diff --git a/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCDATASOURCE.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCDATASOURCE.java new file mode 100644 index 0000000..dfce9e3 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCDATASOURCE.java @@ -0,0 +1,52 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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 int id () + { + return this._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; + } + } +} diff --git a/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCENUMSCOPE.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCENUMSCOPE.java new file mode 100644 index 0000000..1fd8163 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCENUMSCOPE.java @@ -0,0 +1,64 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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 int id () + { + return this._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; + } + } +} diff --git a/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCGroupState.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCGroupState.java new file mode 100644 index 0000000..42c0101 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCGroupState.java @@ -0,0 +1,119 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCITEMDEF.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCITEMDEF.java new file mode 100644 index 0000000..206b9fa --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCITEMDEF.java @@ -0,0 +1,124 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2012 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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.jinterop.dcom.core.JIVariant; + +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 + */ + 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 ( new Integer ( isActive () ? 1 : 0 ) ); + struct.addMember ( Integer.valueOf ( getClientHandle () ) ); + + struct.addMember ( Integer.valueOf ( 0 ) ); // blob size + struct.addMember ( new JIPointer ( null ) ); // blob + + struct.addMember ( Short.valueOf ( getRequestedDataType () ) ); + struct.addMember ( Short.valueOf ( getReserved () ) ); + return struct; + } +} diff --git a/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCITEMRESULT.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCITEMRESULT.java new file mode 100644 index 0000000..6e1d9fc --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCITEMRESULT.java @@ -0,0 +1,104 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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 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; + } + + 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 ( new Integer ( (Integer)struct.getMember ( 0 ) ) ); + result.setCanonicalDataType ( new Short ( (Short)struct.getMember ( 1 ) ) ); + result.setReserved ( new Short ( (Short)struct.getMember ( 2 ) ) ); + result.setAccessRights ( new Integer ( (Integer)struct.getMember ( 3 ) ) ); + + return result; + } +} diff --git a/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCITEMSTATE.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCITEMSTATE.java new file mode 100644 index 0000000..040fa84 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCITEMSTATE.java @@ -0,0 +1,114 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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.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 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; + } + + 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; + } +} diff --git a/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCNAMESPACETYPE.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCNAMESPACETYPE.java new file mode 100644 index 0000000..ab5f375 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCNAMESPACETYPE.java @@ -0,0 +1,52 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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 int id () + { + return this._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; + } + } +} diff --git a/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCSERVERSTATE.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCSERVERSTATE.java new file mode 100644 index 0000000..81f6267 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCSERVERSTATE.java @@ -0,0 +1,64 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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 int id () + { + return this._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; + } + } +} diff --git a/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCSERVERSTATUS.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCSERVERSTATUS.java new file mode 100644 index 0000000..2633d14 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/OPCSERVERSTATUS.java @@ -0,0 +1,201 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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.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 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; + } + + 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; + } +} diff --git a/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/PropertyDescription.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/PropertyDescription.java new file mode 100644 index 0000000..eab7c9f --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/PropertyDescription.java @@ -0,0 +1,59 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/ValueData.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/ValueData.java new file mode 100644 index 0000000..ed44aa3 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/ValueData.java @@ -0,0 +1,65 @@ +/* + * This file is part of the OpenSCADA project + * + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * Copyright (C) 2013 Jens Reimann (ctron@dentrassi.de) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.da; + +import java.util.Calendar; + +import org.jinterop.dcom.core.JIVariant; + +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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/WriteRequest.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/WriteRequest.java new file mode 100644 index 0000000..acb656a --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/WriteRequest.java @@ -0,0 +1,80 @@ +/* + * This file is part of the OpenSCADA project + * + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * Copyright (C) 2013 Jens Reimann (ctron@dentrassi.de) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.da; + +import org.jinterop.dcom.core.JIVariant; + +/** + * Data for a write request to the server + * + * @author Jens Reimann + */ +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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCAsyncIO2.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCAsyncIO2.java new file mode 100644 index 0000000..d4138f6 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCAsyncIO2.java @@ -0,0 +1,141 @@ +/* + * This file is part of the OpenSCADA project + * + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * Copyright (C) 2013 Jens Reimann (ctron@dentrassi.de) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.da.impl; + +import java.net.UnknownHostException; + +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 org.jinterop.dcom.core.JIPointer; +import org.openscada.opc.dcom.common.Result; +import org.openscada.opc.dcom.common.ResultSet; +import org.openscada.opc.dcom.common.impl.BaseCOMObject; +import org.openscada.opc.dcom.da.Constants; +import org.openscada.opc.dcom.da.OPCDATASOURCE; + +public class OPCAsyncIO2 extends BaseCOMObject +{ + 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; + } + } + + 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 ); + } +} diff --git a/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCBrowse.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCBrowse.java new file mode 100644 index 0000000..8a640d2 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCBrowse.java @@ -0,0 +1,35 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.da.impl; + +import java.net.UnknownHostException; + +import org.jinterop.dcom.common.JIException; +import org.jinterop.dcom.core.IJIComObject; +import org.openscada.opc.dcom.common.impl.BaseCOMObject; +import org.openscada.opc.dcom.da.Constants; + +public class OPCBrowse extends BaseCOMObject +{ + public OPCBrowse ( final IJIComObject opcServer ) throws IllegalArgumentException, UnknownHostException, JIException + { + super ( opcServer.queryInterface ( Constants.IOPCBrowse_IID ) ); + } +} diff --git a/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCBrowseServerAddressSpace.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCBrowseServerAddressSpace.java new file mode 100644 index 0000000..f21c82e --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCBrowseServerAddressSpace.java @@ -0,0 +1,157 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.da.impl; + +import java.net.UnknownHostException; + +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.JIPointer; +import org.jinterop.dcom.core.JIString; +import org.openscada.opc.dcom.common.impl.BaseCOMObject; +import org.openscada.opc.dcom.common.impl.EnumString; +import org.openscada.opc.dcom.common.impl.Helper; +import org.openscada.opc.dcom.da.Constants; +import org.openscada.opc.dcom.da.OPCBROWSEDIRECTION; +import org.openscada.opc.dcom.da.OPCBROWSETYPE; +import org.openscada.opc.dcom.da.OPCNAMESPACETYPE; + +/** + * Implementation for IOPCBrowseServerAddressSpace + * @author Jens Reimann + * + */ +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 + */ + 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 + */ + 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 + * @throws IllegalArgumentException + * @throws 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 + */ + 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCDataCallback.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCDataCallback.java new file mode 100644 index 0000000..d940777 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCDataCallback.java @@ -0,0 +1,259 @@ +/* + * This file is part of the OpenSCADA project + * + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * Copyright (C) 2013 Jens Reimann (ctron@dentrassi.de) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.da.impl; + +import java.util.LinkedList; +import java.util.List; + +import org.jinterop.dcom.common.JIException; +import org.jinterop.dcom.core.JIArray; +import org.jinterop.dcom.core.JIFlags; +import org.jinterop.dcom.core.JILocalCoClass; +import org.jinterop.dcom.core.JILocalInterfaceDefinition; +import org.jinterop.dcom.core.JILocalMethodDescriptor; +import org.jinterop.dcom.core.JILocalParamsDescriptor; +import org.jinterop.dcom.core.JIStruct; +import org.jinterop.dcom.core.JIVariant; +import org.openscada.opc.dcom.common.FILETIME; +import org.openscada.opc.dcom.common.KeyedResult; +import org.openscada.opc.dcom.common.KeyedResultSet; +import org.openscada.opc.dcom.common.Result; +import org.openscada.opc.dcom.common.ResultSet; +import org.openscada.opc.dcom.common.impl.EventHandlerImpl; +import org.openscada.opc.dcom.da.Constants; +import org.openscada.opc.dcom.da.IOPCDataCallback; +import org.openscada.opc.dcom.da.ValueData; + +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.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.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.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.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.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.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.openscada.opc.dcom.common.Constants.S_OK }; + } + + this.callback.cancelComplete ( transactionId, serverGroupHandle ); + + // The client must always return S_OK + return new Object[] { 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 void setCallback ( final IOPCDataCallback callback ) + { + this.callback = callback; + } + + public IOPCDataCallback getCallback () + { + return this.callback; + } +} diff --git a/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCGroupStateMgt.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCGroupStateMgt.java new file mode 100644 index 0000000..e78e45d --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCGroupStateMgt.java @@ -0,0 +1,218 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.da.impl; + +import java.net.UnknownHostException; + +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.JIFrameworkHelper; +import org.jinterop.dcom.core.JIPointer; +import org.jinterop.dcom.core.JIString; +import org.jinterop.dcom.impls.JIObjectFactory; +import org.openscada.opc.dcom.common.EventHandler; +import org.openscada.opc.dcom.common.impl.BaseCOMObject; +import org.openscada.opc.dcom.da.Constants; +import org.openscada.opc.dcom.da.IOPCDataCallback; +import org.openscada.opc.dcom.da.OPCGroupState; + +/** + * Implementation of IOPCGroupStateMgt + * + * @author Jens Reimann + */ +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 + */ + 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 + */ + 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 + * @throws UnknownHostException + * @throws 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 + */ + 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCItemIO.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCItemIO.java new file mode 100644 index 0000000..53916a4 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCItemIO.java @@ -0,0 +1,73 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.da.impl; + +import java.net.UnknownHostException; + +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 org.jinterop.dcom.core.JIPointer; +import org.jinterop.dcom.core.JIString; +import org.jinterop.dcom.core.JIVariant; +import org.openscada.opc.dcom.common.FILETIME; +import org.openscada.opc.dcom.common.impl.BaseCOMObject; +import org.openscada.opc.dcom.da.Constants; +import org.openscada.opc.dcom.da.IORequest; + +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] = new Integer ( 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCItemMgt.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCItemMgt.java new file mode 100644 index 0000000..5932270 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCItemMgt.java @@ -0,0 +1,211 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2012 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.da.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 org.jinterop.dcom.core.JIPointer; +import org.jinterop.dcom.core.JIStruct; +import org.openscada.opc.dcom.common.KeyedResult; +import org.openscada.opc.dcom.common.KeyedResultSet; +import org.openscada.opc.dcom.common.Result; +import org.openscada.opc.dcom.common.ResultSet; +import org.openscada.opc.dcom.common.impl.BaseCOMObject; +import org.openscada.opc.dcom.common.impl.Helper; +import org.openscada.opc.dcom.da.Constants; +import org.openscada.opc.dcom.da.OPCITEMDEF; +import 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCItemProperties.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCItemProperties.java new file mode 100644 index 0000000..424a32c --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCItemProperties.java @@ -0,0 +1,154 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.da.impl; + +import java.net.UnknownHostException; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +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 org.jinterop.dcom.core.JIPointer; +import org.jinterop.dcom.core.JIString; +import org.jinterop.dcom.core.JIVariant; +import org.openscada.opc.dcom.common.KeyedResult; +import org.openscada.opc.dcom.common.KeyedResultSet; +import org.openscada.opc.dcom.common.impl.BaseCOMObject; +import org.openscada.opc.dcom.common.impl.Helper; +import org.openscada.opc.dcom.da.Constants; +import org.openscada.opc.dcom.da.PropertyDescription; + +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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCServer.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCServer.java new file mode 100644 index 0000000..f55e59d --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCServer.java @@ -0,0 +1,191 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.da.impl; + +import java.net.UnknownHostException; + +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.JIPointer; +import org.jinterop.dcom.core.JIStruct; +import org.openscada.opc.dcom.common.impl.BaseCOMObject; +import org.openscada.opc.dcom.common.impl.EnumString; +import org.openscada.opc.dcom.common.impl.Helper; +import org.openscada.opc.dcom.common.impl.OPCCommon; +import org.openscada.opc.dcom.da.Constants; +import org.openscada.opc.dcom.da.OPCENUMSCOPE; +import org.openscada.opc.dcom.da.OPCSERVERSTATUS; + +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 + */ + 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 + * @throws IllegalArgumentException + * @throws 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.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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCSyncIO.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCSyncIO.java new file mode 100644 index 0000000..1bf90ad --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/da/impl/OPCSyncIO.java @@ -0,0 +1,113 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.da.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 org.jinterop.dcom.core.JIPointer; +import org.jinterop.dcom.core.JIStruct; +import org.jinterop.dcom.core.JIVariant; +import org.openscada.opc.dcom.common.KeyedResult; +import org.openscada.opc.dcom.common.KeyedResultSet; +import org.openscada.opc.dcom.common.Result; +import org.openscada.opc.dcom.common.ResultSet; +import org.openscada.opc.dcom.common.impl.BaseCOMObject; +import org.openscada.opc.dcom.common.impl.Helper; +import org.openscada.opc.dcom.da.Constants; +import org.openscada.opc.dcom.da.OPCDATASOURCE; +import org.openscada.opc.dcom.da.OPCITEMSTATE; +import 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/list/ClassDetails.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/list/ClassDetails.java new file mode 100644 index 0000000..93e57b2 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/list/ClassDetails.java @@ -0,0 +1,64 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/list/Constants.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/list/Constants.java new file mode 100644 index 0000000..af4863e --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/list/Constants.java @@ -0,0 +1,27 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.list; + +public interface Constants extends 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/org.openscada.opc.dcom/src/org/openscada/opc/dcom/list/impl/OPCServerList.java b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/list/impl/OPCServerList.java new file mode 100644 index 0000000..69ab911 --- /dev/null +++ b/org.openscada.opc.dcom/src/org/openscada/opc/dcom/list/impl/OPCServerList.java @@ -0,0 +1,168 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.dcom.list.impl; + +import java.net.UnknownHostException; + +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.JIClsid; +import org.jinterop.dcom.core.JIFlags; +import org.jinterop.dcom.core.JIPointer; +import org.jinterop.dcom.core.JIString; +import org.openscada.opc.dcom.common.impl.BaseCOMObject; +import org.openscada.opc.dcom.common.impl.EnumGUID; +import org.openscada.opc.dcom.common.impl.Helper; +import org.openscada.opc.dcom.list.ClassDetails; +import org.openscada.opc.dcom.list.Constants; + +import rpc.core.UUID; + +/** + * 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 + * @throws 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/org.openscada.opc.dcom/src/progIdVsClsidDB.properties b/org.openscada.opc.dcom/src/progIdVsClsidDB.properties new file mode 100644 index 0000000..b6dc3ef --- /dev/null +++ b/org.openscada.opc.dcom/src/progIdVsClsidDB.properties @@ -0,0 +1,13 @@ +#progId Vs ClsidDB +#Tue Nov 28 21:56:16 IST 2006 +Word.Application=000209ff-0000-0000-c000-000000000046 +TestCOM123.TestServer2=92a065a9-106a-4cc3-8d67-43e3a1e73df3 +SYSINFO.SysInfo=6fba474b-43ac-11ce-9a0e-00aa0062bb4c +ADODB.Connection=00000514-0000-0010-8000-00aa006d2ea4 +TestJavaServer.TestServer2=617d5a31-a3bf-440f-a58f-1f57f6ac7527 +TestJavaServer.TestServer1=56bad610-0fcb-418a-b25e-174159a4adce +InternetExplorer.Application=0002df01-0000-0000-c000-000000000046 +Excel.Application=00024500-0000-0000-c000-000000000046 +StdCollection.VBCollection=4b738074-ea47-11d2-b25a-00105a022091 +WbemScripting.SWbemLocator=76a64158-cb41-11d1-8b02-00600806d9b6 +PowerPoint.Application=91493441-5a91-11cf-8700-00aa0060263b diff --git a/org.openscada.opc.lib.test/.classpath b/org.openscada.opc.lib.test/.classpath new file mode 100644 index 0000000..00cb339 --- /dev/null +++ b/org.openscada.opc.lib.test/.classpath @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/org.openscada.opc.lib.test/.gitignore b/org.openscada.opc.lib.test/.gitignore new file mode 100644 index 0000000..e660fd9 --- /dev/null +++ b/org.openscada.opc.lib.test/.gitignore @@ -0,0 +1 @@ +bin/ diff --git a/org.openscada.opc.lib.test/.project b/org.openscada.opc.lib.test/.project new file mode 100644 index 0000000..38a8d42 --- /dev/null +++ b/org.openscada.opc.lib.test/.project @@ -0,0 +1,46 @@ + + + org.openscada.opc.lib.test + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.babel.editor.rbeBuilder + + + + + org.eclipse.pde.api.tools.apiAnalysisBuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.m2e.core.maven2Nature + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + org.eclipse.babel.editor.rbeNature + org.eclipse.pde.api.tools.apiAnalysisNature + + diff --git a/org.openscada.opc.lib.test/LICENSE b/org.openscada.opc.lib.test/LICENSE new file mode 100644 index 0000000..0836c46 --- /dev/null +++ b/org.openscada.opc.lib.test/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/org.openscada.opc.lib.test/pom.xml b/org.openscada.opc.lib.test/pom.xml new file mode 100644 index 0000000..d37aea4 --- /dev/null +++ b/org.openscada.opc.lib.test/pom.xml @@ -0,0 +1,37 @@ + + 4.0.0 + org.openscada.opc.lib.test + org.openscada.opc.lib.test + 0.0.1-SNAPSHOT + + + + org.slf4j + slf4j-log4j12 + 1.7.2 + + + commons-logging + commons-logging + 1.2 + + + + org.kohsuke.jinterop + j-interop + 2.0.5 + + + org.openscada.opc.dcom + org.openscada.opc.dcom + 0.0.1-SNAPSHOT + + + org.openscada.utgard + org.openscada.opc.lib + 1.1.0.v20130529 + + + + \ No newline at end of file diff --git a/org.openscada.opc.lib.test/src/org/openscada/opc/lib/DataCallbackDumper.java b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/DataCallbackDumper.java new file mode 100644 index 0000000..ee32769 --- /dev/null +++ b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/DataCallbackDumper.java @@ -0,0 +1,45 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.lib; + +import org.jinterop.dcom.common.JIException; +import org.openscada.opc.lib.da.DataCallback; +import org.openscada.opc.lib.da.Item; +import org.openscada.opc.lib.da.ItemState; + +public class DataCallbackDumper implements DataCallback +{ + + public void changed ( final Item item, final ItemState itemState ) + { + System.out.println ( String.format ( "Item: %s, Value: %s, Timestamp: %tc, Quality: %d", item.getId (), itemState.getValue (), itemState.getTimestamp (), itemState.getQuality () ) ); + + try + { + VariantDumper.dumpValue ( "\t", itemState.getValue () ); + } + catch ( final JIException e ) + { + e.printStackTrace (); + } + + } + +} diff --git a/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest1.java b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest1.java new file mode 100644 index 0000000..f5471a7 --- /dev/null +++ b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest1.java @@ -0,0 +1,112 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.lib; + +import java.util.Map; +import java.util.concurrent.Executors; + +import org.jinterop.dcom.common.JIException; +import org.openscada.opc.lib.common.ConnectionInformation; +import org.openscada.opc.lib.da.Group; +import org.openscada.opc.lib.da.Item; +import org.openscada.opc.lib.da.ItemState; +import org.openscada.opc.lib.da.Server; +import org.openscada.opc.lib.da.browser.Branch; +import org.openscada.opc.lib.da.browser.Leaf; + +public class OPCTest1 +{ + public static void dumpItemState ( final Item item, final ItemState state ) + { + System.out.println ( String.format ( "Item: %s, Value: %s, Timestamp: %tc, Quality: %d", item.getId (), state.getValue (), state.getTimestamp (), state.getQuality () ) ); + } + + public static void dumpTree ( final Branch branch, final int level ) + { + final StringBuilder sb = new StringBuilder (); + for ( int i = 0; i < level; i++ ) + { + sb.append ( " " ); + } + final String indent = sb.toString (); + + for ( final Leaf leaf : branch.getLeaves () ) + { + System.out.println ( indent + "Leaf: " + leaf.getName () + " [" + leaf.getItemId () + "]" ); + } + for ( final Branch subBranch : branch.getBranches () ) + { + System.out.println ( indent + "Branch: " + subBranch.getName () ); + dumpTree ( subBranch, level + 1 ); + } + } + + @SuppressWarnings ( "unused" ) + public static void main ( final String[] args ) throws Throwable + { + // create connection information + final ConnectionInformation ci = new ConnectionInformation (); + ci.setHost ( args[0] ); + ci.setDomain ( args[1] ); + ci.setUser ( args[2] ); + ci.setPassword ( args[3] ); + ci.setClsid ( args[4] ); + + // create a new server + final Server server = new Server ( ci, Executors.newSingleThreadScheduledExecutor () ); + try + { + // connect to server + server.connect (); + + // browse + dumpTree ( server.getTreeBrowser ().browse (), 0 ); + + // add sync reader + + // Add a new group + Group group = server.addGroup ( "test" ); + // group is initially active ... just for demonstration + group.setActive ( true ); + + // We already have our group ... just for demonstration + group = server.findGroup ( "test" ); + + // Add a new item to the group + final Item item = group.addItem ( "Saw-toothed Waves.Int2" ); + // Items are initially active ... just for demonstration + item.setActive ( true ); + + // Add some more items ... including one that is already existing + final Map items = group.addItems ( "Saw-toothed Waves.Int2", "Saw-toothed Waves.Int4" ); + + // sync-read some values + for ( int i = 0; i < 10; i++ ) + { + Thread.sleep ( 100 ); + dumpItemState ( item, item.read ( false ) ); + } + } + catch ( final JIException e ) + { + System.out.println ( String.format ( "%08X: %s", e.getErrorCode (), server.getErrorMessage ( e.getErrorCode () ) ) ); + } + } +} diff --git a/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest2.java b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest2.java new file mode 100644 index 0000000..ee0620b --- /dev/null +++ b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest2.java @@ -0,0 +1,79 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.lib; + +import java.util.concurrent.Executors; + +import org.jinterop.dcom.common.JIException; +import org.openscada.opc.lib.common.ConnectionInformation; +import org.openscada.opc.lib.da.AccessBase; +import org.openscada.opc.lib.da.Server; +import org.openscada.opc.lib.da.SyncAccess; + +/** + * Another test showing the "Access" interface with the SyncAccess implementation. + * @author Jens Reimann + * + */ +public class OPCTest2 +{ + public static void main ( final String[] args ) throws Throwable + { + // create connection information + final ConnectionInformation ci = new ConnectionInformation (); + ci.setHost ( args[0] ); + ci.setDomain ( args[1] ); + ci.setUser ( args[2] ); + ci.setPassword ( args[3] ); + ci.setClsid ( args[4] ); + + String itemId = "Saw-toothed Waves.Int2"; + if ( args.length >= 6 ) + { + itemId = args[5]; + } + + // create a new server + final Server server = new Server ( ci, Executors.newSingleThreadScheduledExecutor () ); + try + { + // connect to server + server.connect (); + + // add sync access + + final AccessBase access = new SyncAccess ( server, 100 ); + access.addItem ( itemId, new DataCallbackDumper () ); + + // start reading + access.bind (); + + // wait a little bit + Thread.sleep ( 10 * 1000 ); + + // stop reading + access.unbind (); + } + catch ( final JIException e ) + { + System.out.println ( String.format ( "%08X: %s", e.getErrorCode (), server.getErrorMessage ( e.getErrorCode () ) ) ); + } + } +} diff --git a/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest3.java b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest3.java new file mode 100644 index 0000000..3730bc1 --- /dev/null +++ b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest3.java @@ -0,0 +1,130 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.lib; + +import java.net.UnknownHostException; +import java.util.concurrent.Executors; + +import org.jinterop.dcom.common.JIException; +import org.openscada.opc.lib.common.ConnectionInformation; +import org.openscada.opc.lib.da.Server; +import org.openscada.opc.lib.da.browser.BaseBrowser; +import org.openscada.opc.lib.da.browser.Branch; +import org.openscada.opc.lib.da.browser.Leaf; +import org.openscada.opc.lib.da.browser.TreeBrowser; + +/** + * Another test showing the browser interfaces + * @author Jens Reimann + * + */ +public class OPCTest3 +{ + + private static void dumpLeaf ( final String prefix, final Leaf leaf ) + { + System.out.println ( prefix + "Leaf: " + leaf.getName () + " [" + leaf.getItemId () + "]" ); + } + + private static void dumpBranch ( final String prefix, final Branch branch ) + { + System.out.println ( prefix + "Branch: " + branch.getName () ); + } + + public static void dumpTree ( final Branch branch, final int level ) + { + final StringBuilder sb = new StringBuilder (); + for ( int i = 0; i < level; i++ ) + { + sb.append ( " " ); + } + final String indent = sb.toString (); + + for ( final Leaf leaf : branch.getLeaves () ) + { + dumpLeaf ( indent, leaf ); + } + for ( final Branch subBranch : branch.getBranches () ) + { + dumpBranch ( indent, subBranch ); + dumpTree ( subBranch, level + 1 ); + } + } + + public static void main ( final String[] args ) throws Throwable + { + // create connection information + final ConnectionInformation ci = new ConnectionInformation (); + ci.setHost ( args[0] ); + ci.setDomain ( args[1] ); + ci.setUser ( args[2] ); + ci.setPassword ( args[3] ); + ci.setClsid ( args[4] ); + + // create a new server + final Server server = new Server ( ci, Executors.newSingleThreadScheduledExecutor () ); + try + { + // connect to server + server.connect (); + + // browse flat + final BaseBrowser flatBrowser = server.getFlatBrowser (); + if ( flatBrowser != null ) + { + for ( final String item : server.getFlatBrowser ().browse ( "" ) ) + { + System.out.println ( item ); + } + } + + // browse tree + final TreeBrowser treeBrowser = server.getTreeBrowser (); + if ( treeBrowser != null ) + { + dumpTree ( treeBrowser.browse (), 0 ); + } + + // browse tree manually + browseTree ( "", treeBrowser, new Branch () ); + } + catch ( final JIException e ) + { + e.printStackTrace (); + System.out.println ( String.format ( "%08X: %s", e.getErrorCode (), server.getErrorMessage ( e.getErrorCode () ) ) ); + } + } + + private static void browseTree ( final String prefix, final TreeBrowser treeBrowser, final Branch branch ) throws IllegalArgumentException, UnknownHostException, JIException + { + treeBrowser.fillLeaves ( branch ); + treeBrowser.fillBranches ( branch ); + + for ( final Leaf leaf : branch.getLeaves () ) + { + dumpLeaf ( "M - " + prefix + " ", leaf ); + } + for ( final Branch subBranch : branch.getBranches () ) + { + dumpBranch ( "M - " + prefix + " ", subBranch ); + browseTree ( prefix + " ", treeBrowser, subBranch ); + } + } +} diff --git a/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest4.java b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest4.java new file mode 100644 index 0000000..3c019dc --- /dev/null +++ b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest4.java @@ -0,0 +1,93 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.lib; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.Executors; + +import org.jinterop.dcom.common.JIException; +import org.openscada.opc.lib.common.ConnectionInformation; +import org.openscada.opc.lib.da.AccessBase; +import org.openscada.opc.lib.da.Async20Access; +import org.openscada.opc.lib.da.Server; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Another test showing the "Access" interface with the Async20Access implementation. + * @author Jens Reimann + * + */ +public class OPCTest4 +{ + private static Logger _log = LoggerFactory.getLogger ( OPCTest4.class ); + + public static void main ( final String[] args ) throws Throwable + { + // create connection information + final ConnectionInformation ci = new ConnectionInformation (); + ci.setHost ( args[0] ); + ci.setDomain ( args[1] ); + ci.setUser ( args[2] ); + ci.setPassword ( args[3] ); + ci.setClsid ( args[4] ); + + final Set items = new HashSet (); + for ( int i = 5; i < args.length; i++ ) + { + items.add ( args[i] ); + } + if ( items.isEmpty () ) + { + items.add ( "Saw-toothed Waves.Int2" ); + } + + // create a new server + final Server server = new Server ( ci, Executors.newSingleThreadScheduledExecutor () ); + try + { + // connect to server + server.connect (); + + // add sync access + final AccessBase access = new Async20Access ( server, 100, false ); + for ( final String itemId : items ) + { + access.addItem ( itemId, new DataCallbackDumper () ); + } + + // start reading + access.bind (); + + // wait a little bit + _log.info ( "Sleep for some seconds to give events a chance..." ); + Thread.sleep ( 10 * 1000 ); + _log.info ( "Returned from sleep" ); + + // stop reading + access.unbind (); + } + catch ( final JIException e ) + { + System.out.println ( String.format ( "%08X: %s", e.getErrorCode (), server.getErrorMessage ( e.getErrorCode () ) ) ); + } + } +} diff --git a/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest5.java b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest5.java new file mode 100644 index 0000000..fc1128e --- /dev/null +++ b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest5.java @@ -0,0 +1,80 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.lib; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Executors; + +import org.jinterop.dcom.common.JIException; +import org.openscada.opc.lib.common.ConnectionInformation; +import org.openscada.opc.lib.da.Async20Access; +import org.openscada.opc.lib.da.Server; + +/** + * Another test showing the "Access" interface with + * the Async20Access implementation. Testing two connections at the same time. + * + * @author Jens Reimann + */ +public class OPCTest5 +{ + public static void main ( final String[] args ) throws Throwable + { + // create connection information + final ConnectionInformation baseInfo = new ConnectionInformation (); + baseInfo.setHost ( args[0] ); + baseInfo.setDomain ( args[1] ); + baseInfo.setUser ( args[2] ); + baseInfo.setPassword ( args[3] ); + + final List testInfo = new LinkedList (); + int i = 0; + + try + { + + while ( args.length > i * 2 + 4 ) + { + final ConnectionInformation ci = new ConnectionInformation ( baseInfo ); + ci.setClsid ( args[i * 2 + 4] ); + final OPCTestInfo ti = new OPCTestInfo (); + ti._info = ci; + ti._itemId = args[i * 2 + 5]; + ti._server = new Server ( ci, Executors.newSingleThreadScheduledExecutor () ); + + ti._server.connect (); + ti._access = new Async20Access ( ti._server, 100, false ); + ti._access.addItem ( ti._itemId, new DataCallbackDumper () ); + ti._access.bind (); + + testInfo.add ( ti ); + i++; + } + + // wait a little bit + Thread.sleep ( 10 * 1000 ); + } + catch ( final JIException e ) + { + System.out.println ( String.format ( "%08X", e.getErrorCode () ) ); + } + } +} diff --git a/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest6.java b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest6.java new file mode 100644 index 0000000..8707f11 --- /dev/null +++ b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest6.java @@ -0,0 +1,92 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.lib; + +import java.util.concurrent.Executors; + +import org.jinterop.dcom.common.JIException; +import org.openscada.opc.lib.common.ConnectionInformation; +import org.openscada.opc.lib.da.AccessBase; +import org.openscada.opc.lib.da.AutoReconnectController; +import org.openscada.opc.lib.da.Server; +import org.openscada.opc.lib.da.SyncAccess; + +/** + * Another test showing the "Access" interface with the SyncAccess implementation + * including the AutoReconnectController which should re-establish the connection + * if it breaks. + * @author Jens Reimann + * + */ +public class OPCTest6 +{ + public static void main ( final String[] args ) throws Throwable + { + // create connection information + final ConnectionInformation ci = new ConnectionInformation (); + ci.setHost ( args[0] ); + ci.setDomain ( args[1] ); + ci.setUser ( args[2] ); + ci.setPassword ( args[3] ); + ci.setClsid ( args[4] ); + + String itemId = "Saw-toothed Waves.Int2"; + if ( args.length >= 6 ) + { + itemId = args[5]; + } + + // create a new server + final Server server = new Server ( ci, Executors.newSingleThreadScheduledExecutor () ); + final AutoReconnectController autoReconnectController = new AutoReconnectController ( server ); + try + { + // connect to server + autoReconnectController.connect (); + + // add sync access + + final AccessBase access = new SyncAccess ( server, 100 ); + access.addItem ( itemId, new DataCallbackDumper () ); + + // start reading + access.bind (); + + // run forever + final boolean running = true; + while ( running ) + { + Thread.sleep ( 10 * 1000 ); + } + + /* + // stop reading + access.unbind (); + + // disconnect + autoReconnectController.disconnect (); + */ + } + catch ( final JIException e ) + { + System.out.println ( String.format ( "%08X: %s", e.getErrorCode (), server.getErrorMessage ( e.getErrorCode () ) ) ); + } + } +} diff --git a/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest7.java b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest7.java new file mode 100644 index 0000000..a216ec7 --- /dev/null +++ b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest7.java @@ -0,0 +1,110 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.lib; + +import java.util.concurrent.Executors; + +import org.jinterop.dcom.common.JIException; +import org.jinterop.dcom.core.JIArray; +import org.jinterop.dcom.core.JIFlags; +import org.jinterop.dcom.core.JIString; +import org.jinterop.dcom.core.JIVariant; +import org.openscada.opc.lib.common.ConnectionInformation; +import org.openscada.opc.lib.da.Group; +import org.openscada.opc.lib.da.Item; +import org.openscada.opc.lib.da.ItemState; +import org.openscada.opc.lib.da.Server; + +/** + * A sample that reads an item and writes back the result. You will need a + * read/write item for this to work. + * @author Jens Reimann <jens.reimann@th4-systems.com> + * + */ +public class OPCTest7 +{ + @SuppressWarnings ( "unused" ) + public static void main ( final String[] args ) throws Throwable + { + // create connection information + final ConnectionInformation ci = new ConnectionInformation (); + ci.setHost ( args[0] ); + ci.setDomain ( args[1] ); + ci.setUser ( args[2] ); + ci.setPassword ( args[3] ); + ci.setClsid ( args[4] ); + + final String itemName = args[5]; + + // create a new server + final Server server = new Server ( ci, Executors.newSingleThreadScheduledExecutor () ); + try + { + // connect to server + server.connect (); + + // Add a new group + final Group group = server.addGroup ( "test" ); + + // Add a new item to the group + final Item item = group.addItem ( itemName ); + + final JIString[] sdata = new JIString[] { new JIString ( "ab", JIFlags.FLAG_REPRESENTATION_STRING_BSTR ), new JIString ( "cd", JIFlags.FLAG_REPRESENTATION_STRING_BSTR ), new JIString ( "ef", JIFlags.FLAG_REPRESENTATION_STRING_BSTR ) }; + final Double[] ddata = new Double[] { 1.1, 2.2, 3.3 }; + final Boolean[] bdata = new Boolean[] { true, false, true, false }; + final Integer[] idata = new Integer[] { 1202, 1203, 1204 }; + final Long[] ldata = new Long[] { 12020001L, 12030001L, 12040001L }; + final Float[] fdata = new Float[] { 1.1f, 1.2f, 1.3f }; + final Byte[] bydata = new Byte[] { 1, 2, 3 }; + final Character[] cdata = new Character[] { 'A', 'B', 'C' }; + + final JIArray array = new JIArray ( ddata, true ); + final JIVariant value = new JIVariant ( array ); + + System.out.println ( "============= PHASE 1 ============= " ); + + // dump the value + VariantDumper.dumpValue ( value ); + + System.out.println ( "============= PHASE 2 ============= " ); + + // now write it to the item + item.write ( value ); + Thread.sleep ( 2500 ); + + System.out.println ( "============= PHASE 3 ============= " ); + + // now read the value back and dump it + final ItemState itemState = item.read ( true ); + VariantDumper.dumpValue ( itemState.getValue () ); + + System.out.println ( "============= PHASE 4 ============= " ); + + // and write the value just read in + item.write ( itemState.getValue () ); + + System.out.println ( "============= COMPLETE ============= " ); + } + catch ( final JIException e ) + { + System.out.println ( String.format ( "%08X: %s", e.getErrorCode (), server.getErrorMessage ( e.getErrorCode () ) ) ); + } + } +} diff --git a/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest8.java b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest8.java new file mode 100644 index 0000000..b3476aa --- /dev/null +++ b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTest8.java @@ -0,0 +1,67 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.lib; + +import java.util.Collection; + +import org.jinterop.dcom.common.JIException; +import org.openscada.opc.dcom.list.ClassDetails; +import org.openscada.opc.lib.list.Categories; +import org.openscada.opc.lib.list.Category; +import org.openscada.opc.lib.list.ServerList; + +/** + * A sample that queries the server browser interface + * @author Jens Reimann <jens.reimann@th4-systems.com> + * + */ +public class OPCTest8 +{ + protected static void showDetails ( final ServerList serverList, final String clsid ) throws JIException + { + final ClassDetails cd = serverList.getDetails ( clsid ); + if ( cd != null ) + { + System.out.println ( cd.getProgId () + " = " + cd.getDescription () ); + } + else + { + System.out.println ( "unknown" ); + } + } + + public static void main ( final String[] args ) throws Throwable + { + final ServerList serverList = new ServerList ( args[0], args[2], args[3], args[1] ); + + final String cls = serverList.getClsIdFromProgId ( "Matrikon.OPC.Simulation.1" ); + System.out.println ( "Matrikon OPC Simulation Server: " + cls ); + showDetails ( serverList, cls ); + + final Collection detailsList = serverList.listServersWithDetails ( new Category[] { Categories.OPCDAServer20 }, new Category[] {} ); + + for ( final ClassDetails details : detailsList ) + { + System.out.println ( String.format ( "Found: %s", details.getClsId () ) ); + System.out.println ( String.format ( "\tProgID: %s", details.getProgId () ) ); + System.out.println ( String.format ( "\tDescription: %s", details.getDescription () ) ); + } + } +} diff --git a/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTestInfo.java b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTestInfo.java new file mode 100644 index 0000000..d27258f --- /dev/null +++ b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/OPCTestInfo.java @@ -0,0 +1,35 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.lib; + +import org.openscada.opc.lib.common.ConnectionInformation; +import org.openscada.opc.lib.da.AccessBase; +import org.openscada.opc.lib.da.Server; + +class OPCTestInfo +{ + ConnectionInformation _info = null; + + String _itemId = null; + + Server _server = null; + + AccessBase _access = null; +} diff --git a/org.openscada.opc.lib.test/src/org/openscada/opc/lib/VariantDumper.java b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/VariantDumper.java new file mode 100644 index 0000000..6026b76 --- /dev/null +++ b/org.openscada.opc.lib.test/src/org/openscada/opc/lib/VariantDumper.java @@ -0,0 +1,134 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2009 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.openscada.opc.lib; + +import org.jinterop.dcom.common.JIException; +import org.jinterop.dcom.core.JIArray; +import org.jinterop.dcom.core.JIFlags; +import org.jinterop.dcom.core.JIString; +import org.jinterop.dcom.core.JIVariant; + +public class VariantDumper +{ + + static protected void dumpArray ( final String prefix, final JIArray array ) throws JIException + { + System.out.println ( prefix + String.format ( "IsConformant: %s, IsVarying: %s", array.isConformant () ? "yes" : "no", array.isVarying () ? "yes" : "no" ) ); + System.out.println ( prefix + String.format ( "Dimensions: %d", array.getDimensions () ) ); + for ( int i = 0; i < array.getDimensions (); i++ ) + { + System.out.println ( prefix + String.format ( "Dimension #%d: Upper Bound: %d", i, array.getUpperBounds ()[i] ) ); + } + + final Object o = array.getArrayInstance (); + System.out.println ( prefix + "Array Instance: " + o.getClass () ); + final Object[] a = (Object[])o; + System.out.println ( prefix + "Array Size: " + a.length ); + + for ( final Object value : a ) + { + dumpValue ( prefix + "\t", value ); + } + } + + static public void dumpValue ( final Object value ) throws JIException + { + dumpValue ( "", value ); + } + + static protected void dumpValue ( final String prefix, final Object value ) throws JIException + { + if ( value instanceof JIVariant ) + { + System.out.println ( prefix + "JIVariant" ); + final JIVariant variant = (JIVariant)value; + System.out.println ( prefix + String.format ( "IsArray: %s, IsByRef: %s, IsNull: %s", variant.isArray () ? "yes" : "no", variant.isByRefFlagSet () ? "yes" : "no", variant.isNull () ? "yes" : "no" ) ); + + if ( variant.isArray () ) + { + dumpArray ( prefix, variant.getObjectAsArray () ); + } + else + { + dumpValue ( prefix + "\t", variant.getObject () ); + } + } + else if ( value instanceof JIString ) + { + final JIString string = (JIString)value; + + String strType; + switch ( string.getType () ) + { + case JIFlags.FLAG_REPRESENTATION_STRING_BSTR: + strType = "BSTR"; + break; + case JIFlags.FLAG_REPRESENTATION_STRING_LPCTSTR: + strType = "LPCSTR"; + break; + case JIFlags.FLAG_REPRESENTATION_STRING_LPWSTR: + strType = "LPWSTR"; + break; + default: + strType = "unknown"; + break; + } + System.out.println ( prefix + String.format ( "JIString: '%s' (%s)", string.getString (), strType ) ); + } + else if ( value instanceof Double ) + { + System.out.println ( prefix + "Double: " + value ); + } + else if ( value instanceof Float ) + { + System.out.println ( prefix + "Float: " + value ); + } + else if ( value instanceof Byte ) + { + System.out.println ( prefix + "Byte: " + value ); + } + else if ( value instanceof Character ) + { + System.out.println ( prefix + "Character: " + value ); + } + else if ( value instanceof Integer ) + { + System.out.println ( prefix + "Integer: " + value ); + } + else if ( value instanceof Short ) + { + System.out.println ( prefix + "Short: " + value ); + } + else if ( value instanceof Long ) + { + System.out.println ( prefix + "Long: " + value ); + } + else if ( value instanceof Boolean ) + { + System.out.println ( prefix + "Boolean: " + value ); + } + + else + { + System.out.println ( prefix + String.format ( "Unknown value type (%s): %s", value.getClass (), value.toString () ) ); + } + } + +} diff --git a/org.openscada.opc.lib/.classpath b/org.openscada.opc.lib/.classpath new file mode 100644 index 0000000..6821461 --- /dev/null +++ b/org.openscada.opc.lib/.classpath @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/org.openscada.opc.lib/.gitignore b/org.openscada.opc.lib/.gitignore new file mode 100644 index 0000000..4cc0812 --- /dev/null +++ b/org.openscada.opc.lib/.gitignore @@ -0,0 +1,2 @@ +bin/ +/target diff --git a/org.openscada.opc.lib/.project b/org.openscada.opc.lib/.project new file mode 100644 index 0000000..95eda57 --- /dev/null +++ b/org.openscada.opc.lib/.project @@ -0,0 +1,46 @@ + + + org.openscada.opc.lib + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.babel.editor.rbeBuilder + + + + + org.eclipse.pde.api.tools.apiAnalysisBuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.m2e.core.maven2Nature + org.eclipse.jdt.core.javanature + org.eclipse.pde.PluginNature + org.eclipse.babel.editor.rbeNature + org.eclipse.pde.api.tools.apiAnalysisNature + + diff --git a/org.openscada.opc.lib/LICENSE b/org.openscada.opc.lib/LICENSE new file mode 100644 index 0000000..0836c46 --- /dev/null +++ b/org.openscada.opc.lib/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/org.openscada.opc.lib/build.properties b/org.openscada.opc.lib/build.properties new file mode 100644 index 0000000..4394f20 --- /dev/null +++ b/org.openscada.opc.lib/build.properties @@ -0,0 +1 @@ +source.. = src diff --git a/org.openscada.opc.lib/pom.xml b/org.openscada.opc.lib/pom.xml new file mode 100644 index 0000000..de7f48f --- /dev/null +++ b/org.openscada.opc.lib/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + org.openscada.utgard + org.openscada.opc.lib + 1.1.0.v20130529 + + + + org.slf4j + slf4j-log4j12 + 1.7.2 + + + commons-logging + commons-logging + 1.2 + + + + org.kohsuke.jinterop + j-interop + 2.0.5 + + + org.openscada.opc.dcom + org.openscada.opc.dcom + 0.0.1-SNAPSHOT + + + + \ No newline at end of file diff --git a/org.openscada.opc.lib/src/org/openscada/opc/lib/common/AlreadyConnectedException.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/common/AlreadyConnectedException.java new file mode 100644 index 0000000..1b279f8 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/common/AlreadyConnectedException.java @@ -0,0 +1,30 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.common; + +public class AlreadyConnectedException extends Exception +{ + + /** + * + */ + private static final long serialVersionUID = -6494637563117314114L; + +} diff --git a/org.openscada.opc.lib/src/org/openscada/opc/lib/common/ConnectionInformation.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/common/ConnectionInformation.java new file mode 100644 index 0000000..1012360 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/common/ConnectionInformation.java @@ -0,0 +1,149 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.common; + +/** + * Holds the connection information + * @author Jens Reimann + * + * 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 ( 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 + */ + 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/org.openscada.opc.lib/src/org/openscada/opc/lib/common/NotConnectedException.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/common/NotConnectedException.java new file mode 100644 index 0000000..d20ccdb --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/common/NotConnectedException.java @@ -0,0 +1,30 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.common; + +public class NotConnectedException extends Exception +{ + + /** + * + */ + private static final long serialVersionUID = -3745147771605524635L; + +} diff --git a/org.openscada.opc.lib/src/org/openscada/opc/lib/da/AccessBase.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/AccessBase.java new file mode 100644 index 0000000..d06e0a8 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/AccessBase.java @@ -0,0 +1,359 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da; + +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 org.jinterop.dcom.common.JIException; +import org.openscada.opc.lib.common.NotConnectedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class AccessBase implements ServerConnectionStateListener +{ + private static Logger logger = LoggerFactory.getLogger ( AccessBase.class ); + + protected Server server = null; + + protected Group group = null; + + protected boolean active = false; + + private final List stateListeners = new CopyOnWriteArrayList (); + + private boolean bound = false; + + /** + * Holds the item to callback assignment + */ + protected Map items = new HashMap (); + + protected Map itemMap = new HashMap (); + + protected Map itemCache = new HashMap (); + + private int period = 0; + + protected Map itemSet = new HashMap (); + + protected String logTag = null; + + protected Logger dataLogger = null; + + 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 ) + { + logger.error ( String.format ( "Failed to change state (%s)", connected ), e ); + } + } + + protected synchronized void start () throws JIException, IllegalArgumentException, UnknownHostException, NotConnectedException, DuplicateGroupException + { + if ( isActive () ) + { + return; + } + + logger.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 + { + logger.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 ) + { + logger.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; + } + logger.warn ( String.format ( "Failed to add item: %s (%08X)", itemId, rc ) ); + + } + catch ( final Exception e ) + { + logger.warn ( "Failed to realize item: " + itemId, e ); + } + } + } + + protected void unrealizeAll () + { + this.items.clear (); + this.itemCache.clear (); + try + { + this.group.clear (); + } + catch ( final JIException e ) + { + logger.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 ) + { + logger.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/org.openscada.opc.lib/src/org/openscada/opc/lib/da/AccessStateListener.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/AccessStateListener.java new file mode 100644 index 0000000..ea20aad --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/AccessStateListener.java @@ -0,0 +1,27 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da; + +public interface AccessStateListener +{ + public abstract void stateChanged ( boolean state ); + + public abstract void errorOccured ( Throwable t ); +} diff --git a/org.openscada.opc.lib/src/org/openscada/opc/lib/da/AddFailedException.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/AddFailedException.java new file mode 100644 index 0000000..ac383fc --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/AddFailedException.java @@ -0,0 +1,61 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da; + +import java.util.HashMap; +import java.util.Map; + +public class AddFailedException extends Exception +{ + + /** + * + */ + private static final long serialVersionUID = 5299486640366935298L; + + 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/org.openscada.opc.lib/src/org/openscada/opc/lib/da/Async20Access.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/Async20Access.java new file mode 100644 index 0000000..f6f0533 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/Async20Access.java @@ -0,0 +1,136 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da; + +import java.net.UnknownHostException; + +import org.jinterop.dcom.common.JIException; +import org.openscada.opc.dcom.common.EventHandler; +import org.openscada.opc.dcom.common.KeyedResult; +import org.openscada.opc.dcom.common.KeyedResultSet; +import org.openscada.opc.dcom.common.ResultSet; +import org.openscada.opc.dcom.da.IOPCDataCallback; +import org.openscada.opc.dcom.da.OPCDATASOURCE; +import org.openscada.opc.dcom.da.ValueData; +import org.openscada.opc.dcom.da.impl.OPCAsyncIO2; +import org.openscada.opc.lib.common.NotConnectedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Async20Access extends AccessBase implements IOPCDataCallback +{ + private static Logger logger = LoggerFactory.getLogger ( Async20Access.class ); + + 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 ) + { + logger.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 ) + { + logger.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 () ); + logger.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 ) + { + logger.debug ( "readComplete - transId {}", transactionId ); + } + + public void writeComplete ( final int transactionId, final int serverGroupHandle, final int masterErrorCode, final ResultSet result ) + { + logger.debug ( "writeComplete - transId {}", transactionId ); + } +} diff --git a/org.openscada.opc.lib/src/org/openscada/opc/lib/da/AutoReconnectController.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/AutoReconnectController.java new file mode 100644 index 0000000..3e4ce52 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/AutoReconnectController.java @@ -0,0 +1,230 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da; + +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AutoReconnectController implements ServerConnectionStateListener +{ + private static Logger _log = LoggerFactory.getLogger ( AutoReconnectController.class ); + + private static final int DEFAULT_DELAY = 5 * 1000; + + private int _delay; + + private final Server _server; + + private final Set _listeners = new CopyOnWriteArraySet (); + + 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/org.openscada.opc.lib/src/org/openscada/opc/lib/da/AutoReconnectListener.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/AutoReconnectListener.java new file mode 100644 index 0000000..a94c3dc --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/AutoReconnectListener.java @@ -0,0 +1,25 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da; + +public interface AutoReconnectListener +{ + public abstract void stateChanged ( AutoReconnectState state ); +} diff --git a/org.openscada.opc.lib/src/org/openscada/opc/lib/da/AutoReconnectState.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/AutoReconnectState.java new file mode 100644 index 0000000..f23fa2e --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/AutoReconnectState.java @@ -0,0 +1,51 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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/org.openscada.opc.lib/src/org/openscada/opc/lib/da/DataCallback.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/DataCallback.java new file mode 100644 index 0000000..71c618c --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/DataCallback.java @@ -0,0 +1,25 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da; + +public interface DataCallback +{ + void changed ( Item item, ItemState itemState ); +} diff --git a/org.openscada.opc.lib/src/org/openscada/opc/lib/da/DuplicateGroupException.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/DuplicateGroupException.java new file mode 100644 index 0000000..1fb20cd --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/DuplicateGroupException.java @@ -0,0 +1,30 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da; + +public class DuplicateGroupException extends Exception +{ + + /** + * + */ + private static final long serialVersionUID = 826553520690295478L; + +} diff --git a/org.openscada.opc.lib/src/org/openscada/opc/lib/da/ErrorMessageResolver.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/ErrorMessageResolver.java new file mode 100644 index 0000000..da0d96d --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/ErrorMessageResolver.java @@ -0,0 +1,80 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da; + +import java.util.HashMap; +import java.util.Map; + +import org.jinterop.dcom.common.JIException; +import org.openscada.opc.dcom.common.impl.OPCCommon; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * An error message resolver that will lookup the error code using the + * server interface and will cache the result locally. + * @author Jens Reimann + * + */ +public class ErrorMessageResolver +{ + private static Logger _log = LoggerFactory.getLogger ( ErrorMessageResolver.class ); + + private OPCCommon _opcCommon = null; + + private final Map _messageCache = new HashMap (); + + 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/org.openscada.opc.lib/src/org/openscada/opc/lib/da/Group.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/Group.java new file mode 100644 index 0000000..36fdfcc --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/Group.java @@ -0,0 +1,437 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da; + +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; + +import org.jinterop.dcom.common.JIException; +import org.openscada.opc.dcom.common.EventHandler; +import org.openscada.opc.dcom.common.KeyedResult; +import org.openscada.opc.dcom.common.KeyedResultSet; +import org.openscada.opc.dcom.common.Result; +import org.openscada.opc.dcom.common.ResultSet; +import org.openscada.opc.dcom.da.IOPCDataCallback; +import org.openscada.opc.dcom.da.OPCDATASOURCE; +import org.openscada.opc.dcom.da.OPCITEMDEF; +import org.openscada.opc.dcom.da.OPCITEMRESULT; +import org.openscada.opc.dcom.da.OPCITEMSTATE; +import org.openscada.opc.dcom.da.impl.OPCAsyncIO2; +import org.openscada.opc.dcom.da.impl.OPCGroupStateMgt; +import org.openscada.opc.dcom.da.impl.OPCItemMgt; +import org.openscada.opc.dcom.da.impl.OPCSyncIO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Group +{ + private static Logger _log = LoggerFactory.getLogger ( Group.class ); + + private static Random _random = new Random (); + + private Server _server = null; + + private final int _serverHandle; + + private OPCGroupStateMgt _group = null; + + private OPCItemMgt _items = null; + + private OPCSyncIO _syncIO = null; + + private final Map _itemHandleMap = new HashMap (); + + private final Map _itemMap = new HashMap (); + + private final Map _itemClientMap = new HashMap (); + + 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 (); + } + + public void setActive ( final boolean state ) throws JIException + { + this._group.setState ( null, state, null, null, null, null ); + } + + /** + * remove the group from the server + * @throws JIException + * + */ + public void remove () throws JIException + { + this._server.removeGroup ( this, true ); + } + + public boolean isActive () throws JIException + { + return this._group.getState ().isActive (); + } + + /** + * Get the group name from the server + * @return The group name fetched from the server + * @throws 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 + */ + 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 + */ + 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 does not 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.openscada.opc.dcom.da.WriteRequest[] wr = new org.openscada.opc.dcom.da.WriteRequest[items.length]; + for ( int i = 0; i < items.length; i++ ) + { + wr[i] = new 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/org.openscada.opc.lib/src/org/openscada/opc/lib/da/Item.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/Item.java new file mode 100644 index 0000000..0f72462 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/Item.java @@ -0,0 +1,83 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da; + +import org.jinterop.dcom.common.JIException; +import org.jinterop.dcom.core.JIVariant; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Item +{ + private static Logger _log = LoggerFactory.getLogger ( Item.class ); + + 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/org.openscada.opc.lib/src/org/openscada/opc/lib/da/ItemState.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/ItemState.java new file mode 100644 index 0000000..74ce707 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/ItemState.java @@ -0,0 +1,163 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da; + +import java.util.Calendar; + +import org.jinterop.dcom.core.JIVariant; + +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/org.openscada.opc.lib/src/org/openscada/opc/lib/da/Server.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/Server.java new file mode 100644 index 0000000..9e01248 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/Server.java @@ -0,0 +1,445 @@ +/* + * This file is part of the openSCADA project + * Copyright (C) 2006-2011 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * openSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * openSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with openSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da; + +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; + +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.openscada.opc.dcom.da.OPCNAMESPACETYPE; +import org.openscada.opc.dcom.da.OPCSERVERSTATUS; +import org.openscada.opc.dcom.da.impl.OPCBrowseServerAddressSpace; +import org.openscada.opc.dcom.da.impl.OPCGroupStateMgt; +import org.openscada.opc.dcom.da.impl.OPCServer; +import org.openscada.opc.lib.common.AlreadyConnectedException; +import org.openscada.opc.lib.common.ConnectionInformation; +import org.openscada.opc.lib.common.NotConnectedException; +import org.openscada.opc.lib.da.browser.FlatBrowser; +import org.openscada.opc.lib.da.browser.TreeBrowser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Server { + private static Logger logger = LoggerFactory.getLogger(Server.class); + + private final ConnectionInformation connectionInformation; + + 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; + + private final Map groups = new HashMap(); + + private final List stateListeners = new CopyOnWriteArrayList(); + + private final ScheduledExecutorService scheduler; + + 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); + logger.info(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.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) { + logger.info("Unknown host when connecting to server", e); + cleanup(); + throw e; + } catch (final JIException e) { + logger.info("Failed to connect to server", e); + cleanup(); + throw e; + } catch (final Throwable e) { + logger.warn("Unknown error", e); + cleanup(); + throw new RuntimeException(e); + } + + notifyConnectionStateChange(true); + } + + /** + * cleanup after the connection is closed + */ + protected void cleanup() { + logger.info("Destroying DCOM session..."); + final JISession destructSession = this.session; + final Thread destructor = new Thread(new Runnable() { + + public void run() { + final long ts = System.currentTimeMillis(); + try { + logger.debug("Starting destruction of DCOM session"); + JISession.destroySession(destructSession); + logger.info("Destructed DCOM session"); + } catch (final Throwable e) { + logger.warn("Failed to destruct DCOM session", e); + } finally { + logger.info(String.format("Session destruction took %s ms", + System.currentTimeMillis() - ts)); + } + } + }, "UtgardSessionDestructor"); + destructor.setName("OPCSessionDestructor"); + destructor.setDaemon(true); + destructor.start(); + logger.info("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 + * @throws UnknownHostException + * @throws 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) { + switch (e.getErrorCode()) { + case 0xC004000C: + throw new DuplicateGroupException(); + default: + 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 + * @throws UnknownHostException + * @throws NotConnectedException + * @throws JIException + * @throws 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 + * @throws UnknownHostException + * @throws 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 + */ + 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) { + logger.info("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/org.openscada.opc.lib/src/org/openscada/opc/lib/da/ServerConnectionStateListener.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/ServerConnectionStateListener.java new file mode 100644 index 0000000..ce867dc --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/ServerConnectionStateListener.java @@ -0,0 +1,25 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da; + +public interface ServerConnectionStateListener +{ + public abstract void connectionStateChanged ( boolean connected ); +} diff --git a/org.openscada.opc.lib/src/org/openscada/opc/lib/da/ServerStateListener.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/ServerStateListener.java new file mode 100644 index 0000000..9193a48 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/ServerStateListener.java @@ -0,0 +1,27 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da; + +import org.openscada.opc.dcom.da.OPCSERVERSTATUS; + +public interface ServerStateListener +{ + public void stateUpdate ( OPCSERVERSTATUS state ); +} diff --git a/org.openscada.opc.lib/src/org/openscada/opc/lib/da/ServerStateOperation.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/ServerStateOperation.java new file mode 100644 index 0000000..1ec7981 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/ServerStateOperation.java @@ -0,0 +1,123 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da; + +import org.openscada.opc.dcom.da.OPCSERVERSTATUS; +import org.openscada.opc.dcom.da.impl.OPCServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A server state operation which can be interruped + * @author Jens Reimann + * + */ +public class ServerStateOperation implements Runnable +{ + private static Logger _log = LoggerFactory.getLogger ( ServerStateOperation.class ); + + 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. + * @return the server state or null if the server is not set. + * @param timeout the timeout in ms + * @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/org.openscada.opc.lib/src/org/openscada/opc/lib/da/ServerStateReader.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/ServerStateReader.java new file mode 100644 index 0000000..6b8f9a7 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/ServerStateReader.java @@ -0,0 +1,108 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da; + +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; + +import org.openscada.opc.dcom.da.OPCSERVERSTATUS; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ServerStateReader +{ + private static Logger _log = LoggerFactory.getLogger ( ServerStateReader.class ); + + private Server _server = null; + + private ScheduledExecutorService _scheduler = null; + + private final List _listeners = new CopyOnWriteArrayList (); + + 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/org.openscada.opc.lib/src/org/openscada/opc/lib/da/SyncAccess.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/SyncAccess.java new file mode 100644 index 0000000..6d13f53 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/SyncAccess.java @@ -0,0 +1,120 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da; + +import java.net.UnknownHostException; +import java.util.Map; + +import org.jinterop.dcom.common.JIException; +import org.openscada.opc.lib.common.NotConnectedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SyncAccess extends AccessBase implements Runnable +{ + private static Logger logger = LoggerFactory.getLogger ( SyncAccess.class ); + + 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 ) + { + logger.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/org.openscada.opc.lib/src/org/openscada/opc/lib/da/UnknownGroupException.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/UnknownGroupException.java new file mode 100644 index 0000000..6694fe7 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/UnknownGroupException.java @@ -0,0 +1,47 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da; + +public class UnknownGroupException extends Exception +{ + private String _name = null; + + public UnknownGroupException ( final String name ) + { + super (); + this._name = name; + } + + /** + * + */ + private static final long serialVersionUID = 1771564928794033075L; + + public String getName () + { + return this._name; + } + + public void setName ( final String name ) + { + this._name = name; + } + +} diff --git a/org.openscada.opc.lib/src/org/openscada/opc/lib/da/WriteRequest.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/WriteRequest.java new file mode 100644 index 0000000..88e6b8a --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/WriteRequest.java @@ -0,0 +1,46 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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/org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/Access.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/Access.java new file mode 100644 index 0000000..fa9e4cc --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/Access.java @@ -0,0 +1,38 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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/org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/BaseBrowser.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/BaseBrowser.java new file mode 100644 index 0000000..76e1d54 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/BaseBrowser.java @@ -0,0 +1,124 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da.browser; + +import java.net.UnknownHostException; +import java.util.Collection; +import java.util.EnumSet; + +import org.jinterop.dcom.common.JIException; +import org.openscada.opc.dcom.common.impl.EnumString; +import org.openscada.opc.dcom.da.OPCBROWSETYPE; +import org.openscada.opc.dcom.da.impl.OPCBrowseServerAddressSpace; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A class implementing base browsing + * @author Jens Reimann + * + */ +public class BaseBrowser +{ + private static Logger _log = LoggerFactory.getLogger ( BaseBrowser.class ); + + 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; + } + + /** + * Set the batch size + * @param batchSize The new batch size + */ + public void setBatchSize ( final int batchSize ) + { + this._batchSize = batchSize; + } + + /** + * Get the batch size + * @return the current batch size + */ + public int getBatchSize () + { + return this._batchSize; + } + + /** + * Perform the browse operation. + * @param type + * @param filterCriteria + * @param accessMask + * @param variantType + * @return The browse result + * @throws IllegalArgumentException + * @throws UnknownHostException + * @throws 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 + * @throws UnknownHostException + * @throws 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/org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/Branch.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/Branch.java new file mode 100644 index 0000000..e09eb22 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/Branch.java @@ -0,0 +1,123 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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/org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/FlatBrowser.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/FlatBrowser.java new file mode 100644 index 0000000..f06d03e --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/FlatBrowser.java @@ -0,0 +1,78 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da.browser; + +import java.net.UnknownHostException; +import java.util.Collection; +import java.util.EnumSet; + +import org.jinterop.dcom.common.JIException; +import org.jinterop.dcom.core.JIVariant; +import org.openscada.opc.dcom.da.OPCBROWSETYPE; +import org.openscada.opc.dcom.da.impl.OPCBrowseServerAddressSpace; + +/** + * Browse through the flat server namespace + * @author Jens Reimann + * + */ +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 + * @throws UnknownHostException + * @throws 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/org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/Leaf.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/Leaf.java new file mode 100644 index 0000000..ffc8c04 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/Leaf.java @@ -0,0 +1,68 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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/org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/TreeBrowser.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/TreeBrowser.java new file mode 100644 index 0000000..3070965 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/da/browser/TreeBrowser.java @@ -0,0 +1,240 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.da.browser; + +import java.net.UnknownHostException; +import java.util.Collection; +import java.util.EnumSet; +import java.util.LinkedList; + +import org.jinterop.dcom.common.JIException; +import org.jinterop.dcom.core.JIVariant; +import org.openscada.opc.dcom.da.OPCBROWSEDIRECTION; +import org.openscada.opc.dcom.da.OPCBROWSETYPE; +import org.openscada.opc.dcom.da.impl.OPCBrowseServerAddressSpace; + +/** + * 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 + * + */ +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 + */ + 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 + */ + 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 + * @throws IllegalArgumentException + * @throws 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 + * @throws UnknownHostException + * @throws 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 + * @throws IllegalArgumentException + * @throws 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 + * @throws UnknownHostException + * @throws 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 + * @throws IllegalArgumentException + * @throws 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 + * @throws UnknownHostException + * @throws 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 + * @throws UnknownHostException + * @throws 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/org.openscada.opc.lib/src/org/openscada/opc/lib/list/Categories.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/list/Categories.java new file mode 100644 index 0000000..c2b0a41 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/list/Categories.java @@ -0,0 +1,43 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.list; + +public interface Categories +{ + /** + * Category of the OPC DA 1.0 Servers + */ + public final static Category OPCDAServer10 = new Category ( org.openscada.opc.dcom.common.Categories.OPCDAServer10 ); + + /** + * Category of the OPC DA 2.0 Servers + */ + public final static Category OPCDAServer20 = new Category ( org.openscada.opc.dcom.common.Categories.OPCDAServer20 ); + + /** + * Category of the OPC DA 3.0 Servers + */ + public final static Category OPCDAServer30 = new Category ( org.openscada.opc.dcom.common.Categories.OPCDAServer30 ); + + /** + * Category of the XML DA 1.0 Servers + */ + public final static Category XMLDAServer10 = new Category ( org.openscada.opc.dcom.common.Categories.XMLDAServer10 ); +} diff --git a/org.openscada.opc.lib/src/org/openscada/opc/lib/list/Category.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/list/Category.java new file mode 100644 index 0000000..b304d15 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/list/Category.java @@ -0,0 +1,76 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package 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/org.openscada.opc.lib/src/org/openscada/opc/lib/list/ServerList.java b/org.openscada.opc.lib/src/org/openscada/opc/lib/list/ServerList.java new file mode 100644 index 0000000..538fd71 --- /dev/null +++ b/org.openscada.opc.lib/src/org/openscada/opc/lib/list/ServerList.java @@ -0,0 +1,179 @@ +/* + * This file is part of the OpenSCADA project + * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) + * + * OpenSCADA is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenSCADA 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenSCADA. If not, see + * for a copy of the LGPLv3 License. + */ + +package org.openscada.opc.lib.list; + +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.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.openscada.opc.dcom.list.ClassDetails; +import org.openscada.opc.dcom.list.Constants; +import org.openscada.opc.dcom.list.impl.OPCServerList; + +import rpc.core.UUID; + +/** + * 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 + * @throws UnknownHostException + * @throws 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 + * @throws UnknownHostException + * @throws 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 + * @throws UnknownHostException + * @throws 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 + */ + 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 + */ + 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 + * @throws UnknownHostException + * @throws 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 + * @throws UnknownHostException + * @throws 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; + } +}