10/01/2019
I am new to OPC UA development and have received a task to design an OPC UA server. The issue is that we want to be able to access (read and update) the nodes inside the namespace not only from the OPC UA clients but also through an internal thread running insider the server application. This can be enabled by keeping a pointer to the node during the node setup on the server, i.e. CreateAddressSpace (inherited from the CustomNodeManager2) like the code below shows:
public override void CreateAddressSpace(IDictionary<NodeId, IList<IReference>> externalReferences)
{
Dictionary<string, UNECEtoOPCUA> csvData = CreateUNECEtoOPCUADictionary();lock (Lock)
{
LoadPredefinedNodes(SystemContext, externalReferences);IList<IReference> references = null;
if (!externalReferences.TryGetValue(Opc.Ua.ObjectIds.ObjectsFolder, out references))
{
externalReferences[Opc.Ua.ObjectIds.ObjectsFolder] = references = new List<IReference>();
}/*** Setup MyStorage node ***/
// This code is auto generated by the OPC UA model compiler
BaseObjectState passiveNode = (BaseObjectState)FindPredefinedNode(new NodeId(Objects.ExAct_TheStorage, NamespaceIndexes[0]), typeof(BaseObjectState));
myStorage = new MyStorageState(null); //variable declared as private
myStorage.Create(SystemContext, passiveNode);
Node_Setup.SetupMyStorageState.Setup(csvData, myStorage, SystemContext, NamespaceIndexes);
// Must do this AFTER any modifications to the node or its childnodes (especially if you're creating new instances (see InstrumentRange above)).
AddPredefinedNode(SystemContext, myStorage);
}
}
And then reach the parameter value inside the node for which I so far have two ways described below:
First, by simply updating the value of the node by going directly to node variable:
public void UpdateMystorage(int newValue)
{
// simply update the value field of the desired parameter:
myStorage.param1.Value += 1;
}
Second, calling the write method provided by the MasterNodeManager:
public void UpdateMystorage(int newValue)
{
WriteValue valueToWrite = new WriteValue();
valueToWrite.NodeId = myStorage.NodeId;
valueToWrite.AttributeId = Attributes.Value;
valueToWrite.Value.Value = newValue;
valueToWrite.Value.StatusCode = StatusCodes.Good;
valueToWrite.Value.ServerTimestamp = DateTime.MinValue;
valueToWrite.Value.SourceTimestamp = DateTime.MinValue;
var nodesToWrite = new WriteValueCollection();
nodesToWrite.Add(valueToWrite);StatusCodeCollection results = null;
DiagnosticInfoCollection diagnosticInfos = null;
var operationContext = new Opc.Ua.Server.OperationContext(new RequestHeader(), Opc.Ua.Server.RequestType.Write);
server.masterNodeManager.Write(operationContext, nodesToWrite, out results, out diagnosticInfos);
}
My first question is if it is at all a good practice to update a node directly from the server thread?
And in case YES, which of the above methods is better? The latter is more complex but I have a feeling that the first approach "might" cause some concurrency issues if a client is to reach the data at the same time.
10/01/2019
Randy Armstrong said
Look at the code in the base NodeManager class (CustomNodeManager).See what locks are created. You must do the same.
Thanks for the answer, using the following code should have the same effect:
server.masterNodeManager.Write(operationContext, nodesToWrite, out results, out diagnosticInfos);
ending up in proper locks.
I just thought it is better to use the already created lock instead of writing my own.
1 Guest(s)