https://github.com/eclipse/milo/blob/ master/milo-examples/server-examples/src/main/java/org/eclipse/milo/examples/server/ExampleNamespace.java
Когда я запустите сервер и подключитесь к нему, как я вижу (используя UAExpert):

Если я вызываю метод SendJoiningprocess внутриqualityServer, он работает правильно, а если вызвать метод под myObject, я вижу:

и если я позвоню, то получу:

Я пытался прочитать документацию, погуглить, если нашел какой-нибудь пример, а также попытаться изменить код, чтобы достичь своей цели, но пока ничего не получается;
Единственная разница между работающим и неработающим методом заключается в ссылке, я могу опубликовать весь код, это просто класс SampleServer (с небольшими изменениями) в репозитории milo:
Код: Выделить всё
/*
* Copyright (c) 2021 the Eclipse Milo Authors
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: E P L - 2 . 0 < b r / > * / < b r / > < b r / > p a c k a g e o r g . e c l i p s e . m i l o . e x a m p l e s . s e r v e r ; < b r / > < b r / > i m p o r t java.lang.reflect.Array;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import org.eclipse.milo.examples.server.methods.GenerateEventMethod;
import org.eclipse.milo.examples.server.methods.GetLatestResult;
import org.eclipse.milo.examples.server.methods.GetResultIdListFiltered;
import org.eclipse.milo.examples.server.methods.ResultManagement;
import org.eclipse.milo.examples.server.methods.SendProcessJoin;
import org.eclipse.milo.examples.server.methods.SqrtMethod;
import org.eclipse.milo.examples.server.types.CustomEnumType;
import org.eclipse.milo.examples.server.types.CustomStructType;
import org.eclipse.milo.examples.server.types.CustomUnionType;
import org.eclipse.milo.opcua.sdk.core.AccessLevel;
import org.eclipse.milo.opcua.sdk.core.Reference;
import org.eclipse.milo.opcua.sdk.core.ValueRank;
import org.eclipse.milo.opcua.sdk.core.ValueRanks;
import org.eclipse.milo.opcua.sdk.server.Lifecycle;
import org.eclipse.milo.opcua.sdk.server.OpcUaServer;
import org.eclipse.milo.opcua.sdk.server.api.DataItem;
import org.eclipse.milo.opcua.sdk.server.api.ManagedNamespaceWithLifecycle;
import org.eclipse.milo.opcua.sdk.server.api.MonitoredItem;
import org.eclipse.milo.opcua.sdk.server.dtd.DataTypeDictionaryManager;
import org.eclipse.milo.opcua.sdk.server.model.nodes.objects.BaseEventTypeNode;
import org.eclipse.milo.opcua.sdk.server.model.nodes.objects.ServerTypeNode;
import org.eclipse.milo.opcua.sdk.server.model.nodes.variables.AnalogItemTypeNode;
import org.eclipse.milo.opcua.sdk.server.nodes.UaDataTypeNode;
import org.eclipse.milo.opcua.sdk.server.nodes.UaFolderNode;
import org.eclipse.milo.opcua.sdk.server.nodes.UaMethodNode;
import org.eclipse.milo.opcua.sdk.server.nodes.UaNode;
import org.eclipse.milo.opcua.sdk.server.nodes.UaObjectNode;
import org.eclipse.milo.opcua.sdk.server.nodes.UaObjectTypeNode;
import org.eclipse.milo.opcua.sdk.server.nodes.UaVariableNode;
import org.eclipse.milo.opcua.sdk.server.nodes.factories.NodeFactory;
import org.eclipse.milo.opcua.sdk.server.nodes.filters.AttributeFilters;
import org.eclipse.milo.opcua.sdk.server.util.SubscriptionModel;
import org.eclipse.milo.opcua.stack.core.AttributeId;
import org.eclipse.milo.opcua.stack.core.BuiltinDataType;
import org.eclipse.milo.opcua.stack.core.Identifiers;
import org.eclipse.milo.opcua.stack.core.UaException;
import org.eclipse.milo.opcua.stack.core.types.builtin.ByteString;
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
import org.eclipse.milo.opcua.stack.core.types.builtin.DateTime;
import org.eclipse.milo.opcua.stack.core.types.builtin.ExtensionObject;
import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName;
import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
import org.eclipse.milo.opcua.stack.core.types.builtin.XmlElement;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
import org.eclipse.milo.opcua.stack.core.types.enumerated.StructureType;
import org.eclipse.milo.opcua.stack.core.types.structured.EnumDefinition;
import org.eclipse.milo.opcua.stack.core.types.structured.EnumDescription;
import org.eclipse.milo.opcua.stack.core.types.structured.EnumField;
import org.eclipse.milo.opcua.stack.core.types.structured.Range;
import org.eclipse.milo.opcua.stack.core.types.structured.StructureDefinition;
import org.eclipse.milo.opcua.stack.core.types.structured.StructureDescription;
import org.eclipse.milo.opcua.stack.core.types.structured.StructureField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ubyte;
import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;
import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ulong;
import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ushort;
public class ExampleNamespace extends ManagedNamespaceWithLifecycle {
public static final String NAMESPACE_URI = "urn:eclipse:milo:hello-world";
private final Logger logger = LoggerFactory.getLogger(getClass());
private volatile Thread eventThread;
private volatile boolean keepPostingEvents = true;
private final Random random = new Random();
private final DataTypeDictionaryManager dictionaryManager;
private final SubscriptionModel subscriptionModel;
ExampleNamespace(OpcUaServer server) {
super(server, NAMESPACE_URI);
subscriptionModel = new SubscriptionModel(server, this);
dictionaryManager = new DataTypeDictionaryManager(getNodeContext(), NAMESPACE_URI);
getLifecycleManager().addLifecycle(dictionaryManager);
getLifecycleManager().addLifecycle(subscriptionModel);
getLifecycleManager().addStartupTask(this::createAndAddNodes);
getLifecycleManager().addLifecycle(new Lifecycle() {
@Override
public void startup() {
startBogusEventNotifier();
}
@Override
public void shutdown() {
try {
keepPostingEvents = false;
eventThread.interrupt();
eventThread.join();
} catch (InterruptedException ignored) {
// ignored
}
}
});
}
private void createAndAddNodes() {
// Create a "QualityServer" folder and add it to the node manager
NodeId folderNodeId = newNodeId("QualityServer");
UaFolderNode folderNode = new UaFolderNode(
getNodeContext(),
folderNodeId,
newQualifiedName("QualityServer"),
LocalizedText.english("QualityServer")
);
getNodeManager().addNode(folderNode);
// Make sure our new folder shows up under the server's Objects folder.
folderNode.addReference(new Reference(
folderNode.getNodeId(),
Identifiers.Organizes,
Identifiers.ObjectsFolder.expanded(),
false
));
addSqrtMethod(folderNode);
//addCustomObjectTypeAndInstance(folderNode);
addJoinProcessAndInstance(folderNode);
addJoinProcee(folderNode);
}
private void addJoinProcee(UaFolderNode folderNode) {
UaMethodNode methodNode = UaMethodNode.builder(getNodeContext())
.setNodeId(newNodeId("HelloWorld/SendJoiningprocess1"))
// .setNodeId(newNodeId(72))
.setBrowseName(newQualifiedName("SendJoiningprocess"))
.setDisplayName(new LocalizedText(null, "SendJoiningprocess"))
.setDescription(
LocalizedText.english("bla bla bla."))
.build();
SendProcessJoin sjp = new SendProcessJoin(methodNode);
//associo il metodo all handelr per I/O
methodNode.setInputArguments(sjp.getInputArguments());
methodNode.setOutputArguments(sjp.getOutputArguments());
methodNode.setInvocationHandler(sjp);
//aggiungo il metodo al nodo
getNodeManager().addNode(methodNode);
//objectTypeNode.addComponent(methodNode);
methodNode.addReference(new Reference(
methodNode.getNodeId(),
// Identifiers.HasModellingRule,
// Identifiers.ModellingRule_Mandatory.expanded(),
Identifiers.HasComponent,
folderNode.getNodeId().expanded(),
false
));
}
private void addSqrtMethod(UaFolderNode folderNode) {
UaMethodNode methodNode = UaMethodNode.builder(getNodeContext())
.setNodeId(newNodeId("HelloWorld/sqrt(x)"))
.setBrowseName(newQualifiedName("sqrt(x)"))
.setDisplayName(new LocalizedText(null, "sqrt(x)"))
.setDescription(
LocalizedText.english("Returns the correctly rounded positive square root of a double value."))
.build();
SqrtMethod sqrtMethod = new SqrtMethod(methodNode);
methodNode.setInputArguments(sqrtMethod.getInputArguments());
methodNode.setOutputArguments(sqrtMethod.getOutputArguments());
methodNode.setInvocationHandler(sqrtMethod);
getNodeManager().addNode(methodNode);
methodNode.addReference(new Reference(
methodNode.getNodeId(),
Identifiers.HasComponent,
folderNode.getNodeId().expanded(),
false
));
}
private void startBogusEventNotifier() {
// Set the EventNotifier bit on Server Node for Events.
UaNode serverNode = getServer()
.getAddressSpaceManager()
.getManagedNode(Identifiers.Server)
.orElse(null);
if (serverNode instanceof ServerTypeNode) {
((ServerTypeNode) serverNode).setEventNotifier(ubyte(1));
// Post a bogus Event every couple seconds
eventThread = new Thread(() -> {
while (keepPostingEvents) {
try {
BaseEventTypeNode eventNode = getServer().getEventFactory().createEvent(
newNodeId(UUID.randomUUID()),
Identifiers.BaseEventType
);
eventNode.setBrowseName(new QualifiedName(1, "foo"));
eventNode.setDisplayName(LocalizedText.english("foo"));
eventNode.setEventId(ByteString.of(new byte[]{0, 1, 2, 3}));
eventNode.setEventType(Identifiers.BaseEventType);
eventNode.setSourceNode(serverNode.getNodeId());
eventNode.setSourceName(serverNode.getDisplayName().getText());
eventNode.setTime(DateTime.now());
eventNode.setReceiveTime(DateTime.NULL_VALUE);
eventNode.setMessage(LocalizedText.english("event message!"));
eventNode.setSeverity(ushort(2));
//noinspection UnstableApiUsage
getServer().getEventBus().post(eventNode);
eventNode.delete();
} catch (Throwable e) {
logger.error("Error creating EventNode: {}", e.getMessage(), e);
}
try {
//noinspection BusyWait
Thread.sleep(2_000);
//logger.info("thread sleppp");
} catch (InterruptedException ignored) {
// ignored
}
}
}, "bogus-event-poster");
eventThread.start();
}
}
@Override
public void onDataItemsCreated(List dataItems) {
subscriptionModel.onDataItemsCreated(dataItems);
}
@Override
public void onDataItemsModified(List dataItems) {
subscriptionModel.onDataItemsModified(dataItems);
}
@Override
public void onDataItemsDeleted(List dataItems) {
subscriptionModel.onDataItemsDeleted(dataItems);
}
@Override
public void onMonitoringModeChanged(List monitoredItems) {
subscriptionModel.onMonitoringModeChanged(monitoredItems);
}
private void addJoinProcessAndInstance(UaFolderNode folderNode) {
// Define a new ObjectType called "MyObjectType".
UaObjectTypeNode objectTypeNode = UaObjectTypeNode.builder(getNodeContext())
.setNodeId(newNodeId("ObjectTypes/MyObjectType"))
.setBrowseName(newQualifiedName("MyObjectType"))
.setDisplayName(LocalizedText.english("MyObjectType"))
.setIsAbstract(false)
.build();
// "Foo" and "Bar" are members. These nodes are what are called "instance declarations" by the spec.
//***************************
//definisco il nodo method
UaMethodNode methodNode = UaMethodNode.builder(getNodeContext())
.setNodeId(newNodeId("HelloWorld/SendJoiningprocess"))
// .setNodeId(newNodeId(72))
.setBrowseName(newQualifiedName("SendJoiningprocess"))
.setDisplayName(new LocalizedText(null, "SendJoiningprocess"))
.setDescription(
LocalizedText.english("bla bla bla."))
.build();
//definisco la classse dove metto argomente e metodo da chiamare
SendProcessJoin sjp = new SendProcessJoin(methodNode);
//associo il metodo all handelr per I/O
methodNode.setInputArguments(sjp.getInputArguments());
methodNode.setOutputArguments(sjp.getOutputArguments());
methodNode.setInvocationHandler(sjp);
//aggiungo il metodo al nodo
//getNodeManager().addNode(methodNode);
objectTypeNode.addComponent(methodNode);
methodNode.addReference(new Reference(
methodNode.getNodeId(),
//Identifiers.HasModellingRule,
//Identifiers.HasComponent,
//objectTypeNode.getNodeId().expanded(),
Identifiers.HasModellingRule,
Identifiers.ModellingRule_Mandatory.expanded(),
false
));
//Identifiers.HasComponent,
//folderNode.getNodeId().expanded(),
//***************************
// Tell the ObjectTypeManager about our new type.
// This let's us use NodeFactory to instantiate instances of the type.
getServer().getObjectTypeManager().registerObjectType(
objectTypeNode.getNodeId(),
UaObjectNode.class,
UaObjectNode::new
);
// Add the inverse SubtypeOf relationship.
objectTypeNode.addReference(new Reference(
objectTypeNode.getNodeId(),
Identifiers.HasSubtype,
Identifiers.BaseObjectType.expanded(),
false
));
// Add type definition and declarations to address space.
getNodeManager().addNode(objectTypeNode);
//getNodeManager().addNode(foo);
getNodeManager().addNode(methodNode);
// Use NodeFactory to create instance of MyObjectType called "MyObject".
// NodeFactory takes care of recursively instantiating MyObject member nodes
// as well as adding all nodes to the address space.
try {
UaObjectNode myObject = (UaObjectNode) getNodeFactory().createNode(
newNodeId("HelloWorld/MyObject"),
objectTypeNode.getNodeId()
);
myObject.setBrowseName(newQualifiedName("MyObject"));
myObject.setDisplayName(LocalizedText.english("MyObject"));
// Add forward and inverse references from the root folder.
folderNode.addOrganizes(myObject);
myObject.addReference(new Reference(
myObject.getNodeId(),
Identifiers.Organizes,
folderNode.getNodeId().expanded(),
false
));
} catch (UaException e) {
logger.error("Error creating MyObjectType instance: {}", e.getMessage(), e);
}
}
}
Подробнее здесь: https://stackoverflow.com/questions/786 ... lipse-milo
Мобильная версия