C#某些计时器序列

时间:2018-11-20 10:40:50

标签: c# winforms

我想对莫尔斯电码闪烁灯进行一系列定时事件。指示灯闪烁时,应该可以使用该程序。

因此,开始时会有一个长时间的停顿,然后它将开始闪烁长而短的信号,中间停顿一会儿,然后重新开始。

示例序列

  • 熄灭3000毫秒
  • 点亮1200毫秒
  • 熄灭500ms
  • 点亮400毫秒
  • 熄灭500ms
  • 点亮1200毫秒
  • 熄灭500ms
  • 点亮400毫秒
  • 重新开始

我尝试了这段代码,但它只会冻结程序

private void Morse()
{
    System.Timers.Timer MorseCode = new System.Timers.Timer(3000);
    MorseCode.Elapsed += new ElapsedEventHandler(long);
    MorseCode.Elapsed += new ElapsedEventHandler(short);
    MorseCode.Elapsed += new ElapsedEventHandler(long);
    MorseCode.Elapsed += new ElapsedEventHandler(short);

    void short(object sender, ElapsedEventArgs e)
    {
        MorseCode.Elapsed += new ElapsedEventHandler(ColorChange);
        MorseCode.Interval = 400;
        MorseCode.Elapsed += new ElapsedEventHandler(ColorChange);
        MorseCode.Interval = 500;
    }
    void long(object sender, ElapsedEventArgs e)
    {
        MorseCode.Elapsed += new ElapsedEventHandler(ColorChange);
        MorseCode.Interval = 1200;
        MorseCode.Elapsed += new ElapsedEventHandler(ColorChange);
        MorseCode.Interval = 500;
    }
    void ColorChange(object sender, ElapsedEventArgs e)
    {
        if(BlinkLight.BackColor == Color.Gray)
        {
            BlinkLight.BackColor = Color.DodgerBlue;
        }
        else
        {
            BlinkLight.BackColor = Color.Gray;
        }
    }    
}

那么我该如何为闪烁的灯光设置不同的时序?

3 个答案:

答案 0 :(得分:0)

为此使用Microsoft的Reactive Framework-NuGet“ System.Reactive.Windows.Forms”。

然后您可以执行以下操作:

int[] timings = new [] { 3000, 1200, 500, 400, 500, 1200, 500, 400 };

IObservable<System.Drawing.Color> sequence =
    Observable
        .Generate(
            0,
            x => x < timings.Length,
            x => x + 1,
            x => x % 2 == 1 ? System.Drawing.Color.DodgerBlue : System.Drawing.Color.Gray,
            x => TimeSpan.FromMilliseconds(timings[x]));

IDisposable subscription =
    sequence
        .Repeat()
        .ObserveOn(BlinkLight)
        .Subscribe(color => BlinkLight.BackColor = color);

答案 1 :(得分:0)

您可以为此使用任务并行库,它包含在.NET 4及更高版本中。这将产生清晰的可读代码,如下所示:

private void Morse()
{
    BlinkShort().ContinueWith(          //
        BlinkShort).ContinueWith(       //  S
        BlinkShort).ContinueWith(       //
        BlinkLong).ContinueWith(        //
        BlinkLong).ContinueWith(        //  O
        BlinkLong).ContinueWith(        // 
        BlinkShort).ContinueWith(       //
        BlinkShort).ContinueWith(       //  S
        BlinkShort);                    // 
}

辅助方法的示例实现:

private Task BlinkShort(Task previousTask = null)
{
    var action = new Action(() =>
    {
        SetColorSafe(true);
        Task.Delay(400).Wait();
        SetColorSafe(false);
        Task.Delay(500).Wait();
    });
    var t = Task.Run(action);
    if (previousTask != null) // already threaded
    {
        t.Wait();
    }

    return t;
}
private Task BlinkLong(Task previousTask = null)
{
    var action = new Action(() =>
    {
        SetColorSafe(true);
        Task.Delay(1200).Wait();
        SetColorSafe(false);
        Task.Delay(500).Wait();
    });
    var t = Task.Run(action);
    if (previousTask != null) // already threaded
    {
        t.Wait();
    }

    return t;
}

private void SetColorSafe(bool on)
{
    if (InvokeRequired)
    {
        Invoke(new Action(() => {
            SetColorSafe(on);
        }));
        return;
    }
    if (on)
    {
        BackColor = Color.DodgerBlue;
    }
    else
    {
        BackColor = Color.Gray;
    }
}

答案 2 :(得分:0)

我喜欢Enigmativity's solution。如果您知道System.Reactive,那就是个选择。

这是一个使用异步任务的简单解决方案。
我正在传递给异步方法:
-一个List<Tuple<int, int>>,代表信号的间隔和持续时间
-将作为视觉输出的Control引用
-CancellationToken,用于发信号通知任务何时终止。

可以取消任务,然后以相同的顺序或新的顺序重新开始。
如果未取消,任务将无限期执行,播放当前序列。
在这里,我正在使用一个按钮来启动任务。我可以是其他人。

请注意,我已经稍微修改了顺序和时间。每个序列迭代之间的暂停都在序列的末尾,因此它会立即启动信号,然后在每个序列执行后将其暂停。

发出取消请求后,任务将在取消之前完成序列:
if (token.IsCancellationRequested) return;仅在序列完成后才检查,以免弄乱时间。但这可以修改为在每次长时间停顿后检查取消。

CancellationTokenSource source;
CancellationToken token;

private void button1_Click(object sender, EventArgs e)
{
    if (source != null)
    {
        source.Cancel();
        source.Dispose();
        source = null;
        return;
    }

    source = new CancellationTokenSource();
    token = source.Token;
    List<Tuple<int, int>> MorseCodeSequence = new List<Tuple<int, int>>()
    {
        new Tuple<int, int>(1200, 200),
        new Tuple<int, int>(400, 200),
        new Tuple<int, int>(1200, 200),
        new Tuple<int, int>(400, 2000)
    };

    Task.Run(()=> MorseSequence(MorseCodeSequence, this.btnMorse, token));
}

public async Task MorseSequence(List<Tuple<int, int>> MorseSequence, Control MorseCodeOutputObject, CancellationToken token)
{
    while (true)
    {
        foreach (Tuple<int, int> MorseTiming in MorseSequence)
        {
            MorseCodeOutputObject.BeginInvoke(new MethodInvoker(() =>
                { MorseCodeOutputObject.BackColor = Color.Cyan; }));
            await Task.Delay(MorseTiming.Item1);
            MorseCodeOutputObject.BeginInvoke(new MethodInvoker(() =>
                { MorseCodeOutputObject.BackColor = Color.Gray; }));
            await Task.Delay(MorseTiming.Item2);
        }
        if (token.IsCancellationRequested) return;
    };
}

SOS序列

List<Tuple<int, int>> SOSMorseSequence = new List<Tuple<int, int>>()
{
    new Tuple<int, int>(400, 200),
    new Tuple<int, int>(400, 200),
    new Tuple<int, int>(400, 300),
    new Tuple<int, int>(1200, 200),
    new Tuple<int, int>(1200, 200),
    new Tuple<int, int>(1200, 300),
    new Tuple<int, int>(400, 200),
    new Tuple<int, int>(400, 200),
    new Tuple<int, int>(400, 2000),
};