├── LICENSE ├── LICENSE.opc.ua.stack ├── Prosys_OPC_UA_Java_SDK_License.pdf ├── LICENSE.slf4j ├── LICENSE.bouncycastle └── LICENSE.apache2.0 ├── .gitignore ├── opc-ua-server ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── prosysopc │ └── ua │ └── samples │ ├── log.properties │ ├── MyAnalogItemListener.java │ ├── MyCertificateValidationListener.java │ ├── AnalogItemListenerTemplate.java │ ├── MyUserValidator.java │ ├── MyEnumType.java │ ├── MyLevelAlarmType.java │ ├── MyEventType.java │ ├── MyIoManagerListener.java │ ├── MyMethodManagerListener.java │ ├── EventHistory.java │ ├── MyNodeManagerListener.java │ ├── ValueHistory.java │ ├── MyEventManagerListener.java │ ├── MyHistorian.java │ ├── MyNodeManager.java │ ├── MyBigNodeManager.java │ └── SampleConsoleServer.java ├── opc-ua-client ├── src │ └── main │ │ ├── java │ │ ├── MyMonitoredDataItemListener.java │ │ ├── log.properties │ │ ├── MyMonitoredEventItemListener.java │ │ ├── MyServerStatusListener.java │ │ ├── MySubscriptionAliveListener.java │ │ ├── SimpleClient.java │ │ ├── MySubscriptionNotificationListener.java │ │ ├── MyCertificateValidationListener.java │ │ └── MyUaClientListener.java │ │ └── resources │ │ └── log.properties └── pom.xml ├── README.md └── pom.xml /LICENSE/LICENSE.opc.ua.stack: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnoker/opcua-tools/HEAD/LICENSE/LICENSE.opc.ua.stack -------------------------------------------------------------------------------- /LICENSE/Prosys_OPC_UA_Java_SDK_License.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnoker/opcua-tools/HEAD/LICENSE/Prosys_OPC_UA_Java_SDK_License.pdf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .DS_Store? 3 | ._* 4 | target/ 5 | PKI/ 6 | USERS_PKI/ 7 | .settings/ 8 | .idea/ 9 | .project 10 | .classpath 11 | log/ 12 | logs/ 13 | *.iml 14 | -------------------------------------------------------------------------------- /opc-ua-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | opcua-tools 7 | com.sia.pnoker.opcua 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | opc-ua-server 13 | 14 | 15 | -------------------------------------------------------------------------------- /opc-ua-client/src/main/java/MyMonitoredDataItemListener.java: -------------------------------------------------------------------------------- 1 | 2 | import org.opcfoundation.ua.builtintypes.DataValue; 3 | 4 | import com.prosysopc.ua.client.MonitoredDataItem; 5 | import com.prosysopc.ua.client.MonitoredDataItemListener; 6 | 7 | /** 8 | * A sampler listener for monitored data changes. 9 | */ 10 | public class MyMonitoredDataItemListener implements MonitoredDataItemListener { 11 | private final SampleConsoleClient client; 12 | 13 | /** 14 | * @param client 15 | */ 16 | public MyMonitoredDataItemListener(SampleConsoleClient client) { 17 | super(); 18 | this.client = client; 19 | } 20 | 21 | @Override 22 | public void onDataChange(MonitoredDataItem sender, DataValue prevValue, DataValue value) { 23 | SampleConsoleClient.println(client.dataValueToString(sender.getNodeId(), sender.getAttributeId(), value)); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /opc-ua-client/src/main/java/log.properties: -------------------------------------------------------------------------------- 1 | # Default logging level, log on console only (add ',file' to log on file as well) 2 | log4j.rootLogger=INFO,console 3 | 4 | # Prosys SDK messages 5 | log4j.logger.com.prosysopc.ua=INFO 6 | 7 | # UA Stack messages 8 | log4j.logger.org.opcfoundation.ua=ERROR 9 | 10 | # console Logger 11 | log4j.appender.console=org.apache.log4j.ConsoleAppender 12 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 13 | log4j.appender.console.layout.ConversionPattern=%d{MM/dd/yyyy HH:mm:ss.SSS} %-5p %m%n 14 | # To show thread IDs and line numbers, use the following 15 | #log4j.appender.console.layout.ConversionPattern=%d{MM/dd/yyyy HH:mm:ss.SSS} %-5p [%t] %-47c %3x - %m%n 16 | 17 | # file Logger 18 | log4j.appender.file=org.apache.log4j.FileAppender 19 | log4j.appender.file.file=SampleConsoleClient.log 20 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 21 | log4j.appender.file.layout.ConversionPattern=%d{MM/dd/yyyy HH:mm:ss.SSS} %-5p [%t] %-47c %3x - %m%n 22 | 23 | -------------------------------------------------------------------------------- /opc-ua-client/src/main/resources/log.properties: -------------------------------------------------------------------------------- 1 | # Default logging level, log on console only (add ',file' to log on file as well) 2 | log4j.rootLogger=INFO,console 3 | 4 | # Prosys SDK messages 5 | log4j.logger.com.prosysopc.ua=INFO 6 | 7 | # UA Stack messages 8 | log4j.logger.org.opcfoundation.ua=ERROR 9 | 10 | # console Logger 11 | log4j.appender.console=org.apache.log4j.ConsoleAppender 12 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 13 | log4j.appender.console.layout.ConversionPattern=%d{MM/dd/yyyy HH:mm:ss.SSS} %-5p %m%n 14 | # To show thread IDs and line numbers, use the following 15 | #log4j.appender.console.layout.ConversionPattern=%d{MM/dd/yyyy HH:mm:ss.SSS} %-5p [%t] %-47c %3x - %m%n 16 | 17 | # file Logger 18 | log4j.appender.file=org.apache.log4j.FileAppender 19 | log4j.appender.file.file=SampleConsoleClient.log 20 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 21 | log4j.appender.file.layout.ConversionPattern=%d{MM/dd/yyyy HH:mm:ss.SSS} %-5p [%t] %-47c %3x - %m%n 22 | 23 | -------------------------------------------------------------------------------- /opc-ua-server/src/main/java/com/prosysopc/ua/samples/log.properties: -------------------------------------------------------------------------------- 1 | # Default logging level, log on console only (add ',file' to log on file as well) 2 | log4j.rootLogger=INFO,console 3 | 4 | # Prosys SDK messages 5 | log4j.logger.com.prosysopc.ua=INFO 6 | 7 | # UA Stack messages 8 | log4j.logger.org.opcfoundation.ua=ERROR 9 | 10 | # console Logger 11 | log4j.appender.console=org.apache.log4j.ConsoleAppender 12 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 13 | log4j.appender.console.layout.ConversionPattern=%d{MM/dd/yyyy HH:mm:ss.SSS} %-5p %m%n 14 | # To show thread IDs and line numbers, use the following 15 | #log4j.appender.console.layout.ConversionPattern=%d{MM/dd/yyyy HH:mm:ss.SSS} %-5p [%t] %-47c %3x - %m%n 16 | 17 | # file Logger 18 | log4j.appender.file=org.apache.log4j.FileAppender 19 | log4j.appender.file.file=SampleConsoleClient.log 20 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 21 | log4j.appender.file.layout.ConversionPattern=%d{MM/dd/yyyy HH:mm:ss.SSS} %-5p [%t] %-47c %3x - %m%n 22 | 23 | -------------------------------------------------------------------------------- /LICENSE/LICENSE.slf4j: -------------------------------------------------------------------------------- 1 | Copyright (c) 2004-2013 QOS.ch All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /opc-ua-client/src/main/java/MyMonitoredEventItemListener.java: -------------------------------------------------------------------------------- 1 | 2 | import org.opcfoundation.ua.builtintypes.QualifiedName; 3 | import org.opcfoundation.ua.builtintypes.Variant; 4 | 5 | import com.prosysopc.ua.client.MonitoredEventItem; 6 | import com.prosysopc.ua.client.MonitoredEventItemListener; 7 | 8 | /** 9 | * A sampler listener for monitored event notifications. 10 | */ 11 | public class MyMonitoredEventItemListener implements MonitoredEventItemListener { 12 | private final SampleConsoleClient client; 13 | private final QualifiedName[] requestedEventFields; 14 | 15 | /** 16 | * @param client 17 | * @param eventFieldNames 18 | */ 19 | public MyMonitoredEventItemListener(SampleConsoleClient client, QualifiedName[] requestedEventFields) { 20 | super(); 21 | this.requestedEventFields = requestedEventFields; 22 | this.client = client; 23 | } 24 | 25 | @Override 26 | public void onEvent(MonitoredEventItem sender, Variant[] eventFields) { 27 | SampleConsoleClient.println(client.eventToString(sender.getNodeId(), requestedEventFields, eventFields)); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /LICENSE/LICENSE.bouncycastle: -------------------------------------------------------------------------------- 1 | License 2 | 3 | Copyright (c) 2000 - 2009 The Legion Of The Bouncy Castle (http://www.bouncycastle.org) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /opc-ua-client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | opcua-tools 7 | com.sia.pnoker.opcua 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | opc-ua-client 13 | 14 | 15 | 16 | org.apache.maven.plugins 17 | maven-compiler-plugin 18 | 19 | 1.6 20 | 1.6 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | com.sia.pnoker.opcua 29 | opc-ua-client 30 | 1.0-SNAPSHOT 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /opc-ua-server/src/main/java/com/prosysopc/ua/samples/MyAnalogItemListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Prosys OPC UA Java SDK 3 | * 4 | * Copyright (c) Prosys PMS Ltd., . 5 | * All rights reserved. 6 | */ 7 | package com.prosysopc.ua.samples; 8 | 9 | import org.opcfoundation.ua.builtintypes.DataValue; 10 | import org.opcfoundation.ua.core.Range; 11 | import org.opcfoundation.ua.core.StatusCodes; 12 | import org.opcfoundation.ua.utils.NumericRange; 13 | 14 | import com.prosysopc.ua.StatusException; 15 | import com.prosysopc.ua.server.ServiceContext; 16 | import com.prosysopc.ua.types.opcua.AnalogItemType; 17 | 18 | /** 19 | * Example how to implement custom behavior by extending a 20 | * {@link com.prosysopc.ua.server.io.UaTypeIoListener} template. 21 | */ 22 | public class MyAnalogItemListener extends AnalogItemListenerTemplate { 23 | @Override 24 | protected boolean onWriteValue(ServiceContext serviceContext, AnalogItemType node, NumericRange indexRange, 25 | DataValue dataValue) throws StatusException { 26 | double value = (Double) dataValue.getValue().getValue(); 27 | Range euRangeValue = node.getEuRange(); 28 | if ((euRangeValue != null) && ((value < euRangeValue.getLow()) || (value > euRangeValue.getHigh()))) 29 | throw new StatusException(StatusCodes.Bad_OutOfRange); 30 | return false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /opc-ua-client/src/main/java/MyServerStatusListener.java: -------------------------------------------------------------------------------- 1 | 2 | import org.opcfoundation.ua.builtintypes.LocalizedText; 3 | import org.opcfoundation.ua.core.ServerState; 4 | import org.opcfoundation.ua.core.ServerStatusDataType; 5 | 6 | import com.prosysopc.ua.client.ServerStatusListener; 7 | import com.prosysopc.ua.client.UaClient; 8 | 9 | /** 10 | * A sampler listener for server status changes. 11 | */ 12 | public class MyServerStatusListener implements ServerStatusListener { 13 | @Override 14 | public void onShutdown(UaClient uaClient, long secondsTillShutdown, LocalizedText shutdownReason) { 15 | // Called when the server state changes to Shutdown 16 | SampleConsoleClient.printf("Server shutdown in %d seconds. Reason: %s\n", secondsTillShutdown, 17 | shutdownReason.getText()); 18 | } 19 | 20 | @Override 21 | public void onStateChange(UaClient uaClient, ServerState oldState, ServerState newState) { 22 | // Called whenever the server state changes 23 | SampleConsoleClient.printf("ServerState changed from %s to %s\n", oldState, newState); 24 | if (newState.equals(ServerState.Unknown)) 25 | SampleConsoleClient.println("ServerStatusError: " + uaClient.getServerStatusError()); 26 | } 27 | 28 | @Override 29 | public void onStatusChange(UaClient uaClient, ServerStatusDataType status) { 30 | // Called whenever the server status changes, typically every 31 | // StatusCheckInterval defined in the UaClient. 32 | // println("ServerStatus: " + status); 33 | } 34 | }; -------------------------------------------------------------------------------- /opc-ua-client/src/main/java/MySubscriptionAliveListener.java: -------------------------------------------------------------------------------- 1 | 2 | import java.util.Calendar; 3 | 4 | import com.prosysopc.ua.client.Subscription; 5 | import com.prosysopc.ua.client.SubscriptionAliveListener; 6 | 7 | /** 8 | * A sampler listener for subscription alive events. 9 | */ 10 | public class MySubscriptionAliveListener implements SubscriptionAliveListener { 11 | 12 | @Override 13 | public void onAfterCreate(Subscription s) { 14 | // the subscription was (re)created to the server 15 | // this happens if the subscription was timed out during 16 | // a communication break and had to be recreated after reconnection 17 | SampleConsoleClient.println(String.format("%tc Subscription created: ID=%d lastAlive=%tc", 18 | Calendar.getInstance(), s.getSubscriptionId().getValue(), s.getLastAlive())); 19 | } 20 | 21 | @Override 22 | public void onAlive(Subscription s) { 23 | // the server acknowledged that the connection is alive, 24 | // although there were no changes to send 25 | SampleConsoleClient.println(String.format("%tc Subscription alive: ID=%d lastAlive=%tc", Calendar.getInstance(), 26 | s.getSubscriptionId().getValue(), s.getLastAlive())); 27 | } 28 | 29 | @Override 30 | public void onTimeout(Subscription s) { 31 | // the server did not acknowledge that the connection is alive, and the 32 | // maxKeepAliveCount has been exceeded 33 | SampleConsoleClient.println(String.format("%tc Subscription timeout: ID=%d lastAlive=%tc", 34 | Calendar.getInstance(), s.getSubscriptionId().getValue(), s.getLastAlive())); 35 | 36 | } 37 | 38 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # opcua-tools 2 | OPC UA Tools (Unified Architecture)标准工具,包括服务器和客户端,并提供调用接口(服务器和客户端)。 3 | 4 | 相关lib文件在[www.unified-automation.com](https://www.unified-automation.com/products/server-sdk/java-ua-server-sdk.html)下载 5 | 6 | ### LICENSE 7 | - Prosys SDK license [Prosys_OPC_UA_Java_SDK_License](/LICENSE/Prosys_OPC_UA_Java_SDK_License.pdf) 8 | 9 | ```html 10 | Prosys OPCUA Java SDK is covered by the license terms in 11 | Prosys_OPC_UA_Java_SDK_License.pdf 12 | ``` 13 | 14 | - Libraries 15 | 16 | ```html 17 | The SDK uses the following libraries, which are covered by individual licenses, 18 | available in the following files. 19 | ``` 20 | 21 | |JAR|Library|License file| 22 | |---|---|---| 23 | |Opc.Ua.Stack*.jar|OPC Foundation Java Stack|[LICENSE.opc.ua.stack](/LICENSE/LICENSE.opc.ua.stack)| 24 | |log4j-1.2.17.jar|Apache Logging Services|[LICENSE.apache2.0](/LICENSE/LICENSE.apache2.0)| 25 | |http-*.jar|Apache HttpComponents|[LICENSE.apache2.0](/LICENSE/LICENSE.apache2.0)| 26 | |commons-logging*.jar|Apache Commons Logging Component|[LICENSE.apache2.0](/LICENSE/LICENSE.apache2.0)| 27 | |bc*.jar|Bouncy Castle security|[LICENSE.bouncycastle](/LICENSE/LICENSE.bouncycastle)| 28 | |sc*.jar|Spongy Castle security|[LICENSE.bouncycastle](/LICENSE/LICENSE.bouncycastle)| 29 | |*slf4j*.jar|Simple Logging Facade for Java (SLF4j)|[LICENSE.slf4j](/LICENSE/LICENSE.slf4j)| 30 | 31 | ------------------------- 32 | 33 | ## OPC UA Client 使用说明 34 | 35 | ### 创建一个无证书(NONE)的 OPC UA Client 36 | 37 | ### 创建一个有证书(SINGN)的 OPC UA Client 38 | 39 | ### 创建一个有证书(SINGNANDENCRYPT)的 OPC UA Client 40 | 41 | ## OPC UA Server 使用说明 -------------------------------------------------------------------------------- /opc-ua-server/src/main/java/com/prosysopc/ua/samples/MyCertificateValidationListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Prosys OPC UA Java SDK 3 | * 4 | * Copyright (c) Prosys PMS Ltd., . 5 | * All rights reserved. 6 | */ 7 | package com.prosysopc.ua.samples; 8 | 9 | import java.security.cert.CertificateParsingException; 10 | import java.util.EnumSet; 11 | 12 | import org.opcfoundation.ua.core.ApplicationDescription; 13 | import org.opcfoundation.ua.transport.security.Cert; 14 | import org.opcfoundation.ua.utils.CertificateUtils; 15 | 16 | import com.prosysopc.ua.CertificateValidationListener; 17 | import com.prosysopc.ua.PkiFileBasedCertificateValidator.CertificateCheck; 18 | import com.prosysopc.ua.PkiFileBasedCertificateValidator.ValidationResult; 19 | 20 | /** 21 | * A sample implementation of a CertificateValidationListener 22 | */ 23 | public class MyCertificateValidationListener implements CertificateValidationListener { 24 | 25 | @Override 26 | public ValidationResult onValidate(Cert certificate, ApplicationDescription applicationDescription, 27 | EnumSet passedChecks) { 28 | try { 29 | SampleConsoleServer.println( 30 | applicationDescription + ", " + CertificateUtils.getApplicationUriOfCertificate(certificate)); 31 | } catch (CertificateParsingException e1) { 32 | throw new RuntimeException(e1); 33 | } 34 | 35 | // Do not mind about URI... 36 | if (passedChecks.containsAll( 37 | EnumSet.of(CertificateCheck.Trusted, CertificateCheck.Validity, CertificateCheck.Signature))) { 38 | if (!passedChecks.contains(CertificateCheck.Uri)) 39 | try { 40 | SampleConsoleServer.println("Client's ApplicationURI (" + applicationDescription.getApplicationUri() 41 | + ") does not match the one in certificate: " 42 | + CertificateUtils.getApplicationUriOfCertificate(certificate)); 43 | } catch (CertificateParsingException e) { 44 | throw new RuntimeException(e); 45 | } 46 | return ValidationResult.AcceptPermanently; 47 | } 48 | return ValidationResult.Reject; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /opc-ua-client/src/main/java/SimpleClient.java: -------------------------------------------------------------------------------- 1 | 2 | import java.io.IOException; 3 | import java.net.UnknownHostException; 4 | import java.util.Locale; 5 | 6 | import org.opcfoundation.ua.builtintypes.DataValue; 7 | import org.opcfoundation.ua.builtintypes.LocalizedText; 8 | import org.opcfoundation.ua.core.ApplicationDescription; 9 | import org.opcfoundation.ua.core.ApplicationType; 10 | import org.opcfoundation.ua.core.Identifiers; 11 | import org.opcfoundation.ua.transport.security.SecurityMode; 12 | 13 | import com.prosysopc.ua.ApplicationIdentity; 14 | import com.prosysopc.ua.SecureIdentityException; 15 | import com.prosysopc.ua.client.UaClient; 16 | 17 | /** 18 | * A very minimal client application. Connects to the server and reads one 19 | * variable. Works with a non-secure connection. 20 | */ 21 | public class SimpleClient { 22 | /** 23 | * @param args 24 | */ 25 | public static void main(String[] args) throws Exception { 26 | UaClient client = new UaClient("opc.tcp://localhost:52520/OPCUA/SampleConsoleServer"); 27 | client.setSecurityMode(SecurityMode.NONE); 28 | initialize(client); 29 | client.connect(); 30 | DataValue value = client.readValue(Identifiers.Server_ServerStatus_State); 31 | System.out.println(value); 32 | client.disconnect(); 33 | } 34 | 35 | /** 36 | * Define a minimal ApplicationIdentity. If you use secure connections, you 37 | * will also need to define the application instance certificate and manage 38 | * server certificates. See the SampleConsoleClient.initialize() for a full 39 | * example of that. 40 | */ 41 | protected static void initialize(UaClient client) 42 | throws SecureIdentityException, IOException, UnknownHostException { 43 | // *** Application Description is sent to the server 44 | ApplicationDescription appDescription = new ApplicationDescription(); 45 | appDescription.setApplicationName(new LocalizedText("SimpleClient", Locale.ENGLISH)); 46 | // 'localhost' (all lower case) in the URI is converted to the actual 47 | // host name of the computer in which the application is run 48 | appDescription.setApplicationUri("urn:localhost:UA:SimpleClient"); 49 | appDescription.setProductUri("urn:prosysopc.com:UA:SimpleClient"); 50 | appDescription.setApplicationType(ApplicationType.Client); 51 | 52 | final ApplicationIdentity identity = new ApplicationIdentity(); 53 | identity.setApplicationDescription(appDescription); 54 | client.setApplicationIdentity(identity); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /opc-ua-server/src/main/java/com/prosysopc/ua/samples/AnalogItemListenerTemplate.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Prosys OPC UA Java SDK 3 | * 4 | * Copyright (c) Prosys PMS Ltd., . 5 | * All rights reserved. 6 | */ 7 | package com.prosysopc.ua.samples; 8 | 9 | import org.opcfoundation.ua.builtintypes.DataValue; 10 | import org.opcfoundation.ua.utils.NumericRange; 11 | 12 | import com.prosysopc.ua.StatusException; 13 | import com.prosysopc.ua.nodes.UaInstance; 14 | import com.prosysopc.ua.nodes.UaValueNode; 15 | import com.prosysopc.ua.server.ServiceContext; 16 | import com.prosysopc.ua.server.io.UaTypeIoListenerImpl; 17 | import com.prosysopc.ua.types.opcua.AnalogItemType; 18 | 19 | /** 20 | * Example {@link com.prosysopc.ua.server.io.UaTypeIoListener} template that 21 | * could be created with code generator in future. 22 | */ 23 | public abstract class AnalogItemListenerTemplate extends UaTypeIoListenerImpl { 24 | @Override 25 | public boolean onWriteValue(ServiceContext serviceContext, UaInstance instance, UaValueNode variable, 26 | NumericRange indexRange, DataValue dataValue) throws StatusException { 27 | AnalogItemType parent = (AnalogItemType) instance; 28 | if (instance == variable) 29 | return onWriteValue(serviceContext, parent, indexRange, dataValue); 30 | String s = variable.getBrowseName().getName(); 31 | if (s.equals("EURange")) 32 | return onWriteEuRange(serviceContext, parent, indexRange, dataValue); 33 | else if (s.equals("EngineeringUnits")) 34 | return onWriteEngineeringUnits(serviceContext, parent, indexRange, dataValue); 35 | else if (s.equals("InstrumentRange")) 36 | return onWriteInstrumentRange(serviceContext, parent, indexRange, dataValue); 37 | return false; 38 | } 39 | 40 | protected boolean onWriteEngineeringUnits(ServiceContext serviceContext, AnalogItemType node, 41 | NumericRange indexRange, DataValue dataValue) { 42 | return false; 43 | } 44 | 45 | protected boolean onWriteEuRange(ServiceContext serviceContext, AnalogItemType node, NumericRange indexRange, 46 | DataValue dataValue) { 47 | return false; 48 | } 49 | 50 | protected boolean onWriteInstrumentRange(ServiceContext serviceContext, AnalogItemType node, 51 | NumericRange indexRange, DataValue dataValue) { 52 | return false; 53 | } 54 | 55 | protected boolean onWriteValue(ServiceContext serviceContext, AnalogItemType node, NumericRange indexRange, 56 | DataValue dataValue) throws StatusException { 57 | return false; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /opc-ua-client/src/main/java/MySubscriptionNotificationListener.java: -------------------------------------------------------------------------------- 1 | 2 | import org.opcfoundation.ua.builtintypes.DataValue; 3 | import org.opcfoundation.ua.builtintypes.DiagnosticInfo; 4 | import org.opcfoundation.ua.builtintypes.ExtensionObject; 5 | import org.opcfoundation.ua.builtintypes.StatusCode; 6 | import org.opcfoundation.ua.builtintypes.UnsignedInteger; 7 | import org.opcfoundation.ua.builtintypes.Variant; 8 | import org.opcfoundation.ua.core.NotificationData; 9 | 10 | import com.prosysopc.ua.client.MonitoredDataItem; 11 | import com.prosysopc.ua.client.MonitoredEventItem; 12 | import com.prosysopc.ua.client.Subscription; 13 | import com.prosysopc.ua.client.SubscriptionNotificationListener; 14 | 15 | /** 16 | * A sampler listener for subscription notifications. 17 | */ 18 | public class MySubscriptionNotificationListener implements SubscriptionNotificationListener { 19 | 20 | @Override 21 | public void onBufferOverflow(Subscription subscription, UnsignedInteger sequenceNumber, 22 | ExtensionObject[] notificationData) { 23 | SampleConsoleClient.println("*** SUBCRIPTION BUFFER OVERFLOW ***"); 24 | } 25 | 26 | @Override 27 | public void onDataChange(Subscription subscription, MonitoredDataItem item, DataValue newValue) { 28 | // Called for each data change notification 29 | } 30 | 31 | @Override 32 | public void onError(Subscription subscription, Object notification, Exception exception) { 33 | // Called if the parsing of the notification data fails, 34 | // notification is either a MonitoredItemNotification or 35 | // an EventList 36 | SampleConsoleClient.printException(exception); 37 | } 38 | 39 | @Override 40 | public void onEvent(Subscription subscription, MonitoredEventItem item, Variant[] eventFields) { 41 | // Called for each event notification 42 | } 43 | 44 | @Override 45 | public long onMissingData(UnsignedInteger lastSequenceNumber, long sequenceNumber, long newSequenceNumber, 46 | StatusCode serviceResult) { 47 | // Called if a data packet is missed due to communication errors and 48 | // failing Republish 49 | SampleConsoleClient.println( 50 | "Data missed: lastSequenceNumber=" + lastSequenceNumber + " newSequenceNumber=" + newSequenceNumber); 51 | return newSequenceNumber; // Accept the default 52 | } 53 | 54 | @Override 55 | public void onNotificationData(Subscription subscription, NotificationData notification) { 56 | // Called after a complete notification data package is 57 | // handled 58 | // if (notification instanceof DataChangeNotification) { 59 | // DataChangeNotification d = (DataChangeNotification) notification; 60 | // SampleConsoleClient.println("onNotificationData: " + 61 | // d.getMonitoredItems().length); 62 | // } 63 | } 64 | 65 | @Override 66 | public void onStatusChange(Subscription subscription, StatusCode oldStatus, StatusCode newStatus, 67 | DiagnosticInfo diagnosticInfo) { 68 | // Called when the subscription status has changed in 69 | // the server 70 | } 71 | 72 | }; 73 | -------------------------------------------------------------------------------- /opc-ua-server/src/main/java/com/prosysopc/ua/samples/MyUserValidator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Prosys OPC UA Java SDK 3 | * 4 | * Copyright (c) Prosys PMS Ltd., . 5 | * All rights reserved. 6 | */ 7 | package com.prosysopc.ua.samples; 8 | 9 | import org.opcfoundation.ua.builtintypes.StatusCode; 10 | import org.opcfoundation.ua.core.StatusCodes; 11 | import org.opcfoundation.ua.core.UserIdentityToken; 12 | import org.opcfoundation.ua.core.UserTokenType; 13 | import org.opcfoundation.ua.transport.security.Cert; 14 | 15 | import com.prosysopc.ua.PkiFileBasedCertificateValidator; 16 | import com.prosysopc.ua.StatusException; 17 | import com.prosysopc.ua.server.ServerUserIdentity; 18 | import com.prosysopc.ua.server.Session; 19 | import com.prosysopc.ua.server.UserValidator; 20 | 21 | /** 22 | * A sample implementation of the UserValidator 23 | */ 24 | public class MyUserValidator implements UserValidator { 25 | 26 | private final PkiFileBasedCertificateValidator userValidator; 27 | 28 | /** 29 | * 30 | */ 31 | public MyUserValidator(PkiFileBasedCertificateValidator userValidator) { 32 | this.userValidator = userValidator; 33 | } 34 | 35 | /* 36 | * (non-Javadoc) 37 | * 38 | * @see 39 | * com.prosysopc.ua.server.UserValidator#onValidate(com.prosysopc.ua.server 40 | * .Session, com.prosysopc.ua.server.SessionManager.ServerUserIdentity) 41 | */ 42 | @Override 43 | public boolean onValidate(Session session, ServerUserIdentity userIdentity) throws StatusException { 44 | // Return true, if the user is allowed access to the server 45 | // Note that the UserIdentity can be of different actual types, 46 | // depending on the selected authentication mode (by the client). 47 | SampleConsoleServer.println("onValidate: userIdentity=" + userIdentity); 48 | if (userIdentity.getType().equals(UserTokenType.UserName)) 49 | if (userIdentity.getName().equals("opcua") && userIdentity.getPassword().equals("opcua")) 50 | return true; 51 | else if (userIdentity.getName().equals("opcua2") && userIdentity.getPassword().equals("opcua2")) 52 | return true; 53 | else 54 | return false; 55 | 56 | // Example for validating the user auth certs via 57 | // PkiFileBasedCertificateValidator 58 | if (userIdentity.getType().equals(UserTokenType.Certificate)) { 59 | Cert cert = userIdentity.getCertificate(); 60 | if (userValidator.validateCertificate(cert).isGood()) 61 | return true; 62 | else 63 | throw new StatusException(new StatusCode(StatusCodes.Bad_IdentityTokenRejected)); 64 | 65 | } 66 | 67 | return true; 68 | } 69 | 70 | /* 71 | * (non-Javadoc) 72 | * 73 | * @see 74 | * com.prosysopc.ua.server.UserValidator#onValidationError(com.prosysopc 75 | * .ua.server.Session, org.opcfoundation.ua.core.UserIdentityToken, 76 | * java.lang.Exception) 77 | */ 78 | @Override 79 | public void onValidationError(Session session, UserIdentityToken userToken, Exception exception) { 80 | SampleConsoleServer 81 | .println("onValidationError: User validation failed: userToken=" + userToken + " error=" + exception); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /opc-ua-server/src/main/java/com/prosysopc/ua/samples/MyEnumType.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Prosys OPC UA Java SDK 3 | * 4 | * Copyright (c) Prosys PMS Ltd., . 5 | * All rights reserved. 6 | */ 7 | package com.prosysopc.ua.samples; 8 | 9 | import java.util.ArrayList; 10 | import java.util.EnumSet; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | import org.opcfoundation.ua.builtintypes.Enumeration; 16 | import org.opcfoundation.ua.builtintypes.LocalizedText; 17 | import org.opcfoundation.ua.builtintypes.UnsignedInteger; 18 | 19 | /** 20 | * A sample enumeration type to be used with an OPC UA DataType. 21 | *

22 | * OPC UA enum types are expected to be zero-based, i.e. the first element is 23 | * supposed to correspond to 0. getEnumStrings() here is coded so that it will 24 | * return null values, though, if not all integer in between values are defined. 25 | *

26 | * Note that it is usually easier to define new data types and use them with the 27 | * information model files and code generation, based on those. 28 | *

29 | * The various {@link #valueOf} methods are useful for converting integer values 30 | * to the enumeration. In OPC UA communication, the enumeration values are 31 | * always passed as integers. 32 | */ 33 | public enum MyEnumType implements Enumeration { 34 | One(1), Three(3), Two(2), Zero(0); 35 | 36 | public static EnumSet ALL = EnumSet.allOf(MyEnumType.class); 37 | public static EnumSet NONE = EnumSet.noneOf(MyEnumType.class); 38 | 39 | private static final Map map; 40 | 41 | static { 42 | map = new HashMap(); 43 | for (MyEnumType i : MyEnumType.values()) 44 | map.put(i.value, i); 45 | } 46 | 47 | public static LocalizedText[] getEnumStrings() { 48 | MyEnumType[] values = MyEnumType.values(); 49 | List enumStrings = new ArrayList(values.length); 50 | for (MyEnumType t : values) { 51 | int index = t.getValue(); 52 | while (enumStrings.size() < (index + 1)) 53 | enumStrings.add(null); 54 | enumStrings.set(index, new LocalizedText(t.name(), LocalizedText.NO_LOCALE)); 55 | } 56 | return enumStrings.toArray(new LocalizedText[enumStrings.size()]); 57 | } 58 | 59 | public static MyEnumType valueOf(int value) { 60 | return map.get(value); 61 | } 62 | 63 | public static MyEnumType[] valueOf(int[] value) { 64 | MyEnumType[] result = new MyEnumType[value.length]; 65 | for (int i = 0; i < value.length; i++) 66 | result[i] = valueOf(value[i]); 67 | return result; 68 | } 69 | 70 | public static MyEnumType valueOf(Integer value) { 71 | return value == null ? null : valueOf(value.intValue()); 72 | } 73 | 74 | public static MyEnumType[] valueOf(Integer[] value) { 75 | MyEnumType[] result = new MyEnumType[value.length]; 76 | for (int i = 0; i < value.length; i++) 77 | result[i] = valueOf(value[i]); 78 | return result; 79 | } 80 | 81 | public static MyEnumType valueOf(UnsignedInteger value) { 82 | return value == null ? null : valueOf(value.intValue()); 83 | } 84 | 85 | public static MyEnumType[] valueOf(UnsignedInteger[] value) { 86 | MyEnumType[] result = new MyEnumType[value.length]; 87 | for (int i = 0; i < value.length; i++) 88 | result[i] = valueOf(value[i]); 89 | return result; 90 | } 91 | 92 | private int value; 93 | 94 | private MyEnumType(int value) { 95 | this.value = value; 96 | } 97 | 98 | /* 99 | * (non-Javadoc) 100 | * 101 | * @see org.opcfoundation.ua.builtintypes.Enumeration#getValue() 102 | */ 103 | @Override 104 | public int getValue() { 105 | return value; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /opc-ua-server/src/main/java/com/prosysopc/ua/samples/MyLevelAlarmType.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Prosys OPC UA Java SDK 3 | * 4 | * Copyright (c) Prosys PMS Ltd., . 5 | * All rights reserved. 6 | */ 7 | package com.prosysopc.ua.samples; 8 | 9 | import org.opcfoundation.ua.builtintypes.DataValue; 10 | import org.opcfoundation.ua.builtintypes.DateTime; 11 | import org.opcfoundation.ua.builtintypes.LocalizedText; 12 | import org.opcfoundation.ua.builtintypes.NodeId; 13 | import org.opcfoundation.ua.builtintypes.QualifiedName; 14 | import org.opcfoundation.ua.builtintypes.Variant; 15 | 16 | import com.prosysopc.ua.nodes.DataChangeListener; 17 | import com.prosysopc.ua.nodes.UaNode; 18 | import com.prosysopc.ua.nodes.UaVariable; 19 | import com.prosysopc.ua.server.NodeManagerUaNode; 20 | import com.prosysopc.ua.server.nodes.UaVariableNode; 21 | import com.prosysopc.ua.types.opcua.server.ExclusiveLevelAlarmTypeNode; 22 | 23 | public class MyLevelAlarmType extends ExclusiveLevelAlarmTypeNode { 24 | private final DataChangeListener listener = new DataChangeListener() { 25 | 26 | @Override 27 | public void onDataChange(UaNode uaNode, DataValue prevValue, DataValue value) { 28 | Variant varValue = value == null ? Variant.NULL : value.getValue(); 29 | DateTime activeTime = value == null ? null : value.getSourceTimestamp(); 30 | if (varValue.isEmpty()) 31 | inactivateAlarm(activeTime); 32 | else 33 | checkAlarm(varValue.floatValue(), activeTime); 34 | } 35 | }; 36 | /** 37 | * 38 | */ 39 | private final MyNodeManager myNodeManager; 40 | 41 | public MyLevelAlarmType(MyNodeManager myNodeManager, NodeManagerUaNode nodeManager, NodeId nodeId, 42 | QualifiedName browseName, LocalizedText displayName) { 43 | super(nodeManager, nodeId, browseName, displayName); 44 | this.myNodeManager = myNodeManager; 45 | } 46 | 47 | @Override 48 | public void setInput(UaVariable node) { 49 | if (getInput() instanceof UaVariableNode) 50 | ((UaVariableNode) getInput()).removeDataChangeListener(listener); 51 | super.setInput(node); 52 | if (node instanceof UaVariableNode) 53 | ((UaVariableNode) node).addDataChangeListener(listener); 54 | } 55 | 56 | private void triggerAlarm(DateTime activeTime) { 57 | // Trigger event 58 | byte[] myEventId = this.myNodeManager.getNextUserEventId(); 59 | triggerEvent(DateTime.currentTime(), activeTime, myEventId); 60 | } 61 | 62 | /** 63 | * Creates an alarm, if it is not active 64 | * 65 | * @param activeTime 66 | */ 67 | protected void activateAlarm(int severity, DateTime activeTime) { 68 | // Note: UaServer does not yet send any event notifications! 69 | if (isEnabled() && (!isActive() || (getSeverity().getValue() != severity))) { 70 | MyNodeManager.println("activateAlarm: severity=" + severity); 71 | setActive(true); 72 | setRetain(true); 73 | setAcked(false); // Also sets confirmed to false 74 | setSeverity(severity); 75 | 76 | triggerAlarm(activeTime); 77 | 78 | } 79 | } 80 | 81 | protected void checkAlarm(float nextValue, DateTime activeTime) { 82 | if (nextValue > getHighHighLimit()) 83 | activateAlarm(700, activeTime); 84 | else if (nextValue > getHighLimit()) 85 | activateAlarm(500, activeTime); 86 | else if (nextValue < getLowLowLimit()) 87 | activateAlarm(700, activeTime); 88 | else if (nextValue < getLowLimit()) 89 | activateAlarm(500, activeTime); 90 | else 91 | inactivateAlarm(activeTime); 92 | } 93 | 94 | protected void inactivateAlarm(DateTime activeTime) { 95 | if (isEnabled() && isActive()) { 96 | MyNodeManager.println("inactivateAlarm"); 97 | setActive(false); 98 | setRetain(!isAcked() && !isConfirmed()); 99 | triggerAlarm(activeTime); 100 | } 101 | } 102 | 103 | } -------------------------------------------------------------------------------- /opc-ua-client/src/main/java/MyCertificateValidationListener.java: -------------------------------------------------------------------------------- 1 | 2 | import java.security.cert.CertificateParsingException; 3 | import java.util.Date; 4 | import java.util.EnumSet; 5 | 6 | import org.opcfoundation.ua.core.ApplicationDescription; 7 | import org.opcfoundation.ua.transport.security.Cert; 8 | import org.opcfoundation.ua.utils.CertificateUtils; 9 | 10 | import com.prosysopc.ua.CertificateValidationListener; 11 | import com.prosysopc.ua.PkiFileBasedCertificateValidator.CertificateCheck; 12 | import com.prosysopc.ua.PkiFileBasedCertificateValidator.ValidationResult; 13 | 14 | /** 15 | * A sampler listener for certificate validation results. 16 | */ 17 | public class MyCertificateValidationListener implements CertificateValidationListener { 18 | 19 | @Override 20 | public ValidationResult onValidate(Cert certificate, ApplicationDescription applicationDescription, 21 | EnumSet passedChecks) { 22 | // Called whenever the PkiFileBasedCertificateValidator has 23 | // validated a certificate 24 | println(""); 25 | println("*** The Server Certificate : "); 26 | println(""); 27 | println("Subject : " + certificate.getCertificate().getSubjectX500Principal().toString()); 28 | println("Issued by : " + certificate.getCertificate().getIssuerX500Principal().toString()); 29 | println("Valid from: " + certificate.getCertificate().getNotBefore().toString()); 30 | println(" to: " + certificate.getCertificate().getNotAfter().toString()); 31 | println(""); 32 | if (!passedChecks.contains(CertificateCheck.Signature)) 33 | println("* The Certificate is NOT SIGNED BY A TRUSTED SIGNER!"); 34 | if (!passedChecks.contains(CertificateCheck.Validity)) { 35 | Date today = new Date(); 36 | final boolean isYoung = certificate.getCertificate().getNotBefore().compareTo(today) > 0; 37 | final boolean isOld = certificate.getCertificate().getNotAfter().compareTo(today) < 0; 38 | final String oldOrYoung = isOld ? "(anymore)" : (isYoung ? "(yet)" : ""); 39 | 40 | println("* The Certificate time interval IS NOT VALID " + oldOrYoung + "!"); 41 | } 42 | if (!passedChecks.contains(CertificateCheck.Uri)) { 43 | println("* The Certificate URI DOES NOT MATCH the ApplicationDescription URI!"); 44 | println(" ApplicationURI in ApplicationDescription = " + applicationDescription.getApplicationUri()); 45 | try { 46 | println(" ApplicationURI in Certificate = " 47 | + CertificateUtils.getApplicationUriOfCertificate(certificate)); 48 | } catch (CertificateParsingException e) { 49 | println(" ApplicationURI in Certificate is INVALID"); 50 | } 51 | } 52 | if (passedChecks.contains(CertificateCheck.SelfSigned)) 53 | println("* The Certificate is self-signed."); 54 | println(""); 55 | // If the certificate is trusted, valid and verified, accept it 56 | if (passedChecks.containsAll(CertificateCheck.COMPULSORY)) 57 | return ValidationResult.AcceptPermanently; 58 | do { 59 | println("Note: If the certificate is not OK,"); 60 | println("you will be prompted again, even if you answer 'Always' here."); 61 | println(""); 62 | println("Do you want to accept this certificate?\n" + " (A=Always, Y=Yes, this time, N=No)\n" 63 | + " (D=Show Details of the Certificate)"); 64 | String input = readInput().toLowerCase(); 65 | if (input.equals("a")) 66 | // if the certificate is not valid anymore or the signature 67 | // is not verified, you will be prompted again, even if you 68 | // select always here 69 | return ValidationResult.AcceptPermanently; 70 | 71 | if (input.equals("y")) 72 | return ValidationResult.AcceptOnce; 73 | if (input.equals("n")) 74 | return ValidationResult.Reject; 75 | if (input.equals("d")) 76 | println("Certificate Details:" + certificate.getCertificate().toString()); 77 | } while (true); 78 | } 79 | 80 | private void println(String string) { 81 | SampleConsoleClient.println(string); 82 | } 83 | 84 | private String readInput() { 85 | return SampleConsoleClient.readInput(false); 86 | } 87 | }; 88 | -------------------------------------------------------------------------------- /opc-ua-server/src/main/java/com/prosysopc/ua/samples/MyEventType.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Prosys OPC UA Java SDK 3 | * 4 | * Copyright (c) Prosys PMS Ltd., . 5 | * All rights reserved. 6 | */ 7 | package com.prosysopc.ua.samples; 8 | 9 | import org.opcfoundation.ua.builtintypes.LocalizedText; 10 | import org.opcfoundation.ua.builtintypes.NodeId; 11 | import org.opcfoundation.ua.builtintypes.QualifiedName; 12 | import org.opcfoundation.ua.builtintypes.UnsignedInteger; 13 | 14 | import com.prosysopc.ua.StatusException; 15 | import com.prosysopc.ua.TypeDefinitionId; 16 | import com.prosysopc.ua.nodes.UaProperty; 17 | import com.prosysopc.ua.nodes.UaVariable; 18 | import com.prosysopc.ua.server.NodeManagerUaNode; 19 | import com.prosysopc.ua.types.opcua.server.BaseEventTypeNode; 20 | 21 | /** 22 | * A sample implementation of a custom event type. 23 | *

24 | * You can use the TypeDefinitionId annotation or getDefaultTypeDefinition() to 25 | * define the type. If you use the annotation, you can also register the type 26 | * and create event instances with the class only. 27 | * 28 | * @see {@link MyNodeManager#createMyEventType} 29 | */ 30 | @TypeDefinitionId(nsu = MyNodeManager.NAMESPACE, i = MyEventType.MY_EVENT_ID) 31 | public class MyEventType extends BaseEventTypeNode { 32 | 33 | public static final int MY_EVENT_ID = 10000; 34 | public static final UnsignedInteger MY_PROPERTY_ID = UnsignedInteger.valueOf(10001); 35 | public static final String MY_PROPERTY_NAME = "MyProperty"; 36 | public static final UnsignedInteger MY_VARIABLE_ID = UnsignedInteger.valueOf(10002); 37 | public static final String MY_VARIABLE_NAME = "MyVariable"; 38 | 39 | /** 40 | * The constructor is used by the NodeBuilder and should not be used 41 | * directly by the application. Therefore we define it with protected 42 | * visibility. 43 | */ 44 | protected MyEventType(NodeManagerUaNode nodeManager, NodeId nodeId, QualifiedName browseName, 45 | LocalizedText displayName) { 46 | super(nodeManager, nodeId, browseName, displayName); 47 | } 48 | 49 | /** 50 | * @return the value of MyProperty 51 | */ 52 | public String getMyProperty() { 53 | UaProperty property = getMyPropertyNode(); 54 | if (property == null) 55 | return null; 56 | return (String) property.getValue().getValue().getValue(); 57 | } 58 | 59 | /** 60 | * @return the myProperty node object 61 | */ 62 | public UaProperty getMyPropertyNode() { 63 | UaProperty property = getProperty(new QualifiedName(getNodeManager().getNamespaceIndex(), MY_PROPERTY_NAME)); 64 | return property; 65 | } 66 | 67 | /** 68 | * @return the value of MyVariable 69 | */ 70 | public Integer getMyVariable() { 71 | UaVariable variable = getMyVariableNode(); 72 | if (variable == null) 73 | return null; 74 | return (Integer) variable.getValue().getValue().getValue(); 75 | } 76 | 77 | /** 78 | * @return the MyVariable node object 79 | */ 80 | public UaVariable getMyVariableNode() { 81 | UaVariable property = (UaVariable) getComponent( 82 | new QualifiedName(getNodeManager().getNamespaceIndex(), MY_VARIABLE_NAME)); 83 | return property; 84 | } 85 | 86 | /** 87 | * @param value 88 | * the value to set to MyProperty 89 | */ 90 | public void setMyProperty(String myValue) { 91 | UaProperty property = getMyPropertyNode(); 92 | if (property != null) 93 | try { 94 | property.setValue(myValue); 95 | } catch (StatusException e) { 96 | throw new RuntimeException(e); 97 | } 98 | else 99 | System.out.println("No property"); 100 | } 101 | 102 | /** 103 | * @param value 104 | * the value to set to MyVariable 105 | */ 106 | public void setMyVariable(int myValue) { 107 | UaVariable variable = getMyVariableNode(); 108 | if (variable != null) 109 | try { 110 | variable.setValue(myValue); 111 | } catch (StatusException e) { 112 | throw new RuntimeException(e); 113 | } 114 | else 115 | System.out.println("No variable"); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /opc-ua-server/src/main/java/com/prosysopc/ua/samples/MyIoManagerListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Prosys OPC UA Java SDK 3 | * 4 | * Copyright (c) Prosys PMS Ltd., . 5 | * All rights reserved. 6 | */ 7 | package com.prosysopc.ua.samples; 8 | 9 | import java.util.EnumSet; 10 | 11 | import org.opcfoundation.ua.builtintypes.DataValue; 12 | import org.opcfoundation.ua.builtintypes.DateTime; 13 | import org.opcfoundation.ua.builtintypes.NodeId; 14 | import org.opcfoundation.ua.builtintypes.UnsignedInteger; 15 | import org.opcfoundation.ua.core.AccessLevel; 16 | import org.opcfoundation.ua.core.TimestampsToReturn; 17 | import org.opcfoundation.ua.utils.NumericRange; 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | 21 | import com.prosysopc.ua.StatusException; 22 | import com.prosysopc.ua.WriteAccess; 23 | import com.prosysopc.ua.nodes.UaMethod; 24 | import com.prosysopc.ua.nodes.UaNode; 25 | import com.prosysopc.ua.nodes.UaValueNode; 26 | import com.prosysopc.ua.nodes.UaVariable; 27 | import com.prosysopc.ua.server.ServiceContext; 28 | import com.prosysopc.ua.server.io.IoManagerListener; 29 | 30 | /** 31 | * A sample implementation of a {@link IoManagerListener} 32 | */ 33 | public class MyIoManagerListener implements IoManagerListener { 34 | private static Logger logger = LoggerFactory.getLogger(MyIoManagerListener.class); 35 | 36 | @Override 37 | public EnumSet onGetUserAccessLevel(ServiceContext serviceContext, NodeId nodeId, UaVariable node) { 38 | // The AccessLevel defines the accessibility of the Variable.Value 39 | // attribute 40 | 41 | // Define anonymous access 42 | // if (serviceContext.getSession().getUserIdentity().getType() 43 | // .equals(UserTokenType.Anonymous)) 44 | // return EnumSet.noneOf(AccessLevel.class); 45 | if (node.getHistorizing()) 46 | return EnumSet.of(AccessLevel.CurrentRead, AccessLevel.CurrentWrite, AccessLevel.HistoryRead); 47 | else 48 | return EnumSet.of(AccessLevel.CurrentRead, AccessLevel.CurrentWrite); 49 | } 50 | 51 | @Override 52 | public Boolean onGetUserExecutable(ServiceContext serviceContext, NodeId nodeId, UaMethod node) { 53 | // Enable execution of all methods that are allowed by default 54 | return true; 55 | } 56 | 57 | @Override 58 | public EnumSet onGetUserWriteMask(ServiceContext serviceContext, NodeId nodeId, UaNode node) { 59 | // Enable writing to everything that is allowed by default 60 | // The WriteMask defines the writable attributes, except for Value, 61 | // which is controlled by UserAccessLevel (above) 62 | 63 | // The following would deny write access for anonymous users: 64 | // if 65 | // (serviceContext.getSession().getUserIdentity().getType().equals( 66 | // UserTokenType.Anonymous)) 67 | // return EnumSet.noneOf(WriteAccess.class); 68 | 69 | return EnumSet.allOf(WriteAccess.class); 70 | } 71 | 72 | @Override 73 | public boolean onReadNonValue(ServiceContext serviceContext, NodeId nodeId, UaNode node, 74 | UnsignedInteger attributeId, DataValue dataValue) throws StatusException { 75 | return false; 76 | } 77 | 78 | @Override 79 | public boolean onReadValue(ServiceContext serviceContext, NodeId nodeId, UaValueNode node, NumericRange indexRange, 80 | TimestampsToReturn timestampsToReturn, DateTime minTimestamp, DataValue dataValue) throws StatusException { 81 | if (logger.isDebugEnabled()) 82 | logger.debug("onReadValue: nodeId=" + nodeId + (node != null ? " node=" + node.getBrowseName() : "")); 83 | return false; 84 | } 85 | 86 | @Override 87 | public boolean onWriteNonValue(ServiceContext serviceContext, NodeId nodeId, UaNode node, 88 | UnsignedInteger attributeId, DataValue dataValue) throws StatusException { 89 | return false; 90 | } 91 | 92 | @Override 93 | public boolean onWriteValue(ServiceContext serviceContext, NodeId nodeId, UaValueNode node, NumericRange indexRange, 94 | DataValue dataValue) throws StatusException { 95 | logger.info("onWriteValue: nodeId=" + nodeId + (node != null ? " node=" + node.getBrowseName() : "") 96 | + (indexRange != null ? " indexRange=" + indexRange : "") + " value=" + dataValue); 97 | return false; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /opc-ua-server/src/main/java/com/prosysopc/ua/samples/MyMethodManagerListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Prosys OPC UA Java SDK 3 | * 4 | * Copyright (c) Prosys PMS Ltd., . 5 | * All rights reserved. 6 | */ 7 | package com.prosysopc.ua.samples; 8 | 9 | import java.util.Arrays; 10 | 11 | import org.opcfoundation.ua.builtintypes.DiagnosticInfo; 12 | import org.opcfoundation.ua.builtintypes.NodeId; 13 | import org.opcfoundation.ua.builtintypes.StatusCode; 14 | import org.opcfoundation.ua.builtintypes.Variant; 15 | import org.opcfoundation.ua.core.StatusCodes; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | 19 | import com.prosysopc.ua.StatusException; 20 | import com.prosysopc.ua.nodes.UaMethod; 21 | import com.prosysopc.ua.nodes.UaNode; 22 | import com.prosysopc.ua.server.CallableListener; 23 | import com.prosysopc.ua.server.MethodManager; 24 | import com.prosysopc.ua.server.ServiceContext; 25 | 26 | /** 27 | * A sample implementation of an MethodManagerListener 28 | */ 29 | public class MyMethodManagerListener implements CallableListener { 30 | 31 | private static Logger logger = LoggerFactory.getLogger(MyMethodManagerListener.class); 32 | final private UaNode myMethod; 33 | 34 | /** 35 | * @param myMethod 36 | * the method node to handle. 37 | */ 38 | public MyMethodManagerListener(UaNode myMethod) { 39 | super(); 40 | this.myMethod = myMethod; 41 | } 42 | 43 | @Override 44 | public boolean onCall(ServiceContext serviceContext, NodeId objectId, UaNode object, NodeId methodId, 45 | UaMethod method, final Variant[] inputArguments, final StatusCode[] inputArgumentResults, 46 | final DiagnosticInfo[] inputArgumentDiagnosticInfos, final Variant[] outputs) throws StatusException { 47 | // Handle method calls 48 | // Note that the outputs array is already allocated 49 | if (methodId.equals(myMethod.getNodeId())) { 50 | logger.info("myMethod: {}", Arrays.toString(inputArguments)); 51 | MethodManager.checkInputArguments(new Class[] { String.class, Double.class }, inputArguments, 52 | inputArgumentResults, inputArgumentDiagnosticInfos, false); 53 | // The argument #0 is the operation to perform 54 | String operation; 55 | try { 56 | operation = (String) inputArguments[0].getValue(); 57 | } catch (ClassCastException e) { 58 | throw inputError(0, e.getMessage(), inputArgumentResults, inputArgumentDiagnosticInfos); 59 | } 60 | // The argument #1 is the input (i.e. operand) 61 | double input; 62 | try { 63 | input = inputArguments[1].intValue(); 64 | } catch (ClassCastException e) { 65 | throw inputError(1, e.getMessage(), inputArgumentResults, inputArgumentDiagnosticInfos); 66 | } 67 | 68 | // The result is the operation applied to input 69 | operation = operation.toLowerCase(); 70 | double result; 71 | if (operation.equals("sin")) 72 | result = Math.sin(Math.toRadians(input)); 73 | else if (operation.equals("cos")) 74 | result = Math.cos(Math.toRadians(input)); 75 | else if (operation.equals("tan")) 76 | result = Math.tan(Math.toRadians(input)); 77 | else if (operation.equals("pow")) 78 | result = input * input; 79 | else 80 | throw inputError(0, "Unknown function '" + operation + "': valid functions are sin, cos, tan, pow", 81 | inputArgumentResults, inputArgumentDiagnosticInfos); 82 | outputs[0] = new Variant(result); 83 | return true; // Handled here 84 | } else 85 | return false; 86 | } 87 | 88 | /** 89 | * Handle an error in method inputs. 90 | * 91 | * @param index 92 | * index of the failing input 93 | * @param message 94 | * error message 95 | * @param inputArgumentResults 96 | * the results array to fill in 97 | * @param inputArgumentDiagnosticInfos 98 | * the diagnostics array to fill in 99 | * @return StatusException that can be thrown to break further method 100 | * handling 101 | */ 102 | private StatusException inputError(final int index, final String message, StatusCode[] inputArgumentResults, 103 | DiagnosticInfo[] inputArgumentDiagnosticInfos) { 104 | logger.info("inputError: #{} message={}", index, message); 105 | inputArgumentResults[index] = new StatusCode(StatusCodes.Bad_InvalidArgument); 106 | final DiagnosticInfo di = new DiagnosticInfo(); 107 | di.setAdditionalInfo(message); 108 | inputArgumentDiagnosticInfos[index] = di; 109 | return new StatusException(StatusCodes.Bad_InvalidArgument); 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /opc-ua-server/src/main/java/com/prosysopc/ua/samples/EventHistory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Prosys OPC UA Java SDK 3 | * 4 | * Copyright (c) Prosys PMS Ltd., . 5 | * All rights reserved. 6 | */ 7 | package com.prosysopc.ua.samples; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | import java.util.concurrent.CopyOnWriteArrayList; 13 | 14 | import org.opcfoundation.ua.builtintypes.DateTime; 15 | import org.opcfoundation.ua.builtintypes.DiagnosticInfo; 16 | import org.opcfoundation.ua.builtintypes.QualifiedName; 17 | import org.opcfoundation.ua.builtintypes.StatusCode; 18 | import org.opcfoundation.ua.core.EventFilter; 19 | import org.opcfoundation.ua.core.EventFilterResult; 20 | import org.opcfoundation.ua.core.HistoryEventFieldList; 21 | 22 | import com.prosysopc.ua.EventData; 23 | import com.prosysopc.ua.EventListener; 24 | import com.prosysopc.ua.nodes.UaNode; 25 | import com.prosysopc.ua.server.ContentFilterDefinition; 26 | import com.prosysopc.ua.server.nodes.UaObjectNode; 27 | 28 | /** 29 | * 30 | */ 31 | public class EventHistory { 32 | private final int capacity = 10000; 33 | private final List events = new CopyOnWriteArrayList(); 34 | private final EventListener listener = new EventListener() { 35 | 36 | @Override 37 | public boolean isMonitored(UaNode event) { 38 | return false; 39 | } 40 | 41 | @Override 42 | public void onEvent(UaNode node, EventData eventData) { 43 | events.add(eventData); 44 | while (events.size() > capacity) 45 | events.remove(0); 46 | } 47 | }; 48 | private final UaObjectNode node; 49 | 50 | /** 51 | * @param node 52 | */ 53 | public EventHistory(UaObjectNode node) { 54 | super(); 55 | this.node = node; 56 | node.addEventListener(listener); 57 | } 58 | 59 | /** 60 | * @param eventIds 61 | * @param operationResults 62 | * @param operationDiagnostics 63 | */ 64 | public void deleteEvents(byte[][] eventIds, StatusCode[] operationResults, DiagnosticInfo[] operationDiagnostics) { 65 | for (int i = events.size() - 1; i >= 0; i--) { 66 | EventData event = events.get(i); 67 | byte[] id1 = event.getEventId(); 68 | for (byte[] eventId : eventIds) 69 | if (Arrays.equals(eventId, id1)) { 70 | events.remove(i); 71 | break; 72 | } 73 | } 74 | } 75 | 76 | /** 77 | * @param startTime 78 | * the start of the interval 79 | * @param endTime 80 | * the end of the interval 81 | * @param maxValues 82 | * maximum number of values to return 83 | * @param eventFilter 84 | * the event filter that defines the fields and events to return 85 | * @param firstIndex 86 | * the index of the first entry in the history data to return 87 | * (i.e. the continuationPoint returned for the previous request) 88 | * @param history 89 | * the list of values to fill in 90 | * @return the first index that was not added to the history, in case there 91 | * are more than maxValues entries to return (i.e. the 92 | * continuationPoint to return) 93 | */ 94 | public Integer readEvents(DateTime startTime, DateTime endTime, int maxValues, EventFilter eventFilter, 95 | List history, int firstIndex) { 96 | int i = 0; 97 | boolean startTimeDefined = startTime.compareTo(DateTime.MIN_VALUE) > 0; 98 | boolean endTimeDefined = endTime.compareTo(DateTime.MIN_VALUE) > 0; 99 | List> fieldPaths = new ArrayList>(); 100 | ContentFilterDefinition filterDefinition = new ContentFilterDefinition(); 101 | EventFilterResult eventFilterResult = new EventFilterResult(); 102 | ContentFilterDefinition.parseEventFilter(node.getNodeManager().getNodeManagerTable().getNodeManagerRoot(), 103 | eventFilter, fieldPaths, filterDefinition, eventFilterResult); 104 | if (startTimeDefined || !endTimeDefined) 105 | for (int j = 0; j < events.size(); j++) { 106 | EventData event = events.get(j); 107 | DateTime t = event.getTime(); 108 | final int compareToEnd = endTimeDefined ? t.compareTo(endTime) : -1; 109 | if (compareToEnd > 0) 110 | break; 111 | else { 112 | final int compareToStart = t.compareTo(startTime); 113 | if (compareToStart >= 0) { 114 | if ((i >= firstIndex) && filterDefinition.evaluate(event, true)) 115 | history.add(new HistoryEventFieldList(event.getFieldValues(fieldPaths))); 116 | i++; 117 | if (history.size() == maxValues) 118 | // Return continuation point if no more events exist 119 | // and both timestamps were defined 120 | return endTimeDefined && (j < events.size()) ? i : null; 121 | } 122 | } 123 | } 124 | else 125 | // !startTimeDefined && endTimeDefined 126 | for (int j = events.size() - 1; j >= 0; j--) { 127 | EventData event = events.get(j); 128 | DateTime t = event.getTime(); 129 | final int compareToEnd = t.compareTo(endTime); 130 | if (compareToEnd > 0) 131 | continue; 132 | else { 133 | if (i >= firstIndex) 134 | history.add(new HistoryEventFieldList(event.getFieldValues(fieldPaths))); 135 | i++; 136 | if (history.size() == maxValues) 137 | // Return continuation point if no more events exist and 138 | // both timestamps were defined 139 | return startTimeDefined && (j > 0) ? i : null; 140 | } 141 | } 142 | return null; 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /opc-ua-client/src/main/java/MyUaClientListener.java: -------------------------------------------------------------------------------- 1 | import com.prosysopc.ua.client.ConnectException; 2 | import com.prosysopc.ua.client.UaClient; 3 | import com.prosysopc.ua.client.UaClientListener; 4 | import org.opcfoundation.ua.application.Session; 5 | import org.opcfoundation.ua.builtintypes.DateTime; 6 | import org.opcfoundation.ua.core.MessageSecurityMode; 7 | import org.opcfoundation.ua.core.PublishRequest; 8 | import org.opcfoundation.ua.core.PublishResponse; 9 | import org.opcfoundation.ua.core.RepublishResponse; 10 | 11 | public class MyUaClientListener implements UaClientListener { 12 | 13 | private static final long ALLOWED_PUBLISHTIME_DIFFERENCE = 3600000; // ms, 14 | // one 15 | // hour 16 | 17 | /** 18 | * Set to true to accept PublishResponses from the future/past of more than 19 | * ALLOWED_PUBLISHTIME_DIFFERENCE 20 | */ 21 | private static boolean publishTimeOverride = false; 22 | 23 | /** 24 | * Unrealistically long session timeout, e.g. one day, in milliseconds 25 | */ 26 | private static final double UNREALISTIC_LONG_TIMEOUT = 86400000; 27 | 28 | /** 29 | * Unrealistically short session timeout, 10 seconds, in milliseconds 30 | */ 31 | private static final double UNREALISTIC_SHORT_TIMEOUT = 10000; 32 | 33 | /* 34 | * (non-Javadoc) 35 | * 36 | * @see 37 | * com.prosysopc.ua.client.UaClientListener#onAfterCreateSessionChannel( 38 | * com.prosysopc.ua.client.UaClient) 39 | */ 40 | @Override 41 | public void onAfterCreateSessionChannel(UaClient client, Session session) throws ConnectException { 42 | Session s = client.getSession(); 43 | if (s.getSessionTimeout() <= UNREALISTIC_SHORT_TIMEOUT) { 44 | pl("The RevisedSessionTimeout is unrealistically short: " + s.getSessionTimeout() 45 | + " ms. Do you still want to connect? y=Yes, anything else is No"); 46 | String input = SampleConsoleClient.readInput(false).toLowerCase(); 47 | if (!input.equals("y")) 48 | throw new ConnectException("Canceled by user", "", null); 49 | } 50 | 51 | if (s.getSessionTimeout() >= UNREALISTIC_LONG_TIMEOUT) { 52 | pl("The RevisedSessionTimeout is unrealistically long: " + s.getSessionTimeout() 53 | + " ms. Do you still want to connect? y=Yes, anything else is No"); 54 | String input = SampleConsoleClient.readInput(false).toLowerCase(); 55 | if (!input.equals("y")) 56 | throw new ConnectException("Canceled by user", "", null); 57 | } 58 | 59 | if ((s.getServerNonce() == null) || (s.getServerNonce().length < 32)) 60 | if (MessageSecurityMode.None != client.getSecurityMode().getMessageSecurityMode()) { 61 | pl("The serverNonce is less than 32 bytes, Do you still want to connect? y=Yes, anything else is No"); 62 | String input = SampleConsoleClient.readInput(false).toLowerCase(); 63 | if (!input.equals("y")) 64 | throw new ConnectException("Canceled by user", "", null); 65 | } 66 | 67 | } 68 | 69 | /* 70 | * (non-Javadoc) 71 | * 72 | * @see com.prosysopc.ua.client.UaClientListener#onBeforePublishRequest(org. 73 | * opcfoundation.ua.core.PublishRequest) 74 | */ 75 | @Override 76 | public void onBeforePublishRequest(UaClient client, PublishRequest publishRequest) { 77 | /* 78 | * Do nothing for now. Saving the request could be implemented here in 79 | * case a comparison to response from validatePublishResponse is wanted 80 | */ 81 | } 82 | 83 | /* 84 | * (non-Javadoc) 85 | * 86 | * @see 87 | * com.prosysopc.ua.client.UaClientListener#validatePublishResponse(org. 88 | * opcfoundation.ua.core.PublishResponse) 89 | */ 90 | @Override 91 | public boolean validatePublishResponse(UaClient client, PublishResponse response) { 92 | return validatePublishTime(response.getNotificationMessage().getPublishTime()); 93 | } 94 | 95 | @Override 96 | public boolean validateRepublishResponse(UaClient client, RepublishResponse response) { 97 | return validatePublishTime(response.getNotificationMessage().getPublishTime()); 98 | } 99 | 100 | /* 101 | * (non-Javadoc) 102 | * 103 | * @see 104 | * com.prosysopc.ua.client.UaClientListener#validateRePublishResponse(org 105 | * .opcfoundation.ua.core.RepublishResponse) 106 | */ 107 | private void pl(String line) { 108 | SampleConsoleClient.println(line); 109 | } 110 | 111 | /** 112 | * @param dateTime 113 | * @return 114 | */ 115 | private boolean validatePublishTime(DateTime publishTime) { 116 | if (publishTimeOverride) 117 | return true; 118 | 119 | /* 120 | * If publishTime is too much into past or future, discard the data 121 | */ 122 | long diff = Math.abs(DateTime.currentTime().getTimeInMillis() - publishTime.getTimeInMillis()); 123 | if ((diff > ALLOWED_PUBLISHTIME_DIFFERENCE) && !publishTime.equals(DateTime.MIN_VALUE) 124 | && !publishTime.equals(DateTime.MAX_VALUE)) { 125 | pl(String.format( 126 | "PublishResponse PublishTime difference to " 127 | + "current time more than allowed, discarding data, (%sms vs %sms)", 128 | diff, ALLOWED_PUBLISHTIME_DIFFERENCE)); 129 | return false; 130 | } 131 | 132 | /* 133 | * CTT, should check if PublishResponse.getResults contains bad 134 | * statuscodes 135 | */ 136 | return true; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.sia.pnoker.opcua 8 | opcua-tools 9 | 1.0-SNAPSHOT 10 | pom 11 | 12 | 13 | opc-ua-server 14 | opc-ua-client 15 | 16 | 17 | 18 | UTF-8 19 | UTF-8 20 | 1.8 21 | 22 | 1.7.13 23 | 1.0-SNAPSHOT 24 | 25 | 26 | 27 | 28 | 29 | org.opcfoundation.ua 30 | opc-ua-stack 31 | 1.02.337.18 32 | 33 | 34 | com.prosysopc.ua 35 | prosys-opc-ua-java-sdk-client-server-evaluation 36 | 2.3.2-781 37 | 38 | 39 | 40 | commons-logging 41 | commons-logging 42 | 1.1.1 43 | 44 | 45 | 46 | 47 | org.bouncycastle 48 | bcpkix-jdk15on 49 | 1.52 50 | 51 | 52 | org.bouncycastle 53 | bcprov-jdk15on 54 | 1.52 55 | 56 | 57 | org.spongycastle 58 | scpkix 59 | 1.52 60 | 61 | 62 | org.spongycastle 63 | scprov 64 | 1.52 65 | 66 | 67 | 68 | 69 | org.apache.httpcomponents 70 | httpclient 71 | 4.2.5 72 | 73 | 74 | org.apache.httpcomponents 75 | httpcore 76 | 4.2.5 77 | 78 | 79 | org.apache.httpcomponents 80 | httpcore-nio 81 | 4.2.5 82 | 83 | 84 | 85 | 86 | org.slf4j 87 | slf4j-api 88 | 1.7.7 89 | 90 | 91 | org.slf4j 92 | slf4j-log4j12 93 | 1.7.7 94 | 95 | 96 | log4j 97 | log4j 98 | 1.2.17 99 | 100 | 101 | 102 | 103 | 104 | 105 | org.apache.maven.plugins 106 | maven-dependency-plugin 107 | 108 | 109 | copy-dependencies 110 | prepare-package 111 | 112 | copy-dependencies 113 | 114 | 115 | ${project.build.directory}/lib 116 | false 117 | false 118 | true 119 | 120 | 121 | 122 | 123 | 124 | maven-assembly-plugin 125 | 126 | 127 | jar-with-dependencies 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | make-assembly 138 | package 139 | 140 | single 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /opc-ua-server/src/main/java/com/prosysopc/ua/samples/MyNodeManagerListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Prosys OPC UA Java SDK 3 | * 4 | * Copyright (c) Prosys PMS Ltd., . 5 | * All rights reserved. 6 | */ 7 | package com.prosysopc.ua.samples; 8 | 9 | import java.util.List; 10 | 11 | import org.opcfoundation.ua.builtintypes.ExpandedNodeId; 12 | import org.opcfoundation.ua.builtintypes.NodeId; 13 | import org.opcfoundation.ua.builtintypes.QualifiedName; 14 | import org.opcfoundation.ua.builtintypes.UnsignedInteger; 15 | import org.opcfoundation.ua.core.AggregateFilterResult; 16 | import org.opcfoundation.ua.core.MonitoringFilter; 17 | import org.opcfoundation.ua.core.MonitoringParameters; 18 | import org.opcfoundation.ua.core.NodeAttributes; 19 | import org.opcfoundation.ua.core.NodeClass; 20 | import org.opcfoundation.ua.core.StatusCodes; 21 | import org.opcfoundation.ua.core.UserTokenType; 22 | import org.opcfoundation.ua.core.ViewDescription; 23 | import org.opcfoundation.ua.utils.NumericRange; 24 | 25 | import com.prosysopc.ua.StatusException; 26 | import com.prosysopc.ua.nodes.UaNode; 27 | import com.prosysopc.ua.nodes.UaReference; 28 | import com.prosysopc.ua.nodes.UaReferenceType; 29 | import com.prosysopc.ua.server.MonitoredDataItem; 30 | import com.prosysopc.ua.server.NodeManagerListener; 31 | import com.prosysopc.ua.server.ServiceContext; 32 | import com.prosysopc.ua.server.Subscription; 33 | 34 | /** 35 | * A sample implementation of a NodeManagerListener 36 | */ 37 | public class MyNodeManagerListener implements NodeManagerListener { 38 | 39 | @Override 40 | public void onAddNode(ServiceContext serviceContext, NodeId parentNodeId, UaNode parent, NodeId nodeId, UaNode node, 41 | NodeClass nodeClass, QualifiedName browseName, NodeAttributes attributes, UaReferenceType referenceType, 42 | ExpandedNodeId typeDefinitionId, UaNode typeDefinition) throws StatusException { 43 | // Notification of a node addition request. 44 | // Note that NodeManagerTable#setNodeManagementEnabled(true) must be 45 | // called to enable these methods. 46 | // Anyway, we just check the user access. 47 | checkUserAccess(serviceContext); 48 | } 49 | 50 | @Override 51 | public void onAddReference(ServiceContext serviceContext, NodeId sourceNodeId, UaNode sourceNode, 52 | ExpandedNodeId targetNodeId, UaNode targetNode, NodeId referenceTypeId, UaReferenceType referenceType, 53 | boolean isForward) throws StatusException { 54 | // Notification of a reference addition request. 55 | // Note that NodeManagerTable#setNodeManagementEnabled(true) must be 56 | // called to enable these methods. 57 | // Anyway, we just check the user access. 58 | checkUserAccess(serviceContext); 59 | } 60 | 61 | @Override 62 | public void onAfterCreateMonitoredDataItem(ServiceContext serviceContext, Subscription subscription, 63 | MonitoredDataItem item) { 64 | // 65 | } 66 | 67 | @Override 68 | public void onAfterDeleteMonitoredDataItem(ServiceContext serviceContext, Subscription subscription, 69 | MonitoredDataItem item) { 70 | // 71 | } 72 | 73 | @Override 74 | public void onAfterModifyMonitoredDataItem(ServiceContext serviceContext, Subscription subscription, 75 | MonitoredDataItem item) { 76 | // 77 | } 78 | 79 | @Override 80 | public boolean onBrowseNode(ServiceContext serviceContext, ViewDescription view, NodeId nodeId, UaNode node, 81 | UaReference reference) { 82 | // Perform custom filtering, for example based on the user 83 | // doing the browse. The method is called separately for each reference. 84 | // Default is to return all references for everyone 85 | return true; 86 | } 87 | 88 | @Override 89 | public void onCreateMonitoredDataItem(ServiceContext serviceContext, Subscription subscription, NodeId nodeId, 90 | UaNode node, UnsignedInteger attributeId, NumericRange indexRange, MonitoringParameters params, 91 | MonitoringFilter filter, AggregateFilterResult filterResult) throws StatusException { 92 | // Notification of a monitored item creation request 93 | 94 | // You may, for example start to monitor the node from a physical 95 | // device, only once you get a request for it from a client 96 | } 97 | 98 | @Override 99 | public void onDeleteMonitoredDataItem(ServiceContext serviceContext, Subscription subscription, 100 | MonitoredDataItem monitoredItem) { 101 | // Notification of a monitored item delete request 102 | } 103 | 104 | @Override 105 | public void onDeleteNode(ServiceContext serviceContext, NodeId nodeId, UaNode node, boolean deleteTargetReferences) 106 | throws StatusException { 107 | // Notification of a node deletion request. 108 | // Note that NodeManagerTable#setNodeManagementEnabled(true) must be 109 | // called to enable these methods. 110 | // Anyway, we just check the user access. 111 | checkUserAccess(serviceContext); 112 | } 113 | 114 | @Override 115 | public void onDeleteReference(ServiceContext serviceContext, NodeId sourceNodeId, UaNode sourceNode, 116 | ExpandedNodeId targetNodeId, UaNode targetNode, NodeId referenceTypeId, UaReferenceType referenceType, 117 | boolean isForward, boolean deleteBidirectional) throws StatusException { 118 | // Notification of a reference deletion request. 119 | // Note that NodeManagerTable#setNodeManagementEnabled(true) must be 120 | // called to enable these methods. 121 | // Anyway, we just check the user access. 122 | checkUserAccess(serviceContext); 123 | } 124 | 125 | @Override 126 | public void onGetReferences(ServiceContext serviceContext, ViewDescription viewDescription, NodeId nodeId, 127 | UaNode node, List references) { 128 | // Add custom references that are not defined in the nodes here. 129 | // Useful for non-UaNode-based node managers - or references. 130 | } 131 | 132 | @Override 133 | public void onModifyMonitoredDataItem(ServiceContext serviceContext, Subscription subscription, 134 | MonitoredDataItem item, UaNode node, MonitoringParameters params, MonitoringFilter filter, 135 | AggregateFilterResult filterResult) { 136 | // Notification of a monitored item modification request 137 | } 138 | 139 | private void checkUserAccess(ServiceContext serviceContext) throws StatusException { 140 | // Do not allow for anonymous users 141 | if (serviceContext.getSession().getUserIdentity().getType().equals(UserTokenType.Anonymous)) 142 | throw new StatusException(StatusCodes.Bad_UserAccessDenied); 143 | } 144 | }; 145 | -------------------------------------------------------------------------------- /opc-ua-server/src/main/java/com/prosysopc/ua/samples/ValueHistory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Prosys OPC UA Java SDK 3 | * 4 | * Copyright (c) Prosys PMS Ltd., . 5 | * All rights reserved. 6 | */ 7 | package com.prosysopc.ua.samples; 8 | 9 | import java.util.List; 10 | import java.util.concurrent.CopyOnWriteArrayList; 11 | 12 | import org.opcfoundation.ua.builtintypes.DataValue; 13 | import org.opcfoundation.ua.builtintypes.DateTime; 14 | import org.opcfoundation.ua.builtintypes.DiagnosticInfo; 15 | import org.opcfoundation.ua.builtintypes.StatusCode; 16 | import org.opcfoundation.ua.builtintypes.UnsignedShort; 17 | import org.opcfoundation.ua.core.StatusCodes; 18 | 19 | import com.prosysopc.ua.StatusException; 20 | import com.prosysopc.ua.nodes.DataChangeListener; 21 | import com.prosysopc.ua.nodes.UaNode; 22 | import com.prosysopc.ua.nodes.UaVariable; 23 | import com.prosysopc.ua.server.nodes.UaVariableNode; 24 | 25 | /** 26 | * A sample class for keeping a history of a variable node. 27 | */ 28 | class ValueHistory { 29 | private int capacity = 10000; 30 | private final DataChangeListener listener = new DataChangeListener() { 31 | 32 | @Override 33 | public void onDataChange(UaNode uaNode, DataValue prevValue, DataValue value) { 34 | values.add(value); 35 | while (values.size() > capacity) 36 | values.remove(0); 37 | } 38 | }; 39 | private final List values = new CopyOnWriteArrayList(); 40 | private final UaVariable variable; 41 | 42 | public ValueHistory(UaVariableNode variable) { 43 | super(); 44 | this.variable = variable; 45 | variable.addDataChangeListener(listener); 46 | } 47 | 48 | /** 49 | * @param reqTimes 50 | * @param operationResults 51 | * @param operationDiagnostics 52 | */ 53 | public void deleteAtTimes(DateTime[] reqTimes, StatusCode[] operationResults, 54 | DiagnosticInfo[] operationDiagnostics) { 55 | for (int i = 0; i < reqTimes.length; i++) 56 | try { 57 | deleteAtTime(reqTimes[i]); 58 | operationResults[i] = StatusCode.GOOD; 59 | } catch (StatusException e) { 60 | operationResults[i] = e.getStatusCode(); 61 | operationDiagnostics[i] = e.getDiagnosticInfo(); 62 | } 63 | } 64 | 65 | /** 66 | * @param startTime 67 | * @param endTime 68 | * @throws StatusException 69 | */ 70 | public void deleteRaw(DateTime startTime, DateTime endTime) throws StatusException { 71 | int i = 0; 72 | // boolean startTimeDefined = startTime.compareTo(DateTime.MIN_VALUE) > 73 | // 0; 74 | boolean endTimeDefined = endTime.compareTo(DateTime.MIN_VALUE) > 0; 75 | if (!endTimeDefined) 76 | throw new StatusException(StatusCodes.Bad_InvalidArgument); 77 | while (values.size() > i) { 78 | DataValue value = values.get(i); 79 | DateTime t = value.getSourceTimestamp(); 80 | if (t == null) 81 | t = value.getServerTimestamp(); 82 | if (t.compareTo(startTime) >= 0) 83 | values.remove(i); 84 | else if (t.compareTo(endTime) >= 0) 85 | break; 86 | else 87 | i++; 88 | } 89 | } 90 | 91 | public int getCapacity() { 92 | return capacity; 93 | } 94 | 95 | /** 96 | * @return the variable 97 | */ 98 | public UaVariable getVariable() { 99 | return variable; 100 | } 101 | 102 | /** 103 | * @param reqTimes 104 | * @return 105 | */ 106 | public DataValue[] readAtTimes(DateTime[] reqTimes) { 107 | if (reqTimes == null) 108 | return null; 109 | DataValue[] values = new DataValue[reqTimes.length]; 110 | for (int i = 0; i < reqTimes.length; i++) { 111 | DateTime t = reqTimes[i]; 112 | // Stepped interpolation used to get values 113 | DataValue v = getValue(t); 114 | values[i] = new DataValue(v == null ? null : v.getValue(), 115 | v == null ? new StatusCode(StatusCodes.Bad_NoData) : v.getStatusCode(), t, UnsignedShort.ZERO, null, 116 | null); 117 | } 118 | return values; 119 | 120 | } 121 | 122 | /** 123 | * Get the values from the history that are between startTime and endTime. 124 | * 125 | * @param startTime 126 | * the start of the interval 127 | * @param endTime 128 | * the end of the interval 129 | * @param maxValues 130 | * maximum number of values to return 131 | * @param returnBounds 132 | * whether values at the ends of the interval should be returned 133 | * as well 134 | * @param firstIndex 135 | * the index of the first entry in the history data to return 136 | * (i.e. the continuationPoint returned for the previous request) 137 | * @param history 138 | * the list of values to fill in 139 | * @return the first index that was not added to the history, in case there 140 | * are more than maxValues entries to return (i.e. the 141 | * continuationPoint to return) 142 | */ 143 | public Integer readRaw(DateTime startTime, DateTime endTime, int maxValues, boolean returnBounds, int firstIndex, 144 | List history) { 145 | int i = 0; 146 | boolean startTimeDefined = startTime.compareTo(DateTime.MIN_VALUE) > 0; 147 | boolean endTimeDefined = endTime.compareTo(DateTime.MIN_VALUE) > 0; 148 | if (startTimeDefined || !endTimeDefined) 149 | for (DataValue value : values) { 150 | DateTime t = value.getSourceTimestamp(); 151 | if (t == null) 152 | t = value.getServerTimestamp(); 153 | final int compareToEnd = endTimeDefined ? t.compareTo(endTime) : -1; 154 | if ((compareToEnd > 0) || (!returnBounds && (compareToEnd == 0))) 155 | break; 156 | else { 157 | final int compareToStart = t.compareTo(startTime); 158 | if ((compareToStart > 0) || (returnBounds && (compareToStart == 0))) { 159 | if (i >= firstIndex) 160 | history.add(value); 161 | i++; 162 | if (history.size() == maxValues) 163 | return i; 164 | } 165 | } 166 | } 167 | else 168 | // !startTimeDefined && endTimeDefined 169 | for (int j = values.size() - 1; j >= 0; j--) { 170 | DataValue value = values.get(j); 171 | DateTime t = value.getSourceTimestamp(); 172 | if (t == null) 173 | t = value.getServerTimestamp(); 174 | final int compareToEnd = t.compareTo(endTime); 175 | if ((compareToEnd > 0) || (!returnBounds && (compareToEnd == 0))) 176 | continue; 177 | else { 178 | if (i >= firstIndex) 179 | history.add(value); 180 | i++; 181 | if (history.size() == maxValues) 182 | return i; 183 | } 184 | } 185 | return null; 186 | } 187 | 188 | /** 189 | * @param capacity 190 | * the capacity to set 191 | */ 192 | public void setCapacity(int capacity) { 193 | if (capacity < 0) 194 | throw new IllegalArgumentException("capacity must be a positive value"); 195 | this.capacity = capacity; 196 | } 197 | 198 | /** 199 | * Delete a single entry from the history 200 | * 201 | * @param timestamp 202 | * the sourceTimestamp to look for 203 | * @throws StatusException 204 | * if no sample with the given timestamp is found 205 | */ 206 | private void deleteAtTime(DateTime timestamp) throws StatusException { 207 | boolean found = false; 208 | for (int i = values.size() - 1; i >= 0; i--) { 209 | int compareTo = timestamp.compareTo(values.get(i).getSourceTimestamp()); 210 | if (compareTo == 0) { 211 | values.remove(i); 212 | found = true; 213 | } else if (compareTo < 0) 214 | break; 215 | } 216 | if (!found) 217 | throw new StatusException(StatusCodes.Bad_NoData); 218 | 219 | } 220 | 221 | /** 222 | * Find the value at the given time from the history using stepped 223 | * interpolation. 224 | * 225 | * @param requestedTime 226 | * the requested time for the value 227 | * @return the last value with a smaller or equal timestamp than the 228 | * requestedTime 229 | */ 230 | private DataValue getValue(DateTime requestedTime) { 231 | // a "brute" find starting from the end 232 | int i = values.size() - 1; 233 | while ((i >= 0) && (values.get(i).getSourceTimestamp().compareTo(requestedTime) > 0)) 234 | i--; 235 | return i < 0 ? null : values.get(i); 236 | } 237 | } -------------------------------------------------------------------------------- /opc-ua-server/src/main/java/com/prosysopc/ua/samples/MyEventManagerListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Prosys OPC UA Java SDK 3 | * 4 | * Copyright (c) Prosys PMS Ltd., . 5 | * All rights reserved. 6 | */ 7 | package com.prosysopc.ua.samples; 8 | 9 | import java.util.Arrays; 10 | 11 | import org.opcfoundation.ua.builtintypes.DateTime; 12 | import org.opcfoundation.ua.builtintypes.LocalizedText; 13 | import org.opcfoundation.ua.builtintypes.NodeId; 14 | import org.opcfoundation.ua.core.EventFilter; 15 | import org.opcfoundation.ua.core.EventFilterResult; 16 | import org.opcfoundation.ua.core.StatusCodes; 17 | 18 | import com.prosysopc.ua.StatusException; 19 | import com.prosysopc.ua.server.EventManager; 20 | import com.prosysopc.ua.server.EventManagerListener; 21 | import com.prosysopc.ua.server.MonitoredEventItem; 22 | import com.prosysopc.ua.server.ServiceContext; 23 | import com.prosysopc.ua.server.Subscription; 24 | import com.prosysopc.ua.types.opcua.server.AcknowledgeableConditionTypeNode; 25 | import com.prosysopc.ua.types.opcua.server.AlarmConditionTypeNode; 26 | import com.prosysopc.ua.types.opcua.server.ConditionTypeNode; 27 | import com.prosysopc.ua.types.opcua.server.ShelvedStateMachineTypeNode; 28 | 29 | /** 30 | * A sample implementation of an EventManagerListener 31 | */ 32 | public class MyEventManagerListener implements EventManagerListener { 33 | 34 | private int eventId = 0; 35 | 36 | @Override 37 | public boolean onAcknowledge(ServiceContext serviceContext, AcknowledgeableConditionTypeNode condition, 38 | byte[] eventId, LocalizedText comment) throws StatusException { 39 | // Handle acknowledge request to a condition event 40 | println("Acknowledge: Condition=" + condition + "; EventId=" + eventIdToString(eventId) + "; Comment=" 41 | + comment); 42 | // If the acknowledged event is no longer active, return an error 43 | if (!Arrays.equals(eventId, condition.getEventId())) 44 | throw new StatusException(StatusCodes.Bad_EventIdUnknown); 45 | if (condition.isAcked()) 46 | throw new StatusException(StatusCodes.Bad_ConditionBranchAlreadyAcked); 47 | 48 | final DateTime now = DateTime.currentTime(); 49 | condition.setAcked(true, now); 50 | final byte[] userEventId = getNextUserEventId(); 51 | // addComment triggers a new event 52 | condition.addComment(eventId, comment, now, userEventId); 53 | return true; // Handled here 54 | // NOTE: If you do not handle acknowledge here, and return false, 55 | // the EventManager (or MethodManager) will call 56 | // condition.acknowledge, which performs the same actions as this 57 | // handler, except for setting Retain 58 | } 59 | 60 | @Override 61 | public boolean onAddComment(ServiceContext serviceContext, ConditionTypeNode condition, byte[] eventId, 62 | LocalizedText comment) throws StatusException { 63 | // Handle add command request to a condition event 64 | println("AddComment: Condition=" + condition + "; Event=" + eventIdToString(eventId) + "; Comment=" + comment); 65 | // Only the current eventId can get comments 66 | if (!Arrays.equals(eventId, condition.getEventId())) 67 | throw new StatusException(StatusCodes.Bad_EventIdUnknown); 68 | // triggers a new event 69 | final byte[] userEventId = getNextUserEventId(); 70 | condition.addComment(eventId, comment, DateTime.currentTime(), userEventId); 71 | return true; // Handled here 72 | // NOTE: If you do not handle addComment here, and return false, 73 | // the EventManager (or MethodManager) will call 74 | // condition.addComment automatically 75 | } 76 | 77 | @Override 78 | public void onAfterCreateMonitoredEventItem(ServiceContext serviceContext, Subscription subscription, 79 | MonitoredEventItem item) { 80 | // 81 | } 82 | 83 | @Override 84 | public void onAfterDeleteMonitoredEventItem(ServiceContext serviceContext, Subscription subscription, 85 | MonitoredEventItem item) { 86 | // 87 | } 88 | 89 | @Override 90 | public void onAfterModifyMonitoredEventItem(ServiceContext serviceContext, Subscription subscription, 91 | MonitoredEventItem item) { 92 | // 93 | } 94 | 95 | @Override 96 | public void onConditionRefresh(ServiceContext serviceContext, Subscription subscription) throws StatusException { 97 | // 98 | } 99 | 100 | @Override 101 | public boolean onConfirm(ServiceContext serviceContext, AcknowledgeableConditionTypeNode condition, byte[] eventId, 102 | LocalizedText comment) throws StatusException { 103 | // Handle confirm request to a condition event 104 | println("Confirm: Condition=" + condition + "; EventId=" + eventIdToString(eventId) + "; Comment=" + comment); 105 | // If the confirmed event is no longer active, return an error 106 | if (!Arrays.equals(eventId, condition.getEventId())) 107 | throw new StatusException(StatusCodes.Bad_EventIdUnknown); 108 | if (condition.isConfirmed()) 109 | throw new StatusException(StatusCodes.Bad_ConditionBranchAlreadyConfirmed); 110 | if (!condition.isAcked()) 111 | throw new StatusException("Condition can only be confirmed when it is acknowledged.", 112 | StatusCodes.Bad_InvalidState); 113 | // If the condition is no longer active, set retain to false, i.e. 114 | // remove it from the visible alarms 115 | if (!(condition instanceof AlarmConditionTypeNode) || !((AlarmConditionTypeNode) condition).isActive()) 116 | condition.setRetain(false); 117 | 118 | final DateTime now = DateTime.currentTime(); 119 | condition.setConfirmed(true, now); 120 | final byte[] userEventId = getNextUserEventId(); 121 | // addComment triggers a new event 122 | condition.addComment(eventId, comment, now, userEventId); 123 | return true; // Handled here 124 | // NOTE: If you do not handle Confirm here, and return false, 125 | // the EventManager (or MethodManager) will call 126 | // condition.confirm, which performs the same actions as this 127 | // handler 128 | } 129 | 130 | @Override 131 | public void onCreateMonitoredEventItem(ServiceContext serviceContext, NodeId nodeId, EventFilter eventFilter, 132 | EventFilterResult filterResult) throws StatusException { 133 | // Item created 134 | } 135 | 136 | @Override 137 | public void onDeleteMonitoredEventItem(ServiceContext serviceContext, Subscription subscription, 138 | MonitoredEventItem monitoredItem) { 139 | // Stop monitoring the item? 140 | } 141 | 142 | @Override 143 | public boolean onDisable(ServiceContext serviceContext, ConditionTypeNode condition) throws StatusException { 144 | // Handle disable request to a condition 145 | println("Disable: Condition=" + condition); 146 | if (condition.isEnabled()) { 147 | DateTime now = DateTime.currentTime(); 148 | // Setting enabled to false, also sets retain to false 149 | condition.setEnabled(false, now); 150 | // notify the clients of the change 151 | condition.triggerEvent(now, null, getNextUserEventId()); 152 | } 153 | return true; // Handled here 154 | // NOTE: If you do not handle disable here, and return false, 155 | // the EventManager (or MethodManager) will request the 156 | // condition to handle the call, and it will unset the enabled 157 | // state, and triggers a new notification event, as here 158 | } 159 | 160 | @Override 161 | public boolean onEnable(ServiceContext serviceContext, ConditionTypeNode condition) throws StatusException { 162 | // Handle enable request to a condition 163 | println("Enable: Condition=" + condition); 164 | if (!condition.isEnabled()) { 165 | DateTime now = DateTime.currentTime(); 166 | condition.setEnabled(true, now); 167 | // You should evaluate the condition now, set Retain to true, 168 | // if necessary and in that case also call triggerEvent 169 | // condition.setRetain(true); 170 | // condition.triggerEvent(now, null, getNextUserEventId()); 171 | } 172 | return true; // Handled here 173 | // NOTE: If you do not handle enable here, and return false, 174 | // the EventManager (or MethodManager) will request the 175 | // condition to handle the call, and it will set the enabled 176 | // state. 177 | 178 | // You should however set the status of the condition yourself 179 | // and trigger a new event if necessary 180 | } 181 | 182 | @Override 183 | public void onModifyMonitoredEventItem(ServiceContext serviceContext, Subscription subscription, 184 | MonitoredEventItem monitoredItem, EventFilter eventFilter, EventFilterResult filterResult) 185 | throws StatusException { 186 | // Modify event monitoring, when the client modifies a monitored 187 | // item 188 | } 189 | 190 | @Override 191 | public boolean onOneshotShelve(ServiceContext serviceContext, AlarmConditionTypeNode condition, 192 | ShelvedStateMachineTypeNode stateMachine) throws StatusException { 193 | return false; 194 | } 195 | 196 | @Override 197 | public boolean onTimedShelve(ServiceContext serviceContext, AlarmConditionTypeNode condition, 198 | ShelvedStateMachineTypeNode stateMachine, double shelvingTime) throws StatusException { 199 | return false; 200 | } 201 | 202 | @Override 203 | public boolean onUnshelve(ServiceContext serviceContext, AlarmConditionTypeNode condition, 204 | ShelvedStateMachineTypeNode stateMachine) throws StatusException { 205 | return false; 206 | } 207 | 208 | private String eventIdToString(byte[] eventId) { 209 | return eventId == null ? "(null)" : Arrays.toString(eventId); 210 | } 211 | 212 | /** 213 | * @param string 214 | */ 215 | private void println(String string) { 216 | MyNodeManager.println(string); 217 | } 218 | 219 | /** 220 | * @return 221 | * @throws RuntimeException 222 | */ 223 | byte[] getNextUserEventId() throws RuntimeException { 224 | return EventManager.createEventId(eventId++); 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /LICENSE/LICENSE.apache2.0: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /opc-ua-server/src/main/java/com/prosysopc/ua/samples/MyHistorian.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Prosys OPC UA Java SDK 3 | * 4 | * Copyright (c) Prosys PMS Ltd., . 5 | * All rights reserved. 6 | */ 7 | package com.prosysopc.ua.samples; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.EnumSet; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | import org.opcfoundation.ua.builtintypes.DataValue; 17 | import org.opcfoundation.ua.builtintypes.DateTime; 18 | import org.opcfoundation.ua.builtintypes.DiagnosticInfo; 19 | import org.opcfoundation.ua.builtintypes.NodeId; 20 | import org.opcfoundation.ua.builtintypes.StatusCode; 21 | import org.opcfoundation.ua.builtintypes.UnsignedInteger; 22 | import org.opcfoundation.ua.builtintypes.Variant; 23 | import org.opcfoundation.ua.core.AccessLevel; 24 | import org.opcfoundation.ua.core.AggregateConfiguration; 25 | import org.opcfoundation.ua.core.EventFilter; 26 | import org.opcfoundation.ua.core.HistoryData; 27 | import org.opcfoundation.ua.core.HistoryEvent; 28 | import org.opcfoundation.ua.core.HistoryEventFieldList; 29 | import org.opcfoundation.ua.core.HistoryModifiedData; 30 | import org.opcfoundation.ua.core.HistoryReadDetails; 31 | import org.opcfoundation.ua.core.HistoryReadValueId; 32 | import org.opcfoundation.ua.core.HistoryUpdateDetails; 33 | import org.opcfoundation.ua.core.HistoryUpdateResult; 34 | import org.opcfoundation.ua.core.PerformUpdateType; 35 | import org.opcfoundation.ua.core.StatusCodes; 36 | import org.opcfoundation.ua.core.TimestampsToReturn; 37 | import org.opcfoundation.ua.utils.NumericRange; 38 | import org.slf4j.Logger; 39 | import org.slf4j.LoggerFactory; 40 | 41 | import com.prosysopc.ua.EventNotifierClass; 42 | import com.prosysopc.ua.ServiceException; 43 | import com.prosysopc.ua.StatusException; 44 | import com.prosysopc.ua.nodes.UaNode; 45 | import com.prosysopc.ua.server.HistoryContinuationPoint; 46 | import com.prosysopc.ua.server.HistoryManagerListener; 47 | import com.prosysopc.ua.server.HistoryResult; 48 | import com.prosysopc.ua.server.ServiceContext; 49 | import com.prosysopc.ua.server.nodes.UaObjectNode; 50 | import com.prosysopc.ua.server.nodes.UaVariableNode; 51 | 52 | /** 53 | * A sample implementation of a data historian. 54 | *

55 | * It is implemented as a HistoryManagerListener. It could as well be a 56 | * HistoryManager, instead. 57 | */ 58 | public class MyHistorian implements HistoryManagerListener { 59 | private static Logger logger = LoggerFactory.getLogger(MyHistorian.class); 60 | private final Map eventHistories = new HashMap(); 61 | 62 | // The variable histories 63 | private final Map variableHistories = new HashMap(); 64 | 65 | public MyHistorian() { 66 | super(); 67 | } 68 | 69 | /** 70 | * Add the object to the historian for event history. 71 | *

72 | * The historian will mark it to contain history (in EventNotifier 73 | * attribute) and it will start monitoring events for it. 74 | * 75 | * @param node 76 | * the object to initialize 77 | */ 78 | public void addEventHistory(UaObjectNode node) { 79 | EventHistory history = new EventHistory(node); 80 | // History can be read 81 | EnumSet eventNotifier = node.getEventNotifier(); 82 | eventNotifier.add(EventNotifierClass.HistoryRead); 83 | node.setEventNotifier(eventNotifier); 84 | 85 | eventHistories.put(node, history); 86 | } 87 | 88 | /** 89 | * Add the variable to the historian. 90 | *

91 | * The historian will mark it to be historized and it will start monitoring 92 | * value changes for it. 93 | * 94 | * @param variable 95 | * the variable to initialize 96 | */ 97 | public void addVariableHistory(UaVariableNode variable) { 98 | ValueHistory history = new ValueHistory(variable); 99 | // History is being collected 100 | variable.setHistorizing(true); 101 | // History can be read 102 | final EnumSet READ_WRITE_HISTORYREAD = EnumSet.of(AccessLevel.CurrentRead, 103 | AccessLevel.CurrentWrite, AccessLevel.HistoryRead); 104 | variable.setAccessLevel(READ_WRITE_HISTORYREAD); 105 | variableHistories.put(variable, history); 106 | } 107 | 108 | @Override 109 | public Object onBeginHistoryRead(ServiceContext serviceContext, HistoryReadDetails details, 110 | TimestampsToReturn timestampsToReturn, HistoryReadValueId[] nodesToRead, 111 | HistoryContinuationPoint[] continuationPoints, HistoryResult[] results) throws ServiceException { 112 | return null; 113 | } 114 | 115 | @Override 116 | public Object onBeginHistoryUpdate(ServiceContext serviceContext, HistoryUpdateDetails[] details, 117 | HistoryUpdateResult[] results, DiagnosticInfo[] diagnosticInfos) throws ServiceException { 118 | return null; 119 | } 120 | 121 | @Override 122 | public void onDeleteAtTimes(ServiceContext serviceContext, Object operationContext, NodeId nodeId, UaNode node, 123 | DateTime[] reqTimes, StatusCode[] operationResults, DiagnosticInfo[] operationDiagnostics) 124 | throws StatusException { 125 | ValueHistory history = variableHistories.get(node); 126 | if (history != null) 127 | history.deleteAtTimes(reqTimes, operationResults, operationDiagnostics); 128 | else 129 | throw new StatusException(StatusCodes.Bad_NoData); 130 | } 131 | 132 | @Override 133 | public void onDeleteEvents(ServiceContext serviceContext, Object operationContext, NodeId nodeId, UaNode node, 134 | byte[][] eventIds, StatusCode[] operationResults, DiagnosticInfo[] operationDiagnostics) 135 | throws StatusException { 136 | EventHistory history = eventHistories.get(node); 137 | if (history != null) 138 | history.deleteEvents(eventIds, operationResults, operationDiagnostics); 139 | else 140 | throw new StatusException(StatusCodes.Bad_NoData); 141 | } 142 | 143 | @Override 144 | public void onDeleteModified(ServiceContext serviceContext, Object operationContext, NodeId nodeId, UaNode node, 145 | DateTime startTime, DateTime endTime) throws StatusException { 146 | throw new StatusException(StatusCodes.Bad_HistoryOperationUnsupported); 147 | } 148 | 149 | @Override 150 | public void onDeleteRaw(ServiceContext serviceContext, Object operationContext, NodeId nodeId, UaNode node, 151 | DateTime startTime, DateTime endTime) throws StatusException { 152 | ValueHistory history = variableHistories.get(node); 153 | if (history != null) 154 | history.deleteRaw(startTime, endTime); 155 | else 156 | throw new StatusException(StatusCodes.Bad_NoData); 157 | } 158 | 159 | @Override 160 | public void onEndHistoryRead(ServiceContext serviceContext, Object operationContext, HistoryReadDetails details, 161 | TimestampsToReturn timestampsToReturn, HistoryReadValueId[] nodesToRead, 162 | HistoryContinuationPoint[] continuationPoints, HistoryResult[] results) throws ServiceException { 163 | } 164 | 165 | @Override 166 | public void onEndHistoryUpdate(ServiceContext serviceContext, Object operationContext, 167 | HistoryUpdateDetails[] details, HistoryUpdateResult[] results, DiagnosticInfo[] diagnosticInfos) 168 | throws ServiceException { 169 | } 170 | 171 | @Override 172 | public Object onReadAtTimes(ServiceContext serviceContext, Object operationContext, 173 | TimestampsToReturn timestampsToReturn, NodeId nodeId, UaNode node, Object continuationPoint, 174 | DateTime[] reqTimes, NumericRange indexRange, HistoryData historyData) throws StatusException { 175 | if (logger.isDebugEnabled()) 176 | logger.debug("onReadAtTimes: reqTimes=[" + reqTimes.length + "] " 177 | + ((reqTimes.length < 20) ? Arrays.toString(reqTimes) : "")); 178 | ValueHistory history = variableHistories.get(node); 179 | if (history != null) 180 | historyData.setDataValues(history.readAtTimes(reqTimes)); 181 | else 182 | throw new StatusException(StatusCodes.Bad_NoData); 183 | return null; 184 | } 185 | 186 | @Override 187 | public Object onReadEvents(ServiceContext serviceContext, Object operationContext, NodeId nodeId, UaNode node, 188 | Object continuationPoint, DateTime startTime, DateTime endTime, UnsignedInteger numValuesPerNode, 189 | EventFilter filter, HistoryEvent historyEvent) throws StatusException { 190 | EventHistory history = eventHistories.get(node); 191 | if (history != null) { 192 | List events = new ArrayList(); 193 | int firstIndex = continuationPoint == null ? 0 : (Integer) continuationPoint; 194 | Integer newContinuationPoint = history.readEvents(startTime, endTime, numValuesPerNode.intValue(), filter, 195 | events, firstIndex); 196 | historyEvent.setEvents(events.toArray(new HistoryEventFieldList[events.size()])); 197 | return newContinuationPoint; 198 | } else 199 | throw new StatusException(StatusCodes.Bad_NoData); 200 | } 201 | 202 | @Override 203 | public Object onReadModified(ServiceContext serviceContext, Object operationContext, 204 | TimestampsToReturn timestampsToReturn, NodeId nodeId, UaNode node, Object continuationPoint, 205 | DateTime startTime, DateTime endTime, UnsignedInteger numValuesPerNode, NumericRange indexRange, 206 | HistoryModifiedData historyData) throws StatusException { 207 | throw new StatusException(StatusCodes.Bad_HistoryOperationUnsupported); 208 | } 209 | 210 | @Override 211 | public Object onReadProcessed(ServiceContext serviceContext, Object operationContext, 212 | TimestampsToReturn timestampsToReturn, NodeId nodeId, UaNode node, Object continuationPoint, 213 | DateTime startTime, DateTime endTime, Double processingInterval, NodeId aggregateType, 214 | AggregateConfiguration aggregateConfiguration, NumericRange indexRange, HistoryData historyData) 215 | throws StatusException { 216 | logger.debug("onReadProcessed: nodeId={}, startTime={}, endime={}, processingInterval={}", nodeId, startTime, 217 | endTime, processingInterval); 218 | throw new StatusException(StatusCodes.Bad_HistoryOperationUnsupported); 219 | } 220 | 221 | @Override 222 | public Object onReadRaw(ServiceContext serviceContext, Object operationContext, 223 | TimestampsToReturn timestampsToReturn, NodeId nodeId, UaNode node, Object continuationPoint, 224 | DateTime startTime, DateTime endTime, UnsignedInteger numValuesPerNode, Boolean returnBounds, 225 | NumericRange indexRange, HistoryData historyData) throws StatusException { 226 | logger.debug("onReadRaw: startTime={} endTime={} numValuesPerNode={}", startTime, endTime, numValuesPerNode); 227 | ValueHistory history = variableHistories.get(node); 228 | if (history != null) { 229 | List values = new ArrayList(); 230 | int firstIndex = continuationPoint == null ? 0 : (Integer) continuationPoint; 231 | Integer newContinuationPoint = history.readRaw(startTime, endTime, numValuesPerNode.intValue(), 232 | returnBounds, firstIndex, values); 233 | historyData.setDataValues(values.toArray(new DataValue[values.size()])); 234 | return newContinuationPoint; 235 | } 236 | return null; 237 | } 238 | 239 | @Override 240 | public void onUpdateData(ServiceContext serviceContext, Object operationContext, NodeId nodeId, UaNode node, 241 | DataValue[] updateValues, PerformUpdateType performInsertReplace, StatusCode[] operationResults, 242 | DiagnosticInfo[] operationDiagnostics) throws StatusException { 243 | throw new StatusException(StatusCodes.Bad_HistoryOperationUnsupported); 244 | } 245 | 246 | @Override 247 | public void onUpdateEvent(ServiceContext serviceContext, Object operationContext, NodeId nodeId, UaNode node, 248 | Variant[] eventFields, EventFilter filter, PerformUpdateType performInsertReplace, 249 | StatusCode[] operationResults, DiagnosticInfo[] operationDiagnostics) throws StatusException { 250 | throw new StatusException(StatusCodes.Bad_HistoryOperationUnsupported); 251 | } 252 | 253 | @Override 254 | public void onUpdateStructureData(ServiceContext serviceContext, Object operationContext, NodeId nodeId, 255 | UaNode node, DataValue[] updateValues, PerformUpdateType performUpdateType, StatusCode[] operationResults, 256 | DiagnosticInfo[] operationDiagnostics) throws StatusException { 257 | throw new StatusException(StatusCodes.Bad_HistoryOperationUnsupported); 258 | } 259 | 260 | }; 261 | -------------------------------------------------------------------------------- /opc-ua-server/src/main/java/com/prosysopc/ua/samples/MyNodeManager.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Prosys OPC UA Java SDK 3 | * 4 | * Copyright (c) Prosys PMS Ltd., . 5 | * All rights reserved. 6 | */ 7 | package com.prosysopc.ua.samples; 8 | 9 | import java.util.Locale; 10 | import java.util.Random; 11 | import java.util.UUID; 12 | 13 | import org.opcfoundation.ua.builtintypes.DataValue; 14 | import org.opcfoundation.ua.builtintypes.DateTime; 15 | import org.opcfoundation.ua.builtintypes.LocalizedText; 16 | import org.opcfoundation.ua.builtintypes.NodeId; 17 | import org.opcfoundation.ua.builtintypes.QualifiedName; 18 | import org.opcfoundation.ua.builtintypes.UnsignedInteger; 19 | import org.opcfoundation.ua.core.AccessLevel; 20 | import org.opcfoundation.ua.core.Argument; 21 | import org.opcfoundation.ua.core.Identifiers; 22 | import org.opcfoundation.ua.core.NodeClass; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | import com.prosysopc.ua.StatusException; 27 | import com.prosysopc.ua.ValueRanks; 28 | import com.prosysopc.ua.nodes.UaDataType; 29 | import com.prosysopc.ua.nodes.UaNode; 30 | import com.prosysopc.ua.nodes.UaNodeFactoryException; 31 | import com.prosysopc.ua.nodes.UaObject; 32 | import com.prosysopc.ua.nodes.UaObjectType; 33 | import com.prosysopc.ua.nodes.UaType; 34 | import com.prosysopc.ua.nodes.UaVariable; 35 | import com.prosysopc.ua.server.CallableListener; 36 | import com.prosysopc.ua.server.MethodManagerUaNode; 37 | import com.prosysopc.ua.server.ModellingRule; 38 | import com.prosysopc.ua.server.NodeManagerUaNode; 39 | import com.prosysopc.ua.server.UaInstantiationException; 40 | import com.prosysopc.ua.server.UaServer; 41 | import com.prosysopc.ua.server.nodes.CacheVariable; 42 | import com.prosysopc.ua.server.nodes.PlainMethod; 43 | import com.prosysopc.ua.server.nodes.PlainProperty; 44 | import com.prosysopc.ua.server.nodes.PlainVariable; 45 | import com.prosysopc.ua.server.nodes.UaDataTypeNode; 46 | import com.prosysopc.ua.server.nodes.UaObjectNode; 47 | import com.prosysopc.ua.server.nodes.UaObjectTypeNode; 48 | import com.prosysopc.ua.server.nodes.UaVariableNode; 49 | import com.prosysopc.ua.types.opcua.server.BaseEventTypeNode; 50 | import com.prosysopc.ua.types.opcua.server.ExclusiveLevelAlarmTypeNode; 51 | import com.prosysopc.ua.types.opcua.server.ExclusiveLimitState; 52 | import com.prosysopc.ua.types.opcua.server.FolderTypeNode; 53 | 54 | /** 55 | * A sample customized node manager, which actually just overrides the standard 56 | * NodeManagerUaNode and initializes the nodes for the demo. 57 | */ 58 | public class MyNodeManager extends NodeManagerUaNode { 59 | public static final String NAMESPACE = "http://www.prosysopc.com/OPCUA/SampleAddressSpace"; 60 | private static final Logger logger = LoggerFactory.getLogger(MyNodeManager.class); 61 | private static boolean stackTraceOnException; 62 | 63 | /** 64 | * @param e 65 | */ 66 | private static void printException(Exception e) { 67 | if (stackTraceOnException) 68 | e.printStackTrace(); 69 | else { 70 | println(e.toString()); 71 | if (e.getCause() != null) 72 | println("Caused by: " + e.getCause()); 73 | } 74 | } 75 | 76 | /** 77 | * @param string 78 | */ 79 | protected static void println(String string) { 80 | System.out.println(string); 81 | } 82 | 83 | private ExclusiveLevelAlarmTypeNode myAlarm; 84 | 85 | private UaObjectNode myDevice; 86 | 87 | // private MyEventType myEvent; 88 | 89 | private UaVariableNode myLevel; 90 | 91 | private PlainMethod myMethod; 92 | 93 | private CallableListener myMethodManagerListener; 94 | 95 | private FolderTypeNode myObjectsFolder; 96 | 97 | private PlainVariable mySwitch; 98 | 99 | double dx = 1; 100 | 101 | final MyEventManagerListener myEventManagerListener = new MyEventManagerListener(); 102 | 103 | /** 104 | * Creates a new instance of MyNodeManager 105 | * 106 | * @param server 107 | * the server in which the node manager is created. 108 | * @param namespaceUri 109 | * the namespace URI for the nodes 110 | * @throws StatusException 111 | * if something goes wrong in the initialization 112 | * @throws UaInstantiationException 113 | */ 114 | public MyNodeManager(UaServer server, String namespaceUri) throws StatusException, UaInstantiationException { 115 | super(server, namespaceUri); 116 | } 117 | 118 | /** 119 | * @return 120 | */ 121 | public UaObjectNode[] getHistorizableEvents() { 122 | return new UaObjectNode[] { myObjectsFolder, myDevice }; 123 | } 124 | 125 | /** 126 | * @return 127 | */ 128 | public UaVariableNode[] getHistorizableVariables() { 129 | return new UaVariableNode[] { myLevel, mySwitch }; 130 | } 131 | 132 | /** 133 | * 134 | */ 135 | public void sendEvent() { 136 | // If the type has TypeDefinitionId, you can use the class 137 | MyEventType ev = createEvent(MyEventType.class); 138 | ev.setMessage("MyEvent"); 139 | ev.setMyVariable(new Random().nextInt()); 140 | ev.setMyProperty("Property Value " + ev.getMyVariable()); 141 | ev.triggerEvent(null); 142 | } 143 | 144 | /** 145 | * 146 | */ 147 | public void simulate() { 148 | final DataValue v = myLevel.getValue(); 149 | Double nextValue = v.isNull() ? 0 : v.getValue().doubleValue() + dx; 150 | if (nextValue <= 0) 151 | dx = 1; 152 | else if (nextValue >= 100) 153 | dx = -1; 154 | try { 155 | ((CacheVariable) myLevel).updateValue(nextValue); 156 | if (nextValue > myAlarm.getHighHighLimit()) 157 | activateAlarm(700, ExclusiveLimitState.HighHigh); 158 | else if (nextValue > myAlarm.getHighLimit()) 159 | activateAlarm(500, ExclusiveLimitState.High); 160 | else if (nextValue < myAlarm.getLowLowLimit()) 161 | activateAlarm(700, ExclusiveLimitState.Low); 162 | else if (nextValue < myAlarm.getLowLimit()) 163 | activateAlarm(500, ExclusiveLimitState.LowLow); 164 | else 165 | inactivateAlarm(); 166 | } catch (Exception e) { 167 | logger.error("Error while simulating", e); 168 | // printException(e); 169 | throw new RuntimeException(e); // End the task 170 | } 171 | 172 | } 173 | 174 | /** 175 | * Creates an alarm, if it is not active 176 | * 177 | * @param limitState 178 | */ 179 | private void activateAlarm(int severity, ExclusiveLimitState limitState) { 180 | if (myAlarm.isEnabled() && (!myAlarm.isActive() || (myAlarm.getSeverity().getValue() != severity))) { 181 | println("activateAlarm: severity=" + severity); 182 | myAlarm.setActive(true); 183 | myAlarm.setRetain(true); 184 | myAlarm.setAcked(false); // Also sets confirmed to false 185 | myAlarm.setSeverity(severity); 186 | myAlarm.getLimitStateNode().setCurrentLimitState(limitState); 187 | 188 | triggerEvent(myAlarm); 189 | 190 | // If you wish to check whether any clients are monitoring your 191 | // alarm, you can use the following 192 | 193 | // logger.info("myAlarm is monitored=" + 194 | // myAlarm.isMonitoredForEvents()); 195 | } 196 | } 197 | 198 | private void createAddressSpace() throws StatusException, UaInstantiationException { 199 | // +++ My nodes +++ 200 | 201 | int ns = getNamespaceIndex(); 202 | 203 | // My Event Manager Listener 204 | this.getEventManager().setListener(myEventManagerListener); 205 | 206 | // UA types and folders which we will use 207 | final UaObject objectsFolder = getServer().getNodeManagerRoot().getObjectsFolder(); 208 | final UaType baseObjectType = getServer().getNodeManagerRoot().getType(Identifiers.BaseObjectType); 209 | final UaType baseDataVariableType = getServer().getNodeManagerRoot().getType(Identifiers.BaseDataVariableType); 210 | 211 | // Folder for my objects 212 | final NodeId myObjectsFolderId = new NodeId(ns, "MyObjectsFolder"); 213 | myObjectsFolder = createInstance(FolderTypeNode.class, "MyObjects", myObjectsFolderId); 214 | 215 | this.addNodeAndReference(objectsFolder, myObjectsFolder, Identifiers.Organizes); 216 | 217 | // My Device Type 218 | 219 | final NodeId myDeviceTypeId = new NodeId(ns, "MyDeviceType"); 220 | UaObjectType myDeviceType = new UaObjectTypeNode(this, myDeviceTypeId, "MyDeviceType", Locale.ENGLISH); 221 | this.addNodeAndReference(baseObjectType, myDeviceType, Identifiers.HasSubtype); 222 | 223 | // My Device 224 | 225 | final NodeId myDeviceId = new NodeId(ns, "MyDevice"); 226 | myDevice = new UaObjectNode(this, myDeviceId, "MyDevice", Locale.ENGLISH); 227 | myDevice.setTypeDefinition(myDeviceType); 228 | myObjectsFolder.addReference(myDevice, Identifiers.HasComponent, false); 229 | 230 | // My Level Type 231 | 232 | final NodeId myLevelTypeId = new NodeId(ns, "MyLevelType"); 233 | UaType myLevelType = this.addType(myLevelTypeId, "MyLevelType", baseDataVariableType); 234 | 235 | // My Level Measurement 236 | 237 | final NodeId myLevelId = new NodeId(ns, "MyLevel"); 238 | UaType doubleType = getServer().getNodeManagerRoot().getType(Identifiers.Double); 239 | myLevel = new CacheVariable(this, myLevelId, "MyLevel", LocalizedText.NO_LOCALE); 240 | myLevel.setDataType(doubleType); 241 | myLevel.setTypeDefinition(myLevelType); 242 | myDevice.addComponent(myLevel); 243 | 244 | // My Switch 245 | // Use PlainVariable and addComponent() to add it to myDevice 246 | // Note that we use NodeIds instead of UaNodes to define the data type 247 | // and type definition 248 | 249 | NodeId mySwitchId = new NodeId(ns, "MySwitch"); 250 | mySwitch = new PlainVariable(this, mySwitchId, "MySwitch", LocalizedText.NO_LOCALE); 251 | mySwitch.setDataTypeId(Identifiers.Boolean); 252 | mySwitch.setTypeDefinitionId(Identifiers.BaseDataVariableType); 253 | myDevice.addComponent(mySwitch); // addReference(...Identifiers.HasComponent...); 254 | 255 | // Initial value 256 | mySwitch.setCurrentValue(false); 257 | 258 | // A sample alarm node 259 | createAlarmNode(myLevel); 260 | 261 | // A sample custom event type 262 | createMyEventType(); 263 | 264 | // A sample enumeration type 265 | createMyEnumNode(); 266 | 267 | // A sample method node 268 | createMethodNode(); 269 | } 270 | 271 | /** 272 | * Create a sample alarm node structure. 273 | * 274 | * @param source 275 | * 276 | * @throws StatusException 277 | * @throws UaInstantiationException 278 | */ 279 | private void createAlarmNode(UaVariable source) throws StatusException, UaInstantiationException { 280 | 281 | // Level Alarm from the LevelMeasurement 282 | 283 | // See the Spec. Part 9. Appendix B.2 for a similar example 284 | 285 | int ns = this.getNamespaceIndex(); 286 | final NodeId myAlarmId = new NodeId(ns, source.getNodeId().getValue() + ".Alarm"); 287 | String name = source.getBrowseName().getName() + "Alarm"; 288 | myAlarm = createInstance(ExclusiveLevelAlarmTypeNode.class, name, myAlarmId); 289 | 290 | // ConditionSource is the node which has this condition 291 | myAlarm.setSource(source); 292 | // Input is the node which has the measurement that generates the alarm 293 | myAlarm.setInput(source); 294 | 295 | myAlarm.setMessage("Level exceeded"); // Default locale 296 | myAlarm.setMessage("Füllständalarm!", Locale.GERMAN); 297 | myAlarm.setSeverity(500); // Medium level warning 298 | myAlarm.setHighHighLimit(90.0); 299 | myAlarm.setHighLimit(70.0); 300 | myAlarm.setLowLowLimit(10.0); 301 | myAlarm.setLowLimit(30.0); 302 | myAlarm.setEnabled(true); 303 | myDevice.addComponent(myAlarm); // addReference(...Identifiers.HasComponent...) 304 | 305 | // + HasCondition, the SourceNode of the reference should normally 306 | // correspond to the Source set above 307 | source.addReference(myAlarm, Identifiers.HasCondition, false); 308 | 309 | // + EventSource, the target of the EventSource is normally the 310 | // source of the HasCondition reference 311 | myDevice.addReference(source, Identifiers.HasEventSource, false); 312 | 313 | // + HasNotifier, these are used to link the source of the EventSource 314 | // up in the address space hierarchy 315 | myObjectsFolder.addReference(myDevice, Identifiers.HasNotifier, false); 316 | } 317 | 318 | /** 319 | * Create a sample method. 320 | * 321 | * @throws StatusException 322 | */ 323 | private void createMethodNode() throws StatusException { 324 | int ns = this.getNamespaceIndex(); 325 | final NodeId myMethodId = new NodeId(ns, "MyMethod"); 326 | myMethod = new PlainMethod(this, myMethodId, "MyMethod", Locale.ENGLISH); 327 | Argument[] inputs = new Argument[2]; 328 | inputs[0] = new Argument(); 329 | inputs[0].setName("Operation"); 330 | inputs[0].setDataType(Identifiers.String); 331 | inputs[0].setValueRank(ValueRanks.Scalar); 332 | inputs[0].setArrayDimensions(null); 333 | inputs[0].setDescription(new LocalizedText( 334 | "The operation to perform on parameter: valid functions are sin, cos, tan, pow", Locale.ENGLISH)); 335 | inputs[1] = new Argument(); 336 | inputs[1].setName("Parameter"); 337 | inputs[1].setDataType(Identifiers.Double); 338 | inputs[1].setValueRank(ValueRanks.Scalar); 339 | inputs[1].setArrayDimensions(null); 340 | inputs[1].setDescription(new LocalizedText("The parameter for operation", Locale.ENGLISH)); 341 | myMethod.setInputArguments(inputs); 342 | 343 | Argument[] outputs = new Argument[1]; 344 | outputs[0] = new Argument(); 345 | outputs[0].setName("Result"); 346 | outputs[0].setDataType(Identifiers.Double); 347 | outputs[0].setValueRank(ValueRanks.Scalar); 348 | outputs[0].setArrayDimensions(null); 349 | outputs[0].setDescription(new LocalizedText("The result of 'operation(parameter)'", Locale.ENGLISH)); 350 | myMethod.setOutputArguments(outputs); 351 | 352 | this.addNodeAndReference(myDevice, myMethod, Identifiers.HasComponent); 353 | 354 | // Create the listener that handles the method calls 355 | myMethodManagerListener = new MyMethodManagerListener(myMethod); 356 | MethodManagerUaNode m = (MethodManagerUaNode) this.getMethodManager(); 357 | m.addCallListener(myMethodManagerListener); 358 | } 359 | 360 | /** 361 | * @throws StatusException 362 | * if the necessary type node(s) are not found 363 | * 364 | */ 365 | private void createMyEnumNode() throws StatusException { 366 | // An example showing how a new enumeration type can be defined in code. 367 | // It is usually easier to define new types using information models and 368 | // generating Java code out of those. See the more about that in the 369 | // 'codegen' documentation. 370 | 371 | // 1. Create the type node... 372 | 373 | NodeId myEnumTypeId = new NodeId(this.getNamespaceIndex(), "MyEnumType"); 374 | 375 | UaDataType myEnumType = new UaDataTypeNode(this, myEnumTypeId, "MyEnumType", LocalizedText.NO_LOCALE); 376 | 377 | // ... as sub type of Enumeration 378 | UaType enumerationType = getServer().getNodeManagerRoot().getType(Identifiers.Enumeration); 379 | enumerationType.addSubType(myEnumType); 380 | 381 | // 2. Add the EnumStrings property ... 382 | 383 | NodeId myEnumStringsId = new NodeId(this.getNamespaceIndex(), "MyEnumType_EnumStrings"); 384 | ; 385 | PlainProperty enumStringsProperty = new PlainProperty(this, myEnumStringsId, 386 | new QualifiedName("EnumStrings"), new LocalizedText("EnumStrings", LocalizedText.NO_LOCALE)); 387 | enumStringsProperty.setDataTypeId(Identifiers.LocalizedText); 388 | enumStringsProperty.setValueRank(ValueRanks.OneDimension); 389 | enumStringsProperty.setArrayDimensions(new UnsignedInteger[] { UnsignedInteger.ZERO }); 390 | enumStringsProperty.setAccessLevel(AccessLevel.READONLY); 391 | enumStringsProperty.addReference(Identifiers.ModellingRule_Mandatory, Identifiers.HasModellingRule, false); 392 | 393 | myEnumType.addProperty(enumStringsProperty); 394 | 395 | // ... with Value 396 | enumStringsProperty.setCurrentValue(MyEnumType.getEnumStrings()); 397 | 398 | // 3. Create the instance 399 | 400 | NodeId myEnumObjectId = new NodeId(this.getNamespaceIndex(), "MyEnumObject"); 401 | PlainVariable myEnumVariable = new PlainVariable(this, myEnumObjectId, "MyEnumObject", 402 | LocalizedText.NO_LOCALE); 403 | myEnumVariable.setDataType(myEnumType); 404 | 405 | // .. as a component of myDevice 406 | myDevice.addComponent(myEnumVariable); 407 | 408 | // 4. Initialize the value 409 | myEnumVariable.setCurrentValue(MyEnumType.One); 410 | } 411 | 412 | /** 413 | * A sample custom event type. 414 | *

415 | * NOTE that it is usually easier to create new types using the Information 416 | * Models and export them from XML to the server. You can also generate the 417 | * respective Java types with the 'codegen' from the same XML. In this 418 | * example, we will construct the type into the address space "manually". 419 | * MyEventType is also hand-coded and is registered to be used to create the 420 | * instances of that type. 421 | *

422 | * When the type definition is in the address space, and the respective Java 423 | * class is registered to the server, it will create those instances, for 424 | * example as shown in {@link #sendEvent()}. 425 | * 426 | * @throws StatusException 427 | */ 428 | private void createMyEventType() throws StatusException { 429 | int ns = this.getNamespaceIndex(); 430 | 431 | NodeId myEventTypeId = new NodeId(ns, MyEventType.MY_EVENT_ID); 432 | UaObjectType myEventType = new UaObjectTypeNode(this, myEventTypeId, "MyEventType", LocalizedText.NO_LOCALE); 433 | getServer().getNodeManagerRoot().getType(Identifiers.BaseEventType).addSubType(myEventType); 434 | 435 | NodeId myVariableId = new NodeId(ns, MyEventType.MY_VARIABLE_ID); 436 | PlainVariable myVariable = new PlainVariable(this, myVariableId, MyEventType.MY_VARIABLE_NAME, 437 | LocalizedText.NO_LOCALE); 438 | myVariable.setDataTypeId(Identifiers.Int32); 439 | // The modeling rule must be defined for the mandatory elements to 440 | // ensure that the event instances will also get the elements. 441 | myVariable.addModellingRule(ModellingRule.Mandatory); 442 | myEventType.addComponent(myVariable); 443 | 444 | NodeId myPropertyId = new NodeId(ns, MyEventType.MY_PROPERTY_ID); 445 | PlainProperty myProperty = new PlainProperty(this, myPropertyId, MyEventType.MY_PROPERTY_NAME, 446 | LocalizedText.NO_LOCALE); 447 | myProperty.setDataTypeId(Identifiers.String); 448 | myProperty.addModellingRule(ModellingRule.Mandatory); 449 | myEventType.addProperty(myProperty); 450 | 451 | getServer().registerClass(MyEventType.class, myEventTypeId); 452 | } 453 | 454 | /** 455 | * 456 | */ 457 | private void inactivateAlarm() { 458 | if (myAlarm.isEnabled() && myAlarm.isActive()) { 459 | println("inactivateAlarm"); 460 | myAlarm.setActive(false); 461 | myAlarm.setRetain(!myAlarm.isAcked()); 462 | myAlarm.getLimitStateNode().setCurrentLimitState(ExclusiveLimitState.None); 463 | 464 | triggerEvent(myAlarm); 465 | } 466 | } 467 | 468 | /** 469 | * Send an event notification. 470 | * 471 | * @param event 472 | * The event to trigger. 473 | */ 474 | private void triggerEvent(BaseEventTypeNode event) { 475 | // Trigger event 476 | final DateTime now = DateTime.currentTime(); 477 | byte[] myEventId = getNextUserEventId(); 478 | /* byte[] fullEventId = */event.triggerEvent(now, now, myEventId); 479 | } 480 | 481 | /** 482 | * @return 483 | */ 484 | protected byte[] getNextUserEventId() { 485 | return myEventManagerListener.getNextUserEventId(); 486 | } 487 | 488 | /* 489 | * (non-Javadoc) 490 | * 491 | * @see com.prosysopc.ua.server.NodeManagerUaNode#init() 492 | */ 493 | @Override 494 | protected void init() throws StatusException, UaNodeFactoryException { 495 | super.init(); 496 | 497 | createAddressSpace(); 498 | } 499 | 500 | /** 501 | * 502 | */ 503 | // protected void initMyEvent() { 504 | // if (myEvent == null) 505 | // myEvent = new MyEventType(this); 506 | // } 507 | 508 | /** 509 | * Send an event 510 | * 511 | * @throws StatusException 512 | */ 513 | // protected void sendEvent() throws StatusException { 514 | // // 1. send a standard SystemEventType here 515 | // SystemEventTypeNode newEvent = createEvent(SystemEventTypeNode.class); 516 | // 517 | // newEvent.setMessage("New event"); 518 | // // Set the severity of the event between 1 and 1000 519 | // newEvent.setSeverity(1); 520 | // // By default the event is sent for the "Server" object. If you want to 521 | // // send it for some other object, use Source (or SourceNode), e.g. 522 | // // newEvent.setSource(myDevice); 523 | // triggerEvent(newEvent); 524 | // 525 | // // 2. Send our own event 526 | // 527 | // initMyEvent(); 528 | // myEvent.setSource(myObjectsFolder); 529 | // 530 | // myEvent.setMyVariable(myEvent.getMyVariable() + 1); 531 | // myEvent.setMyProperty(DateTime.currentTime().toString()); 532 | // triggerEvent(myEvent); 533 | // this.deleteNode(myEvent, true, true); 534 | // } 535 | 536 | void addNode(String name) { 537 | // Initialize NodeVersion property, to enable ModelChangeEvents 538 | myObjectsFolder.initNodeVersion(); 539 | 540 | getServer().getNodeManagerRoot().beginModelChange(); 541 | try { 542 | NodeId nodeId = new NodeId(this.getNamespaceIndex(), UUID.randomUUID()); 543 | 544 | UaNode node = this.getNodeFactory().createNode(NodeClass.Variable, nodeId, name, Locale.ENGLISH, 545 | Identifiers.PropertyType); 546 | myObjectsFolder.addComponent(node); 547 | } catch (UaNodeFactoryException e) { 548 | printException(e); 549 | } catch (IllegalArgumentException e) { 550 | printException(e); 551 | } finally { 552 | getServer().getNodeManagerRoot().endModelChange(); 553 | } 554 | } 555 | 556 | void deleteNode(QualifiedName nodeName) throws StatusException { 557 | UaNode node = myObjectsFolder.getComponent(nodeName); 558 | if (node != null) { 559 | getServer().getNodeManagerRoot().beginModelChange(); 560 | try { 561 | this.deleteNode(node, true, true); 562 | } finally { 563 | getServer().getNodeManagerRoot().endModelChange(); 564 | } 565 | } else 566 | println("MyObjects does not contain a component with name " + nodeName); 567 | } 568 | } 569 | -------------------------------------------------------------------------------- /opc-ua-server/src/main/java/com/prosysopc/ua/samples/MyBigNodeManager.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Prosys OPC UA Java SDK 3 | * 4 | * Copyright (c) Prosys PMS Ltd., . 5 | * All rights reserved. 6 | */ 7 | package com.prosysopc.ua.samples; 8 | 9 | import java.util.Collection; 10 | import java.util.Locale; 11 | import java.util.Map; 12 | import java.util.TreeMap; 13 | import java.util.concurrent.ConcurrentHashMap; 14 | import java.util.concurrent.CopyOnWriteArrayList; 15 | 16 | import org.opcfoundation.ua.builtintypes.DataValue; 17 | import org.opcfoundation.ua.builtintypes.DateTime; 18 | import org.opcfoundation.ua.builtintypes.ExpandedNodeId; 19 | import org.opcfoundation.ua.builtintypes.LocalizedText; 20 | import org.opcfoundation.ua.builtintypes.NodeId; 21 | import org.opcfoundation.ua.builtintypes.QualifiedName; 22 | import org.opcfoundation.ua.builtintypes.StatusCode; 23 | import org.opcfoundation.ua.builtintypes.UnsignedInteger; 24 | import org.opcfoundation.ua.builtintypes.Variant; 25 | import org.opcfoundation.ua.common.ServiceResultException; 26 | import org.opcfoundation.ua.core.AccessLevel; 27 | import org.opcfoundation.ua.core.Attributes; 28 | import org.opcfoundation.ua.core.Identifiers; 29 | import org.opcfoundation.ua.core.NodeClass; 30 | import org.opcfoundation.ua.core.StatusCodes; 31 | import org.opcfoundation.ua.core.TimestampsToReturn; 32 | import org.opcfoundation.ua.utils.NumericRange; 33 | import org.slf4j.Logger; 34 | import org.slf4j.LoggerFactory; 35 | 36 | import com.prosysopc.ua.EventNotifierClass; 37 | import com.prosysopc.ua.StatusException; 38 | import com.prosysopc.ua.ValueRanks; 39 | import com.prosysopc.ua.nodes.UaNode; 40 | import com.prosysopc.ua.nodes.UaReference; 41 | import com.prosysopc.ua.nodes.UaReferenceType; 42 | import com.prosysopc.ua.nodes.UaValueNode; 43 | import com.prosysopc.ua.server.IoManager; 44 | import com.prosysopc.ua.server.MonitoredDataItem; 45 | import com.prosysopc.ua.server.MonitoredItem; 46 | import com.prosysopc.ua.server.NodeManager; 47 | import com.prosysopc.ua.server.ServiceContext; 48 | import com.prosysopc.ua.server.Subscription; 49 | import com.prosysopc.ua.server.UaServer; 50 | 51 | /** 52 | * A sample implementation of a NodeManager which does not use UaNode objects, 53 | * but connects to an underlying system for the data. 54 | */ 55 | public class MyBigNodeManager extends NodeManager { 56 | 57 | public class DataItem { 58 | private NodeId dataType = Identifiers.Double; 59 | private final String name; 60 | private StatusCode status = new StatusCode(StatusCodes.Bad_WaitingForInitialData); 61 | private DateTime timestamp; 62 | private double value; 63 | 64 | /** 65 | * @param name 66 | * @param value 67 | */ 68 | public DataItem(String name) { 69 | super(); 70 | this.name = name; 71 | } 72 | 73 | /** 74 | * @return the dataType 75 | */ 76 | public NodeId getDataType() { 77 | return dataType; 78 | } 79 | 80 | /** 81 | * 82 | */ 83 | public void getDataValue(DataValue dataValue) { 84 | dataValue.setValue(new Variant(getValue())); 85 | dataValue.setStatusCode(getStatus()); 86 | dataValue.setServerTimestamp(DateTime.currentTime()); 87 | dataValue.setSourceTimestamp(timestamp); 88 | } 89 | 90 | /** 91 | * @return the name 92 | */ 93 | public String getName() { 94 | return name; 95 | } 96 | 97 | /** 98 | * @return the status 99 | */ 100 | public StatusCode getStatus() { 101 | return status; 102 | } 103 | 104 | /** 105 | * The timestamp defined when the value or status changed. 106 | * 107 | * @return the timestamp 108 | */ 109 | public DateTime getTimestamp() { 110 | return timestamp; 111 | } 112 | 113 | /** 114 | * @return the value 115 | */ 116 | public double getValue() { 117 | return value; 118 | } 119 | 120 | /** 121 | * @param dataType 122 | * the dataType to set 123 | */ 124 | public void setDataType(NodeId dataType) { 125 | this.dataType = dataType; 126 | } 127 | 128 | /** 129 | * @param value 130 | * the value to set 131 | */ 132 | public void setValue(double value) { 133 | setValue(value, StatusCode.GOOD); 134 | } 135 | 136 | /** 137 | * @param value 138 | * the value to set 139 | * @param status 140 | * the status to set 141 | */ 142 | public void setValue(double value, StatusCode status) { 143 | if (status == null) 144 | status = StatusCode.BAD; 145 | if ((this.value != value) || !this.status.equals(status)) { 146 | this.value = value; 147 | this.status = status; 148 | this.timestamp = DateTime.currentTime(); 149 | } 150 | } 151 | } 152 | 153 | /** 154 | * An IO Manager which provides the values for the attributes of the nodes. 155 | */ 156 | public class MyBigIoManager extends IoManager { 157 | 158 | /** 159 | * Constructor for the IoManager. 160 | * 161 | * @param nodeManager 162 | * the node manager that uses this IO Manager. 163 | */ 164 | public MyBigIoManager(NodeManager nodeManager) { 165 | super(nodeManager); 166 | } 167 | 168 | /* 169 | * (non-Javadoc) 170 | * 171 | * @see com.prosysopc.ua.server.IoManager#readNonValue(com.prosysopc.ua. 172 | * server .ServiceContext, org.opcfoundation.ua.builtintypes.NodeId, 173 | * com.prosysopc.ua.nodes.UaNode, 174 | * org.opcfoundation.ua.builtintypes.UnsignedInteger, 175 | * org.opcfoundation.ua.builtintypes.DataValue) 176 | */ 177 | @Override 178 | protected void readNonValue(ServiceContext serviceContext, Object operationContext, NodeId nodeId, UaNode node, 179 | UnsignedInteger attributeId, DataValue dataValue) throws StatusException { 180 | Object value = null; 181 | UnsignedInteger status = StatusCodes.Bad_AttributeIdInvalid; 182 | 183 | DataItem dataItem = getDataItem(nodeId); 184 | final ExpandedNodeId expandedNodeId = getNamespaceTable().toExpandedNodeId(nodeId); 185 | if (attributeId.equals(Attributes.NodeId)) 186 | value = nodeId; 187 | else if (attributeId.equals(Attributes.BrowseName)) 188 | value = getBrowseName(expandedNodeId, node); 189 | else if (attributeId.equals(Attributes.DisplayName)) 190 | value = getDisplayName(expandedNodeId, node, null); 191 | else if (attributeId.equals(Attributes.Description)) 192 | status = StatusCodes.Bad_AttributeIdInvalid; 193 | else if (attributeId.equals(Attributes.NodeClass)) 194 | value = getNodeClass(expandedNodeId, node); 195 | else if (attributeId.equals(Attributes.WriteMask)) 196 | value = UnsignedInteger.ZERO; 197 | // the following are only requested for the DataItems 198 | else if (dataItem != null) { 199 | if (attributeId.equals(Attributes.DataType)) 200 | value = Identifiers.Double; 201 | else if (attributeId.equals(Attributes.ValueRank)) 202 | value = ValueRanks.Scalar; 203 | else if (attributeId.equals(Attributes.ArrayDimensions)) 204 | status = StatusCodes.Bad_AttributeIdInvalid; 205 | else if (attributeId.equals(Attributes.AccessLevel)) 206 | value = AccessLevel.getMask(AccessLevel.READONLY); 207 | else if (attributeId.equals(Attributes.UserAccessLevel)) 208 | value = AccessLevel.getMask(AccessLevel.READONLY); 209 | else if (attributeId.equals(Attributes.Historizing)) 210 | value = false; 211 | } 212 | // and this is only requested for the folder 213 | else if (attributeId.equals(Attributes.EventNotifier)) 214 | value = EventNotifierClass.getMask(EventNotifierClass.NONE); 215 | 216 | if (value == null) 217 | dataValue.setStatusCode(status); 218 | else 219 | dataValue.setValue(new Variant(value)); 220 | dataValue.setServerTimestamp(DateTime.currentTime()); 221 | } 222 | 223 | /* 224 | * (non-Javadoc) 225 | * 226 | * @see 227 | * com.prosysopc.ua.server.IoManager#readValue(com.prosysopc.ua.server 228 | * .ServiceContext, org.opcfoundation.ua.builtintypes.NodeId, 229 | * com.prosysopc.ua.nodes.UaVariable, 230 | * org.opcfoundation.ua.utils.NumericRange, 231 | * org.opcfoundation.ua.core.TimestampsToReturn, 232 | * org.opcfoundation.ua.builtintypes.DateTime, 233 | * org.opcfoundation.ua.builtintypes.DataValue) 234 | */ 235 | @Override 236 | protected void readValue(ServiceContext serviceContext, Object operationContext, NodeId nodeId, 237 | UaValueNode node, NumericRange indexRange, TimestampsToReturn timestampsToReturn, DateTime minTimestamp, 238 | DataValue dataValue) throws StatusException { 239 | DataItem dataItem = getDataItem(nodeId); 240 | if (dataItem == null) 241 | throw new StatusException(StatusCodes.Bad_NodeIdInvalid); 242 | dataItem.getDataValue(dataValue); 243 | 244 | } 245 | 246 | // If you wish to enable writing, also disable simulation in 247 | // MyBigNodeManager.simulate() and check the value of WriteMask returned 248 | // (above). 249 | // /* 250 | // * (non-Javadoc) 251 | // * 252 | // * @see 253 | // * 254 | // com.prosysopc.ua.server.IoManager#writeValue(com.prosysopc.ua.server 255 | // * .ServiceContext, org.opcfoundation.ua.builtintypes.NodeId, 256 | // * com.prosysopc.ua.nodes.UaVariable, 257 | // * org.opcfoundation.ua.utils.NumericRange, 258 | // * org.opcfoundation.ua.builtintypes.DataValue) 259 | // */ 260 | // @Override 261 | // protected boolean writeValue(ServiceContext serviceContext, 262 | // NodeId nodeId, UaVariable node, NumericRange indexRange, 263 | // DataValue dataValue) throws StatusException { 264 | // DataItem dataItem = getDataItem(nodeId); 265 | // if (dataItem == null) 266 | // throw new StatusException(StatusCodes.Bad_NodeIdInvalid); 267 | // dataItem.setValue(dataValue.getValue().doubleValue(), 268 | // dataValue.getStatusCode()); 269 | // return true; 270 | // } 271 | } 272 | 273 | /** 274 | * 275 | */ 276 | public class MyReference extends UaReference { 277 | 278 | private final NodeId referenceTypeId; 279 | private final ExpandedNodeId sourceId; 280 | private final ExpandedNodeId targetId; 281 | 282 | /** 283 | * @param sourceId 284 | * @param targetId 285 | * @param referenceType 286 | */ 287 | public MyReference(ExpandedNodeId sourceId, ExpandedNodeId targetId, NodeId referenceType) { 288 | super(); 289 | this.sourceId = sourceId; 290 | this.targetId = targetId; 291 | this.referenceTypeId = referenceType; 292 | } 293 | 294 | /** 295 | * @param sourceId 296 | * @param targetId 297 | * @param referenceType 298 | */ 299 | public MyReference(NodeId sourceId, NodeId targetId, NodeId referenceType) { 300 | this(getNamespaceTable().toExpandedNodeId(sourceId), getNamespaceTable().toExpandedNodeId(targetId), 301 | referenceType); 302 | } 303 | 304 | /* 305 | * (non-Javadoc) 306 | * 307 | * @see com.prosysopc.ua.nodes.UaReference#delete() 308 | */ 309 | @Override 310 | public void delete() { 311 | throw new RuntimeException("StatusCodes.Bad_NotImplemented"); 312 | } 313 | 314 | /* 315 | * (non-Javadoc) 316 | * 317 | * @see 318 | * com.prosysopc.ua.nodes.UaReference#getIsInverse(org.opcfoundation 319 | * .ua.builtintypes.NodeId) 320 | */ 321 | @Override 322 | public boolean getIsInverse(NodeId nodeId) { 323 | try { 324 | if (nodeId.equals(getNamespaceTable().toNodeId(sourceId))) 325 | return false; 326 | if (nodeId.equals(getNamespaceTable().toNodeId(targetId))) 327 | return true; 328 | } catch (ServiceResultException e) { 329 | throw new RuntimeException(e); 330 | } 331 | throw new RuntimeException("not a source nor target"); 332 | } 333 | 334 | /* 335 | * (non-Javadoc) 336 | * 337 | * @see 338 | * com.prosysopc.ua.nodes.UaReference#getIsInverse(com.prosysopc.ua. 339 | * nodes.UaNode) 340 | */ 341 | @Override 342 | public boolean getIsInverse(UaNode node) { 343 | return getIsInverse(node.getNodeId()); 344 | } 345 | 346 | /* 347 | * (non-Javadoc) 348 | * 349 | * @see com.prosysopc.ua.nodes.UaReference#getReferenceType() 350 | */ 351 | @Override 352 | public UaReferenceType getReferenceType() { 353 | try { 354 | return (UaReferenceType) getNodeManagerTable().getNode(getReferenceTypeId()); 355 | } catch (StatusException e) { 356 | throw new RuntimeException(e); 357 | } 358 | } 359 | 360 | /* 361 | * (non-Javadoc) 362 | * 363 | * @see com.prosysopc.ua.nodes.UaReference#getReferenceTypeId() 364 | */ 365 | @Override 366 | public NodeId getReferenceTypeId() { 367 | return referenceTypeId; 368 | } 369 | 370 | /* 371 | * (non-Javadoc) 372 | * 373 | * @see com.prosysopc.ua.nodes.UaReference#getSourceId() 374 | */ 375 | @Override 376 | public ExpandedNodeId getSourceId() { 377 | return sourceId; 378 | } 379 | 380 | /* 381 | * (non-Javadoc) 382 | * 383 | * @see com.prosysopc.ua.nodes.UaReference#getSourceNode() 384 | */ 385 | @Override 386 | public UaNode getSourceNode() { 387 | return null; // new UaExternalNodeImpl(myNodeManager, sourceId); 388 | } 389 | 390 | /* 391 | * (non-Javadoc) 392 | * 393 | * @see com.prosysopc.ua.nodes.UaReference#getTargetId() 394 | */ 395 | @Override 396 | public ExpandedNodeId getTargetId() { 397 | return targetId; 398 | } 399 | 400 | /* 401 | * (non-Javadoc) 402 | * 403 | * @see com.prosysopc.ua.nodes.UaReference#getTargetNode() 404 | */ 405 | @Override 406 | public UaNode getTargetNode() { 407 | return null; // new UaExternalNodeImpl(myNodeManager, targetId); 408 | } 409 | 410 | } 411 | 412 | private static ExpandedNodeId DataItemType; 413 | 414 | private static final Logger logger = LoggerFactory.getLogger(MyBigNodeManager.class); 415 | 416 | private final ExpandedNodeId DataItemFolder; 417 | 418 | private final Map dataItems; 419 | 420 | private final Map> monitoredItems = new ConcurrentHashMap>(); 421 | 422 | @SuppressWarnings("unused") 423 | private final MyBigIoManager myBigIoManager; 424 | 425 | private double t = 0; 426 | 427 | /** 428 | * Default constructor 429 | * 430 | * @param server 431 | * the UaServer, which owns the NodeManager 432 | * @param namespaceUri 433 | * the namespace which this node manager handles 434 | * @param nofItems 435 | * number of data items to create for the manager 436 | */ 437 | public MyBigNodeManager(UaServer server, String namespaceUri, int nofItems) { 438 | super(server, namespaceUri); 439 | DataItemType = new ExpandedNodeId(null, getNamespaceIndex(), "DataItemType"); 440 | DataItemFolder = new ExpandedNodeId(null, getNamespaceIndex(), "MyBigNodeManager"); 441 | try { 442 | getNodeManagerTable().getNodeManagerRoot().getObjectsFolder() 443 | .addReference(getNamespaceTable().toNodeId(DataItemFolder), Identifiers.Organizes, false); 444 | } catch (ServiceResultException e) { 445 | throw new RuntimeException(e); 446 | } 447 | dataItems = new TreeMap(); 448 | for (int i = 0; i < nofItems; i++) 449 | addDataItem(String.format("DataItem_%04d", i)); 450 | 451 | myBigIoManager = new MyBigIoManager(this); 452 | } 453 | 454 | /** 455 | * Adds a new node - unless it already exists in the node manager. 456 | * 457 | * @param node 458 | * the node to add. The NodeId of the node must have the same 459 | * namepsaceIndex as the node manager. 460 | * @return newNode, if it was added or the node with the same NodeId if such 461 | * was already in the address space. 462 | * @throws StatusException 463 | * if the NodeId is invalid (i.e. null) 464 | */ 465 | @Override 466 | public UaNode addNode(UaNode node) throws StatusException { 467 | return null; 468 | } 469 | 470 | /* 471 | * (non-Javadoc) 472 | * 473 | * @see 474 | * com.prosysopc.ua.server.NodeManager#getVariableDataType(org.opcfoundation 475 | * .ua.builtintypes.NodeId) 476 | */ 477 | @Override 478 | public NodeId getVariableDataType(NodeId nodeId, UaValueNode variable) throws StatusException { 479 | DataItem item = getDataItem(nodeId); 480 | return item.getDataType(); 481 | } 482 | 483 | /* 484 | * (non-Javadoc) 485 | * 486 | * @see com.prosysopc.ua.server.NodeManager#hasNode(org.opcfoundation.ua. 487 | * builtintypes .NodeId) 488 | */ 489 | @Override 490 | public boolean hasNode(NodeId nodeId) { 491 | return nodeId.getValue().equals("MyBigNodeManager") || nodeId.equals(DataItemType) 492 | || (getDataItem(nodeId) != null); 493 | } 494 | 495 | /** 496 | * @param name 497 | */ 498 | private void addDataItem(String name) { 499 | dataItems.put(name, new DataItem(name)); 500 | } 501 | 502 | /** 503 | * Finds the DataItem corresponding to the NodeId 504 | * 505 | * @param nodeId 506 | * ID of the node - the Value part corresponds to the name of the 507 | * item 508 | * @return the DataItem object 509 | */ 510 | private DataItem getDataItem(ExpandedNodeId nodeId) { 511 | String name = (String) nodeId.getValue(); 512 | return dataItems.get(name); 513 | } 514 | 515 | /** 516 | * Finds the DataItem corresponding to the NodeId 517 | * 518 | * @param nodeId 519 | * ID of the node - the Value part corresponds to the name of the 520 | * item 521 | * @return the DataItem object 522 | */ 523 | private DataItem getDataItem(NodeId nodeId) { 524 | String name = (String) nodeId.getValue(); 525 | return dataItems.get(name); 526 | } 527 | 528 | /** 529 | * @param nodeId 530 | * @return 531 | */ 532 | private String getNodeName(ExpandedNodeId nodeId) { 533 | String name = nodeId.getValue().toString(); 534 | if (getNamespaceTable().nodeIdEquals(nodeId, DataItemType)) 535 | name = "DataItemType"; 536 | if (getNamespaceTable().nodeIdEquals(nodeId, DataItemFolder)) 537 | name = "MyBigNodeManager"; 538 | else { 539 | DataItem dataItem = getDataItem(nodeId); 540 | // Use the namespaceIndex of the NodeManager name space also for the 541 | // browse names 542 | if (dataItem != null) 543 | name = dataItem.getName(); 544 | } 545 | return name; 546 | } 547 | 548 | /** 549 | * Send a data change notification for all monitored data items that are 550 | * monitoring the dataItme 551 | * 552 | * @param dataItem 553 | */ 554 | private void notifyMonitoredDataItems(DataItem dataItem) { 555 | // Get the list of items watching dataItem 556 | Collection c = monitoredItems.get(dataItem.getName()); 557 | if (c != null) 558 | for (MonitoredDataItem item : c) { 559 | DataValue dataValue = new DataValue(); 560 | dataItem.getDataValue(dataValue); 561 | item.notifyDataChange(dataValue); 562 | } 563 | } 564 | 565 | /* 566 | * (non-Javadoc) 567 | * 568 | * @see 569 | * com.prosysopc.ua.server.NodeManager#afterCreateMonitoredDataItem(com. 570 | * prosysopc.ua.server.ServiceContext, com.prosysopc.ua.server.Subscription, 571 | * com.prosysopc.ua.server.MonitoredDataItem) 572 | */ 573 | @Override 574 | protected void afterCreateMonitoredDataItem(ServiceContext serviceContext, Subscription subscription, 575 | MonitoredDataItem item) { 576 | // Add all items that monitor the same node to the same collection 577 | final Object dataItemName = item.getNodeId().getValue(); 578 | Collection c = monitoredItems.get(dataItemName); 579 | if (c == null) { 580 | c = new CopyOnWriteArrayList(); 581 | monitoredItems.put((String) dataItemName, c); 582 | } 583 | c.add(item); 584 | logger.debug("afterCreateMonitoredDataItem: nodeId={} c.size()={}", item.getNodeId(), c.size()); 585 | } 586 | 587 | /* 588 | * (non-Javadoc) 589 | * 590 | * @see 591 | * com.prosysopc.ua.server.NodeManager#deleteMonitoredItem(com.prosysopc 592 | * .ua.server.ServiceContext, com.prosysopc.ua.server.Subscription, 593 | * com.prosysopc.ua.server.MonitoredItem) 594 | */ 595 | @Override 596 | protected void deleteMonitoredItem(ServiceContext serviceContext, Subscription subscription, MonitoredItem item) 597 | throws StatusException { 598 | // Find the collection in which the monitoredItem is 599 | // and remove the item from the collection 600 | Object dataItemName = item.getNodeId().getValue(); 601 | Collection c = monitoredItems.get(dataItemName); 602 | if (c != null) { 603 | logger.debug("deleteMonitoredItem: collection size={}", c.size()); 604 | c.remove(item); 605 | if (c.isEmpty()) { 606 | monitoredItems.remove(dataItemName); 607 | logger.debug("deleteMonitoredItem: monitoredItems size={}", monitoredItems.size()); 608 | } 609 | } 610 | } 611 | 612 | /* 613 | * (non-Javadoc) 614 | * 615 | * @see 616 | * com.prosysopc.ua.server.NodeManager#getBrowseName(org.opcfoundation.ua 617 | * .builtintypes.ExpandedNodeId, com.prosysopc.ua.nodes.UaNode) 618 | */ 619 | @Override 620 | protected QualifiedName getBrowseName(ExpandedNodeId nodeId, UaNode node) { 621 | final String name = getNodeName(nodeId); 622 | return new QualifiedName(getNamespaceIndex(), name); 623 | } 624 | 625 | /* 626 | * (non-Javadoc) 627 | * 628 | * @see 629 | * com.prosysopc.ua.server.NodeManager#getDisplayName(org.opcfoundation. 630 | * ua.builtintypes.ExpandedNodeId, com.prosysopc.ua.nodes.UaNode, 631 | * java.util.Locale) 632 | */ 633 | @Override 634 | protected LocalizedText getDisplayName(ExpandedNodeId nodeId, UaNode targetNode, Locale locale) { 635 | final String name = getNodeName(nodeId); 636 | return new LocalizedText(name, LocalizedText.NO_LOCALE); 637 | } 638 | 639 | /* 640 | * (non-Javadoc) 641 | * 642 | * @see 643 | * com.prosysopc.ua.server.NodeManager#getNodeClass(org.opcfoundation.ua 644 | * .builtintypes.ExpandedNodeId, com.prosysopc.ua.nodes.UaNode) 645 | */ 646 | @Override 647 | protected NodeClass getNodeClass(NodeId nodeId, UaNode node) { 648 | if (getNamespaceTable().nodeIdEquals(nodeId, DataItemType)) 649 | return NodeClass.VariableType; 650 | if (getNamespaceTable().nodeIdEquals(nodeId, DataItemFolder)) 651 | return NodeClass.Object; 652 | // All data items are variables 653 | return NodeClass.Variable; 654 | } 655 | 656 | /* 657 | * (non-Javadoc) 658 | * 659 | * @see 660 | * com.prosysopc.ua.server.NodeManager#getReferences(org.opcfoundation.ua 661 | * .builtintypes.NodeId, com.prosysopc.ua.nodes.UaNode) 662 | */ 663 | @Override 664 | protected UaReference[] getReferences(NodeId nodeId, UaNode node) { 665 | try { 666 | // Define reference to our type 667 | if (nodeId.equals(getNamespaceTable().toNodeId(DataItemType))) 668 | return new UaReference[] { new MyReference(new ExpandedNodeId(Identifiers.BaseDataVariableType), 669 | DataItemType, Identifiers.HasSubtype) }; 670 | // Define reference from and to our Folder for the DataItems 671 | if (nodeId.equals(getNamespaceTable().toNodeId(DataItemFolder))) { 672 | UaReference[] folderItems = new UaReference[dataItems.size() + 2]; 673 | // Inverse reference to the ObjectsFolder 674 | folderItems[0] = new MyReference(new ExpandedNodeId(Identifiers.ObjectsFolder), DataItemFolder, 675 | Identifiers.Organizes); 676 | // Type definition reference 677 | folderItems[1] = new MyReference(DataItemFolder, 678 | getTypeDefinition(getNamespaceTable().toExpandedNodeId(nodeId), node), 679 | Identifiers.HasTypeDefinition); 680 | int i = 2; 681 | // Reference to all items in the folder 682 | for (DataItem d : dataItems.values()) { 683 | folderItems[i] = new MyReference(DataItemFolder, 684 | new ExpandedNodeId(null, getNamespaceIndex(), d.getName()), Identifiers.HasComponent); 685 | i++; 686 | } 687 | return folderItems; 688 | } 689 | } catch (ServiceResultException e) { 690 | throw new RuntimeException(e); 691 | } 692 | 693 | // Define references from our DataItems 694 | DataItem dataItem = getDataItem(nodeId); 695 | if (dataItem == null) 696 | return null; 697 | final ExpandedNodeId dataItemId = new ExpandedNodeId(null, getNamespaceIndex(), dataItem.getName()); 698 | return new UaReference[] { 699 | // Inverse reference to the folder 700 | new MyReference(DataItemFolder, dataItemId, Identifiers.HasComponent), 701 | // Type definition 702 | new MyReference(dataItemId, DataItemType, Identifiers.HasTypeDefinition) }; 703 | } 704 | 705 | /* 706 | * (non-Javadoc) 707 | * 708 | * @see 709 | * com.prosysopc.ua.server.NodeManager#getTypeDefinition(org.opcfoundation 710 | * .ua.builtintypes.ExpandedNodeId, com.prosysopc.ua.nodes.UaNode) 711 | */ 712 | @Override 713 | protected ExpandedNodeId getTypeDefinition(ExpandedNodeId nodeId, UaNode node) { 714 | // ExpandedNodeId.equals cannot be trusted, since some IDs are defined 715 | // with NamespaceIndex while others use NamespaceUri 716 | if (getNamespaceTable().nodeIdEquals(nodeId, DataItemType)) 717 | return null; 718 | if (getNamespaceTable().nodeIdEquals(nodeId, DataItemFolder)) 719 | return getNamespaceTable().toExpandedNodeId(Identifiers.FolderType); 720 | return DataItemType; 721 | } 722 | 723 | void simulate() { 724 | t = t + (Math.PI / 180); 725 | double value = 100 * Math.sin(t); 726 | for (DataItem d : dataItems.values()) { 727 | d.setValue(value); 728 | notifyMonitoredDataItems(d); 729 | } 730 | } 731 | 732 | } 733 | -------------------------------------------------------------------------------- /opc-ua-server/src/main/java/com/prosysopc/ua/samples/SampleConsoleServer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Prosys OPC UA Java SDK 3 | * 4 | * Copyright (c) Prosys PMS Ltd., . 5 | * All rights reserved. 6 | */ 7 | package com.prosysopc.ua.samples; 8 | 9 | import java.io.BufferedReader; 10 | import java.io.File; 11 | import java.io.IOException; 12 | import java.io.InputStreamReader; 13 | import java.net.URISyntaxException; 14 | import java.net.URL; 15 | import java.security.cert.CertificateException; 16 | import java.util.GregorianCalendar; 17 | import java.util.Locale; 18 | import java.util.Map; 19 | import java.util.Map.Entry; 20 | import java.util.TreeMap; 21 | import java.util.concurrent.Executors; 22 | import java.util.concurrent.ScheduledExecutorService; 23 | import java.util.concurrent.TimeUnit; 24 | 25 | import org.apache.log4j.PropertyConfigurator; 26 | import org.opcfoundation.ua.builtintypes.DateTime; 27 | import org.opcfoundation.ua.builtintypes.LocalizedText; 28 | import org.opcfoundation.ua.builtintypes.QualifiedName; 29 | import org.opcfoundation.ua.core.ApplicationDescription; 30 | import org.opcfoundation.ua.core.ApplicationType; 31 | import org.opcfoundation.ua.core.Identifiers; 32 | import org.opcfoundation.ua.core.UserTokenPolicy; 33 | import org.opcfoundation.ua.transport.security.HttpsSecurityPolicy; 34 | import org.opcfoundation.ua.transport.security.KeyPair; 35 | import org.opcfoundation.ua.transport.security.SecurityMode; 36 | import org.opcfoundation.ua.utils.CertificateUtils; 37 | import org.opcfoundation.ua.utils.EndpointUtil; 38 | import org.slf4j.Logger; 39 | import org.slf4j.LoggerFactory; 40 | import org.xml.sax.SAXException; 41 | 42 | import com.prosysopc.ua.ApplicationIdentity; 43 | import com.prosysopc.ua.CertificateValidationListener; 44 | import com.prosysopc.ua.ModelException; 45 | import com.prosysopc.ua.PkiFileBasedCertificateValidator; 46 | import com.prosysopc.ua.SecureIdentityException; 47 | import com.prosysopc.ua.StatusException; 48 | import com.prosysopc.ua.UaAddress; 49 | import com.prosysopc.ua.UaApplication.Protocol; 50 | import com.prosysopc.ua.nodes.UaProperty; 51 | import com.prosysopc.ua.server.FileNodeManager; 52 | import com.prosysopc.ua.server.NodeBuilderException; 53 | import com.prosysopc.ua.server.NodeManagerListener; 54 | import com.prosysopc.ua.server.UaInstantiationException; 55 | import com.prosysopc.ua.server.UaServer; 56 | import com.prosysopc.ua.server.UaServerException; 57 | import com.prosysopc.ua.server.UserValidator; 58 | import com.prosysopc.ua.server.compliance.ComplianceNodeManager; 59 | import com.prosysopc.ua.server.compliance.NonUaNodeComplianceNodeManager; 60 | import com.prosysopc.ua.server.nodes.FileFolderType; 61 | import com.prosysopc.ua.server.nodes.UaObjectNode; 62 | import com.prosysopc.ua.server.nodes.UaVariableNode; 63 | import com.prosysopc.ua.types.opcua.server.BuildInfoTypeNode; 64 | 65 | /** 66 | * A sample OPC UA server application. 67 | */ 68 | public class SampleConsoleServer { 69 | enum Action { 70 | ADD_NODE('a', "add a new node") { 71 | @Override 72 | ActionResult performAction(SampleConsoleServer s) { 73 | println("Enter the name of the new node (enter 'x' to cancel)"); 74 | String name = readInput(); 75 | if (!name.equals("x")) 76 | s.myNodeManager.addNode(name); 77 | return ActionResult.NOTHING; 78 | } 79 | }, 80 | 81 | CLOSE('x', "close the server") { 82 | @Override 83 | ActionResult performAction(SampleConsoleServer s) { 84 | return ActionResult.CLOSE_SERVER; 85 | } 86 | }, 87 | 88 | DELETE_NODE('d', "delete a node") { 89 | @Override 90 | ActionResult performAction(SampleConsoleServer s) throws StatusException { 91 | println("Enter the name of the node to delete (enter 'x' to cancel)"); 92 | String input = readInput(); 93 | if (!input.equals("x")) { 94 | QualifiedName nodeName = new QualifiedName(s.myNodeManager.getNamespaceIndex(), input); 95 | s.myNodeManager.deleteNode(nodeName); 96 | } 97 | return ActionResult.NOTHING; 98 | } 99 | }, 100 | 101 | ENABLE_DIAGNOSTICS('D', "enable/disable server diagnostics") { 102 | @Override 103 | ActionResult performAction(SampleConsoleServer s) throws StatusException { 104 | final UaProperty enabledFlag = s.server.getNodeManagerRoot().getServerData().getServerDiagnosticsNode() 105 | .getEnabledFlagNode(); 106 | boolean newValue = !((Boolean) enabledFlag.getValue().getValue().getValue()); 107 | enabledFlag.setValue(Boolean.valueOf(newValue)); 108 | println("Server Diagnostics " + (newValue ? "Enabled" : "Disabled")); 109 | return ActionResult.NOTHING; 110 | } 111 | }, 112 | 113 | SEND_EVENT('e', "send an event") { 114 | @Override 115 | ActionResult performAction(SampleConsoleServer s) { 116 | s.sendEvent(); 117 | return ActionResult.NOTHING; 118 | } 119 | }; 120 | 121 | static Map actionMap = new TreeMap(); 122 | 123 | static { 124 | for (Action a : Action.values()) 125 | actionMap.put(a.getKey(), a); 126 | } 127 | 128 | public static Action parseAction(Character s) { 129 | return actionMap.get(s); 130 | } 131 | 132 | private final String description; 133 | private final Character key; 134 | 135 | Action(Character key, String description) { 136 | this.key = key; 137 | this.description = description; 138 | } 139 | 140 | public String getDescription() { 141 | return description; 142 | } 143 | 144 | /** 145 | * @return the key 146 | */ 147 | public Character getKey() { 148 | return key; 149 | } 150 | 151 | /** 152 | * Perform the Action 153 | * 154 | * @param s 155 | * the SampleConsoleServer instance (inner enums are static, 156 | * so this is a "trick" to access SampleConsoleServer's 157 | * fields from the inner enum) 158 | * @return ActionResult 159 | * @throws Exception 160 | */ 161 | abstract ActionResult performAction(SampleConsoleServer s) throws Exception; 162 | } 163 | 164 | enum ActionResult { 165 | CLOSE_SERVER, NOTHING; 166 | } 167 | 168 | /** 169 | * Number of nodes to create for the Big Node Manager. This can be modified 170 | * from the command line. 171 | */ 172 | private static int bigAddressSpaceNodes = 1000; 173 | private static Logger logger = LoggerFactory.getLogger(SampleConsoleServer.class); 174 | private static boolean stackTraceOnException = false; 175 | protected static String APP_NAME = "SampleConsoleServer"; 176 | 177 | protected static String discoveryServerUrl = "opc.tcp://localhost:4840"; 178 | 179 | /** 180 | * @param args 181 | * command line arguments for the application 182 | * @throws StatusException 183 | * if the server address space creation fails 184 | * @throws UaServerException 185 | * if the server initialization parameters are invalid 186 | * @throws CertificateException 187 | * if the application certificate or private key, cannot be 188 | * loaded from the files due to certificate errors 189 | */ 190 | public static void main(String[] args) throws Exception { 191 | // Initialize log4j logging 192 | PropertyConfigurator.configureAndWatch(SampleConsoleServer.class.getResource("log.properties").getFile(), 5000); 193 | 194 | try { 195 | if (!parseCmdLineArgs(args)) { 196 | usage(); 197 | return; 198 | } 199 | } catch (IllegalArgumentException e) { 200 | println("Invalid cmd line argument: " + e.getMessage()); 201 | usage(); 202 | return; 203 | } 204 | 205 | // *** Initialization and Start Up 206 | SampleConsoleServer sampleConsoleServer = new SampleConsoleServer(); 207 | 208 | // Initialize the server 209 | sampleConsoleServer.initialize(52520, 52443, APP_NAME); 210 | 211 | // Create the address space 212 | sampleConsoleServer.createAddressSpace(); 213 | 214 | // TCP Buffer size parameters - this may help with high traffic 215 | // situations. 216 | // See http://fasterdata.es.net/host-tuning/background/ for some hints 217 | // how to use it 218 | // UATcpServer.setReceiveBufferSize(700000); 219 | 220 | // Start the server, when you have finished your own initializations 221 | // This will allow connections from the clients 222 | // Start up the server (enabling or disabling diagnostics according to 223 | // the cmd line args) 224 | sampleConsoleServer.run(getUseDiags(args)); 225 | } 226 | 227 | /** 228 | * @param e 229 | */ 230 | public static void printException(Exception e) { 231 | if (stackTraceOnException) 232 | e.printStackTrace(); 233 | else { 234 | println(e.toString()); 235 | if (e.getCause() != null) 236 | println("Caused by: " + e.getCause()); 237 | } 238 | } 239 | 240 | /** 241 | * @param string 242 | */ 243 | public static void println(String string) { 244 | System.out.println(string); 245 | } 246 | 247 | /** 248 | * @return 249 | */ 250 | private static Action readAction() { 251 | return Action.parseAction(readInput().charAt(0)); 252 | } 253 | 254 | /** 255 | * @return 256 | */ 257 | private static String readInput() { 258 | BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); 259 | String s = null; 260 | do 261 | try { 262 | s = stdin.readLine(); 263 | } catch (IOException e) { 264 | printException(e); 265 | } 266 | while ((s == null) || (s.length() == 0)); 267 | return s; 268 | } 269 | 270 | /** 271 | * Check if diagnostics is enabled from the command line 272 | * 273 | * @param args 274 | * @return 275 | */ 276 | protected static boolean getUseDiags(String[] args) { 277 | for (String arg : args) 278 | if (arg.equals("-enablesessiondiags")) 279 | return true; 280 | return false; 281 | } 282 | 283 | /** 284 | * Parse Command line arguments. Expected options: 285 | *

    286 | *
  • -d connect to a discovery server instead of a normal server 287 | *
  • -t show stack trace with exceptions 288 | *
  • -n do not prompt for the server URI, if it is not specified 289 | *
290 | * 291 | * Also expects to get the serverUri - if not, it is prompted (unless -n 292 | * given) 293 | * 294 | * @param args 295 | * the arguments 296 | * @return 297 | */ 298 | protected static boolean parseCmdLineArgs(String[] args) throws IllegalArgumentException { 299 | int i = 0; 300 | while ((args.length > i) && ((args[i].startsWith("-") || args[i].startsWith("/")))) { 301 | if (args[i].equals("-t")) 302 | stackTraceOnException = true; 303 | else if (args[i].equals("-b")) 304 | bigAddressSpaceNodes = Integer.parseInt(args[++i]); 305 | else if (args[i].equals("-k")) 306 | CertificateUtils.setKeySize(Integer.parseInt(args[++i])); 307 | else if (args[i].equals("-d")) 308 | discoveryServerUrl = args[++i]; 309 | else if (args[i].equals("-d-")) 310 | discoveryServerUrl = ""; 311 | else if (args[i].equals("-?")) 312 | return false; 313 | else 314 | throw new IllegalArgumentException(args[i]); 315 | i++; 316 | } 317 | return true; 318 | } 319 | 320 | /** 321 | * 322 | */ 323 | protected static void usage() { 324 | println("Usage: " + APP_NAME + " [-b] [-t] [serverUri]"); 325 | println(" -b n Define number of nodes to create in the BigNodeManager (default=1000)"); 326 | println(" -k keySize Define the size of the public key of the application certificate (default 1024; other valid values 2048, 4096)"); 327 | println(" -d url Define the DiscoveryServerUrl to register the application to"); 328 | println(" -d- Define that the application should not be registered to a DiscoveryServer"); 329 | println(" -t Output stack trace for errors"); 330 | println(" -? Show this help text"); 331 | println(""); 332 | } 333 | 334 | static void printMenu() { 335 | println(""); 336 | println(""); 337 | println(""); 338 | System.out.println("-------------------------------------------------------"); 339 | for (Entry a : Action.actionMap.entrySet()) 340 | println("- Enter " + a.getKey() + " to " + a.getValue().getDescription()); 341 | } 342 | 343 | private final Runnable simulationTask = new Runnable() { 344 | 345 | @Override 346 | public void run() { 347 | if (server.isRunning()) { 348 | logger.debug("Simulating"); 349 | simulate(); 350 | } 351 | } 352 | }; 353 | 354 | private final ScheduledExecutorService simulator = Executors.newScheduledThreadPool(10); 355 | protected ComplianceNodeManager complianceNodeManager; 356 | protected FileNodeManager fileNodeManager; 357 | protected MyBigNodeManager myBigNodeManager; 358 | protected MyHistorian myHistorian = new MyHistorian(); 359 | protected MyNodeManager myNodeManager; 360 | protected NodeManagerListener myNodeManagerListener = new MyNodeManagerListener(); 361 | protected NonUaNodeComplianceNodeManager nonUaNodeComplianceManager; 362 | protected UaServer server; 363 | protected UserValidator userValidator; 364 | protected final CertificateValidationListener validationListener = new MyCertificateValidationListener(); 365 | 366 | public UaServer getServer() { 367 | return server; 368 | } 369 | 370 | /** 371 | * Create a sample node manager, which does not use UaNode objects. These 372 | * are suitable for managing big address spaces for data that is in practice 373 | * available from another existing subsystem. 374 | */ 375 | private void createBigNodeManager() { 376 | myBigNodeManager = new MyBigNodeManager(server, "http://www.prosysopc.com/OPCUA/SampleBigAddressSpace", 377 | bigAddressSpaceNodes); 378 | } 379 | 380 | /** 381 | * @throws StatusException 382 | * 383 | */ 384 | private void createFileNodeManager() throws StatusException { 385 | fileNodeManager = new FileNodeManager(getServer(), "http://www.prosysopc.com/OPCUA/FileTransfer", "Files"); 386 | getServer().getNodeManagerRoot().getObjectsFolder().addReference(fileNodeManager.getRootFolder(), 387 | Identifiers.Organizes, false); 388 | FileFolderType folder = fileNodeManager.addFolder("Folder"); 389 | folder.setFilter("*"); 390 | } 391 | 392 | /** 393 | * Create a sample address space with a new folder, a device object, a level 394 | * variable, and an alarm condition. 395 | *

396 | * The method demonstrates the basic means to create the nodes and 397 | * references into the address space. 398 | *

399 | * Simulation of the level measurement is defined in 400 | * {@link #startSimulation()} 401 | * 402 | * @throws StatusException 403 | * if the referred type nodes are not found from the address 404 | * space 405 | * @throws UaInstantiationException 406 | * @throws NodeBuilderException 407 | * @throws URISyntaxException 408 | * @throws ModelException 409 | * @throws IOException 410 | * @throws SAXException 411 | * 412 | */ 413 | protected void createAddressSpace() throws StatusException, UaInstantiationException, NodeBuilderException { 414 | // Load the standard information models 415 | loadInformationModels(); 416 | 417 | // My Node Manager 418 | myNodeManager = new MyNodeManager(server, MyNodeManager.NAMESPACE); 419 | 420 | myNodeManager.addListener(myNodeManagerListener); 421 | 422 | // My I/O Manager Listener 423 | myNodeManager.getIoManager().addListeners(new MyIoManagerListener()); 424 | 425 | // My HistoryManager 426 | myNodeManager.getHistoryManager().setListener(myHistorian); 427 | 428 | // ComplianceNodeManagers 429 | complianceNodeManager = new ComplianceNodeManager(server, "http://www.prosysopc.com/OPCUA/ComplianceNodes"); 430 | nonUaNodeComplianceManager = new NonUaNodeComplianceNodeManager(server, 431 | "http://www.prosysopc.com/OPCUA/ComplianceNonUaNodes"); 432 | 433 | // A sample node manager that can handle a big amount of UA nodes 434 | // without creating UaNode objects in memory 435 | createBigNodeManager(); 436 | 437 | createFileNodeManager(); 438 | 439 | logger.info("Address space created."); 440 | } 441 | 442 | /** 443 | * Initialize the information to the Server BuildInfo structure 444 | */ 445 | protected void initBuildInfo() { 446 | // Initialize BuildInfo - using the version info from the SDK 447 | // You should replace this with your own build information 448 | 449 | final BuildInfoTypeNode buildInfo = server.getNodeManagerRoot().getServerData().getServerStatusNode() 450 | .getBuildInfoNode(); 451 | 452 | buildInfo.setProductName(APP_NAME); 453 | 454 | // Fetch version information from the package manifest 455 | final Package sdkPackage = UaServer.class.getPackage(); 456 | final String implementationVersion = sdkPackage.getImplementationVersion(); 457 | if (implementationVersion != null) { 458 | int splitIndex = implementationVersion.lastIndexOf("."); 459 | final String softwareVersion = implementationVersion.substring(0, splitIndex); 460 | String buildNumber = implementationVersion.substring(splitIndex + 1); 461 | 462 | buildInfo.setManufacturerName(sdkPackage.getImplementationVendor()); 463 | buildInfo.setSoftwareVersion(softwareVersion); 464 | buildInfo.setBuildNumber(buildNumber); 465 | 466 | } 467 | 468 | final URL classFile = UaServer.class.getResource("/com/prosysopc/ua/samples/server/SampleConsoleServer.class"); 469 | if (classFile != null) { 470 | final File mfFile = new File(classFile.getFile()); 471 | GregorianCalendar c = new GregorianCalendar(); 472 | c.setTimeInMillis(mfFile.lastModified()); 473 | buildInfo.setBuildDate(new DateTime(c)); 474 | } 475 | } 476 | 477 | /** 478 | * 479 | */ 480 | protected void initHistory() { 481 | for (UaVariableNode v : myNodeManager.getHistorizableVariables()) 482 | myHistorian.addVariableHistory(v); 483 | for (UaObjectNode o : myNodeManager.getHistorizableEvents()) 484 | myHistorian.addEventHistory(o); 485 | } 486 | 487 | protected void initialize(int port, int httpsPort, String applicationName) 488 | throws SecureIdentityException, IOException, UaServerException { 489 | 490 | // *** Create the server 491 | server = new UaServer(); 492 | // Uncomment to enable IPv6 networking 493 | // server.setEnableIPv6(true); 494 | 495 | // Use PKI files to keep track of the trusted and rejected client 496 | // certificates... 497 | final PkiFileBasedCertificateValidator validator = new PkiFileBasedCertificateValidator(); 498 | server.setCertificateValidator(validator); 499 | // ...and react to validation results with a custom handler 500 | validator.setValidationListener(validationListener); 501 | 502 | // *** Application Description is sent to the clients 503 | ApplicationDescription appDescription = new ApplicationDescription(); 504 | // 'localhost' (all lower case) in the ApplicationName and 505 | // ApplicationURI is converted to the actual host name of the computer 506 | // (including the possible domain part) in which the application is run. 507 | // (as available from ApplicationIdentity.getActualHostName()) 508 | // 'hostname' is converted to the host name without the domain part. 509 | // (as available from 510 | // ApplicationIdentity.getActualHostNameWithoutDomain()) 511 | appDescription.setApplicationName(new LocalizedText(applicationName + "@localhost")); 512 | appDescription.setApplicationUri("urn:localhost:OPCUA:" + applicationName); 513 | appDescription.setProductUri("urn:prosysopc.com:OPCUA:" + applicationName); 514 | appDescription.setApplicationType(ApplicationType.Server); 515 | 516 | // *** Server Endpoints 517 | // TCP Port number for the UA Binary protocol 518 | server.setPort(Protocol.OpcTcp, port); 519 | // TCP Port for the HTTPS protocol 520 | server.setPort(Protocol.Https, httpsPort); 521 | 522 | // optional server name part of the URI (default for all protocols) 523 | server.setServerName("OPCUA/" + applicationName); 524 | 525 | // Optionally restrict the InetAddresses to which the server is bound. 526 | // You may also specify the addresses for each Protocol. 527 | // This is the default (isEnableIPv6 defines whether IPv6 address should 528 | // be included in the bound addresses. Note that it requires Java 7 or 529 | // later to work in practice in Windows): 530 | server.setBindAddresses(EndpointUtil.getInetAddresses(server.isEnableIPv6())); 531 | 532 | // *** Certificates 533 | 534 | File privatePath = new File(validator.getBaseDir(), "private"); 535 | 536 | // Define a certificate for a Certificate Authority (CA) which is used 537 | // to issue the keys. Especially 538 | // the HTTPS certificate should be signed by a CA certificate, in order 539 | // to make the .NET applications trust it. 540 | // 541 | // If you have a real CA, you should use that instead of this sample CA 542 | // and create the keys with it. 543 | // Here we use the IssuerCertificate only to sign the HTTPS certificate 544 | // (below) and not the Application Instance Certificate. 545 | KeyPair issuerCertificate = ApplicationIdentity.loadOrCreateIssuerCertificate("ProsysSampleCA", privatePath, 546 | "opcua", 3650, false); 547 | 548 | // If you wish to use big certificates (4096 bits), you will need to 549 | // define two certificates for your application, since to interoperate 550 | // with old applications, you will also need to use a small certificate 551 | // (up to 2048 bits). 552 | 553 | // Also, 4096 bits can only be used with Basic256Sha256 security 554 | // profile, which is currently not enabled by default, so we will also 555 | // leave the the keySizes array as null. In that case, the default key 556 | // size defined by CertificateUtils.getKeySize() is used. 557 | int[] keySizes = null; 558 | 559 | // Use 0 to use the default keySize and default file names as before 560 | // (for other values the file names will include the key size). 561 | // keySizes = new int[] { 0, 4096 }; 562 | 563 | // *** Application Identity 564 | 565 | // Define the Server application identity, including the Application 566 | // Instance Certificate (but don't sign it with the issuerCertificate as 567 | // explained above). 568 | final ApplicationIdentity identity = ApplicationIdentity.loadOrCreateCertificate(appDescription, 569 | "Sample Organisation", /* Private Key Password */"opcua", 570 | /* Key File Path */privatePath, 571 | /* Issuer Certificate & Private Key */null, 572 | /* Key Sizes for instance certificates to create */keySizes, 573 | /* Enable renewing the certificate */true); 574 | 575 | // Create the HTTPS certificate bound to the hostname. 576 | // The HTTPS certificate must be created, if you enable HTTPS. 577 | String hostName = ApplicationIdentity.getActualHostName(); 578 | identity.setHttpsCertificate(ApplicationIdentity.loadOrCreateHttpsCertificate(appDescription, hostName, "opcua", 579 | issuerCertificate, privatePath, true)); 580 | 581 | server.setApplicationIdentity(identity); 582 | 583 | // *** Security settings 584 | // Define the security modes to support for the Binary protocol - 585 | // ALL is the default 586 | server.setSecurityModes(SecurityMode.ALL); 587 | // The TLS security policies to use for HTTPS 588 | server.getHttpsSettings().setHttpsSecurityPolicies(HttpsSecurityPolicy.ALL); 589 | 590 | // Number of threads to reserve for the HTTPS server, default is 10 591 | // server.setHttpsWorkerThreadCount(10); 592 | 593 | // Define a custom certificate validator for the HTTPS certificates 594 | server.getHttpsSettings().setCertificateValidator(validator); 595 | // client.getHttpsSettings().setCertificateValidator(...); 596 | 597 | // Or define just a validation rule to check the hostname defined for 598 | // the certificate; ALLOW_ALL_HOSTNAME_VERIFIER is the default 599 | // client.getHttpsSettings().setHostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); 600 | 601 | // Define the supported user Token policies 602 | server.addUserTokenPolicy(UserTokenPolicy.ANONYMOUS); 603 | server.addUserTokenPolicy(UserTokenPolicy.SECURE_USERNAME_PASSWORD); 604 | server.addUserTokenPolicy(UserTokenPolicy.SECURE_CERTIFICATE); 605 | // Define a validator for checking the user accounts 606 | final PkiFileBasedCertificateValidator userCertValidator = new PkiFileBasedCertificateValidator("USERS_PKI"); 607 | userValidator = new MyUserValidator(userCertValidator); 608 | server.setUserValidator(userValidator); 609 | 610 | // Register on the local discovery server (if present) 611 | try { 612 | UaAddress discoveryAddress = new UaAddress(discoveryServerUrl); 613 | server.setDiscoveryServerAddress(discoveryAddress); 614 | } catch (URISyntaxException e) { 615 | logger.error("DiscoveryURL is not valid", e); 616 | } 617 | 618 | // *** init() creates the service handlers and the default endpoints 619 | // according to the above settings 620 | server.init(); 621 | 622 | initBuildInfo(); 623 | 624 | // "Safety limits" for ill-behaving clients 625 | server.getSessionManager().setMaxSessionCount(500); 626 | server.getSessionManager().setMaxSessionTimeout(3600000); // one hour 627 | server.getSubscriptionManager().setMaxSubscriptionCount(50); 628 | 629 | // You can do your own additions to server initializations here 630 | 631 | } 632 | 633 | /** 634 | * Load information models into the address space. Also register classes, to 635 | * be able to use the respective Java classes with 636 | * NodeManagerUaNode.createInstance(). 637 | * 638 | * See the codegen/Readme.md on instructions how to use your own models. 639 | */ 640 | protected void loadInformationModels() { 641 | // Uncomment to take the extra information models in use. 642 | 643 | // // Register generated classes 644 | // server.registerModel(com.prosysopc.ua.types.di.server.InformationModel.MODEL); 645 | // server.registerModel(com.prosysopc.ua.types.adi.server.InformationModel.MODEL); 646 | // server.registerModel(com.prosysopc.ua.types.plc.server.InformationModel.MODEL); 647 | // 648 | // // Load the standard information models 649 | // try { 650 | // server.getAddressSpace().loadModel( 651 | // UaServer.class.getResource("Opc.Ua.Di.NodeSet2.xml") 652 | // .toURI()); 653 | // server.getAddressSpace().loadModel( 654 | // UaServer.class.getResource("Opc.Ua.Adi.NodeSet2.xml") 655 | // .toURI()); 656 | // server.getAddressSpace().loadModel( 657 | // UaServer.class.getResource("Opc.Ua.Plc.NodeSet2.xml") 658 | // .toURI()); 659 | // } catch (Exception e) { 660 | // throw new RuntimeException(e); 661 | // } 662 | } 663 | 664 | /* 665 | * Main loop for user selecting OPC UA calls 666 | */ 667 | protected void mainMenu() { 668 | 669 | /******************************************************************************/ 670 | /* Wait for user command to execute next action. */ 671 | do { 672 | printMenu(); 673 | 674 | try { 675 | Action action = readAction(); 676 | if (action != null) { 677 | ActionResult actionResult = action.performAction(this); 678 | switch (actionResult) { 679 | case CLOSE_SERVER: 680 | return; // closes server 681 | case NOTHING: 682 | continue; // continue looping menu 683 | } 684 | } 685 | } catch (Exception e) { 686 | printException(e); 687 | } 688 | 689 | } while (true); 690 | /******************************************************************************/ 691 | } 692 | 693 | /** 694 | * Run the server. 695 | * 696 | * @param enableSessionDiagnostics 697 | * @throws UaServerException 698 | * @throws StatusException 699 | */ 700 | protected void run(boolean enableSessionDiagnostics) throws UaServerException, StatusException { 701 | server.start(); 702 | initHistory(); 703 | if (enableSessionDiagnostics) 704 | server.getNodeManagerRoot().getServerData().getServerDiagnosticsNode().setEnabled(true); 705 | startSimulation(); 706 | 707 | // *** Main Menu Loop 708 | mainMenu(); 709 | 710 | // *** End 711 | stopSimulation(); 712 | // Notify the clients about a shutdown, with a 5 second delay 713 | println("Shutting down..."); 714 | server.shutdown(5, new LocalizedText("Closed by user", Locale.ENGLISH)); 715 | println("Closed."); 716 | } 717 | 718 | /** 719 | * 720 | */ 721 | protected void sendEvent() { 722 | myNodeManager.sendEvent(); 723 | } 724 | 725 | protected void simulate() { 726 | myNodeManager.simulate(); 727 | myBigNodeManager.simulate(); 728 | } 729 | 730 | /** 731 | * Starts the simulation of the level measurement. 732 | */ 733 | protected void startSimulation() { 734 | simulator.scheduleAtFixedRate(simulationTask, 1000, 1000, TimeUnit.MILLISECONDS); 735 | logger.info("Simulation started."); 736 | } 737 | 738 | /** 739 | * Ends simulation. 740 | */ 741 | protected void stopSimulation() { 742 | simulator.shutdown(); 743 | logger.info("Simulation stopped."); 744 | } 745 | } 746 | --------------------------------------------------------------------------------