用于分布式领导者选举算法的可视化模拟器

时间:2016-02-28 19:18:15

标签: c# algorithm visual-studio simulation distributed-computing

这会有点长,所以请耐心等待。

我编写了一个Visual Studio C#控制台程序来模拟单向环网中的distributed leader election algorithms

首先让我简要描述一下领导者选举问题:假设你有一个环形网络,其中包含分配给每个节点的唯一(或非唯一,但让我们考虑唯一的情况)ID的节点。它们中的每一个都具有相同的程序(算法)。消息可以在一个预定方向(CW或CCW)上仅在邻居之间传递。一旦算法完成,一个节点必须被选为“领导者”,所有其他节点应该知道领导者已被选举并且领导者的ID。

最简单的是,LCR算法(我已经使用过)如下所示。在步骤1,每个节点在消息中将它们自己的ID发送给它们(例如)CW邻居。这意味着每个人都会收到一个。收到消息后:

  • 如果它大于您的ID,请将其传递并将自己标记为无效
  • 如果小于您的ID,请将其丢弃
  • 如果您在邮件中收到自己的ID,请选择自己为领导

这样,在n步之后(n =网络的大小),只有具有最大ID的节点保持活动状态并被选为领导者。然后它在环上发送一条消息,通知每个人它是领导者,当它收到该消息时,我们就完成了执行。

我编写了一个如下控制台程序。我有我的主程序,我的算法在一个单独的DLL中。 DLL具有TimeTick()函数,其中包含算法的所有步骤。从我的主程序中,我以用户指定的时间间隔(例如10ms)重复调用TimeTick()函数。因此,在我的程序中执行一次TimeTick()类似于在算法中传递“step”的一条消息。我可以访问DLL中的所有重要变量,因此从我的主程序中我可以知道每个节点的状态,它是否处于活动状态,是否知道领导者当选等等。

以下是执行以下响铃的示例屏幕截图:顺时针顺序为1-3-2。

enter image description here

不要担心所有的混乱,注意'活跃'和'选中'列。它们代表每个节点中两个变量的状态。如果为真,则表示活跃,并且分别知道当选的领导者。

现在,我想将其转换为可视化程序。也就是说,我想在每个TimeTick()上显示一个节点环,如果Active == TRUE,则显示一种颜色,如果Active == FALSE,则将它们交叉。此外,我想在每个节点旁边显示节点中各种变量的状态。例如,上面的执行,如果以我想要的方式直观显示,将如下所示。每个环都是每个时间步显示的环。

Click to enlarge

我该怎么做?在我深入研究它之前,我想从你们那里得到一些意见,因此发帖。

我的第一个问题,我在运行时之前不知道响铃大小,那么如何在运行时显示我的'节点'?我想我必须使用图片控件或类似的东西。我知道我无法显示大量节点,所以我可以自由地限制环的大小。基本上,我想以相等的距离将我所拥有的节点数量以圆形方向分开。

我还希望,如示例图像所示,彼此相邻显示的各种变量的状态。那么我是否应该创建一个包含我想要显示的所有内容的新类,然后以某种方式将它附加到图片控件上?

任何引导我朝着良好方向发展的提示都会很方便!

2 个答案:

答案 0 :(得分:3)

你有很多选择。一个相对简单的方法是使用命名空间System.Drawing(GDI +)在自定义WinForms控件上绘制图形。对于这个简单的图形,您不需要更多的性能,也需要更复杂的游戏引擎。 GDI +非常快。绘制自己的图形使您可以灵活地在可变配置中生成任何颜色的任何类型的形状,也可以绘制文本。

只需从System.Windows.Forms.Control派生一个类即可创建自定义控件。编译后,此控件将自动显示在窗体设计器的工具箱中,您可以将其拖放到窗体表面上。

public class ElectionDisplayControl : Control
{
    public ElectionDisplayControl()
    {
        this.DoubleBuffered = true;
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        Graphics g = e.Graphics;
        g.SetHighQuality();

        // TODO: put your drawing logic here!
    }

    protected override void OnResize(EventArgs e)
    {
        Invalidate();
        base.OnResize(e);
    }
}

重要的是要了解您不应该通过直接调用OnPaint方法来绘制。而是在每个TimeTick上调用控件的Invalidate方法。 Windows决定何时必须重绘控件并在必要时调用OnPaint。 (例如,当您的控件隐藏在另一个表单后面并且现在变得可见时。)

// At each tick:
electionDisplayControl1.Invalidate(); 

DoubleBuffered消除了闪烁。在调整大小时重绘控件是很好的。您可以将控件锚定到表单的边缘,以允许用户通过调整表单大小来调整其大小。确定相对于控件大小的形状大小是个好主意。这会创建自动缩放效果。您可以使用g.ScaleTransform(sx, sy);g.TranslateTransform(dx, dy);轻松调整坐标系。

您可以在此处找到教程:GDI+ & Graphics GDI+ Tutorial for Beginners(以及其他许多here)。

如何在圆上均匀分布形状?那你需要一些数学。以角度 phi 计算圆上的点(假设圆的中心位于{x = 0,y = 0}):

x = radius * cos(phi)
y = -radius * sin(phi)

y的减号是因为y轴指向GDI +中的向下。静态System.Math类期望以弧度表示的角度,即整圆= 2 * pi。

我忘记了:SetHighQuality是我的扩展方法之一。这里有一些其他有用的:

public static class GraphicsExtensions
    public static void SetHighQuality(this Graphics g)
    {
        g.CompositingMode = CompositingMode.SourceOver;
        g.CompositingQuality = CompositingQuality.HighQuality;
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.SmoothingMode = SmoothingMode.HighQuality;
        g.PixelOffsetMode = PixelOffsetMode.HighQuality;
    }

    public static void DrawCircle(this Graphics g, Pen pen, float centerX, float centerY, float radius)
    {
        g.DrawEllipse(pen, centerX - radius, centerY - radius, radius + radius, radius + radius);
    }

    public static void FillCircle(this Graphics g, Brush brush, float centerX, float centerY, float radius)
    {
        g.FillEllipse(brush, centerX - radius, centerY - radius, radius + radius, radius + radius);
    }
}

答案 1 :(得分:0)

嘿我的第一个答案就在这里,如果我可以改进任何事情,请告诉我。

您可以使用表单应用程序对其进行非常简单的直观表示。

我不确定它是否正确使用了计时器,但您确实可以使用计时器检查脚本中的变量是否已更改,如果是,您只需更改文本根据具体情况,激活或取消激活标签。

对于交叉/圆形的东西,我会使用Picturebox,只需在值发生变化时从代码中更改图像。

            RSS_Button.Image = Properties.Resources.RSS;

我以前使用图片框制作自定义按钮(我的图片框名为rss_button)。

我希望它有所帮助!