2014-01-21 125 views
2

我正在开发一个应用程序来读取Beckhoff PLC的值。 PLC有一个.net库,我可以用它来连接我的程序和PLC。用C#读取递归集合

PLC上的每个变量都是TcAdsSymbolInfo类型的符号。这个类有几个成员,其中一个是TcAdsSymbolInfo的集合,等等......你可以看到这是怎么回事。基本上我有一个树形结构,有很多符号,每个符号都可以有子符号。 Beckhoff

我想要做的是,为每个符号读取所有的子符号,这个工作如果我手工做,只是尝试看第一个子符号级别,但我真的需要所有这些。

我试图创建接收一个TcAdsSymbolInfo和结束通话本身,而是这将引发一个堆栈溢出异常

private void ReadSubsymbols(TcAdsSymbolInfo t) 
    { 
     if (t.SubSymbolCount > 0) 
     { 
      foreach (TcAdsSymbolInfo subsymbol in t.SubSymbols) 
      { 
       if (!symbols.ContainsKey(subsymbol.Name)) 
        symbols.Add(subsymbol.Name, subsymbol); 
       try 
       { 
        ReadSubsymbols(subsymbol); 
       } 
       catch (Exception Ex) 
       { 
        Console.WriteLine(Ex.ToString() + " - " + Ex.Message); 
       } 
      } 
     } 
    } 

符号就是在那里我存储符号名称和符号字典的递归函数本身。

请尝试从PLC部分摘要,因为我认为这只是一个纯粹的逻辑/编程问题。在与PLC进行通信或读取和写入数值时,我没有任何问题。唯一的问题是阅读这个结构。

我可以使用一段时间或任何其他类型的循环做任何seguegestion?任何不会抛出异常的东西?

在此先感谢。

+0

你或许应该寻找树的遍历算法。 – Magus

回答

1

这是一个简单的树行走。基本逻辑是从根开始:

  • 如果根为空,树是空的:我们完成了。
  • 如果root不为null,
    • 访问它
    • 然后递归参观它的每个孩子。

简单。大多数情况下:D当你在图中遇到循环时(例如,当一个子节点链接回它自己的父节点时),你会遇到麻烦的地方。如果你有周期,你必须跟踪你访问过的节点(就像我的例子那样)并且检查周期(我的例子不这么做)。

鉴于一类是这样的:

class SymbolInfo 
{ 
    public string Name { get ; set ; } 
    public SortedSet<SymbolInfo> Subsymbols { get ; set ; } 

    public SymbolInfo(string name) 
    { 
    this.Name = name ; 
    this.Subsymbols = new SortedSet<SymbolInfo>(new SymbolInfo.Comparer()) ; 
    } 

    public override string ToString() 
    { 
    return this.Name ?? "-null-" ; 
    } 

    private class Comparer : IComparer<SymbolInfo> 
    { 
    public int Compare(SymbolInfo x , SymbolInfo y) 
    { 
     return string.Compare(x.Name,y.Name,StringComparison.InvariantCultureIgnoreCase) ; 
    } 
    } 
} 

树步行看起来是这样的:

public static IEnumerable<string> TreeWalk(SymbolInfo root , List<SymbolInfo> visited) 
{ 
    if (root != null) 
    { 
    visited.Add(root) ; 
    yield return string.Join(" -> " , visited) ; 
    foreach (SymbolInfo child in root.Subsymbols) 
    { 
     foreach (string childPath in TreeWalk(child , visited)) 
     { 
     yield return childPath ; 
     } 
    } 
    visited.RemoveAt(visited.Count-1) ; 
    } 
} 

而鉴于这样构建的树:

private static SymbolInfo LoadTree() 
{ 
    SymbolInfo a = new SymbolInfo("A") ; 
    SymbolInfo b = new SymbolInfo("B") ; 
    SymbolInfo c = new SymbolInfo("C") ; 
    SymbolInfo d = new SymbolInfo("D") ; 
    SymbolInfo e = new SymbolInfo("E") ; 
    SymbolInfo f = new SymbolInfo("F") ; 
    SymbolInfo g = new SymbolInfo("G") ; 
    SymbolInfo h = new SymbolInfo("H") ; 
    SymbolInfo i = new SymbolInfo("I") ; 

    a.Subsymbols.Add(b) ; 
    a.Subsymbols.Add(c) ; 
    a.Subsymbols.Add(d) ; 

    b.Subsymbols.Add(e) ; 

    c.Subsymbols.Add(f) ; 
    c.Subsymbols.Add(g) ; 

    f.Subsymbols.Add(h) ; 
    f.Subsymbols.Add(i) ; 

    return a ; 
} 

我们可以调用它像这样:

SymbolInfo root = LoadTree() ; 

foreach (string path in TreeWalk(root , new List<SymbolInfo>())) 
{ 
    Console.WriteLine(path) ; 
} 

要产生以下的输出:

A 
A -> B 
A -> B -> E 
A -> C 
A -> C -> F 
A -> C -> F -> H 
A -> C -> F -> I 
A -> C -> G 
A -> D 
+0

感谢您的帮助。我遵循你的样本,并能解决我的问题。 –

2

你可以使用迭代树遍历。 但是在尝试这个之前,请确保堆栈溢出是由于递归变得太深。很有可能这是一个编码错误允许递归无限下降。

你可以不喜欢下面的伪代码:

Push root node to stack 

While (stack is not empty) 
{ 
    current node = pop from stack 
    process current node (and other processing goes here) 

    add all children with nodes to stack 
} 

见这个例子中,解决了类似的问题与目录: http://msdn.microsoft.com/en-us/library/bb513869.aspx

+2

另外到什么乔治说,你可能要注意你是否真的有只,还是在现实中是一个周期性的图形(又名有其祖先节点中的一个作为子节点),这也解释了一棵树堆栈耗尽。 – elgonzo

+2

这是一个很好的迭代示例。而且,我同意,您必须确保堆栈溢出是递归调用的直接结果。否则,迭代方法仍将无限期地运行。机会在某处有一个循环。您可以通过存储和检查访问节点的引用来避免循环。 – Xenolightning

0
public IEnumerable<TcAdsSymbolInfo> GetSymbols(IEnumerable<TcAdsSymbolInfo> set) 
{ 
    if (!set.Any()) 
     return Enumerable.Empty<TcAdsSymbolInfo>(); 

    return set.SelectMany(sym => GetSymbols(sym.SubSymbols))); 
} 

您可以将结果然后转换成你的字典并检查被骗等。