For this type of deal, I think a Cron type scheduler would be perfect and simplier and easier to reason about. Something like below.
public static void TestCron()
{
// Set clock for testing. 6/8/2011 1:00:45pm
Utility.SetLocalTime(new DateTime(2011, 6, 8, 13, 0, 45));
Cron cron = new Cron();
cron.Start();
// mth, dow, hr, min
Task t1On = Task.FromTime(-1, -1, 13, 1, (Task t) =>
{
Debug.Print("\nRun zone 1,2,3 on Weds at 1:01pm : " + t.ToString() + "\n");
// Open zones 1,2,3 here.
});
Task t1Off = Task.FromTime(-1, -1, -1, -1, (Task t) =>
{
Debug.Print("\nTurning off zones 1,2,3 every minute\n");
// Turn off 1,2,3 zones here.
});
Task t2On = Task.FromTime(-1, (int)DayOfWeek.Wednesday, 13, 3, (Task t) =>
{
Debug.Print("\nRun zones 4,5,6 on Weds at 1:03pm : " + t.ToString() + "\n");
// Open zones 1,2,3 here.
});
Task t2Off = Task.FromTime(-1, (int)DayOfWeek.Wednesday, 13, 4, (Task t) =>
{
Debug.Print("\nTurning off zones 4,5,6 on Weds at 1:04pm\n");
});
cron.Add(t1On, t1Off, t2On, t2Off);
}
//
// Cron.cs
//
using System;
using Microsoft.SPOT;
using System.Collections;
using System.Threading;
namespace FezPandaApp1
{
public delegate void CronAction(Task t);
public class Cron
{
ArrayList list;
bool started;
public Cron()
{
list = new ArrayList();
}
public void Add(Task task)
{
if (task == null) throw new ArgumentNullException("task");
lock (list)
{
list.Add(task);
}
}
public void Add(params Task[] tasks)
{
if (tasks == null) return;
lock (list)
{
foreach (Task t in tasks)
{
list.Add(t);
}
}
}
public Array CronTasks
{
get
{
lock(list)
{
Array[] tl = new Array[list.Count];
this.list.CopyTo(tl);
return tl;
}
}
}
public bool IsStarted
{
get { return this.started; }
}
public void Start()
{
lock (list)
{
if (started) return;
started = true;
new Thread(Loop).Start();
}
}
public void Stop()
{
lock (list)
{
this.started = false;
}
}
private void Loop()
{
Debug.Print("\nCron scheduler started at: " + DateTime.Now.ToString());
while (started)
{
// Sleep till top of next minute.
int msToTopOfNext = MSToNextMinute() + 1000; // Add a seconds to make sure we are passed top of next minute.
Debug.Print("Cron waiting for ms: " + msToTopOfNext);
Thread.Sleep(msToTopOfNext);
// Check list of tasks to start. Each started on a new thread.
DateTime now = DateTime.Now;
Debug.Print("Cron checking tasks at: " + now + " ms:" + now.Millisecond );
int count = 0;
foreach(Task t in list)
{
if (!RunNow(t))
continue;
count++;
// Run task.
Thread worker = new Thread(()=>
{
try
{
t.Action(t);
}
catch (Exception ex)
{
t.LastError = ex;
}
});
worker.Start();
}
Debug.Print("Cron jobs started this minute: " + count);
}
Debug.Print("Cron scheduler thread stopped.");
}
private bool RunNow(Task t)
{
DateTime now = DateTime.Now;
if ( (now.Month == t.Month || t.Month == -1) // -1 matches any month.
&& ((int)now.DayOfWeek == t.DayOfWeek || t.DayOfWeek == -1) // -1 matches any DOW.
&& (now.Hour == t.Hour || t.Hour == -1) // -1 matches any hour.
&& (now.Minute == t.Minute || t.Minute == -1 ) ) // -1 match any minute.
return true;
else
return false;
}
private int MSToNextMinute()
{
DateTime now = DateTime.Now;
DateTime thisMin = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0);
DateTime next = thisMin.AddMinutes(1);
long ticksToNext = (next - now).Ticks;
int msToNext = (int)(ticksToNext / TimeSpan.TicksPerMillisecond);
return msToNext;
}
}
public class Task
{
int id;
private static int counter = 0;
private CronAction action;
int month;
int dayOfWeek;
int hour;
int minute;
Exception error;
public Task(int month, int dayOfWeek, int hour, int minute, CronAction action)
{
if (action == null) throw new ArgumentNullException("action");
if (month < -1 || month > 12) throw new ArgumentOutOfRangeException("month");
if (dayOfWeek < -1 || dayOfWeek > 6) throw new ArgumentOutOfRangeException("dayOfWeek");
if (hour < -1 || hour > 23) throw new ArgumentOutOfRangeException("hour");
if (minute < -1 || minute > 59) throw new ArgumentOutOfRangeException("minute");
this.id = Interlocked.Increment(ref counter);
this.month = month;
this.dayOfWeek = dayOfWeek;
this.hour = hour;
this.minute = minute;
this.action = action;
}
public static Task FromTime(int month, int dayOfWeek, int hour, int minute, CronAction action)
{
return new Task(month, dayOfWeek, hour, minute, action);
}
public int ID
{
get { return this.id; }
}
public int Month
{
get { return this.month; }
}
public int DayOfWeek
{
get { return this.dayOfWeek; }
}
public int Hour
{
get { return this.hour; }
}
public int Minute
{
get { return this.minute; }
}
public CronAction Action
{
get { return action; }
}
public Exception LastError
{
get { return this.error; }
internal set { this.error = value; }
}
public override string ToString()
{
return "ID: " + this.id + " Month: " + this.month + " DOW: " + this.dayOfWeek + " Hour: " + this.hour + " Min: " + this.minute;
}
}
}