IVSoftware.Portable.WatchdogTimer 1.1.2

There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package IVSoftware.Portable.WatchdogTimer --version 1.1.2
NuGet\Install-Package IVSoftware.Portable.WatchdogTimer -Version 1.1.2
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="IVSoftware.Portable.WatchdogTimer" Version="1.1.2" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add IVSoftware.Portable.WatchdogTimer --version 1.1.2
#r "nuget: IVSoftware.Portable.WatchdogTimer, 1.1.2"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install IVSoftware.Portable.WatchdogTimer as a Cake Addin
#addin nuget:?package=IVSoftware.Portable.WatchdogTimer&version=1.1.2

// Install IVSoftware.Portable.WatchdogTimer as a Cake Tool
#tool nuget:?package=IVSoftware.Portable.WatchdogTimer&version=1.1.2

Watchdog timer

A timer designed to complete after the time span set by the Interval property has elapsed since the most recent call to the StartOrRestart method. This results in a single completion regardless of the number of restarts. Invoking the Cancel method negates any enqueued action or event in the queue.


Methods

/// <summary>
/// Restart the watchdog timer.
/// </summary>
/// <remarks>
/// Core method that can take a parameterized action as well as a custom EventArgs object.
/// </remarks>
public void StartOrRestart(Action action, EventArgs e)
{
    Running = true;
    _wdtCount++;
    var capturedCount = _wdtCount;
    _isCancelled= false;
    Task
        .Delay(Interval)
        .GetAwaiter()
        .OnCompleted(() =>
        {
            // If the 'captured' localCount has not changed after awaiting the Interval, 
            // it indicates that no new 'bones' have been thrown during that interval.        
            if (capturedCount.Equals(_wdtCount) && !_isCancelled)
            {
                action?.Invoke();
                Running = false;
                RanToCompletion?.Invoke(this, e ?? EventArgs.Empty);
            }
        });
}

/// <summary>
/// Restart the watchdog timer.
/// </summary>
/// <remarks>
/// Subscribe to the RanToCompletion event to receive notification of completion.  
/// On completion, fire an event with an empty EventArgs object.
/// </remarks>
public void StartOrRestart() => StartOrRestart(null, EventArgs.Empty);

/// <summary>
/// Restart the watchdog timer.
/// </summary>
/// <remarks>
/// Subscribe to the RanToCompletion event to receive notification of completion.  
/// On completion, fire an event using a custom parameterized EventArgs object.
/// </remarks>
public void StartOrRestart(EventArgs e) => StartOrRestart(null, e);

/// <summary>
/// Restart the watchdog timer.
/// </summary>
/// <remarks>
/// Subscribe to the RanToCompletion event to receive notification of completion.  
/// On completion, invoke a parameterized action.
/// </remarks>
public void StartOrRestart(Action action) => StartOrRestart(action, EventArgs.Empty);
    

public void Cancel() => _isCancelled = true;    

Properties

public TimeSpan Interval { get; set; } = TimeSpan.FromSeconds(1);

public bool Running { get; private set; }

Event

public event EventHandler RanToCompletion;

Usage

Source and demo code is available on the GitHub.

using IVSoftware.Portable;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace testbench
{
    internal class Program
    {
        static void Main(string[] args)
        {
            runAsync();

            Console.ReadKey();
        }

        private static async void runAsync()
        {
            BringConsoleToFront();

            // Three second watchdog timer
            WatchdogTimer _wdt = new WatchdogTimer { Interval = TimeSpan.FromSeconds(3) };
            // System.Diagnostics.Stopwatch for testing purposes.
            Stopwatch _stopWatch = new Stopwatch();

            #region T E S T    A C T I O N
            Console.WriteLine("T E S T    A C T I O N");
            _stopWatch.Start();
            Console.WriteLine($"{_stopWatch.Elapsed} START");

            _wdt.StartOrRestart(()=> MarkEmailAsRead(id: 1));
            await Task.Delay(TimeSpan.FromSeconds(1));
            Console.WriteLine(_stopWatch.Elapsed);

            _wdt.StartOrRestart(() => MarkEmailAsRead(id: 2));
            await Task.Delay(TimeSpan.FromSeconds(1));
            Console.WriteLine(_stopWatch.Elapsed);

            _wdt.StartOrRestart(() => MarkEmailAsRead(id: 3)); 
            // Should run to completion
            await Task.Delay(TimeSpan.FromSeconds(4));
            Console.WriteLine(_stopWatch.Elapsed + Environment.NewLine);

            _wdt.StartOrRestart(() => MarkEmailAsRead(id: 10));
            await Task.Delay(TimeSpan.FromSeconds(2));
            Console.WriteLine(_stopWatch.Elapsed);

            _wdt.StartOrRestart(() => MarkEmailAsRead(id: 20));
            await Task.Delay(TimeSpan.FromSeconds(1));
            Console.WriteLine(_stopWatch.Elapsed);

            _wdt.StartOrRestart(() => MarkEmailAsRead(id: 30));
            // Should run to completion
            await Task.Delay(TimeSpan.FromSeconds(4));
            Console.WriteLine(_stopWatch.Elapsed + Environment.NewLine);
            #endregion T E S T    A C T I O N

            #region T E S T    C A N C E L
            Console.WriteLine("T E S T    C A N C E L");

            _stopWatch.Restart();
            Console.WriteLine($"{_stopWatch.Elapsed} START");

            _wdt.StartOrRestart(() => MarkEmailAsRead(id: 1), new CustomEventArgs(id: 1));
            await Task.Delay(TimeSpan.FromSeconds(1));
            Console.WriteLine(_stopWatch.Elapsed);

            _wdt.StartOrRestart(() => MarkEmailAsRead(id: 2), new CustomEventArgs(id: 2));
            await Task.Delay(TimeSpan.FromSeconds(1));
            Console.WriteLine(_stopWatch.Elapsed);

            _wdt.StartOrRestart(() => MarkEmailAsRead(id: 3), new CustomEventArgs(id: 3));
            // Cancel event and allow enough time to otherwise complete.
            await Task.Delay(TimeSpan.FromSeconds(2));
            _wdt.Cancel();
            await Task.Delay(TimeSpan.FromSeconds(2));
            Console.WriteLine(_stopWatch.Elapsed + Environment.NewLine);

            _wdt.StartOrRestart(() => MarkEmailAsRead(id: 10), new CustomEventArgs(id: 10));
            await Task.Delay(TimeSpan.FromSeconds(2));
            Console.WriteLine(_stopWatch.Elapsed);

            _wdt.StartOrRestart(() => MarkEmailAsRead(id: 20), new CustomEventArgs(id: 20));
            await Task.Delay(TimeSpan.FromSeconds(1));
            Console.WriteLine(_stopWatch.Elapsed);

            _wdt.StartOrRestart(() => MarkEmailAsRead(id: 30), new CustomEventArgs(id: 30));
            // Cancel event and allow enough time to otherwise complete.
            await Task.Delay(TimeSpan.FromSeconds(2));
            _wdt.Cancel();
            await Task.Delay(TimeSpan.FromSeconds(2));
            Console.WriteLine(_stopWatch.Elapsed + Environment.NewLine);
            #endregion T E S T    C A N C E L

            #region T E S T    E V E N T
            _wdt.RanToCompletion += onWdtRanToCompletion;

            Console.WriteLine("T E S T    E V E N T");
            _stopWatch.Restart();
            Console.WriteLine($"{_stopWatch.Elapsed} START");

            _wdt.StartOrRestart(new CustomEventArgs(id: 1));
            await Task.Delay(TimeSpan.FromSeconds(1));
            Console.WriteLine(_stopWatch.Elapsed);

            _wdt.StartOrRestart(new CustomEventArgs(id: 2));
            await Task.Delay(TimeSpan.FromSeconds(1));
            Console.WriteLine(_stopWatch.Elapsed);

            _wdt.StartOrRestart(new CustomEventArgs(id: 3));
            // Should run to completion
            await Task.Delay(TimeSpan.FromSeconds(4));
            Console.WriteLine(_stopWatch.Elapsed + Environment.NewLine);


            Console.WriteLine(_stopWatch.Elapsed);
            _wdt.StartOrRestart(new CustomEventArgs(id: 10));
            await Task.Delay(TimeSpan.FromSeconds(2));
            Console.WriteLine(_stopWatch.Elapsed);

            _wdt.StartOrRestart(new CustomEventArgs(id: 20));
            await Task.Delay(TimeSpan.FromSeconds(1));
            Console.WriteLine(_stopWatch.Elapsed);

            _wdt.StartOrRestart(new CustomEventArgs(id: 30));
            // Should run to completion
            await Task.Delay(TimeSpan.FromSeconds(4));
            Console.WriteLine(_stopWatch.Elapsed + Environment.NewLine);
            Console.WriteLine($"DONE {_stopWatch.Elapsed}");
            #endregion T E S T    E V E N T


            #region L o c a l F x
            void MarkEmailAsRead(int id)
            {
                Console.WriteLine($"Expired: {_stopWatch.Elapsed}");
                Console.WriteLine($"The email with the ID '{id}' has been marked read");
            }

            void onWdtRanToCompletion(object sender, EventArgs e)
            {
                if (e is CustomEventArgs ePlus)
                {
                    Console.WriteLine($"Expired: {_stopWatch.Elapsed}");
                    Console.WriteLine($"The email with the ID '{ePlus.Id}' has been marked read");
                }
            }

            void BringConsoleToFront()
            {
                SetForegroundWindow(GetConsoleWindow());
            }
            #endregion L o c a l F x
        }
        
        [DllImport("kernel32.dll", ExactSpelling = true)]
        public static extern IntPtr GetConsoleWindow();

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetForegroundWindow(IntPtr hWnd);
    }
    class CustomEventArgs : EventArgs
    {
        public CustomEventArgs(int id)
        {
            Id = id;
        }

        public int Id { get; }
    }
}
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.2.1-beta 0 11/7/2023
1.1.2 1 8/25/2023
1.1.1 0 3/10/2023
1.1.0 0 3/10/2023
1.0.2 0 11/7/2023