Skip to content

Commit

Permalink
Merge pull request #10 from hylasoft-usa/async
Browse files Browse the repository at this point in the history
add async methods to interface
  • Loading branch information
itajaja committed Aug 14, 2015
2 parents 11957e2 + e7229cc commit c4e623a
Show file tree
Hide file tree
Showing 11 changed files with 116 additions and 47 deletions.
4 changes: 2 additions & 2 deletions h-opc-cli/Repl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ private static void ShowHelp()

private void ShowSubnodes()
{
var nodes = _currentNode.SubNodes;
var nodes = _client.ExploreFolder(_currentNode.Tag);
if (nodes == null || !nodes.Any())
Console.WriteLine("no subnodes");
else foreach (var node in nodes)
Expand All @@ -162,7 +162,7 @@ private static Command CreateCommand(string line)

private string GenerateRelativeTag(string relativeTag)
{
var node = _currentNode.SubNodes
var node = _client.ExploreFolder(_currentNode.Tag)
.SingleOrDefault(n => n.Name == relativeTag);
return node == null ? relativeTag : node.Tag;
}
Expand Down
23 changes: 23 additions & 0 deletions h-opc/Common/IClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Hylasoft.Opc.Common
{
Expand Down Expand Up @@ -67,5 +68,27 @@ public interface IClient<out TNode> : IDisposable
/// E.g: the tag `foo.bar` finds the sub nodes of `bar` on the folder `foo`</param>
/// <returns>The list of sub-nodes</returns>
IEnumerable<TNode> ExploreFolder(string tag);

/// <summary>
/// Read a tag asynchronusly
/// </summary>
Task<T> ReadAsync<T>(string tag);

/// <summary>
/// Write a value on the specified opc tag asynchronously
/// </summary>
Task WriteAsync<T>(string tag, T item);

/// <summary>
/// Finds a node on the Opc Server asynchronously
/// </summary>
Task<Node> FindNodeAsync(string tag);

/// <summary>
/// Explore a folder on the Opc Server asynchronously
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures",
Justification = "Task")]
Task<IEnumerable<Node>> ExploreFolderAsync(string tag);
}
}
26 changes: 2 additions & 24 deletions h-opc/Common/Node.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,8 @@ namespace Hylasoft.Opc.Common
/// <summary>
/// Base class representing a node on the OPC server
/// </summary>
public class Node
public abstract class Node
{
private IEnumerable<Node> _subNodes;

/// <summary>
/// Gets the client that the node belongs to
/// </summary>
public IClient<Node> Client { get; private set; }

/// <summary>
/// Gets the displayed name of the node
/// </summary>
Expand All @@ -32,12 +25,10 @@ public class Node
/// <summary>
/// Creates a new node
/// </summary>
/// <param name="client">the client the node belongs to</param>
/// <param name="name">the name of the node</param>
/// <param name="parent">The parent node</param>
protected Node(IClient<Node> client, string name, Node parent = null)
protected Node(string name, Node parent = null)
{
Client = client;
Name = name;
Parent = parent;
if (parent != null && !string.IsNullOrEmpty(parent.Tag))
Expand All @@ -46,19 +37,6 @@ protected Node(IClient<Node> client, string name, Node parent = null)
Tag = name;
}

/// <summary>
/// Gets the list of subnodes from the server
/// </summary>
public IEnumerable<Node> SubNodes
{
get
{
if (_subNodes == null)
_subNodes = Client.ExploreFolder(Tag);
return _subNodes;
}
}

/// <summary>
/// Overrides ToString()
/// </summary>
Expand Down
12 changes: 6 additions & 6 deletions h-opc/Da/DaClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Hylasoft.Opc.Da
/// <summary>
/// Client Implementation for DA
/// </summary>
public class DaClient : IClient<DaNode>
public partial class DaClient : IClient<DaNode>
{
private readonly URL _url;
private OpcDa.Server _server;
Expand Down Expand Up @@ -47,8 +47,9 @@ public void Connect()
return;
_server = new OpcDa.Server(new Factory(), _url);
_server.Connect();
RootNode = new DaNode(this, string.Empty, string.Empty);
AddNodeToCache(RootNode);
var root = new DaNode(string.Empty, string.Empty);
RootNode = root;
AddNodeToCache(root);
}

/// <summary>
Expand All @@ -75,7 +76,6 @@ public T Read<T>(string tag)
{
var item = new OpcDa.Item { ItemName = tag };
var result = _server.Read(new[] { item })[0];

CheckResult(result, tag);

return (T)result.Value;
Expand Down Expand Up @@ -144,7 +144,7 @@ public DaNode FindNode(string tag)
var item = new OpcDa.Item { ItemName = tag };
var result = _server.Read(new[] { item })[0];
CheckResult(result, tag);
var node = new DaNode(this, item.ItemName, item.ItemName, RootNode);
var node = new DaNode(item.ItemName, item.ItemName, RootNode);
AddNodeToCache(node);
return node;
}
Expand All @@ -165,7 +165,7 @@ public IEnumerable<DaNode> ExploreFolder(string tag)
var parent = FindNode(tag);
OpcDa.BrowsePosition p;
var nodes = _server.Browse(new ItemIdentifier(parent.Tag), new OpcDa.BrowseFilters(), out p)
.Select(t => new DaNode(this, t.Name, t.ItemName, parent))
.Select(t => new DaNode(t.Name, t.ItemName, parent))
.ToList();
//add nodes to cache
foreach (var node in nodes)
Expand Down
45 changes: 45 additions & 0 deletions h-opc/Da/DaClient_async.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Hylasoft.Opc.Common;
using OpcDa = Opc.Da;

namespace Hylasoft.Opc.Da
{
/// <summary>
/// Client Implementation for DA
/// </summary>
public partial class DaClient
{
/// <summary>
/// Read a tag asynchronusly
/// </summary>
public async Task<T> ReadAsync<T>(string tag)
{
return await Task.Run(() => Read<T>(tag));
}

/// <summary>
/// Write a value on the specified opc tag asynchronously
/// </summary>
public async Task WriteAsync<T>(string tag, T item)
{
await Task.Run(() => Write(tag, item));
}

/// <summary>
/// Finds a node on the Opc Server asynchronously
/// </summary>
public async Task<Node> FindNodeAsync(string tag)
{
return await Task.Run(() => FindNode(tag));
}

/// <summary>
/// Explore a folder on the Opc Server asynchronously
/// </summary>
public async Task<IEnumerable<Node>> ExploreFolderAsync(string tag)
{
return await Task.Run(() => ExploreFolder(tag));
}
}
}
5 changes: 2 additions & 3 deletions h-opc/Da/DaNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@ public class DaNode : Node
/// <summary>
/// Instantiates a DaNode class
/// </summary>
/// <param name="client">the client the node belongs to</param>
/// <param name="name">the name of the node</param>
/// <param name="tag"></param>
/// <param name="parent">The parent node</param>
public DaNode(IClient<Node> client, string name, string tag, Node parent = null)
: base(client, name, parent)
public DaNode(string name, string tag, Node parent = null)
: base(name, parent)
{
Tag = tag;
}
Expand Down
5 changes: 2 additions & 3 deletions h-opc/Ua/NodeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@ public static class NodeExtensions
/// Converts an OPC Foundation node to an Hylasoft OPC UA Node
/// </summary>
/// <param name="node">The node to convert</param>
/// <param name="client">the client the node belongs to</param>
/// <param name="parent">the parent node (optional)</param>
/// <returns></returns>
internal static UaNode ToHylaNode(this OpcF.ReferenceDescription node, UaClient client, Node parent = null)
internal static UaNode ToHylaNode(this OpcF.ReferenceDescription node, Node parent = null)
{
var name = node.DisplayName.ToString();
var nodeId = node.NodeId.ToString();
return new UaNode(client, name, nodeId, parent);
return new UaNode(name, nodeId, parent);
}
}
}
34 changes: 30 additions & 4 deletions h-opc/Ua/UaClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ namespace Hylasoft.Opc.Ua
/// <summary>
/// Client Implementation for UA
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling",
Justification = "Doesn't make sense to split this class")]
public class UaClient : IClient<UaNode>
{
private readonly UaClientOptions _options = new UaClientOptions();
private readonly Uri _serverUrl;
private Session _session;

private readonly IDictionary<string, UaNode> _nodesCache = new Dictionary<string, UaNode>();
private readonly IDictionary<string, IList<UaNode>> _folderCache = new Dictionary<string, IList<UaNode>>();

/// <summary>
/// Creates a server object
Expand All @@ -42,7 +46,7 @@ public UaClientOptions Options
private void PostInitializeSession()
{
var node = _session.NodeCache.Find(ObjectIds.ObjectsFolder);
RootNode = new UaNode(this, string.Empty, node.NodeId.ToString());
RootNode = new UaNode(string.Empty, node.NodeId.ToString());
AddNodeToCache(RootNode);
Status = OpcStatus.Connected;
}
Expand Down Expand Up @@ -329,21 +333,35 @@ public void Monitor<T>(string tag, Action<T, Action> callback)
/// <returns>The list of sub-nodes</returns>
public IEnumerable<UaNode> ExploreFolder(string tag)
{
IList<UaNode> nodes;
_folderCache.TryGetValue(tag, out nodes);
if (nodes != null)
return nodes;

var folder = FindNode(tag);
var nodes = ClientUtils.Browse(_session, folder.NodeId)
nodes = ClientUtils.Browse(_session, folder.NodeId)
.GroupBy(n => n.NodeId) //this is to select distinct
.Select(n => n.First())
.Where(n => n.NodeClass == NodeClass.Variable || n.NodeClass == NodeClass.Object)
.Select(n => n.ToHylaNode(this, folder))
.Select(n => n.ToHylaNode(folder))
.ToList();

//add nodes to cache
_folderCache.Add(tag, nodes);
foreach (var node in nodes)
AddNodeToCache(node);

return nodes;
}

/// <summary>
/// Explores a folder asynchronously
/// </summary>
public async Task<IEnumerable<Common.Node>> ExploreFolderAsync(string tag)
{
return await Task.Run(() => ExploreFolder(tag));
}

/// <summary>
/// Finds a node on the Opc Server
/// </summary>
Expand All @@ -368,6 +386,14 @@ public UaNode FindNode(string tag)
throw new OpcException(string.Format("The tag \"{0}\" doesn't exist on the Server", tag));
}

/// <summary>
/// Find node asynchronously
/// </summary>
public async Task<Common.Node> FindNodeAsync(string tag)
{
return await Task.Run(() => FindNode(tag));
}

/// <summary>
/// Gets the root node of the server
/// </summary>
Expand Down Expand Up @@ -491,7 +517,7 @@ private UaNode FindNode(string tag, UaNode node)
UaNode found;
try
{
var subNodes = node.SubNodes.Cast<UaNode>();
var subNodes = ExploreFolder(node.Tag);
found = subNodes.Single(n => n.Name == head);
}
catch (Exception ex)
Expand Down
5 changes: 2 additions & 3 deletions h-opc/Ua/UaNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@ public class UaNode : Node
/// <summary>
/// Instantiates a UaNode class
/// </summary>
/// <param name="client">the client the node belongs to</param>
/// <param name="name">the name of the node</param>
/// <param name="nodeId">The UA Id of the node</param>
/// <param name="parent">The parent node</param>
internal UaNode(IClient<UaNode> client, string name, string nodeId, Node parent = null)
: base(client, name, parent)
internal UaNode(string name, string nodeId, Node parent = null)
: base(name, parent)
{
NodeId = nodeId;
}
Expand Down
1 change: 1 addition & 0 deletions h-opc/h-opc.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
<Compile Include="Common\IClient.cs" />
<Compile Include="Common\OpcException.cs" />
<Compile Include="Common\OpcStatus.cs" />
<Compile Include="Da\DaClient_async.cs" />
<Compile Include="Da\DaClient.cs" />
<Compile Include="Da\DaNode.cs" />
<Compile Include="Ua\ClientUtils.cs" />
Expand Down
3 changes: 1 addition & 2 deletions tests/UaTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,7 @@ public void BrowseFolderTest()
{
var node = _client.FindNode("Server.ServerStatus.BuildInfo");

// ReSharper disable once PossibleNullReferenceException
var subNodes = node.SubNodes;
var subNodes = _client.ExploreFolder(node.Tag);
Expect(subNodes.Count()).ToBe(6);
}

Expand Down

0 comments on commit c4e623a

Please sign in to comment.