.
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 |
--------------------------------------------------------------------------------