Reputation: 19396
I have a panel Siemens TP1200 Comfort that I have configure as OPC AU server. This panel has some tags (nodes) from which I would like to get the value from a C# application.
I have read the examples of the OPC UA github project: https://github.com/OPCFoundation/UA-.NETStandard.
I am able to connect to the panel and get the root, but if I debug and I check the structure of the root, I don't see any property for the value neither a collection of childs nodes, so I don't know how to find a node by its name.
Is there a method something like GetNodeVale(NodeName);
I don't show any code because I am really lost with OPC, it is my first attempt to implement a simple client in which I want to read that of a node (a tag) but I am not able to do it.
Thanks.
Upvotes: 5
Views: 10137
Reputation: 132
If you are using the OPCFoundation.Netstandard.OPC.Ua SDK. I can provide you some examples, since i just had to take a deep dive into this specific topic.
It is quite a bit complicated at first, since there isn't really any form of full documentation.
Since the answer above me explained setting up the session pretty well already, i wanted to supply some more examples, that worked for me.
When getting any NodeId
you need to pass in the symbolicsNamespace Id.
Since i'm using a Siemens PLC in the background, i know that this namespace is always called "SYM:". Once you know the name of your node, you could use a method like this one:
public Task<ushort> GetSymbolicsNameSpaceId()
{
_session.FetchNamespaceTables();
var namespaces = _session.NamespaceUris.ToArray();
ushort index = (ushort)Array.IndexOf(namespaces, "SYM:");
return Task.FromResult(index);
}
Firstly, if you want to check the current server status before sending or reading anything, you can do that, by getting the Variables.Server_ServerStatus
NodeId and pass it into a regular _session.ReadValue()
to receive the corresponding DataValue
.
public Task<ServerStatusDataType> GetServerStatus()
{
// Get the current DataValue object for the ServerStatus node
NodeId nodeId = new NodeId(Variables.Server_ServerStatus);
DataValue dataValue = _session.ReadValue(nodeId);
// Unpack the ExtensionObject that the DataValue contains, then return ServerStatusDataType object
// that represents the current server status
ExtensionObject extensionObject = (ExtensionObject)dataValue.Value;
ServerStatusDataType serverStatus = (ServerStatusDataType)extensionObject.Body;
return Task.FromResult(serverStatus);
}
To get a value from a specific OPC Server variable, you need to get the corresponding NodeId first, which can be done by simply calling the NodeId
constructor with the NodeIdentifier, VariableName and the symbolics namespaceId:
public Task<DataValue>? GetValue(string nodeName, string varName, ushort namespaceIndex)
{
// To read a value, you require the node's ID. This can be either its unique integer ID, or a string
// identifier along with the namespace which that identifier belongs to. Integer IDs are most useful
// for acquiring nodes defined in the OPC UA standard, such as the ServerStatus node. The namespace
// of a tag may differ depending on the OPC server being used, with KEPServer having a tag namespace
// of 2. The only namespace that is guaranteed to remain the same is namespace 0, which contains the
// nodes defined in the OPC UA standard.
NodeId nodeId = new NodeId($"{nodeName}.{varName}", namespaceIndex);
try
{
return Task.FromResult(_session.ReadValue(nodeId));
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return null;
}
}
Writing a value was a bit more of a challenge to understand, but i'm gonna try to explain it a bit further.
Since the PLC behind the OPC Server has it's own datatypes which do not directly correspond to our C# datatypes, we have to cast the value into the correct corresponding type, here is my full implementation of writing a variable:
// Get type of variable in OPC Server which should be written and cast the value before actually writing it
public Task<bool> WriteValue(string nodeName, string varName, ushort namespaceIndex, string value)
{
try
{
NodeId nodeId = new NodeId($"{nodeName}.{varName}", namespaceIndex);
// Read the node you want to write to
var nodeToWrIteTo = _session.ReadValue(nodeId);
// Get type of the specific variable you want to write
BuiltInType type = nodeToWrIteTo.WrappedValue.TypeInfo.BuiltInType;
// Get the corresponding C# datatype
Type csType = Type.GetType($"System.{type}");
// Cast the value
var castedValue = Convert.ChangeType(value, csType);
// Create a WriteValue object with the new value
var writeValue = new WriteValue
{
NodeId = nodeId,
AttributeId = Attributes.Value,
Value = new DataValue(new Variant(castedValue))
};
// Write the new value to the node
_session.Write(null, new WriteValueCollection { writeValue }, out StatusCodeCollection statusCodeCollection, out DiagnosticInfoCollection diagnosticInfo);
// Check the results to make sure the write succeeded
if (statusCodeCollection[0].Code != Opc.Ua.StatusCodes.Good)
{
return Task.FromResult(false);
}
return Task.FromResult(true);
}
catch (Exception)
{
return Task.FromResult(false);
}
}
Good Luck!
P.S.: This is still a poc on my end, so do not rely on my errorhandling.
Upvotes: 5
Reputation: 778
To answer your question on how to get a node I am going to use the OPCua fx library: https://docs.traeger.de/en/software/sdk/opc-ua/net/client.development.guide
(below is a version with OPC foundation)
It has very good documentation and is easy to understand.
First of all install OPCua fx using nuget.
Next you need a few things.
After this. Connect with the adress from before:
string opcUrl = "opc.tcp://192.168.54.200:4840/";
var client = new OpcClient(opcUrl);
client.Connect();
In the documentation is also examples with username, password and certificates.
After you can connect to you OPCua server you can start reading nodes:
var node = client.ReadNode("ns=4;i=3");
Ns means namespace and I believe that the I stands for id. This is how you can read a node. It is also possible to put a subscription on the node. Which is also explained in the documentation.
After this you can write them:
Console.WriteLine("node" + node.ToString());
Good Luck!
EDIT: There is also a good tutorial from a very lovely guy named Hans: https://www.youtube.com/watch?v=KCW23eq4auw
EDIT2: Since most of you not want to spend 900euros for a licence (including me). I made another version for OPC foundation: https://www.nuget.org/packages/OPCFoundation.NetStandard.Opc.Ua.Client/
First of all I have to give credits to: https://github.com/mdjglover/OPC-UA-READER/blob/main/OPC%20UA%20Reader/OPCUAClient.cs
Since this is almost impossible to figure out how this works but I found this repo that explains it very good!
If you use the explanation from the first version for the values you can include them in this code:
// Most basic configuration setup required to create session
ApplicationConfiguration configuration = new ApplicationConfiguration();
ClientConfiguration clientConfiguration = new ClientConfiguration();
configuration.ClientConfiguration = clientConfiguration;
// Create an endpoint to connect to
string serverURL = "opc.tcp://192.168.54.200:4840/";
try
{
// As the server instance I'm connecting to isn't using security, I've passed false as the second argument here.
EndpointDescription endpointDescription = CoreClientUtils.SelectEndpoint(serverURL, false);
EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(configuration);
ConfiguredEndpoint endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);
// Session options
// Sets whether or not the discovery endpoint is used to update the endpoint description before connecting.
bool updateBeforeConnect = false;
// Sets whether or not the domain in the certificate must match the endpoint used
bool checkDomain = false;
// The name to assign to the session
string sessionName = configuration.ApplicationName;
// The session's timeout interval
uint sessionTimeout = 60000;
// The identity of the user attempting to connect. This can be anonymous as is used here,
// or can be specified by a variety of means, including username and password, certificate,
// or token.
UserIdentity user = new UserIdentity();
// List of preferred locales
List<string> preferredLocales = null;
// Create the session
Session session = Session.Create(
configuration,
endpoint,
updateBeforeConnect,
checkDomain,
sessionName,
sessionTimeout,
user,
preferredLocales
).Result;
NodeId nodeId = new NodeId("ns=4;i=3");
var value = session.ReadValue(nodeId);
Console.WriteLine(value);
}
catch
{
return;
}
This code is mostly made by someone
Upvotes: 7
Reputation: 335
There is the sample repos UA-.NETStandard-Samples. Maybe the examples help you?
Edit: I also found an better example for the UA-.NETStandard here my be this helps also. Also the workflow described in the other answer is very good and should more or less the same.
Upvotes: 2