 |
|
 |
Sir,
The Scheduler you created works absolutely fine in many cases but when I uncheck the Check box for the very day i'm creating the Schedule, the Scheduler is still invoked at least once before it stops working for that day.
I request your guidance to find a workaround for this.
|
|
|
|
 |
|
 |
good example but you have some cross-threading issues that are exposed when upgraded to a VS 2010 project... a few simple modifications of Invoking the UI for updating fixes the problems...
|
|
|
|
 |
|
|
 |
|
|
 |
|
 |
Many thanks for the tip!
|
|
|
|
 |
|
 |
Hi
I just try the application on VS 2008, and it does not work. After pressing 'Test Code' , at the window of Schedules View, when the Messagebox with the ScheduleCallback message appear, there is an Exception
System.InvalidOperationException was unhandled
Message="Cross-thread operation not valid: Control 'SchedulesView' accessed from a thread other than the thread it was created on."
Source="System.Windows.Forms"
StackTrace:
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.ListView.ListViewNativeItemCollection.DisplayIndexToID(Int32 displayIndex)
at System.Windows.Forms.ListView.ListViewNativeItemCollection.get_Item(Int32 displayIndex)
at System.Windows.Forms.ListView.ListViewItemCollection.get_Item(Int32 index)
at EventScheduler.SchedulerUI.OnSchedulerEvent(SchedulerEventType type, String scheduleName) in C:\Serraview\EventScheduler_src\EventScheduler\SchedulerUI.cs:line 616
at EventScheduler.Scheduler.DispatchEvents(Object obj) in C:\Serraview\EventScheduler_src\EventScheduler\Scheduler.cs:line 65
at System.Threading._TimerCallback.TimerCallback_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading._TimerCallback.PerformTimerCallback(Object state)
InnerException:
|
|
|
|
 |
|
 |
I had same issue with VS2008. For a quick fix I used the following at the end of SchedulerUI.InitializeComponent(). Works like a charm.
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
Mind you I wouldn't recommend going into production this way, but it does get example to work.
|
|
|
|
 |
|
|
 |
|
 |
new version for schedle?
you send my eamil?
thanks
|
|
|
|
 |
|
 |
Hello everybody
Does anybody know how can I persist Schedule data on a database?
I want to have all the schedule data on a database instead of adding them manually...
I was thinking how I could design some tables for this, but I don't have much experience... If somebody could help me would be great.
Thanks!!
|
|
|
|
 |
|
 |
Hi, thank you for showing us how you implement this component. I am now using this to implement a backup function for my application. I have made many changes to your code where for most part it is unrecognizable, most noticeably to be able to save the schedules into XML via serialization. I would like to use some of your concepts and sample code for a commercial application, is this alright with you? Thanks once again.
I love codes that just work!!
|
|
|
|
 |
|
 |
Joseph,
please feel free to use the idea. If you can give credit to the article in the application you are building, I will be very happy.
Good luck.
Sriram Chitturi
|
|
|
|
 |
|
 |
Has anyone had any experience of using this component in a non-gui application?
A console app or (more interestingly) a windows service?
|
|
|
|
 |
|
 |
I took the code and modified it for my service. I think customization is part of the game when we borrow code from internet :->
|
|
|
|
 |
|
 |
After using these classes in couple projects, I found tons of mistakes.
Here is my version that I use (it is also not perfect, but IMHO more usefull). There are changes basically in every class:
Schedule.cs
using System;
using System.Threading;
namespace EventScheduler
{
// delegate for OnTrigger() event
public delegate void Invoke(string scheduleName);
// enumeration for schedule types
public enum ScheduleType { ONETIME, INTERVAL, DAILY, WEEKLY, MONTHLY };
// base class for all schedules
abstract public class Schedule : IComparable
{
public event Invoke OnTrigger;
protected string m_name; // name of the schedule
protected ScheduleType m_type; // type of schedule
protected bool m_active; // is schedule active ?
protected DateTime m_nextTime; // time when this schedule is invoked next, used by scheduler
// m_fromTime and m_toTime are used to defined a time range during the day
// between which the schedule can run.
// This is useful to define a range of working hours during which a schedule can run
protected TimeSpan m_fromTime;
protected TimeSpan m_toTime;
// Array containing the 7 weekdays and their status
// Using DayOfWeek enumeration for index of this array
bool[] m_workingWeekDays = new bool[]{true, true, true, true, true, true, true};
// time interval used by schedules like IntervalSchedule
protected TimeSpan m_interval = new TimeSpan();
public void SetWorkingDaysOnly()
{
SetWeekDay(DayOfWeek.Monday, true);
SetWeekDay(DayOfWeek.Tuesday, true);
SetWeekDay(DayOfWeek.Wednesday, true);
SetWeekDay(DayOfWeek.Thursday, true);
SetWeekDay(DayOfWeek.Friday, true);
SetWeekDay(DayOfWeek.Saturday, false);
SetWeekDay(DayOfWeek.Sunday, false);
}
// Accessor for type of schedule
public ScheduleType Type
{
get { return m_type; }
}
// Accessor for name of schedule
// Name is set in constructor only and cannot be changed
public string Name
{
get { return m_name; }
}
public bool Active
{
get { return m_active; }
set { m_active = value; }
}
// check if no week days are active
protected bool NoFreeWeekDay()
{
bool check = false;
for (int index=0; index<7; check = check|m_workingWeekDays[index], index++);
return check;
}
// Setting the status of a week day
public void SetWeekDay(DayOfWeek day, bool On)
{
m_workingWeekDays[(int)day] = On;
Active = true; // assuming
// Make schedule inactive if all weekdays are inactive
// If a schedule is not using the weekdays the array should not be touched
if (NoFreeWeekDay())
Active = false;
}
// Return if the week day is set active
public bool WeekDayActive(DayOfWeek day)
{
return m_workingWeekDays[(int)day];
}
// Method which will return when the Schedule has to be invoked next
// This method is used by Scheduler for sorting Schedule objects in the list
public DateTime NextInvokeTime
{
get { return m_nextTime; }
}
// Accessor for m_interval in seconds
// Put a lower limit on the interval to reduce burden on resources
// I am using a lower limit of 30 seconds
public TimeSpan Interval
{
get { return m_interval; }
set {
if (value.TotalSeconds < 1)
throw new SchedulerException("Interval cannot be less than 1 second");
m_interval = value;
}
}
// Constructor
public Schedule(string name, ScheduleType type)
{
m_type = type;
m_name = name;
m_nextTime = DateTime.Now;
}
// Sets the next time this Schedule is kicked off and kicks off events on
// a seperate thread, freeing the Scheduler to continue
public void TriggerEvents()
{
CalculateNextInvokeTime(); // first set next invoke time to continue with rescheduling
ThreadStart ts = new ThreadStart(KickOffEvents);
Thread t = new Thread(ts);
t.Start();
}
// Implementation of ThreadStart delegate.
// Used by Scheduler to kick off events on a seperate thread
private void KickOffEvents()
{
if (OnTrigger != null)
OnTrigger(Name);
}
// To be implemented by specific schedule objects when to invoke the schedule next
internal abstract void CalculateNextInvokeTime();
// check to see if the Schedule can be invoked on the week day it is next scheduled
protected bool CanInvokeOnNextWeekDay()
{
return m_workingWeekDays[(int)m_nextTime.DayOfWeek];
}
// Check to see if the next time calculated is within the time range
// given by m_fromTime and m_toTime
// The ranges can be during a day, for eg. 9 AM to 6 PM on same day
// or overlapping 2 different days like 10 PM to 5 AM (i.e over the night)
protected bool IsInvokeTimeInTimeRange()
{
if (m_fromTime < m_toTime) // eg. like 9 AM to 6 PM
return (m_nextTime.TimeOfDay >= m_fromTime && m_nextTime.TimeOfDay <= m_toTime);
else // eg. like 10 PM to 5 AM
return (m_nextTime.TimeOfDay >= m_toTime && m_nextTime.TimeOfDay <= m_fromTime);
}
// IComparable interface implementation is used to sort the array of Schedules
// by the Scheduler
public int CompareTo(object obj)
{
if (obj is Schedule)
{
return m_nextTime.CompareTo(((Schedule)obj).m_nextTime);
}
throw new Exception("Not a Schedule object");
}
}
}
Scheduler.cs
using System;
using System.Threading;
using System.Collections;
namespace EventScheduler
{
// enumeration of Scheduler events used by the delegate
public enum SchedulerEventType { CREATED, DELETED, INVOKED };
// delegate for Scheduler events
public delegate void SchedulerEventDelegate(SchedulerEventType type, string scheduleName);
// This is the main class which will maintain the list of Schedules
// and also manage them, like rescheduling, deleting schedules etc.
public sealed class Scheduler
{
// Event raised when for any event inside the scheduler
static public event SchedulerEventDelegate OnSchedulerEvent;
// next event which needs to be kicked off,
// this is set when a new Schedule is added or after invoking a Schedule
static Schedule m_nextSchedule = null;
static ArrayList m_schedulesList = new ArrayList(); // list of schedules
static Timer m_timer = new Timer(new TimerCallback(DispatchEvents), // main timer
null,
Timeout.Infinite,
Timeout.Infinite);
// Get schedule at a particular index in the array list
public static Schedule GetScheduleAt(int index)
{
if (index < 0 || index >= m_schedulesList.Count)
return null;
return (Schedule)m_schedulesList[index];
}
// Number of schedules in the list
public static int Count()
{
return m_schedulesList.Count;
}
// Indexer to access a Schedule object by name
public static Schedule GetSchedule(string scheduleName)
{
for (int index=0; index < m_schedulesList.Count; index++)
if (((Schedule)m_schedulesList[index]).Name == scheduleName)
return (Schedule)m_schedulesList[index];
return null;
}
// call back for the timer function
static void DispatchEvents(object obj) // obj ignored
{
if (m_nextSchedule == null)
return;
m_nextSchedule.TriggerEvents(); // make this happen on a thread to let this thread continue
if (m_nextSchedule.Type == ScheduleType.ONETIME)
{
RemoveSchedule(m_nextSchedule); // remove the schedule from the list
}
else
{
if (OnSchedulerEvent != null)
OnSchedulerEvent(SchedulerEventType.INVOKED, m_nextSchedule.Name);
m_schedulesList.Sort();
SetNextEventTime();
}
}
// method to set the time when the timer should wake up to invoke the next schedule
static void SetNextEventTime()
{
if (m_schedulesList.Count == 0)
{
m_timer.Change(Timeout.Infinite, Timeout.Infinite); // this will put the timer to sleep
return;
}
m_nextSchedule = (Schedule)m_schedulesList[0];
TimeSpan ts = m_nextSchedule.NextInvokeTime.Subtract(DateTime.Now);
if (ts < TimeSpan.Zero)
ts = TimeSpan.Zero; // time already past, set timer to 0 to execute callback
m_timer.Change((int)ts.TotalMilliseconds, Timeout.Infinite); // invoke after the timespan
}
// add a new schedule
public static void AddSchedule(Schedule s)
{
if (GetSchedule(s.Name) != null)
throw new SchedulerException("Schedule with the same name already exists");
m_schedulesList.Add(s);
m_schedulesList.Sort();
// adjust the next event time if schedule is added at the top of the list
if (m_schedulesList[0] == s)
SetNextEventTime();
if (OnSchedulerEvent != null)
OnSchedulerEvent(SchedulerEventType.CREATED, s.Name);
}
// remove a schedule object from the list
public static void RemoveSchedule(Schedule s)
{
m_schedulesList.Remove(s);
SetNextEventTime();
if (OnSchedulerEvent != null)
OnSchedulerEvent(SchedulerEventType.DELETED, s.Name);
}
// remove all schedules
public static void RemoveAllSchedules()
{
while(m_schedulesList.Count > 0)
{
String name = ((Schedule)m_schedulesList[0]).Name;
m_schedulesList.Remove((Schedule)m_schedulesList[0]);
SetNextEventTime();
if(OnSchedulerEvent != null)
OnSchedulerEvent(SchedulerEventType.DELETED, name);
}
}
// remove schedule by name
public static void RemoveSchedule(string name)
{
RemoveSchedule(GetSchedule(name));
}
}
}
SchedulerExceptions.cs
using System;
namespace EventScheduler
{
///
/// Summary description for SchedulerException.
///
public class SchedulerException : Exception
{
public SchedulerException(string msg) : base(msg)
{
}
}
}
ScheduleTypes.cs
using System;
namespace EventScheduler
{
// OneTimeSchedule is used to schedule an event to run only once
// Used by specific tasks to check self status
public class OneTimeSchedule : Schedule
{
public OneTimeSchedule(String name, DateTime startTime)
: base(name, ScheduleType.ONETIME)
{
m_nextTime = startTime;
}
internal override void CalculateNextInvokeTime()
{
// it does not matter, since this is a one time schedule
m_nextTime = DateTime.MaxValue;
}
}
// IntervalSchedule is used to schedule an event to be invoked at regular intervals
// the interval is specified in seconds. Useful mainly in checking status of threads
// and connections. Use an interval of 60 hours for an hourly schedule
public class IntervalSchedule : Schedule
{
public IntervalSchedule(String name, TimeSpan TimeInterval,
TimeSpan fromTime, TimeSpan toTime) // time range for the day
: base(name, ScheduleType.INTERVAL)
{
if(TimeInterval.Ticks >= TimeSpan.TicksPerDay)
throw new SchedulerException("TimeInterval > max ticks per day");
if(fromTime.Ticks >= TimeSpan.TicksPerDay)
throw new SchedulerException("fromTime > max ticks per day");
if(toTime.Ticks >= TimeSpan.TicksPerDay)
throw new SchedulerException("toTime > max ticks per day");
m_fromTime = fromTime;
m_toTime = toTime;
Interval = TimeInterval;
DateTime now = DateTime.Now;
DateTime start = new DateTime(now.Year, now.Month, now.Day, m_fromTime.Hours, m_fromTime.Minutes, m_fromTime.Seconds, m_fromTime.Milliseconds);
DateTime end = new DateTime(now.Year, now.Month, now.Day, m_toTime.Hours, m_toTime.Minutes, m_toTime.Seconds, m_toTime.Milliseconds);
m_nextTime = start;
if(fromTime < toTime)
{
do
{
if(now <= start)
break;
if(now >= end || now + TimeInterval >= end)
{
m_nextTime = start.AddDays(1);
break;
}
long ticks_from_start = now.TimeOfDay.Ticks - start.TimeOfDay.Ticks;
long whole_past_ticks_intervals = ticks_from_start / TimeInterval.Ticks;
start = start.AddTicks(TimeInterval.Ticks * whole_past_ticks_intervals + TimeInterval.Ticks);
} while(false);
}
else
throw new SchedulerException("Not supported time range");
m_nextTime = start;
}
internal override void CalculateNextInvokeTime()
{
// add the interval of m_seconds
m_nextTime = m_nextTime.AddTicks(Interval.Ticks);
// if next invoke time is not within the time range, then set it to next start time
if (! IsInvokeTimeInTimeRange())
{
Boolean bAddDay = false;
if(m_nextTime.TimeOfDay > m_toTime)
bAddDay = true;
// set m_nextTime to the beginning of a new day
m_nextTime = new DateTime(m_nextTime.Year, m_nextTime.Month, m_nextTime.Day, m_fromTime.Hours, m_fromTime.Minutes, m_fromTime.Seconds, m_fromTime.Milliseconds);
if(bAddDay)
m_nextTime = m_nextTime.AddDays(1);
}
// check to see if the next invoke time is on a working day
while(!CanInvokeOnNextWeekDay())
m_nextTime = m_nextTime.AddDays(1); // start checking on the next day
}
}
// Daily schedule is used set off to the event every day
// Mainly useful in maintanance, recovery, logging and report generation
// Restictions can be imposed on the week days on which to run the schedule
public class DailySchedule : Schedule
{
public DailySchedule(String name, TimeSpan startTime)
: base(name, ScheduleType.DAILY)
{
if(startTime.Ticks >= TimeSpan.TicksPerDay)
throw new SchedulerException("startTime > max ticks per day");
DateTime current = DateTime.Now;
DateTime today_start = new DateTime(current.Year, current.Month, current.Day, startTime.Hours, startTime.Minutes, startTime.Seconds, startTime.Milliseconds);
int res = DateTime.Compare(current, today_start);
if(res > 0)// current > start_time
today_start = today_start.AddDays(1);
m_nextTime = today_start;
}
internal override void CalculateNextInvokeTime()
{
// add a day, and check for any weekday restrictions and keep adding a day
m_nextTime = m_nextTime.AddDays(1);
while(!CanInvokeOnNextWeekDay())
m_nextTime = m_nextTime.AddDays(1);
}
}
// Weekly schedules
public class WeeklySchedule : Schedule
{
public WeeklySchedule(String name, DateTime startTime)
: base(name, ScheduleType.WEEKLY)
{
m_nextTime = startTime;
}
public WeeklySchedule(String name, DayOfWeek day, TimeSpan time)
: base(name, ScheduleType.WEEKLY)
{
DateTime now = DateTime.Now;
DateTime this_week = now.Date.AddDays(-(int)now.DayOfWeek);
m_nextTime = this_week.Add(time);
if(m_nextTime <= DateTime.Now)
CalculateNextInvokeTime();
}
// add a week (or 7 days) to the date
internal override void CalculateNextInvokeTime()
{
m_nextTime = m_nextTime.AddDays(7);
}
}
// Monthly schedule - used to kick off an event every month on the same day as scheduled
// and also at the same hour and minute as given in start time
public class MonthlySchedule : Schedule
{
public MonthlySchedule(String name, DateTime startTime)
: base(name, ScheduleType.MONTHLY)
{
m_nextTime = startTime;
}
public MonthlySchedule(String name, TimeSpan startDateTimeFromTheBeginningOfTheMonth)
: base(name, ScheduleType.MONTHLY)
{
DateTime now = DateTime.Now;
DateTime dt = new DateTime(now.Year, now.Month, 1) + startDateTimeFromTheBeginningOfTheMonth;
dt = dt.AddDays(-1);
if(dt < now)
dt = dt.AddMonths(1);
m_nextTime = dt;
}
// add a month to the present time
internal override void CalculateNextInvokeTime()
{
m_nextTime = m_nextTime.AddMonths(1);
}
}
}
Dima
http://www.mmexplorer.com[^]
|
|
|
|
 |
|
 |
Dear Dima.
Maybe you can find some times for update all code, because after yours updates, examples in archive EventScheduler_src.zip compiled with some errors.
|
|
|
|
 |
|
 |
yeah, examples might not be fully compatible, because I did several changes that I needed for my projects. If you have any questions how to use the code, do not hesitate and ask me
Dima
www.mmexplorer.com[^]
|
|
|
|
 |
|
 |
That is why commenting on others code is easy, right ? :->
|
|
|
|
 |
|
|
 |
|
 |
Your code almost works with one schedule.
I believe you need locks for ADD and REMOVE and anything else that plays with the schedules beyond that
Perhaps use:
lock (m_schedulesList) {
}
From a coding perspective, there appears to be a mishmash of fields and properties used. Better to go with Properties.
Some of your limits are quite arbitrary - it's not hard to make it work with no start delay and an interval of 1 second.
I think your Daily crashes if the time to run has already passed for the day you try to Add it.
I don't mind the events running on a thread but perhaps it could be an option in the Add so that short, frequent events could run on the timer's own thread. Perhaps a try/catch around the call to the delegate would help keep things alive.
"If your world is free, someone will take it away."
|
|
|
|
 |
|
 |
Scehdules set on an interval I find don't actually expire and reschedule to the next day. Notice the code segment.
Original:
internal override void CalculateNextInvokeTime()
{
// add the interval of m_seconds
m_nextTime = m_nextTime.AddSeconds(Interval);
// if next invoke time is not within the time range, then set it to next start time
if (! IsInvokeTimeInTimeRange())
{
if (m_nextTime.TimeOfDay < m_fromTime)
m_nextTime.AddSeconds(m_fromTime.Seconds - m_nextTime.TimeOfDay.Seconds);
else
m_nextTime.AddSeconds((24 * 3600) - m_nextTime.TimeOfDay.Seconds + m_fromTime.Seconds);
}
// check to see if the next invoke time is on a working day
while (! CanInvokeOnNextWeekDay())
m_nextTime = m_nextTime.AddDays(1); // start checking on the next day
}
}
This is my version of the routine that sets the next invoke time:
internal override void CalculateNextInvokeTime()
{
// add the interval of m_seconds
m_nextTime = m_nextTime.AddSeconds(Interval);
// if next invoke time is not within the time range, then set it to next start time
if (! IsInvokeTimeInTimeRange())
{
if (m_nextTime.TimeOfDay < m_fromTime)
m_nextTime = m_nextTime.AddSeconds(m_fromTime.Seconds - m_nextTime.TimeOfDay.Seconds);
else
//m_nextTime = m_nextTime.AddSeconds((24 * 3600) - m_nextTime.TimeOfDay.Seconds + m_fromTime.Seconds);
m_nextTime = new DateTime(m_nextTime.Year,m_nextTime.Month,m_nextTime.AddDays(1).Day,m_fromTime.Hours,m_fromTime.Minutes,m_fromTime.Seconds,m_fromTime.Milliseconds);
}
// check to see if the next invoke time is on a working day
while (! CanInvokeOnNextWeekDay())
m_nextTime = m_nextTime.AddDays(1); // start checking on the next day
}
}
Did you notice something in the original version? Every developer makes this mistake at some point. When using the "add..." functions on a variable, it doesn't actually manipulate the variable itself, instead, it returns another variable with it's added changes. Seeing this inclines me to think this source hasn't been tested entirely, which is fine, that is why I am writing this message \=)
Happy coding!
Kurios
|
|
|
|
 |
|
 |
Plz upload the updated bug fixed version.
Thanks,
The G Force
|
|
|
|
 |
|
 |
How do I convert this code to vb.net
I don't know c# or vb.net what is the best way to start?
|
|
|
|
 |
|
 |
Goto this page and get this great tool to help ypu learn how others doit!!!
http://www.aisto.com/roeder/dotnet/
Dissasembler called : .NET Reflector.....
TRIDEX
======
|
|
|
|
 |
|
 |
i have many tasks to execute,and every task takes much time.if I use this,it will produce many threads ,so the memory is very high.how to resolve it?
|
|
|
|
 |