读取数据类型方法改为批量

This commit is contained in:
Diego2098
2023-10-15 20:21:23 +08:00
parent 8b9e0dd6ea
commit 3656c0d524

View File

@@ -213,14 +213,14 @@ public class OPCUAClient : IDisposable
DisplayName = subscriptionName
};
List<MonitoredItem> monitoredItems = new();
var variableNodes = await ReadNodesAsync(items);
for (int i = 0; i < items.Length; i++)
{
try
{
var variableNode = await ReadNodeAsync(items[i], false);
var item = new MonitoredItem
{
StartNodeId = variableNode.NodeId,
StartNodeId = variableNodes[i].NodeId,
AttributeId = Attributes.Value,
DisplayName = items[i],
Filter = OPCNode.DeadBand == 0 ? null : new DataChangeFilter() { DeadbandValue = OPCNode.DeadBand, DeadbandType = (int)DeadbandType.Absolute, Trigger = DataChangeTrigger.StatusValue },
@@ -307,11 +307,11 @@ public class OPCUAClient : IDisposable
}
private async void Callback(MonitoredItem monitoreditem, MonitoredItemNotificationEventArgs monitoredItemNotificationEventArgs)
private void Callback(MonitoredItem monitoreditem, MonitoredItemNotificationEventArgs monitoredItemNotificationEventArgs)
{
try
{
var variableNode = await ReadNodeAsync(monitoreditem.StartNodeId.ToString(), false);
var variableNode = ReadNode(monitoreditem.StartNodeId.ToString(), false);
foreach (var value in monitoreditem.DequeueValues())
{
if (value.Value != null)
@@ -680,634 +680,66 @@ public class OPCUAClient : IDisposable
return value;
}
}
NodeId nodeToRead = new(nodeIdStr);
var node = (VariableNode)await ReadNodeAsync(nodeToRead, NodeClass.Unspecified, false, cancellationToken);
await typeSystem.LoadType(node.DataType, true, false);
var node = (VariableNode)await m_session.ReadNodeAsync(nodeToRead, NodeClass.Variable, false, cancellationToken);
await typeSystem.LoadType(node.DataType);
_variableDicts.AddOrUpdate(nodeIdStr, node);
return node;
}
/// <summary>
/// 从服务器或缓存读取节点
/// </summary>
private VariableNode ReadNode(string nodeIdStr, bool isOnlyServer = true)
{
if (!isOnlyServer)
{
if (_variableDicts.TryGetValue(nodeIdStr, out var value))
{
return value;
}
}
NodeId nodeToRead = new(nodeIdStr);
var node = (VariableNode)m_session.ReadNode(nodeToRead, NodeClass.Variable, false);
_variableDicts.AddOrUpdate(nodeIdStr, node);
return node;
}
#endregion
#region session
/// <inheritdoc/>
public async Task<Node> ReadNodeAsync(
NodeId nodeId,
NodeClass nodeClass,
bool optionalAttributes = true,
CancellationToken ct = default)
{
// build list of attributes.
var attributes = CreateAttributes(nodeClass, optionalAttributes);
// build list of values to read.
ReadValueIdCollection itemsToRead = new ReadValueIdCollection();
foreach (uint attributeId in attributes.Keys)
{
ReadValueId itemToRead = new ReadValueId
{
NodeId = nodeId,
AttributeId = attributeId
};
itemsToRead.Add(itemToRead);
}
// read from server.
ReadResponse readResponse = await m_session.ReadAsync(
null,
0,
TimestampsToReturn.Neither,
itemsToRead, ct).ConfigureAwait(false);
DataValueCollection values = readResponse.Results;
DiagnosticInfoCollection diagnosticInfos = readResponse.DiagnosticInfos;
ClientBase.ValidateResponse(values, itemsToRead);
ClientBase.ValidateDiagnosticInfos(diagnosticInfos, itemsToRead);
return ProcessReadResponse(readResponse.ResponseHeader, attributes, itemsToRead, values, diagnosticInfos);
}
/// <summary>
/// Creates a Node based on the read response.
/// 从服务器读取节点
/// </summary>
private Node ProcessReadResponse(
ResponseHeader responseHeader,
IDictionary<uint, DataValue> attributes,
ReadValueIdCollection itemsToRead,
DataValueCollection values,
DiagnosticInfoCollection diagnosticInfos)
private async Task<List<Node>> ReadNodesAsync(string[] nodeIdStrs, CancellationToken cancellationToken = default)
{
// process results.
int? nodeClass = null;
for (int ii = 0; ii < itemsToRead.Count; ii++)
List<NodeId> nodeIds = new List<NodeId>();
foreach (var item in nodeIdStrs)
{
uint attributeId = itemsToRead[ii].AttributeId;
// the node probably does not exist if the node class is not found.
if (attributeId == Attributes.NodeClass)
NodeId nodeToRead = new(item);
nodeIds.Add(nodeToRead);
}
(IList<Node>, IList<ServiceResult>) nodes = await m_session.ReadNodesAsync(nodeIds, NodeClass.Variable, false, cancellationToken);
for (int i = 0; i < nodes.Item1.Count; i++)
{
if (StatusCode.IsGood(nodes.Item2[i].StatusCode))
{
if (!DataValue.IsGood(values[ii]))
{
throw ServiceResultException.Create(values[ii].StatusCode, ii, diagnosticInfos, responseHeader.StringTable);
}
// check for valid node class.
nodeClass = values[ii].Value as int?;
if (nodeClass == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Node does not have a valid value for NodeClass: {0}.", values[ii].Value);
}
var node = ((VariableNode)nodes.Item1[i]);
await typeSystem.LoadType(node.DataType);
_variableDicts.AddOrUpdate(nodeIdStrs[i], node);
}
else
{
if (!DataValue.IsGood(values[ii]))
{
// check for unsupported attributes.
if (values[ii].StatusCode == StatusCodes.BadAttributeIdInvalid)
{
continue;
}
// ignore errors on optional attributes
if (StatusCode.IsBad(values[ii].StatusCode))
{
if (attributeId == Attributes.AccessRestrictions ||
attributeId == Attributes.Description ||
attributeId == Attributes.RolePermissions ||
attributeId == Attributes.UserRolePermissions ||
attributeId == Attributes.DataTypeDefinition ||
attributeId == Attributes.AccessLevelEx ||
attributeId == Attributes.UserWriteMask ||
attributeId == Attributes.WriteMask)
{
continue;
}
}
// all supported attributes must be readable.
if (attributeId != Attributes.Value)
{
throw ServiceResultException.Create(values[ii].StatusCode, ii, diagnosticInfos, responseHeader.StringTable);
}
}
}
attributes[attributeId] = values[ii];
}
Node node;
DataValue value;
switch ((NodeClass)nodeClass.Value)
{
default:
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Node does not have a valid value for NodeClass: {0}.", nodeClass.Value);
}
case NodeClass.Object:
{
ObjectNode objectNode = new ObjectNode();
value = attributes[Attributes.EventNotifier];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Object does not support the EventNotifier attribute.");
}
objectNode.EventNotifier = (byte)value.GetValue(typeof(byte));
node = objectNode;
break;
}
case NodeClass.ObjectType:
{
ObjectTypeNode objectTypeNode = new ObjectTypeNode();
value = attributes[Attributes.IsAbstract];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "ObjectType does not support the IsAbstract attribute.");
}
objectTypeNode.IsAbstract = (bool)value.GetValue(typeof(bool));
node = objectTypeNode;
break;
}
case NodeClass.Variable:
{
VariableNode variableNode = new VariableNode();
// DataType Attribute
value = attributes[Attributes.DataType];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Variable does not support the DataType attribute.");
}
variableNode.DataType = (NodeId)value.GetValue(typeof(NodeId));
// ValueRank Attribute
value = attributes[Attributes.ValueRank];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Variable does not support the ValueRank attribute.");
}
variableNode.ValueRank = (int)value.GetValue(typeof(int));
// ArrayDimensions Attribute
value = attributes[Attributes.ArrayDimensions];
if (value != null)
{
if (value.Value == null)
{
variableNode.ArrayDimensions = Array.Empty<uint>();
}
else
{
variableNode.ArrayDimensions = (uint[])value.GetValue(typeof(uint[]));
}
}
// AccessLevel Attribute
value = attributes[Attributes.AccessLevel];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Variable does not support the AccessLevel attribute.");
}
variableNode.AccessLevel = (byte)value.GetValue(typeof(byte));
// UserAccessLevel Attribute
value = attributes[Attributes.UserAccessLevel];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Variable does not support the UserAccessLevel attribute.");
}
variableNode.UserAccessLevel = (byte)value.GetValue(typeof(byte));
// Historizing Attribute
value = attributes[Attributes.Historizing];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Variable does not support the Historizing attribute.");
}
variableNode.Historizing = (bool)value.GetValue(typeof(bool));
// MinimumSamplingInterval Attribute
value = attributes[Attributes.MinimumSamplingInterval];
if (value != null)
{
variableNode.MinimumSamplingInterval = Convert.ToDouble(attributes[Attributes.MinimumSamplingInterval].Value);
}
// AccessLevelEx Attribute
value = attributes[Attributes.AccessLevelEx];
if (value != null)
{
variableNode.AccessLevelEx = (uint)value.GetValue(typeof(uint));
}
node = variableNode;
break;
}
case NodeClass.VariableType:
{
VariableTypeNode variableTypeNode = new VariableTypeNode();
// IsAbstract Attribute
value = attributes[Attributes.IsAbstract];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "VariableType does not support the IsAbstract attribute.");
}
variableTypeNode.IsAbstract = (bool)value.GetValue(typeof(bool));
// DataType Attribute
value = attributes[Attributes.DataType];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "VariableType does not support the DataType attribute.");
}
variableTypeNode.DataType = (NodeId)value.GetValue(typeof(NodeId));
// ValueRank Attribute
value = attributes[Attributes.ValueRank];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "VariableType does not support the ValueRank attribute.");
}
variableTypeNode.ValueRank = (int)value.GetValue(typeof(int));
// ArrayDimensions Attribute
value = attributes[Attributes.ArrayDimensions];
if (value != null && value.Value != null)
{
variableTypeNode.ArrayDimensions = (uint[])value.GetValue(typeof(uint[]));
}
node = variableTypeNode;
break;
}
case NodeClass.Method:
{
MethodNode methodNode = new MethodNode();
// Executable Attribute
value = attributes[Attributes.Executable];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Method does not support the Executable attribute.");
}
methodNode.Executable = (bool)value.GetValue(typeof(bool));
// UserExecutable Attribute
value = attributes[Attributes.UserExecutable];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Method does not support the UserExecutable attribute.");
}
methodNode.UserExecutable = (bool)value.GetValue(typeof(bool));
node = methodNode;
break;
}
case NodeClass.DataType:
{
DataTypeNode dataTypeNode = new DataTypeNode();
// IsAbstract Attribute
value = attributes[Attributes.IsAbstract];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "DataType does not support the IsAbstract attribute.");
}
dataTypeNode.IsAbstract = (bool)value.GetValue(typeof(bool));
// DataTypeDefinition Attribute
value = attributes[Attributes.DataTypeDefinition];
if (value != null)
{
dataTypeNode.DataTypeDefinition = value.Value as ExtensionObject;
}
node = dataTypeNode;
break;
}
case NodeClass.ReferenceType:
{
ReferenceTypeNode referenceTypeNode = new ReferenceTypeNode();
// IsAbstract Attribute
value = attributes[Attributes.IsAbstract];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "ReferenceType does not support the IsAbstract attribute.");
}
referenceTypeNode.IsAbstract = (bool)value.GetValue(typeof(bool));
// Symmetric Attribute
value = attributes[Attributes.Symmetric];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "ReferenceType does not support the Symmetric attribute.");
}
referenceTypeNode.Symmetric = (bool)value.GetValue(typeof(bool));
// InverseName Attribute
value = attributes[Attributes.InverseName];
if (value != null && value.Value != null)
{
referenceTypeNode.InverseName = (LocalizedText)value.GetValue(typeof(LocalizedText));
}
node = referenceTypeNode;
break;
}
case NodeClass.View:
{
ViewNode viewNode = new ViewNode();
// EventNotifier Attribute
value = attributes[Attributes.EventNotifier];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "View does not support the EventNotifier attribute.");
}
viewNode.EventNotifier = (byte)value.GetValue(typeof(byte));
// ContainsNoLoops Attribute
value = attributes[Attributes.ContainsNoLoops];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "View does not support the ContainsNoLoops attribute.");
}
viewNode.ContainsNoLoops = (bool)value.GetValue(typeof(bool));
node = viewNode;
break;
}
}
// NodeId Attribute
value = attributes[Attributes.NodeId];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Node does not support the NodeId attribute.");
}
node.NodeId = (NodeId)value.GetValue(typeof(NodeId));
node.NodeClass = (NodeClass)nodeClass.Value;
// BrowseName Attribute
value = attributes[Attributes.BrowseName];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Node does not support the BrowseName attribute.");
}
node.BrowseName = (QualifiedName)value.GetValue(typeof(QualifiedName));
// DisplayName Attribute
value = attributes[Attributes.DisplayName];
if (value == null)
{
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Node does not support the DisplayName attribute.");
}
node.DisplayName = (LocalizedText)value.GetValue(typeof(LocalizedText));
// all optional attributes follow
// Description Attribute
if (attributes.TryGetValue(Attributes.Description, out value) &&
value != null && value.Value != null)
{
node.Description = (LocalizedText)value.GetValue(typeof(LocalizedText));
}
// WriteMask Attribute
if (attributes.TryGetValue(Attributes.WriteMask, out value) &&
value != null)
{
node.WriteMask = (uint)value.GetValue(typeof(uint));
}
// UserWriteMask Attribute
if (attributes.TryGetValue(Attributes.UserWriteMask, out value) &&
value != null)
{
node.UserWriteMask = (uint)value.GetValue(typeof(uint));
}
// RolePermissions Attribute
if (attributes.TryGetValue(Attributes.RolePermissions, out value) &&
value != null)
{
ExtensionObject[] rolePermissions = value.Value as ExtensionObject[];
if (rolePermissions != null)
{
node.RolePermissions = new RolePermissionTypeCollection();
foreach (ExtensionObject rolePermission in rolePermissions)
{
node.RolePermissions.Add(rolePermission.Body as RolePermissionType);
}
_logAction?.Invoke(3, this, $"获取服务器节点信息失败{nodes.Item2[i]}", null);
}
}
// UserRolePermissions Attribute
if (attributes.TryGetValue(Attributes.UserRolePermissions, out value) &&
value != null)
{
ExtensionObject[] userRolePermissions = value.Value as ExtensionObject[];
if (userRolePermissions != null)
{
node.UserRolePermissions = new RolePermissionTypeCollection();
foreach (ExtensionObject rolePermission in userRolePermissions)
{
node.UserRolePermissions.Add(rolePermission.Body as RolePermissionType);
}
}
}
// AccessRestrictions Attribute
if (attributes.TryGetValue(Attributes.AccessRestrictions, out value) &&
value != null)
{
node.AccessRestrictions = (ushort)value.GetValue(typeof(ushort));
}
return node;
return nodes.Item1.ToList();
}
/// <summary>
/// Create a dictionary of attributes to read for a nodeclass.
/// </summary>
private IDictionary<uint, DataValue> CreateAttributes(NodeClass nodeclass = NodeClass.Unspecified, bool optionalAttributes = true)
{
// Attributes to read for all types of nodes
var attributes = new SortedDictionary<uint, DataValue>() {
{ Attributes.NodeId, null },
{ Attributes.NodeClass, null },
{ Attributes.BrowseName, null },
{ Attributes.DisplayName, null },
};
switch (nodeclass)
{
case NodeClass.Object:
attributes.Add(Attributes.EventNotifier, null);
break;
case NodeClass.Variable:
attributes.Add(Attributes.DataType, null);
attributes.Add(Attributes.ValueRank, null);
attributes.Add(Attributes.ArrayDimensions, null);
attributes.Add(Attributes.AccessLevel, null);
attributes.Add(Attributes.UserAccessLevel, null);
attributes.Add(Attributes.Historizing, null);
attributes.Add(Attributes.MinimumSamplingInterval, null);
attributes.Add(Attributes.AccessLevelEx, null);
break;
case NodeClass.Method:
attributes.Add(Attributes.Executable, null);
attributes.Add(Attributes.UserExecutable, null);
break;
case NodeClass.ObjectType:
attributes.Add(Attributes.IsAbstract, null);
break;
case NodeClass.VariableType:
attributes.Add(Attributes.IsAbstract, null);
attributes.Add(Attributes.DataType, null);
attributes.Add(Attributes.ValueRank, null);
attributes.Add(Attributes.ArrayDimensions, null);
break;
case NodeClass.ReferenceType:
attributes.Add(Attributes.IsAbstract, null);
attributes.Add(Attributes.Symmetric, null);
attributes.Add(Attributes.InverseName, null);
break;
case NodeClass.DataType:
attributes.Add(Attributes.IsAbstract, null);
attributes.Add(Attributes.DataTypeDefinition, null);
break;
case NodeClass.View:
attributes.Add(Attributes.EventNotifier, null);
attributes.Add(Attributes.ContainsNoLoops, null);
break;
default:
// build complete list of attributes.
attributes = new SortedDictionary<uint, DataValue> {
{ Attributes.NodeId, null },
{ Attributes.NodeClass, null },
{ Attributes.BrowseName, null },
{ Attributes.DisplayName, null },
//{ Attributes.Description, null },
//{ Attributes.WriteMask, null },
//{ Attributes.UserWriteMask, null },
{ Attributes.DataType, null },
{ Attributes.ValueRank, null },
{ Attributes.ArrayDimensions, null },
{ Attributes.AccessLevel, null },
{ Attributes.UserAccessLevel, null },
{ Attributes.MinimumSamplingInterval, null },
{ Attributes.Historizing, null },
{ Attributes.EventNotifier, null },
{ Attributes.Executable, null },
{ Attributes.UserExecutable, null },
{ Attributes.IsAbstract, null },
{ Attributes.InverseName, null },
{ Attributes.Symmetric, null },
{ Attributes.ContainsNoLoops, null },
{ Attributes.DataTypeDefinition, null },
//{ Attributes.RolePermissions, null },
//{ Attributes.UserRolePermissions, null },
//{ Attributes.AccessRestrictions, null },
{ Attributes.AccessLevelEx, null }
};
break;
}
if (optionalAttributes)
{
attributes.Add(Attributes.Description, null);
attributes.Add(Attributes.WriteMask, null);
attributes.Add(Attributes.UserWriteMask, null);
attributes.Add(Attributes.RolePermissions, null);
attributes.Add(Attributes.UserRolePermissions, null);
attributes.Add(Attributes.AccessRestrictions, null);
}
return attributes;
}
#endregion
#region
/// <summary>