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

Visual Simulator for Distributed Leader Election Algorithm

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

我已经编写了一个 Visual Studio C# 控制台程序来模拟 distributed leader election algorithms 在单向环形网络中。

让我先简要描述一下领导者选举问题:假设您有一个环形网络,由节点组成,每个节点都分配有唯一(或非唯一,但让我们考虑唯一的情况)ID。他们每个人都有相同的程序(算法)。消息只能在邻居之间以一个预先确定的方向(CW 或 CCW)传递。算法完成后,必须选出一个节点作为 'leader',所有其他节点都应该知道已选出领导者和领导者的 ID。

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

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

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

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

不要担心所有的混乱,请注意 'Active' 和 'Elected' 列。这些代表每个节点中两个变量的状态。 If true, it means active and it knows the elected leader respectively.

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

我该怎么做?在深入探讨之前,我想听听大家的一些意见,因此 post.

我的第一个问题,直到 运行 时间我才知道戒指尺寸,那么我如何在 运行 时间显示我的 'nodes'?我假设我必须使用图片控制或类似的东西。我知道我不能显示大量节点,所以我可以自由限制环的大小。基本上,我想 space 计算出我在圆形方向上等距离的节点数。

我还希望如示例图像所示,将各种变量的状态并排显示。那么我是否应该创建一个新的 class 来包含我想要显示的所有内容,然后以某种方式将其附加到画面控制?

任何引导我走向正确方向的提示都会很方便!

嘿,我的第一个回答在这里,所以如果我可以改进任何地方,请告诉我。

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

我不确定计时器的使用是否正确,但您确实可以使用计时器来检查脚本中的变量是否发生了变化,如果发生了变化,您只需更改文本即可根据情况将标签设置为 Activated 或 Deactivated。

对于 cross/circle 的内容,我会使用 Picturebox,并在值发生变化时简单地从代码中更改图像。

            RSS_Button.Image = Properties.Resources.RSS;

是我以前用picturebox做自定义按钮的(我的picturebox取名为rss_button)。

希望对您有所帮助!

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

只需从 System.Windows.Forms.Control 派生 class 即可创建自定义控件。编译后,该控件会自动出现在窗体设计器的工具箱中,您可以将其拖放到窗体表面。

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); and g.TranslateTransform(dx, dy);.

轻松调整坐标系

您可以在此处找到教程:GDI+ 和图形 GDI+ 初学者教程(以及许多其他教程here)。


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

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

y 的减号是因为在 GDI+ 中 y 轴指向下方。 static System.Math class 需要以弧度为单位的角度,即一个完整的圆 = 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);
    }
}