diff --git a/Baget/UtilidadesTSL4net.c.nuspec b/Baget/UtilidadesTSL4net.c.nuspec new file mode 100644 index 0000000..9b197b2 --- /dev/null +++ b/Baget/UtilidadesTSL4net.c.nuspec @@ -0,0 +1,18 @@ + + + + UtilidadesTSL4net.c + 1.0.0.0 + Manuel + Tecnosis S.A. + false + Utilidades c# WPF Tecnosis. + UtilidadesTSL4net.c .net48 + + + + + + + + \ No newline at end of file diff --git a/Baget/nuget.exe b/Baget/nuget.exe new file mode 100644 index 0000000..94aada9 Binary files /dev/null and b/Baget/nuget.exe differ diff --git a/EventStorage.cs b/EventStorage.cs new file mode 100644 index 0000000..f3b3b4f --- /dev/null +++ b/EventStorage.cs @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Xml; +using System.Xml.XPath; + +namespace UtilidadesCSharp.Schedule +{ + /// + /// Null event strorage disables error recovery by returning now for the last time an event fired. + /// + public class NullEventStorage : IEventStorage + { + public NullEventStorage() + { + } + + public void RecordLastTime(DateTime Time) + { + } + + public DateTime ReadLastTime() + { + return DateTime.Now; + } + } + + /// + /// Local event strorage keeps the last time in memory so that skipped events are not recovered. + /// + public class LocalEventStorage : IEventStorage + { + public LocalEventStorage() + { + _LastTime = DateTime.MaxValue; + } + + public void RecordLastTime(DateTime Time) + { + _LastTime = Time; + } + + public DateTime ReadLastTime() + { + if (_LastTime == DateTime.MaxValue) + _LastTime = DateTime.Now; + return _LastTime; + } + + DateTime _LastTime; + } + + /// + /// FileEventStorage saves the last time in an XmlDocument so that recovery will include periods that the + /// process is shutdown. + /// + public class FileEventStorage : IEventStorage + { + public FileEventStorage(string FileName, string XPath) + { + _FileName = FileName; + _XPath = XPath; + } + + public void RecordLastTime(DateTime Time) + { + _Doc.SelectSingleNode(_XPath).Value = Time.ToString(); + _Doc.Save(_FileName); + } + + public DateTime ReadLastTime() + { + _Doc.Load(_FileName); + string Value = _Doc.SelectSingleNode(_XPath).Value; + if (Value == null || Value == string.Empty) + return DateTime.Now; + return DateTime.Parse(Value); + } + + string _FileName; + string _XPath; + XmlDocument _Doc = new XmlDocument(); + } +} diff --git a/IScheduledItem.cs b/IScheduledItem.cs new file mode 100644 index 0000000..bd23e0b --- /dev/null +++ b/IScheduledItem.cs @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Collections; + +namespace UtilidadesCSharp.Schedule +{ + /// + /// IScheduledItem represents a scheduled event. You can query it for the number of events that occur + /// in a time interval and for the remaining interval before the next event. + /// + public interface IScheduledItem + { + /// + /// Returns the times of the events that occur in the given time interval. The interval is closed + /// at the start and open at the end so that intervals can be stacked without overlapping. + /// + /// The beginning of the interval + /// The end of the interval + /// All events >= Begin and < End + void AddEventsInInterval(DateTime Begin, DateTime End, ArrayList List); + + /// + /// Returns the next run time of the scheduled item. Optionally excludes the starting time. + /// + /// The starting time of the interval + /// if true then the starting time is included in the query false, it is excluded. + /// The next execution time either on or after the starting time. + DateTime NextRunTime(DateTime time, bool IncludeStartTime); + } + +} diff --git a/MethodCall.cs b/MethodCall.cs new file mode 100644 index 0000000..a0f39b7 --- /dev/null +++ b/MethodCall.cs @@ -0,0 +1,332 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Collections; +using System.Reflection; +using System.Text.RegularExpressions; + +namespace UtilidadesCSharp.Schedule +{ + /// + /// IParameterSetter represents a serialized parameter list. This is used to provide a partial specialized + /// method call. This is useful for remote invocation of method calls. For example if you have a method with + /// 3 parameters. The first 2 might represent static data such as a report and a storage location. The third + /// might be the time that the report is invoked, which is only known when the method is invoked. Using this, + /// you just pass the method and the first 2 parameters to a timer object, which supplies the 3rd parameter. + /// Without these objects, you would have to generate a custom object type for each method you wished to + /// execute in this manner and store the static parameters as instance variables. + /// + public interface IParameterSetter + { + /// + /// This resets the setter to the beginning. It is used for setters that rely on positional state + /// information. It is called prior to setting any method values. + /// + void reset(); + /// + /// This method is used to both query support for setting a parameter and actually set the value. + /// True is returned if the parameter passed in is updated. + /// + /// The reflection information about this parameter. + /// The location of the prameter in the parameter list. + /// The parameter object + /// true if the parameter is matched and false otherwise + bool GetParameterValue(ParameterInfo pi, int ParameterLoc, ref object parameter); + } + + /// + /// This setter object takes a simple object array full of parameter data. It applys the objects in order + /// to the method parameter list. + /// + public class OrderParameterSetter : IParameterSetter + { + public OrderParameterSetter(params object[] _Params) + { + _ParamList = _Params; + } + public void reset() + { + _counter = 0; + } + public bool GetParameterValue(ParameterInfo pi, int ParameterLoc, ref object parameter) + { + if (_counter >= _ParamList.Length) + return false; + parameter = _ParamList[_counter++]; + return true; + } + + object[] _ParamList; + int _counter; + } + + /// + /// This setter object stores the parameter data in a Hashtable and uses the hashtable keys to match + /// the parameter names of the method to the parameter data. This allows methods to be called like + /// stored procedures, with the parameters being passed in independent of order. + /// + public class NamedParameterSetter : IParameterSetter + { + public NamedParameterSetter(Hashtable Params) + { + _Params = Params; + } + public void reset() + { + } + public bool GetParameterValue(ParameterInfo pi, int ParameterLoc, ref object parameter) + { + string ParamName = pi.Name; + if (!_Params.ContainsKey(ParamName)) + return false; + parameter = _Params[ParamName]; + return true; + } + Hashtable _Params; + } + + + /// + /// ParameterSetterList maintains a collection of IParameterSetter objects and applies them in order to each + /// parameter of the method. Each time a match occurs the next parameter is tried starting with the first + /// setter object until it is matched. + /// + public class ParameterSetterList + { + public void Add(IParameterSetter setter) + { + _List.Add(setter); + } + + public IParameterSetter[] ToArray() + { + return (IParameterSetter[])_List.ToArray(typeof(IParameterSetter)); + } + + public void reset() + { + foreach(IParameterSetter Setter in _List) + Setter.reset(); + } + + public object[] GetParameters(MethodInfo Method) + { + ParameterInfo[] Params = Method.GetParameters(); + object[] Values = new object[Params.Length]; + //TODO: Update to iterate backwards + for(int i=0; i + /// IMethodCall represents a partially specified parameter data list and a method. This allows methods to be + /// dynamically late invoked for things like timers and other event driven frameworks. + /// + public interface IMethodCall + { + ParameterSetterList ParamList { get; } + object Execute(); + object Execute(IParameterSetter Params); + void EventHandler(object obj, EventArgs e); + IAsyncResult BeginExecute(AsyncCallback callback, object obj); + IAsyncResult BeginExecute(IParameterSetter Params, AsyncCallback callback, object obj); + } + + delegate object Exec(); + delegate object Exec2(IParameterSetter Params); + + /// + /// Method call captures the data required to do a defered method call. + /// + public class DelegateMethodCall : MethodCallBase, IMethodCall + { + public DelegateMethodCall(Delegate f) + { + _f = f; + } + + public DelegateMethodCall(Delegate f, params object[] Params) + { + if (f.Method.GetParameters().Length < Params.Length) + throw new ArgumentException("Too many parameters specified for delegate", "f"); + + _f = f; + ParamList.Add(new OrderParameterSetter(Params)); + } + + public DelegateMethodCall(Delegate f, IParameterSetter Params) + { + _f = f; + ParamList.Add(Params); + } + + Delegate _f; + + public Delegate f + { + get { return _f; } + set { _f = value; } + } + + public MethodInfo Method + { + get { return _f.Method; } + } + + public object Execute() + { + return f.DynamicInvoke(GetParameterList(Method)); + } + + public object Execute(IParameterSetter Params) + { + return f.DynamicInvoke(GetParameterList(Method, Params)); + } + + public void EventHandler(object obj, EventArgs e) + { + Execute(); + } + + Exec _exec; + public IAsyncResult BeginExecute(AsyncCallback callback, object obj) + { + _exec = new Exec(Execute); + return _exec.BeginInvoke(callback, obj); + } + + public IAsyncResult BeginExecute(IParameterSetter Params, AsyncCallback callback, object obj) + { + Exec2 exec = new Exec2(Execute); + return exec.BeginInvoke(Params, callback, obj); + } + } + + public class DynamicMethodCall : MethodCallBase, IMethodCall + { + public DynamicMethodCall(MethodInfo method) + { + _obj = null; + _method = method; + } + + public DynamicMethodCall(object obj, MethodInfo method) + { + _obj = obj; + _method = method; + } + + public DynamicMethodCall(object obj, MethodInfo method, IParameterSetter setter) + { + _obj = obj; + _method = method; + ParamList.Add(setter); + } + + object _obj; + MethodInfo _method; + + public MethodInfo Method + { + get { return _method; } + set { _method = value; } + } + + public object Execute() + { + return _method.Invoke(_obj, GetParameterList(Method)); + } + + public object Execute(IParameterSetter Params) + { + return _method.Invoke(_obj, GetParameterList(Method, Params)); + } + + public void EventHandler(object obj, EventArgs e) + { + Execute(); + } + + Exec _exec; + public IAsyncResult BeginExecute(AsyncCallback callback, object obj) + { + _exec = new Exec(Execute); + return _exec.BeginInvoke(callback, null); + } + + public IAsyncResult BeginExecute(IParameterSetter Params, AsyncCallback callback, object obj) + { + Exec2 exec = new Exec2(Execute); + return exec.BeginInvoke(Params, callback, null); + } + } + + /// + /// This is a base class that handles the Parameter list management for the 2 dynamic method call methods. + /// + public class MethodCallBase + { + ParameterSetterList _ParamList = new ParameterSetterList(); + + public ParameterSetterList ParamList + { + get { return _ParamList; } + } + + protected object[] GetParameterList(MethodInfo Method) + { + ParamList.reset(); + object[] Params = ParamList.GetParameters(Method); + return Params; + } + + protected object[] GetParameterList(MethodInfo Method, IParameterSetter Params) + { + ParamList.reset(); + object[] objParams = ParamList.GetParameters(Method, Params); + return objParams; + } + } + +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f9af44f --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// La información general sobre un ensamblado se controla mediante el siguiente +// conjunto de atributos. Cambie estos atributos para modificar la información +// asociada con un ensamblado. +[assembly: AssemblyTitle("UtilidadesTSL4net.c")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany(".")] +[assembly: AssemblyProduct("UtilidadesTSL4net.c")] +[assembly: AssemblyCopyright("© . 2008")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Si establece ComVisible como false hace que los tipos de este ensamblado no sean visibles +// a los componentes COM. Si necesita obtener acceso a un tipo en este ensamblado desde +// COM, establezca el atributo ComVisible como true en este tipo. +[assembly: ComVisible(false)] + +// El siguiente GUID sirve como identificador de la biblioteca de tipos si este proyecto se expone a COM +[assembly: Guid("51c70542-3016-463d-8d03-d306aae697f8")] + +// La información de versión de un ensamblado consta de los cuatro valores siguientes: +// +// Versión principal +// Versión secundaria +// Número de versión de compilación +// Revisión +// +// Puede especificar todos los valores o puede establecer como valores predeterminados los números de revisión y generación +// mediante el asterisco ('*'), como se muestra a continuación: +[assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyFileVersion("1.0.*")] diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs new file mode 100644 index 0000000..0907d74 --- /dev/null +++ b/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// Este código fue generado por una herramienta. +// Versión de runtime:4.0.30319.42000 +// +// Los cambios en este archivo podrían causar un comportamiento incorrecto y se perderán si +// se vuelve a generar el código. +// +//------------------------------------------------------------------------------ + +namespace Utilidadescsharp.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.8.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/Properties/Settings.settings b/Properties/Settings.settings new file mode 100644 index 0000000..049245f --- /dev/null +++ b/Properties/Settings.settings @@ -0,0 +1,6 @@ + + + + + + diff --git a/ReportTimer.cs b/ReportTimer.cs new file mode 100644 index 0000000..3a2f2a7 --- /dev/null +++ b/ReportTimer.cs @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; + +namespace UtilidadesCSharp.Schedule +{ + public class ReportEventArgs : EventArgs + { + public ReportEventArgs(DateTime Time, int reportNo) { EventTime = Time; ReportNo = reportNo; } + public int ReportNo; + public DateTime EventTime; + } + + public delegate void ReportEventHandler(object sender, ReportEventArgs e); + + /// + /// Summary description for ReportTimer. + /// + public class ReportTimer : ScheduleTimerBase + { + public void AddReportEvent(IScheduledItem Schedule, int reportNo) + { + if (Elapsed == null) + throw new Exception("You must set elapsed before adding Events"); + AddJob(new TimerJob(Schedule, new DelegateMethodCall(Handler, Elapsed, reportNo))); + } + + public void AddAsyncReportEvent(IScheduledItem Schedule, int reportNo) + { + if (Elapsed == null) + throw new Exception("You must set elapsed before adding Events"); + TimerJob Event = new TimerJob(Schedule, new DelegateMethodCall(Handler, Elapsed, reportNo)); + Event.SyncronizedEvent = false; + AddJob(Event); + } + + public event ReportEventHandler Elapsed; + + delegate void ConvertHandler(ReportEventHandler Handler, int ReportNo, object sender, DateTime time); + static ConvertHandler Handler = new ConvertHandler(Converter); + static void Converter(ReportEventHandler Handler, int ReportNo, object sender, DateTime time) + { + if (Handler == null) + throw new ArgumentNullException("Handler"); + if (sender == null) + throw new ArgumentNullException("sender"); + Handler(sender, new ReportEventArgs(time, ReportNo)); + } + } +} diff --git a/ScheduleFilter.cs b/ScheduleFilter.cs new file mode 100644 index 0000000..466e6a9 --- /dev/null +++ b/ScheduleFilter.cs @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Collections; + +namespace UtilidadesCSharp.Schedule +{ + /// + /// This is an empty filter that does not filter any of the events. + /// + public class Filter : IResultFilter + { + public static IResultFilter Empty = new Filter(); + private Filter() {} + + public void FilterResultsInInterval(DateTime Start, DateTime End, ArrayList List) + { + if (List == null) + return; + List.Sort(); + } + } + + /// + /// This causes only the first event of the interval to be counted. + /// + public class FirstEventFilter : IResultFilter + { + public static IResultFilter Filter = new FirstEventFilter(); + private FirstEventFilter() {} + + public void FilterResultsInInterval(DateTime Start, DateTime End, ArrayList List) + { + if (List == null) + return; + if (List.Count < 2) + return; + List.Sort(); + List.RemoveRange(1, List.Count-1); + } + } + + /// + /// This causes only the last event of the interval to be counted. + /// + public class LastEventFilter : IResultFilter + { + public static IResultFilter Filter = new LastEventFilter(); + private LastEventFilter() {} + + public void FilterResultsInInterval(DateTime Start, DateTime End, ArrayList List) + { + if (List == null) + return; + if (List.Count < 2) + return; + List.Sort(); + List.RemoveRange(0, List.Count-1); + } + } + +} diff --git a/ScheduleTimer.cs b/ScheduleTimer.cs new file mode 100644 index 0000000..acace75 --- /dev/null +++ b/ScheduleTimer.cs @@ -0,0 +1,254 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Collections; +using System.Timers; +using System.Diagnostics; + +namespace UtilidadesCSharp.Schedule +{ + /// + /// ScheduleTimer represents a timer that fires on a more human friendly schedule. For example it is easy to + /// set it to fire every day at 6:00PM. It is useful for batch jobs or alarms that might be difficult to + /// schedule with the native .net timers. + /// It is similar to the .net timer that it is based on with the start and stop methods functioning similarly. + /// The main difference is the event uses a different delegate and arguement since the .net timer argument + /// class is not creatable. + /// + public class ScheduleTimerBase : IDisposable + { + public ScheduleTimerBase() + { + _Timer = new Timer(); + _Timer.AutoReset = false; + _Timer.Elapsed += new ElapsedEventHandler(Timer_Elapsed); + _Jobs = new TimerJobList(); + _LastTime = DateTime.MaxValue; + } + + /// + /// Adds a job to the timer. This method passes in a delegate and the parameters similar to the Invoke method of windows forms. + /// + /// The schedule that this delegate is to be run on. + /// The delegate to run + /// The method parameters to pass if you leave any DateTime parameters unbound, then they will be set with the scheduled run time of the + /// method. Any unbound object parameters will get this Job object passed in. + public void AddJob(IScheduledItem Schedule, Delegate f, params object[] Params) + { + _Jobs.Add(new TimerJob(Schedule, new DelegateMethodCall(f, Params))); + } + + /// + /// Adds a job to the timer to operate asyncronously. + /// + /// The schedule that this delegate is to be run on. + /// The delegate to run + /// The method parameters to pass if you leave any DateTime parameters unbound, then they will be set with the scheduled run time of the + /// method. Any unbound object parameters will get this Job object passed in. + public void AddAsyncJob(IScheduledItem Schedule, Delegate f, params object[] Params) + { + TimerJob Event = new TimerJob(Schedule, new DelegateMethodCall(f, Params)); + Event.SyncronizedEvent = false; + _Jobs.Add(Event); + } + + /// + /// Adds a job to the timer. + /// + /// + public void AddJob(TimerJob Event) + { + _Jobs.Add(Event); + } + + /// + /// Clears out all scheduled jobs. + /// + public void ClearJobs() + { + _Jobs.Clear(); + } + + /// + /// Begins executing all assigned jobs at the scheduled times + /// + public void Start() + { + _StopFlag = false; + QueueNextTime(EventStorage.ReadLastTime()); + } + + /// + /// Halts executing all jobs. When the timer is restarted all jobs that would have run while the timer was stopped are re-tried. + /// + public void Stop() + { + _StopFlag = true; + _Timer.Stop(); + } + + /// + /// EventStorage determines the method used to store the last event fire time. It defaults to keeping it in memory. + /// + public IEventStorage EventStorage = new LocalEventStorage(); + public event ExceptionEventHandler Error; + + #region Private Methods and Fields + /// + /// This is here to enhance accuracy. Even if nothing is scheduled the timer sleeps for a maximum of 1 minute. + /// + private static TimeSpan MAX_INTERVAL = new TimeSpan(0, 1, 0); + + private DateTime _LastTime; + private Timer _Timer; + private TimerJobList _Jobs; + private volatile bool _StopFlag; + + private double NextInterval(DateTime thisTime) + { + TimeSpan interval = _Jobs.NextRunTime(thisTime)-thisTime; + if (interval > MAX_INTERVAL) + interval = MAX_INTERVAL; + //Handles the case of 0 wait time, the interval property requires a duration > 0. + return (interval.TotalMilliseconds == 0) ? 1 : interval.TotalMilliseconds; + } + + private void QueueNextTime(DateTime thisTime) + { + _Timer.Interval = NextInterval(thisTime); + System.Diagnostics.Debug.WriteLine(_Timer.Interval); + _LastTime = thisTime; + EventStorage.RecordLastTime(thisTime); + _Timer.Start(); + } + + private void Timer_Elapsed(object sender, ElapsedEventArgs e) + { + try + { + if (_Jobs == null) + return; + + _Timer.Stop(); + + foreach(TimerJob Event in _Jobs.Jobs) + { + try { Event.Execute(this, _LastTime, e.SignalTime, Error); } + catch (Exception ex) { OnError(DateTime.Now, Event, ex); } + } + } + catch (Exception ex) + { + OnError(DateTime.Now, null, ex); + } + finally + { + if (_StopFlag == false) + QueueNextTime(e.SignalTime); + } + } + + private void OnError(DateTime eventTime, TimerJob job, Exception e) + { + if (Error == null) + return; + + try { Error(this, new ExceptionEventArgs(eventTime, e)); } + catch (Exception) {} + } + #endregion + + #region IDisposable Members + + public void Dispose() + { + if (_Timer != null) + _Timer.Dispose(); + } + + #endregion + } + + public class ScheduleTimer : ScheduleTimerBase + { + /// + /// Add event is used in conjunction with the Elaspsed event handler. Set the Elapsed handler, add your schedule and call start. + /// + /// The schedule to fire the event at. Adding additional schedules will cause the event to fire whenever either schedule calls for it. + public void AddEvent(IScheduledItem Schedule) + { + if (Elapsed == null) + throw new ArgumentNullException("Elapsed", "member variable is null."); + + AddJob(new TimerJob(Schedule, new DelegateMethodCall(Elapsed))); + } + + /// + /// The event to fire when you only need to fire one event. + /// + public event ScheduledEventHandler Elapsed; + } + + /// + /// ExceptionEventArgs allows exceptions to be captured and sent to the OnError event of the timer. + /// + public class ExceptionEventArgs : EventArgs + { + public ExceptionEventArgs(DateTime eventTime, Exception e) + { + EventTime = eventTime; + Error = e; + } + public DateTime EventTime; + public Exception Error; + } + + /// + /// ExceptionEventHandler is the method type used by the OnError event for the timer. + /// + public delegate void ExceptionEventHandler(object sender, ExceptionEventArgs Args); + + public class ScheduledEventArgs : EventArgs + { + public ScheduledEventArgs(DateTime eventTime) + { + EventTime = eventTime; + } + public DateTime EventTime; + } + + public delegate void ScheduledEventHandler(object sender, ScheduledEventArgs e); + + /// + /// The IResultFilter interface represents filters that either sort the events for an interval or + /// remove duplicate events either selecting the first or the last event. + /// + public interface IResultFilter + { + void FilterResultsInInterval(DateTime Start, DateTime End, ArrayList List); + } + + /// + /// IEventStorage is used to provide persistance of schedule during service shutdowns. + /// + public interface IEventStorage + { + void RecordLastTime(DateTime Time); + DateTime ReadLastTime(); + } + + +} \ No newline at end of file diff --git a/ScheduledItems/BlockWrapper.cs b/ScheduledItems/BlockWrapper.cs new file mode 100644 index 0000000..3427f75 --- /dev/null +++ b/ScheduledItems/BlockWrapper.cs @@ -0,0 +1,80 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Collections; + +namespace UtilidadesCSharp.Schedule +{ + /// + /// This class will be used to implement a filter that enables a window of activity. For cases where you want to + /// run every 15 minutes between 6:00 AM and 5:00 PM. Or just on weekdays or weekends. + /// + public class BlockWrapper : IScheduledItem + { + public BlockWrapper(IScheduledItem item, string StrBase, string BeginOffset, string EndOffset) + { + _Item = item; + _Begin = new ScheduledTime(StrBase, BeginOffset); + _End = new ScheduledTime(StrBase, EndOffset); + } + public void AddEventsInInterval(DateTime Begin, DateTime End, ArrayList List) + { + DateTime Next = NextRunTime(Begin, true); + while (Next < End) + { + List.Add(Next); + Next = NextRunTime(Next, false); + } + } + + public DateTime NextRunTime(DateTime time, bool AllowExact) + { + return NextRunTime(time, 100, AllowExact); + } + + DateTime NextRunTime(DateTime time, int count, bool AllowExact) + { + if (count == 0) + throw new Exception("Invalid block wrapper combination."); + + DateTime + temp = _Item.NextRunTime(time, AllowExact), + begin = _Begin.NextRunTime(time, true), + end = _End.NextRunTime(time, true); + System.Diagnostics.Debug.WriteLine(string.Format("{0} {1} {2} {3}", time, begin, end, temp)); + bool A = temp > end, B = temp < begin, C = end < begin; + System.Diagnostics.Debug.WriteLine(string.Format("{0} {1} {2}", A, B, C)); + if (C) + { + if (A && B) + return NextRunTime(begin, --count, false); + else + return temp; + } + else + { + if (!A && !B) + return temp; + if (!A) + return NextRunTime(begin, --count, false); + else + return NextRunTime(end, --count, false); + } + } + private IScheduledItem _Item; + private ScheduledTime _Begin, _End; + } +} \ No newline at end of file diff --git a/ScheduledItems/EventInstance.cs b/ScheduledItems/EventInstance.cs new file mode 100644 index 0000000..09a1021 --- /dev/null +++ b/ScheduledItems/EventInstance.cs @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Collections; + +namespace UtilidadesCSharp.Schedule +{ + + /// + /// There have been quite a few requests to allow scheduling of multiple delegates and method parameter data + /// from the same timer. This class allows you to match the event with the time that it fired. I want to keep + /// the same simple implementation of the EventQueue and interval classes since they can be reused elsewhere. + /// The timer should be responsible for matching this data up. + /// + public class EventInstance : IComparable + { + public EventInstance(DateTime time, IScheduledItem scheduleItem, object data) + { + Time = time; + ScheduleItem = scheduleItem; + Data = data; + } + public DateTime Time; + public IScheduledItem ScheduleItem; + public object Data; + + public int CompareTo(object obj) + { + if (obj is EventInstance) + return Time.CompareTo(((EventInstance)obj).Time); + if (obj is DateTime) + return Time.CompareTo((DateTime)obj); + return 0; + } + } + +} + + diff --git a/ScheduledItems/EventQueue.cs b/ScheduledItems/EventQueue.cs new file mode 100644 index 0000000..685f0b2 --- /dev/null +++ b/ScheduledItems/EventQueue.cs @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Collections; + +namespace UtilidadesCSharp.Schedule +{ + /// + /// The event queue is a collection of scheduled items that represents the union of all child scheduled items. + /// This is useful for events that occur every 10 minutes or at multiple intervals not covered by the simple + /// scheduled items. + /// + public class EventQueue : IScheduledItem + { + public EventQueue() + { + _List = new ArrayList(); + } + /// + /// Adds a ScheduledTime to the queue. + /// + /// The scheduled time to add + public void Add(IScheduledItem time) + { + _List.Add(time); + } + + /// + /// Clears the list of scheduled times. + /// + public void Clear() + { + _List.Clear(); + } + + /// + /// Adds the running time for all events in the list. + /// + /// The beginning time of the interval + /// The end time of the interval + /// The list to add times to. + public void AddEventsInInterval(DateTime Begin, DateTime End, ArrayList List) + { + foreach(IScheduledItem st in _List) + st.AddEventsInInterval(Begin, End, List); + List.Sort(); + } + + /// + /// Returns the first time after the starting time for all events in the list. + /// + /// The starting time. + /// If this is true then it allows the return time to match the time parameter, false forces the return time to be greater then the time parameter + /// Either the next event after the input time or greater or equal to depending on the AllowExact parameter. + public DateTime NextRunTime(DateTime time, bool AllowExact) + { + DateTime next = DateTime.MaxValue; + //Get minimum datetime from the list. + foreach(IScheduledItem st in _List) + { + DateTime Proposed = st.NextRunTime(time, AllowExact); + next = (Proposed < next) ? Proposed : next; + } + return next; + } + private ArrayList _List; + } +} \ No newline at end of file diff --git a/ScheduledItems/ScheduledTime.cs b/ScheduledItems/ScheduledTime.cs new file mode 100644 index 0000000..b0e2331 --- /dev/null +++ b/ScheduledItems/ScheduledTime.cs @@ -0,0 +1,177 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Specialized; + +namespace UtilidadesCSharp.Schedule +{ + public enum EventTimeBase + { + BySecond = 1, + ByMinute = 2, + Hourly = 3, + Daily = 4, + Weekly = 5, + Monthly = 6, + } + + /// + /// This class represents a simple schedule. It can represent a repeating event that occurs anywhere from every + /// second to once a month. It consists of an enumeration to mark the interval and an offset from that interval. + /// For example new ScheduledTime(Hourly, new TimeSpan(0, 15, 0)) would represent an event that fired 15 minutes + /// after the hour every hour. + /// + [Serializable] + public class ScheduledTime : IScheduledItem + { + public ScheduledTime(EventTimeBase Base, TimeSpan Offset) + { + _Base = Base; + _Offset = Offset; + } + + /// + /// intializes a simple scheduled time element from a pair of strings. + /// Here are the supported formats + /// + /// BySecond - single integer representing the offset in ms + /// ByMinute - A comma seperate list of integers representing the number of seconds and ms + /// Hourly - A comma seperated list of integers representing the number of minutes, seconds and ms + /// Daily - A time in HH:mm:ss AM/PM format + /// Weekly - n, time where n represents an integer and time is a time in the Daily format + /// Monthly - the same format as weekly. + /// + /// + /// A string representing the base enumeration for the scheduled time + /// A string representing the offset for the time. + public ScheduledTime(string StrBase, string StrOffset) + { + //TODO:Create an IScheduled time factory method. + _Base = (EventTimeBase)Enum.Parse(typeof(EventTimeBase), StrBase, true); + Init(StrOffset); + } + + public int ArrayAccess(string[] Arr, int i) + { + if (i >= Arr.Length) + return 0; + return int.Parse(Arr[i]); + } + + public void AddEventsInInterval(DateTime Begin, DateTime End, ArrayList List) + { + DateTime Next = NextRunTime(Begin, true); + while (Next < End) + { + List.Add(Next); + Next = IncInterval(Next); + } + } + + public DateTime NextRunTime(DateTime time, bool AllowExact) + { + DateTime NextRun = LastSyncForTime(time) + _Offset; + if (NextRun == time && AllowExact) + return time; + if (NextRun > time) + return NextRun; + return IncInterval(NextRun); + } + + + private DateTime LastSyncForTime(DateTime time) + { + switch (_Base) + { + case EventTimeBase.BySecond: + return new DateTime(time.Year, time.Month, time.Day, time.Hour, time.Minute, time.Second); + case EventTimeBase.ByMinute: + return new DateTime(time.Year, time.Month, time.Day, time.Hour, time.Minute, 0); + case EventTimeBase.Hourly: + return new DateTime(time.Year, time.Month, time.Day, time.Hour, 0, 0); + case EventTimeBase.Daily: + return new DateTime(time.Year, time.Month, time.Day); + case EventTimeBase.Weekly: + return (new DateTime(time.Year, time.Month, time.Day)).AddDays(-(int)time.DayOfWeek); + case EventTimeBase.Monthly: + return new DateTime(time.Year, time.Month, 1); + } + throw new Exception("Invalid base specified for timer."); + } + + private DateTime IncInterval(DateTime Last) + { + switch (_Base) + { + case EventTimeBase.BySecond: + return Last.AddSeconds(1); + case EventTimeBase.ByMinute: + return Last.AddMinutes(1); + case EventTimeBase.Hourly: + return Last.AddHours(1); + case EventTimeBase.Daily: + return Last.AddDays(1); + case EventTimeBase.Weekly: + return Last.AddDays(7); + case EventTimeBase.Monthly: + return Last.AddMonths(1); + } + throw new Exception("Invalid base specified for timer."); + } + + private void Init(string StrOffset) + { + switch (_Base) + { + case EventTimeBase.BySecond: + _Offset = new TimeSpan(0, 0, 0, 0, int.Parse(StrOffset)); + break; + case EventTimeBase.ByMinute: + string[] ArrMinute = StrOffset.Split(','); + _Offset = new TimeSpan(0, 0, 0, ArrayAccess(ArrMinute, 0), ArrayAccess(ArrMinute, 1)); + break; + case EventTimeBase.Hourly: + string[] ArrHour = StrOffset.Split(','); + _Offset = new TimeSpan(0, 0, ArrayAccess(ArrHour, 0), ArrayAccess(ArrHour, 1), ArrayAccess(ArrHour, 2)); + break; + case EventTimeBase.Daily: + DateTime Daytime = DateTime.Parse(StrOffset); + _Offset = new TimeSpan(0, Daytime.Hour, Daytime.Minute, Daytime.Second, Daytime.Millisecond); + break; + case EventTimeBase.Weekly: + string[] ArrWeek = StrOffset.Split(','); + if (ArrWeek.Length != 2) + throw new Exception("Weekly offset must be in the format n, time where n is the day of the week starting with 0 for sunday"); + DateTime WeekTime = DateTime.Parse(ArrWeek[1]); + _Offset = new TimeSpan(int.Parse(ArrWeek[0]), WeekTime.Hour, WeekTime.Minute, WeekTime.Second, WeekTime.Millisecond); + break; + case EventTimeBase.Monthly: + string[] ArrMonth = StrOffset.Split(','); + if (ArrMonth.Length != 2) + throw new Exception("Monthly offset must be in the format n, time where n is the day of the month starting with 1 for the first day of the month."); + DateTime MonthTime = DateTime.Parse(ArrMonth[1]); + _Offset = new TimeSpan(int.Parse(ArrMonth[0])-1, MonthTime.Hour, MonthTime.Minute, MonthTime.Second, MonthTime.Millisecond); + break; + default: + throw new Exception("Invalid base specified for timer."); + } + } + + private EventTimeBase _Base; + private TimeSpan _Offset; + } +} \ No newline at end of file diff --git a/ScheduledItems/SimpleInterval.cs b/ScheduledItems/SimpleInterval.cs new file mode 100644 index 0000000..50f5639 --- /dev/null +++ b/ScheduledItems/SimpleInterval.cs @@ -0,0 +1,90 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ +using System; +using System.Collections; +using System.Diagnostics; + +namespace UtilidadesCSharp.Schedule +{ + /// + /// The simple interval represents the simple scheduling that .net supports natively. It consists of a start + /// absolute time and an interval that is counted off from the start time. + /// + [Serializable] + public class SimpleInterval : IScheduledItem + { + public SimpleInterval(DateTime StartTime, TimeSpan Interval) + { + _Interval = Interval; + _StartTime = StartTime; + _EndTime = DateTime.MaxValue; + } + public SimpleInterval(DateTime StartTime, TimeSpan Interval, int count) + { + _Interval = Interval; + _StartTime = StartTime; + _EndTime = StartTime + TimeSpan.FromTicks(Interval.Ticks*count); + } + public SimpleInterval(DateTime StartTime, TimeSpan Interval, DateTime EndTime) + { + _Interval = Interval; + _StartTime = StartTime; + _EndTime = EndTime; + } + public void AddEventsInInterval(DateTime Begin, DateTime End, ArrayList List) + { + if (End <= _StartTime) + return; + DateTime Next = NextRunTime(Begin, true); + while (Next < End) + { + List.Add(Next); + Next = NextRunTime(Next, false); + } + } + + public DateTime NextRunTime(DateTime time, bool AllowExact) + { + DateTime returnTime = NextRunTimeInt(time, AllowExact); + Debug.WriteLine(time); + Debug.WriteLine(returnTime); + Debug.WriteLine(_EndTime); + return (returnTime >= _EndTime) ? DateTime.MaxValue : returnTime; + } + + private DateTime NextRunTimeInt(DateTime time, bool AllowExact) + { + TimeSpan Span = time-_StartTime; + if (Span < TimeSpan.Zero) + return _StartTime; + if (ExactMatch(time)) + return AllowExact ? time : time + _Interval; + uint msRemaining = (uint)(_Interval.TotalMilliseconds - ((uint)Span.TotalMilliseconds % (uint)_Interval.TotalMilliseconds)); + return time.AddMilliseconds(msRemaining); + } + + private bool ExactMatch(DateTime time) + { + TimeSpan Span = time-_StartTime; + if (Span < TimeSpan.Zero) + return false; + return (Span.TotalMilliseconds % _Interval.TotalMilliseconds) == 0; + } + + private TimeSpan _Interval; + private DateTime _StartTime; + private DateTime _EndTime; + } +} \ No newline at end of file diff --git a/ScheduledItems/SingleEvent.cs b/ScheduledItems/SingleEvent.cs new file mode 100644 index 0000000..29ae869 --- /dev/null +++ b/ScheduledItems/SingleEvent.cs @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ +using System; + +namespace UtilidadesCSharp.Schedule +{ + /// Single event represents an event which only fires once. + public class SingleEvent : IScheduledItem + { + public SingleEvent(DateTime eventTime) + { + _EventTime = eventTime; + } + #region IScheduledItem Members + + public void AddEventsInInterval(DateTime Begin, DateTime End, System.Collections.ArrayList List) + { + if (Begin <= _EventTime && End > _EventTime) + List.Add(_EventTime); + } + + public DateTime NextRunTime(DateTime time, bool IncludeStartTime) + { + if (IncludeStartTime) + return (_EventTime >= time) ? _EventTime : DateTime.MaxValue; + else + return (_EventTime > time) ? _EventTime : DateTime.MaxValue; + } + private DateTime _EventTime; + + #endregion + } +} diff --git a/TimerJob.cs b/TimerJob.cs new file mode 100644 index 0000000..9e4d9c8 --- /dev/null +++ b/TimerJob.cs @@ -0,0 +1,181 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Collections; +using System.Reflection; +using System.Timers; + +namespace UtilidadesCSharp.Schedule +{ + /// + /// Timer job groups a schedule, syncronization data, a result filter, method information and an enabled state so that multiple jobs + /// can be managed by the same timer. Each one operating independently of the others with different syncronization and recovery settings. + /// + public class TimerJob + { + + + + + + + public TimerJob(IScheduledItem schedule, IMethodCall method) + { + Schedule = schedule; + Method = method; + _ExecuteHandler = new ExecuteHandler(ExecuteInternal); + } + public IScheduledItem Schedule; + public bool SyncronizedEvent = true; + public IResultFilter Filter; + public IMethodCall Method; +// public IJobLog Log; + public bool Enabled = true; + + public DateTime NextRunTime(DateTime time, bool IncludeStartTime) + { + if (!Enabled) + return DateTime.MaxValue; + return Schedule.NextRunTime(time, IncludeStartTime); + } + + public void Execute(object sender, DateTime Begin, DateTime End, ExceptionEventHandler Error) + { + if (!Enabled) + return; + + ArrayList EventList = new ArrayList(); + Schedule.AddEventsInInterval(Begin, End, EventList); + + if (Filter != null) + Filter.FilterResultsInInterval(Begin, End, EventList); + + foreach(DateTime EventTime in EventList) + { + if (SyncronizedEvent) + _ExecuteHandler(sender, EventTime, Error); + else + _ExecuteHandler.BeginInvoke(sender, EventTime, Error, null, null); + } + } + + private void ExecuteInternal(object sender, DateTime EventTime, ExceptionEventHandler Error) + { + try + { + TimerParameterSetter Setter = new TimerParameterSetter(EventTime, sender); + Method.Execute(Setter); + } + catch (Exception ex) + { + if (Error != null) + try { Error(this, new ExceptionEventArgs(EventTime, ex)); } catch {} + } + } + + private delegate void ExecuteHandler(object sender, DateTime EventTime, ExceptionEventHandler Error); + + private ExecuteHandler _ExecuteHandler; + } + + /// + /// Timer job manages a group of timer jobs. + /// + public class TimerJobList + { + public TimerJobList() + { + _List = new ArrayList(); + } + + public void Add(TimerJob Event) + { + _List.Add(Event); + } + + public void Clear() + { + _List.Clear(); + } + + /// + /// Gets the next time any of the jobs in the list will run. Allows matching the exact start time. If no matches are found the return + /// is DateTime.MaxValue; + /// + /// The starting time for the interval being queried. This time is included in the interval + /// The first absolute date one of the jobs will execute on. If none of the jobs needs to run DateTime.MaxValue is returned. + public DateTime NextRunTime(DateTime time) + { + DateTime next = DateTime.MaxValue; + //Get minimum datetime from the list. + foreach(TimerJob Job in _List) + { + DateTime Proposed = Job.NextRunTime(time, true); + next = (Proposed < next) ? Proposed : next; + } + return next; + } + + public TimerJob[] Jobs + { + get { return (TimerJob[])_List.ToArray(typeof(TimerJob)); } + } + + private ArrayList _List; + } + + /// + /// The timer job allows delegates to be specified with unbound parameters. This ParameterSetter assigns all unbound datetime parameters + /// with the specified time and all unbound object parameters with the calling object. + /// + public class TimerParameterSetter : IParameterSetter + { + /// + /// Initalize the ParameterSetter with the time to pass to unbound time parameters and object to pass to unbound object parameters. + /// + /// The time to pass to the unbound DateTime parameters + /// The object to pass to the unbound object parameters + public TimerParameterSetter(DateTime time, object sender) + { + _time = time; + _sender = sender; + } + public void reset() + { + } + public bool GetParameterValue(ParameterInfo pi, int ParameterLoc, ref object parameter) + { + switch(pi.ParameterType.Name.ToLower()) + { + case "datetime": + parameter = _time; + return true; + case "object": + parameter = _sender; + return true; + case "scheduledeventargs": + parameter = new ScheduledEventArgs(_time); + return true; + case "eventargs": + parameter = new ScheduledEventArgs(_time); + return true; + } + return false; + } + DateTime _time; + object _sender; + } +} diff --git a/UtilidadesTSL4net.c.csproj b/UtilidadesTSL4net.c.csproj new file mode 100644 index 0000000..f30f060 --- /dev/null +++ b/UtilidadesTSL4net.c.csproj @@ -0,0 +1,143 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {BF2447F5-65A1-4A47-A16E-F2227634E050} + Library + Properties + Utilidadescsharp + UtilidadesTSL4net.c + + + 3.5 + + + v4.8 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + SAK + SAK + SAK + SAK + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AnyCPU + AllRules.ruleset + + + false + false + + + pdbonly + true + bin\release\ + TRACE + prompt + 4 + x86 + AllRules.ruleset + + + false + + + + + + + + + + + + + + True + True + Settings.settings + + + + + + + + + + + + + UserControl + + + + + + Designer + ucImagen.cs + + + + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + + + \ No newline at end of file diff --git a/UtilidadesTSL4net.c.sln b/UtilidadesTSL4net.c.sln new file mode 100644 index 0000000..586a0bf --- /dev/null +++ b/UtilidadesTSL4net.c.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.6.11806.211 stable +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UtilidadesTSL4net.c", "UtilidadesTSL4net.c.csproj", "{BF2447F5-65A1-4A47-A16E-F2227634E050}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BF2447F5-65A1-4A47-A16E-F2227634E050}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BF2447F5-65A1-4A47-A16E-F2227634E050}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BF2447F5-65A1-4A47-A16E-F2227634E050}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BF2447F5-65A1-4A47-A16E-F2227634E050}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7AC260D0-FB99-4CCE-9185-C8A31FF3F375} + EndGlobalSection +EndGlobal diff --git a/UtilidadesTSL4net.c/EventStorage.cs b/UtilidadesTSL4net.c/EventStorage.cs new file mode 100644 index 0000000..f3b3b4f --- /dev/null +++ b/UtilidadesTSL4net.c/EventStorage.cs @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Xml; +using System.Xml.XPath; + +namespace UtilidadesCSharp.Schedule +{ + /// + /// Null event strorage disables error recovery by returning now for the last time an event fired. + /// + public class NullEventStorage : IEventStorage + { + public NullEventStorage() + { + } + + public void RecordLastTime(DateTime Time) + { + } + + public DateTime ReadLastTime() + { + return DateTime.Now; + } + } + + /// + /// Local event strorage keeps the last time in memory so that skipped events are not recovered. + /// + public class LocalEventStorage : IEventStorage + { + public LocalEventStorage() + { + _LastTime = DateTime.MaxValue; + } + + public void RecordLastTime(DateTime Time) + { + _LastTime = Time; + } + + public DateTime ReadLastTime() + { + if (_LastTime == DateTime.MaxValue) + _LastTime = DateTime.Now; + return _LastTime; + } + + DateTime _LastTime; + } + + /// + /// FileEventStorage saves the last time in an XmlDocument so that recovery will include periods that the + /// process is shutdown. + /// + public class FileEventStorage : IEventStorage + { + public FileEventStorage(string FileName, string XPath) + { + _FileName = FileName; + _XPath = XPath; + } + + public void RecordLastTime(DateTime Time) + { + _Doc.SelectSingleNode(_XPath).Value = Time.ToString(); + _Doc.Save(_FileName); + } + + public DateTime ReadLastTime() + { + _Doc.Load(_FileName); + string Value = _Doc.SelectSingleNode(_XPath).Value; + if (Value == null || Value == string.Empty) + return DateTime.Now; + return DateTime.Parse(Value); + } + + string _FileName; + string _XPath; + XmlDocument _Doc = new XmlDocument(); + } +} diff --git a/UtilidadesTSL4net.c/IScheduledItem.cs b/UtilidadesTSL4net.c/IScheduledItem.cs new file mode 100644 index 0000000..bd23e0b --- /dev/null +++ b/UtilidadesTSL4net.c/IScheduledItem.cs @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Collections; + +namespace UtilidadesCSharp.Schedule +{ + /// + /// IScheduledItem represents a scheduled event. You can query it for the number of events that occur + /// in a time interval and for the remaining interval before the next event. + /// + public interface IScheduledItem + { + /// + /// Returns the times of the events that occur in the given time interval. The interval is closed + /// at the start and open at the end so that intervals can be stacked without overlapping. + /// + /// The beginning of the interval + /// The end of the interval + /// All events >= Begin and < End + void AddEventsInInterval(DateTime Begin, DateTime End, ArrayList List); + + /// + /// Returns the next run time of the scheduled item. Optionally excludes the starting time. + /// + /// The starting time of the interval + /// if true then the starting time is included in the query false, it is excluded. + /// The next execution time either on or after the starting time. + DateTime NextRunTime(DateTime time, bool IncludeStartTime); + } + +} diff --git a/UtilidadesTSL4net.c/MethodCall.cs b/UtilidadesTSL4net.c/MethodCall.cs new file mode 100644 index 0000000..a0f39b7 --- /dev/null +++ b/UtilidadesTSL4net.c/MethodCall.cs @@ -0,0 +1,332 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Collections; +using System.Reflection; +using System.Text.RegularExpressions; + +namespace UtilidadesCSharp.Schedule +{ + /// + /// IParameterSetter represents a serialized parameter list. This is used to provide a partial specialized + /// method call. This is useful for remote invocation of method calls. For example if you have a method with + /// 3 parameters. The first 2 might represent static data such as a report and a storage location. The third + /// might be the time that the report is invoked, which is only known when the method is invoked. Using this, + /// you just pass the method and the first 2 parameters to a timer object, which supplies the 3rd parameter. + /// Without these objects, you would have to generate a custom object type for each method you wished to + /// execute in this manner and store the static parameters as instance variables. + /// + public interface IParameterSetter + { + /// + /// This resets the setter to the beginning. It is used for setters that rely on positional state + /// information. It is called prior to setting any method values. + /// + void reset(); + /// + /// This method is used to both query support for setting a parameter and actually set the value. + /// True is returned if the parameter passed in is updated. + /// + /// The reflection information about this parameter. + /// The location of the prameter in the parameter list. + /// The parameter object + /// true if the parameter is matched and false otherwise + bool GetParameterValue(ParameterInfo pi, int ParameterLoc, ref object parameter); + } + + /// + /// This setter object takes a simple object array full of parameter data. It applys the objects in order + /// to the method parameter list. + /// + public class OrderParameterSetter : IParameterSetter + { + public OrderParameterSetter(params object[] _Params) + { + _ParamList = _Params; + } + public void reset() + { + _counter = 0; + } + public bool GetParameterValue(ParameterInfo pi, int ParameterLoc, ref object parameter) + { + if (_counter >= _ParamList.Length) + return false; + parameter = _ParamList[_counter++]; + return true; + } + + object[] _ParamList; + int _counter; + } + + /// + /// This setter object stores the parameter data in a Hashtable and uses the hashtable keys to match + /// the parameter names of the method to the parameter data. This allows methods to be called like + /// stored procedures, with the parameters being passed in independent of order. + /// + public class NamedParameterSetter : IParameterSetter + { + public NamedParameterSetter(Hashtable Params) + { + _Params = Params; + } + public void reset() + { + } + public bool GetParameterValue(ParameterInfo pi, int ParameterLoc, ref object parameter) + { + string ParamName = pi.Name; + if (!_Params.ContainsKey(ParamName)) + return false; + parameter = _Params[ParamName]; + return true; + } + Hashtable _Params; + } + + + /// + /// ParameterSetterList maintains a collection of IParameterSetter objects and applies them in order to each + /// parameter of the method. Each time a match occurs the next parameter is tried starting with the first + /// setter object until it is matched. + /// + public class ParameterSetterList + { + public void Add(IParameterSetter setter) + { + _List.Add(setter); + } + + public IParameterSetter[] ToArray() + { + return (IParameterSetter[])_List.ToArray(typeof(IParameterSetter)); + } + + public void reset() + { + foreach(IParameterSetter Setter in _List) + Setter.reset(); + } + + public object[] GetParameters(MethodInfo Method) + { + ParameterInfo[] Params = Method.GetParameters(); + object[] Values = new object[Params.Length]; + //TODO: Update to iterate backwards + for(int i=0; i + /// IMethodCall represents a partially specified parameter data list and a method. This allows methods to be + /// dynamically late invoked for things like timers and other event driven frameworks. + /// + public interface IMethodCall + { + ParameterSetterList ParamList { get; } + object Execute(); + object Execute(IParameterSetter Params); + void EventHandler(object obj, EventArgs e); + IAsyncResult BeginExecute(AsyncCallback callback, object obj); + IAsyncResult BeginExecute(IParameterSetter Params, AsyncCallback callback, object obj); + } + + delegate object Exec(); + delegate object Exec2(IParameterSetter Params); + + /// + /// Method call captures the data required to do a defered method call. + /// + public class DelegateMethodCall : MethodCallBase, IMethodCall + { + public DelegateMethodCall(Delegate f) + { + _f = f; + } + + public DelegateMethodCall(Delegate f, params object[] Params) + { + if (f.Method.GetParameters().Length < Params.Length) + throw new ArgumentException("Too many parameters specified for delegate", "f"); + + _f = f; + ParamList.Add(new OrderParameterSetter(Params)); + } + + public DelegateMethodCall(Delegate f, IParameterSetter Params) + { + _f = f; + ParamList.Add(Params); + } + + Delegate _f; + + public Delegate f + { + get { return _f; } + set { _f = value; } + } + + public MethodInfo Method + { + get { return _f.Method; } + } + + public object Execute() + { + return f.DynamicInvoke(GetParameterList(Method)); + } + + public object Execute(IParameterSetter Params) + { + return f.DynamicInvoke(GetParameterList(Method, Params)); + } + + public void EventHandler(object obj, EventArgs e) + { + Execute(); + } + + Exec _exec; + public IAsyncResult BeginExecute(AsyncCallback callback, object obj) + { + _exec = new Exec(Execute); + return _exec.BeginInvoke(callback, obj); + } + + public IAsyncResult BeginExecute(IParameterSetter Params, AsyncCallback callback, object obj) + { + Exec2 exec = new Exec2(Execute); + return exec.BeginInvoke(Params, callback, obj); + } + } + + public class DynamicMethodCall : MethodCallBase, IMethodCall + { + public DynamicMethodCall(MethodInfo method) + { + _obj = null; + _method = method; + } + + public DynamicMethodCall(object obj, MethodInfo method) + { + _obj = obj; + _method = method; + } + + public DynamicMethodCall(object obj, MethodInfo method, IParameterSetter setter) + { + _obj = obj; + _method = method; + ParamList.Add(setter); + } + + object _obj; + MethodInfo _method; + + public MethodInfo Method + { + get { return _method; } + set { _method = value; } + } + + public object Execute() + { + return _method.Invoke(_obj, GetParameterList(Method)); + } + + public object Execute(IParameterSetter Params) + { + return _method.Invoke(_obj, GetParameterList(Method, Params)); + } + + public void EventHandler(object obj, EventArgs e) + { + Execute(); + } + + Exec _exec; + public IAsyncResult BeginExecute(AsyncCallback callback, object obj) + { + _exec = new Exec(Execute); + return _exec.BeginInvoke(callback, null); + } + + public IAsyncResult BeginExecute(IParameterSetter Params, AsyncCallback callback, object obj) + { + Exec2 exec = new Exec2(Execute); + return exec.BeginInvoke(Params, callback, null); + } + } + + /// + /// This is a base class that handles the Parameter list management for the 2 dynamic method call methods. + /// + public class MethodCallBase + { + ParameterSetterList _ParamList = new ParameterSetterList(); + + public ParameterSetterList ParamList + { + get { return _ParamList; } + } + + protected object[] GetParameterList(MethodInfo Method) + { + ParamList.reset(); + object[] Params = ParamList.GetParameters(Method); + return Params; + } + + protected object[] GetParameterList(MethodInfo Method, IParameterSetter Params) + { + ParamList.reset(); + object[] objParams = ParamList.GetParameters(Method, Params); + return objParams; + } + } + +} diff --git a/UtilidadesTSL4net.c/Properties/AssemblyInfo.cs b/UtilidadesTSL4net.c/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c75d144 --- /dev/null +++ b/UtilidadesTSL4net.c/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// La información general sobre un ensamblado se controla mediante el siguiente +// conjunto de atributos. Cambie estos atributos para modificar la información +// asociada con un ensamblado. +[assembly: AssemblyTitle("UtilidadesTSL4net.c")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany(".")] +[assembly: AssemblyProduct("UtilidadesTSL4net.c")] +[assembly: AssemblyCopyright("© . 2008")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Si establece ComVisible como false hace que los tipos de este ensamblado no sean visibles +// a los componentes COM. Si necesita obtener acceso a un tipo en este ensamblado desde +// COM, establezca el atributo ComVisible como true en este tipo. +[assembly: ComVisible(false)] + +// El siguiente GUID sirve como identificador de la biblioteca de tipos si este proyecto se expone a COM +[assembly: Guid("51c70542-3016-463d-8d03-d306aae697f8")] + +// La información de versión de un ensamblado consta de los cuatro valores siguientes: +// +// Versión principal +// Versión secundaria +// Número de versión de compilación +// Revisión +// +// Puede especificar todos los valores o puede establecer como valores predeterminados los números de revisión y generación +// mediante el asterisco ('*'), como se muestra a continuación: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/UtilidadesTSL4net.c/Properties/Settings.Designer.cs b/UtilidadesTSL4net.c/Properties/Settings.Designer.cs new file mode 100644 index 0000000..7b1c916 --- /dev/null +++ b/UtilidadesTSL4net.c/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// Este código fue generado por una herramienta. +// Versión de runtime:4.0.30319.1 +// +// Los cambios en este archivo podrían causar un comportamiento incorrecto y se perderán si +// se vuelve a generar el código. +// +//------------------------------------------------------------------------------ + +namespace Utilidadescsharp.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/UtilidadesTSL4net.c/Properties/Settings.settings b/UtilidadesTSL4net.c/Properties/Settings.settings new file mode 100644 index 0000000..049245f --- /dev/null +++ b/UtilidadesTSL4net.c/Properties/Settings.settings @@ -0,0 +1,6 @@ + + + + + + diff --git a/UtilidadesTSL4net.c/ReportTimer.cs b/UtilidadesTSL4net.c/ReportTimer.cs new file mode 100644 index 0000000..3a2f2a7 --- /dev/null +++ b/UtilidadesTSL4net.c/ReportTimer.cs @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; + +namespace UtilidadesCSharp.Schedule +{ + public class ReportEventArgs : EventArgs + { + public ReportEventArgs(DateTime Time, int reportNo) { EventTime = Time; ReportNo = reportNo; } + public int ReportNo; + public DateTime EventTime; + } + + public delegate void ReportEventHandler(object sender, ReportEventArgs e); + + /// + /// Summary description for ReportTimer. + /// + public class ReportTimer : ScheduleTimerBase + { + public void AddReportEvent(IScheduledItem Schedule, int reportNo) + { + if (Elapsed == null) + throw new Exception("You must set elapsed before adding Events"); + AddJob(new TimerJob(Schedule, new DelegateMethodCall(Handler, Elapsed, reportNo))); + } + + public void AddAsyncReportEvent(IScheduledItem Schedule, int reportNo) + { + if (Elapsed == null) + throw new Exception("You must set elapsed before adding Events"); + TimerJob Event = new TimerJob(Schedule, new DelegateMethodCall(Handler, Elapsed, reportNo)); + Event.SyncronizedEvent = false; + AddJob(Event); + } + + public event ReportEventHandler Elapsed; + + delegate void ConvertHandler(ReportEventHandler Handler, int ReportNo, object sender, DateTime time); + static ConvertHandler Handler = new ConvertHandler(Converter); + static void Converter(ReportEventHandler Handler, int ReportNo, object sender, DateTime time) + { + if (Handler == null) + throw new ArgumentNullException("Handler"); + if (sender == null) + throw new ArgumentNullException("sender"); + Handler(sender, new ReportEventArgs(time, ReportNo)); + } + } +} diff --git a/UtilidadesTSL4net.c/ScheduleFilter.cs b/UtilidadesTSL4net.c/ScheduleFilter.cs new file mode 100644 index 0000000..466e6a9 --- /dev/null +++ b/UtilidadesTSL4net.c/ScheduleFilter.cs @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Collections; + +namespace UtilidadesCSharp.Schedule +{ + /// + /// This is an empty filter that does not filter any of the events. + /// + public class Filter : IResultFilter + { + public static IResultFilter Empty = new Filter(); + private Filter() {} + + public void FilterResultsInInterval(DateTime Start, DateTime End, ArrayList List) + { + if (List == null) + return; + List.Sort(); + } + } + + /// + /// This causes only the first event of the interval to be counted. + /// + public class FirstEventFilter : IResultFilter + { + public static IResultFilter Filter = new FirstEventFilter(); + private FirstEventFilter() {} + + public void FilterResultsInInterval(DateTime Start, DateTime End, ArrayList List) + { + if (List == null) + return; + if (List.Count < 2) + return; + List.Sort(); + List.RemoveRange(1, List.Count-1); + } + } + + /// + /// This causes only the last event of the interval to be counted. + /// + public class LastEventFilter : IResultFilter + { + public static IResultFilter Filter = new LastEventFilter(); + private LastEventFilter() {} + + public void FilterResultsInInterval(DateTime Start, DateTime End, ArrayList List) + { + if (List == null) + return; + if (List.Count < 2) + return; + List.Sort(); + List.RemoveRange(0, List.Count-1); + } + } + +} diff --git a/UtilidadesTSL4net.c/ScheduleTimer.cs b/UtilidadesTSL4net.c/ScheduleTimer.cs new file mode 100644 index 0000000..acace75 --- /dev/null +++ b/UtilidadesTSL4net.c/ScheduleTimer.cs @@ -0,0 +1,254 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Collections; +using System.Timers; +using System.Diagnostics; + +namespace UtilidadesCSharp.Schedule +{ + /// + /// ScheduleTimer represents a timer that fires on a more human friendly schedule. For example it is easy to + /// set it to fire every day at 6:00PM. It is useful for batch jobs or alarms that might be difficult to + /// schedule with the native .net timers. + /// It is similar to the .net timer that it is based on with the start and stop methods functioning similarly. + /// The main difference is the event uses a different delegate and arguement since the .net timer argument + /// class is not creatable. + /// + public class ScheduleTimerBase : IDisposable + { + public ScheduleTimerBase() + { + _Timer = new Timer(); + _Timer.AutoReset = false; + _Timer.Elapsed += new ElapsedEventHandler(Timer_Elapsed); + _Jobs = new TimerJobList(); + _LastTime = DateTime.MaxValue; + } + + /// + /// Adds a job to the timer. This method passes in a delegate and the parameters similar to the Invoke method of windows forms. + /// + /// The schedule that this delegate is to be run on. + /// The delegate to run + /// The method parameters to pass if you leave any DateTime parameters unbound, then they will be set with the scheduled run time of the + /// method. Any unbound object parameters will get this Job object passed in. + public void AddJob(IScheduledItem Schedule, Delegate f, params object[] Params) + { + _Jobs.Add(new TimerJob(Schedule, new DelegateMethodCall(f, Params))); + } + + /// + /// Adds a job to the timer to operate asyncronously. + /// + /// The schedule that this delegate is to be run on. + /// The delegate to run + /// The method parameters to pass if you leave any DateTime parameters unbound, then they will be set with the scheduled run time of the + /// method. Any unbound object parameters will get this Job object passed in. + public void AddAsyncJob(IScheduledItem Schedule, Delegate f, params object[] Params) + { + TimerJob Event = new TimerJob(Schedule, new DelegateMethodCall(f, Params)); + Event.SyncronizedEvent = false; + _Jobs.Add(Event); + } + + /// + /// Adds a job to the timer. + /// + /// + public void AddJob(TimerJob Event) + { + _Jobs.Add(Event); + } + + /// + /// Clears out all scheduled jobs. + /// + public void ClearJobs() + { + _Jobs.Clear(); + } + + /// + /// Begins executing all assigned jobs at the scheduled times + /// + public void Start() + { + _StopFlag = false; + QueueNextTime(EventStorage.ReadLastTime()); + } + + /// + /// Halts executing all jobs. When the timer is restarted all jobs that would have run while the timer was stopped are re-tried. + /// + public void Stop() + { + _StopFlag = true; + _Timer.Stop(); + } + + /// + /// EventStorage determines the method used to store the last event fire time. It defaults to keeping it in memory. + /// + public IEventStorage EventStorage = new LocalEventStorage(); + public event ExceptionEventHandler Error; + + #region Private Methods and Fields + /// + /// This is here to enhance accuracy. Even if nothing is scheduled the timer sleeps for a maximum of 1 minute. + /// + private static TimeSpan MAX_INTERVAL = new TimeSpan(0, 1, 0); + + private DateTime _LastTime; + private Timer _Timer; + private TimerJobList _Jobs; + private volatile bool _StopFlag; + + private double NextInterval(DateTime thisTime) + { + TimeSpan interval = _Jobs.NextRunTime(thisTime)-thisTime; + if (interval > MAX_INTERVAL) + interval = MAX_INTERVAL; + //Handles the case of 0 wait time, the interval property requires a duration > 0. + return (interval.TotalMilliseconds == 0) ? 1 : interval.TotalMilliseconds; + } + + private void QueueNextTime(DateTime thisTime) + { + _Timer.Interval = NextInterval(thisTime); + System.Diagnostics.Debug.WriteLine(_Timer.Interval); + _LastTime = thisTime; + EventStorage.RecordLastTime(thisTime); + _Timer.Start(); + } + + private void Timer_Elapsed(object sender, ElapsedEventArgs e) + { + try + { + if (_Jobs == null) + return; + + _Timer.Stop(); + + foreach(TimerJob Event in _Jobs.Jobs) + { + try { Event.Execute(this, _LastTime, e.SignalTime, Error); } + catch (Exception ex) { OnError(DateTime.Now, Event, ex); } + } + } + catch (Exception ex) + { + OnError(DateTime.Now, null, ex); + } + finally + { + if (_StopFlag == false) + QueueNextTime(e.SignalTime); + } + } + + private void OnError(DateTime eventTime, TimerJob job, Exception e) + { + if (Error == null) + return; + + try { Error(this, new ExceptionEventArgs(eventTime, e)); } + catch (Exception) {} + } + #endregion + + #region IDisposable Members + + public void Dispose() + { + if (_Timer != null) + _Timer.Dispose(); + } + + #endregion + } + + public class ScheduleTimer : ScheduleTimerBase + { + /// + /// Add event is used in conjunction with the Elaspsed event handler. Set the Elapsed handler, add your schedule and call start. + /// + /// The schedule to fire the event at. Adding additional schedules will cause the event to fire whenever either schedule calls for it. + public void AddEvent(IScheduledItem Schedule) + { + if (Elapsed == null) + throw new ArgumentNullException("Elapsed", "member variable is null."); + + AddJob(new TimerJob(Schedule, new DelegateMethodCall(Elapsed))); + } + + /// + /// The event to fire when you only need to fire one event. + /// + public event ScheduledEventHandler Elapsed; + } + + /// + /// ExceptionEventArgs allows exceptions to be captured and sent to the OnError event of the timer. + /// + public class ExceptionEventArgs : EventArgs + { + public ExceptionEventArgs(DateTime eventTime, Exception e) + { + EventTime = eventTime; + Error = e; + } + public DateTime EventTime; + public Exception Error; + } + + /// + /// ExceptionEventHandler is the method type used by the OnError event for the timer. + /// + public delegate void ExceptionEventHandler(object sender, ExceptionEventArgs Args); + + public class ScheduledEventArgs : EventArgs + { + public ScheduledEventArgs(DateTime eventTime) + { + EventTime = eventTime; + } + public DateTime EventTime; + } + + public delegate void ScheduledEventHandler(object sender, ScheduledEventArgs e); + + /// + /// The IResultFilter interface represents filters that either sort the events for an interval or + /// remove duplicate events either selecting the first or the last event. + /// + public interface IResultFilter + { + void FilterResultsInInterval(DateTime Start, DateTime End, ArrayList List); + } + + /// + /// IEventStorage is used to provide persistance of schedule during service shutdowns. + /// + public interface IEventStorage + { + void RecordLastTime(DateTime Time); + DateTime ReadLastTime(); + } + + +} \ No newline at end of file diff --git a/UtilidadesTSL4net.c/ScheduledItems/BlockWrapper.cs b/UtilidadesTSL4net.c/ScheduledItems/BlockWrapper.cs new file mode 100644 index 0000000..3427f75 --- /dev/null +++ b/UtilidadesTSL4net.c/ScheduledItems/BlockWrapper.cs @@ -0,0 +1,80 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Collections; + +namespace UtilidadesCSharp.Schedule +{ + /// + /// This class will be used to implement a filter that enables a window of activity. For cases where you want to + /// run every 15 minutes between 6:00 AM and 5:00 PM. Or just on weekdays or weekends. + /// + public class BlockWrapper : IScheduledItem + { + public BlockWrapper(IScheduledItem item, string StrBase, string BeginOffset, string EndOffset) + { + _Item = item; + _Begin = new ScheduledTime(StrBase, BeginOffset); + _End = new ScheduledTime(StrBase, EndOffset); + } + public void AddEventsInInterval(DateTime Begin, DateTime End, ArrayList List) + { + DateTime Next = NextRunTime(Begin, true); + while (Next < End) + { + List.Add(Next); + Next = NextRunTime(Next, false); + } + } + + public DateTime NextRunTime(DateTime time, bool AllowExact) + { + return NextRunTime(time, 100, AllowExact); + } + + DateTime NextRunTime(DateTime time, int count, bool AllowExact) + { + if (count == 0) + throw new Exception("Invalid block wrapper combination."); + + DateTime + temp = _Item.NextRunTime(time, AllowExact), + begin = _Begin.NextRunTime(time, true), + end = _End.NextRunTime(time, true); + System.Diagnostics.Debug.WriteLine(string.Format("{0} {1} {2} {3}", time, begin, end, temp)); + bool A = temp > end, B = temp < begin, C = end < begin; + System.Diagnostics.Debug.WriteLine(string.Format("{0} {1} {2}", A, B, C)); + if (C) + { + if (A && B) + return NextRunTime(begin, --count, false); + else + return temp; + } + else + { + if (!A && !B) + return temp; + if (!A) + return NextRunTime(begin, --count, false); + else + return NextRunTime(end, --count, false); + } + } + private IScheduledItem _Item; + private ScheduledTime _Begin, _End; + } +} \ No newline at end of file diff --git a/UtilidadesTSL4net.c/ScheduledItems/EventInstance.cs b/UtilidadesTSL4net.c/ScheduledItems/EventInstance.cs new file mode 100644 index 0000000..09a1021 --- /dev/null +++ b/UtilidadesTSL4net.c/ScheduledItems/EventInstance.cs @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Collections; + +namespace UtilidadesCSharp.Schedule +{ + + /// + /// There have been quite a few requests to allow scheduling of multiple delegates and method parameter data + /// from the same timer. This class allows you to match the event with the time that it fired. I want to keep + /// the same simple implementation of the EventQueue and interval classes since they can be reused elsewhere. + /// The timer should be responsible for matching this data up. + /// + public class EventInstance : IComparable + { + public EventInstance(DateTime time, IScheduledItem scheduleItem, object data) + { + Time = time; + ScheduleItem = scheduleItem; + Data = data; + } + public DateTime Time; + public IScheduledItem ScheduleItem; + public object Data; + + public int CompareTo(object obj) + { + if (obj is EventInstance) + return Time.CompareTo(((EventInstance)obj).Time); + if (obj is DateTime) + return Time.CompareTo((DateTime)obj); + return 0; + } + } + +} + + diff --git a/UtilidadesTSL4net.c/ScheduledItems/EventQueue.cs b/UtilidadesTSL4net.c/ScheduledItems/EventQueue.cs new file mode 100644 index 0000000..685f0b2 --- /dev/null +++ b/UtilidadesTSL4net.c/ScheduledItems/EventQueue.cs @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Collections; + +namespace UtilidadesCSharp.Schedule +{ + /// + /// The event queue is a collection of scheduled items that represents the union of all child scheduled items. + /// This is useful for events that occur every 10 minutes or at multiple intervals not covered by the simple + /// scheduled items. + /// + public class EventQueue : IScheduledItem + { + public EventQueue() + { + _List = new ArrayList(); + } + /// + /// Adds a ScheduledTime to the queue. + /// + /// The scheduled time to add + public void Add(IScheduledItem time) + { + _List.Add(time); + } + + /// + /// Clears the list of scheduled times. + /// + public void Clear() + { + _List.Clear(); + } + + /// + /// Adds the running time for all events in the list. + /// + /// The beginning time of the interval + /// The end time of the interval + /// The list to add times to. + public void AddEventsInInterval(DateTime Begin, DateTime End, ArrayList List) + { + foreach(IScheduledItem st in _List) + st.AddEventsInInterval(Begin, End, List); + List.Sort(); + } + + /// + /// Returns the first time after the starting time for all events in the list. + /// + /// The starting time. + /// If this is true then it allows the return time to match the time parameter, false forces the return time to be greater then the time parameter + /// Either the next event after the input time or greater or equal to depending on the AllowExact parameter. + public DateTime NextRunTime(DateTime time, bool AllowExact) + { + DateTime next = DateTime.MaxValue; + //Get minimum datetime from the list. + foreach(IScheduledItem st in _List) + { + DateTime Proposed = st.NextRunTime(time, AllowExact); + next = (Proposed < next) ? Proposed : next; + } + return next; + } + private ArrayList _List; + } +} \ No newline at end of file diff --git a/UtilidadesTSL4net.c/ScheduledItems/ScheduledTime.cs b/UtilidadesTSL4net.c/ScheduledItems/ScheduledTime.cs new file mode 100644 index 0000000..49b2798 --- /dev/null +++ b/UtilidadesTSL4net.c/ScheduledItems/ScheduledTime.cs @@ -0,0 +1,177 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Specialized; + +namespace UtilidadesCSharp.Schedule +{ + public enum EventTimeBase + { + BySecond = 1, + ByMinute = 2, + Hourly = 3, + Daily = 4, + Weekly = 5, + Monthly = 6, + } + + /// + /// This class represents a simple schedule. It can represent a repeating event that occurs anywhere from every + /// second to once a month. It consists of an enumeration to mark the interval and an offset from that interval. + /// For example new ScheduledTime(Hourly, new TimeSpan(0, 15, 0)) would represent an event that fired 15 minutes + /// after the hour every hour. + /// + [Serializable] + public class ScheduledTime : IScheduledItem + { + public ScheduledTime(EventTimeBase Base, TimeSpan Offset) + { + _Base = Base; + _Offset = Offset; + } + + /// + /// intializes a simple scheduled time element from a pair of strings. + /// Here are the supported formats + /// + /// BySecond - single integer representing the offset in ms + /// ByMinute - A comma seperate list of integers representing the number of seconds and ms + /// Hourly - A comma seperated list of integers representing the number of minutes, seconds and ms + /// Daily - A time in hh:mm:ss AM/PM format + /// Weekly - n, time where n represents an integer and time is a time in the Daily format + /// Monthly - the same format as weekly. + /// + /// + /// A string representing the base enumeration for the scheduled time + /// A string representing the offset for the time. + public ScheduledTime(string StrBase, string StrOffset) + { + //TODO:Create an IScheduled time factory method. + _Base = (EventTimeBase)Enum.Parse(typeof(EventTimeBase), StrBase, true); + Init(StrOffset); + } + + public int ArrayAccess(string[] Arr, int i) + { + if (i >= Arr.Length) + return 0; + return int.Parse(Arr[i]); + } + + public void AddEventsInInterval(DateTime Begin, DateTime End, ArrayList List) + { + DateTime Next = NextRunTime(Begin, true); + while (Next < End) + { + List.Add(Next); + Next = IncInterval(Next); + } + } + + public DateTime NextRunTime(DateTime time, bool AllowExact) + { + DateTime NextRun = LastSyncForTime(time) + _Offset; + if (NextRun == time && AllowExact) + return time; + if (NextRun > time) + return NextRun; + return IncInterval(NextRun); + } + + + private DateTime LastSyncForTime(DateTime time) + { + switch (_Base) + { + case EventTimeBase.BySecond: + return new DateTime(time.Year, time.Month, time.Day, time.Hour, time.Minute, time.Second); + case EventTimeBase.ByMinute: + return new DateTime(time.Year, time.Month, time.Day, time.Hour, time.Minute, 0); + case EventTimeBase.Hourly: + return new DateTime(time.Year, time.Month, time.Day, time.Hour, 0, 0); + case EventTimeBase.Daily: + return new DateTime(time.Year, time.Month, time.Day); + case EventTimeBase.Weekly: + return (new DateTime(time.Year, time.Month, time.Day)).AddDays(-(int)time.DayOfWeek); + case EventTimeBase.Monthly: + return new DateTime(time.Year, time.Month, 1); + } + throw new Exception("Invalid base specified for timer."); + } + + private DateTime IncInterval(DateTime Last) + { + switch (_Base) + { + case EventTimeBase.BySecond: + return Last.AddSeconds(1); + case EventTimeBase.ByMinute: + return Last.AddMinutes(1); + case EventTimeBase.Hourly: + return Last.AddHours(1); + case EventTimeBase.Daily: + return Last.AddDays(1); + case EventTimeBase.Weekly: + return Last.AddDays(7); + case EventTimeBase.Monthly: + return Last.AddMonths(1); + } + throw new Exception("Invalid base specified for timer."); + } + + private void Init(string StrOffset) + { + switch (_Base) + { + case EventTimeBase.BySecond: + _Offset = new TimeSpan(0, 0, 0, 0, int.Parse(StrOffset)); + break; + case EventTimeBase.ByMinute: + string[] ArrMinute = StrOffset.Split(','); + _Offset = new TimeSpan(0, 0, 0, ArrayAccess(ArrMinute, 0), ArrayAccess(ArrMinute, 1)); + break; + case EventTimeBase.Hourly: + string[] ArrHour = StrOffset.Split(','); + _Offset = new TimeSpan(0, 0, ArrayAccess(ArrHour, 0), ArrayAccess(ArrHour, 1), ArrayAccess(ArrHour, 2)); + break; + case EventTimeBase.Daily: + DateTime Daytime = DateTime.Parse(StrOffset); + _Offset = new TimeSpan(0, Daytime.Hour, Daytime.Minute, Daytime.Second, Daytime.Millisecond); + break; + case EventTimeBase.Weekly: + string[] ArrWeek = StrOffset.Split(','); + if (ArrWeek.Length != 2) + throw new Exception("Weekly offset must be in the format n, time where n is the day of the week starting with 0 for sunday"); + DateTime WeekTime = DateTime.Parse(ArrWeek[1]); + _Offset = new TimeSpan(int.Parse(ArrWeek[0]), WeekTime.Hour, WeekTime.Minute, WeekTime.Second, WeekTime.Millisecond); + break; + case EventTimeBase.Monthly: + string[] ArrMonth = StrOffset.Split(','); + if (ArrMonth.Length != 2) + throw new Exception("Monthly offset must be in the format n, time where n is the day of the month starting with 1 for the first day of the month."); + DateTime MonthTime = DateTime.Parse(ArrMonth[1]); + _Offset = new TimeSpan(int.Parse(ArrMonth[0])-1, MonthTime.Hour, MonthTime.Minute, MonthTime.Second, MonthTime.Millisecond); + break; + default: + throw new Exception("Invalid base specified for timer."); + } + } + + private EventTimeBase _Base; + private TimeSpan _Offset; + } +} \ No newline at end of file diff --git a/UtilidadesTSL4net.c/ScheduledItems/SimpleInterval.cs b/UtilidadesTSL4net.c/ScheduledItems/SimpleInterval.cs new file mode 100644 index 0000000..50f5639 --- /dev/null +++ b/UtilidadesTSL4net.c/ScheduledItems/SimpleInterval.cs @@ -0,0 +1,90 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ +using System; +using System.Collections; +using System.Diagnostics; + +namespace UtilidadesCSharp.Schedule +{ + /// + /// The simple interval represents the simple scheduling that .net supports natively. It consists of a start + /// absolute time and an interval that is counted off from the start time. + /// + [Serializable] + public class SimpleInterval : IScheduledItem + { + public SimpleInterval(DateTime StartTime, TimeSpan Interval) + { + _Interval = Interval; + _StartTime = StartTime; + _EndTime = DateTime.MaxValue; + } + public SimpleInterval(DateTime StartTime, TimeSpan Interval, int count) + { + _Interval = Interval; + _StartTime = StartTime; + _EndTime = StartTime + TimeSpan.FromTicks(Interval.Ticks*count); + } + public SimpleInterval(DateTime StartTime, TimeSpan Interval, DateTime EndTime) + { + _Interval = Interval; + _StartTime = StartTime; + _EndTime = EndTime; + } + public void AddEventsInInterval(DateTime Begin, DateTime End, ArrayList List) + { + if (End <= _StartTime) + return; + DateTime Next = NextRunTime(Begin, true); + while (Next < End) + { + List.Add(Next); + Next = NextRunTime(Next, false); + } + } + + public DateTime NextRunTime(DateTime time, bool AllowExact) + { + DateTime returnTime = NextRunTimeInt(time, AllowExact); + Debug.WriteLine(time); + Debug.WriteLine(returnTime); + Debug.WriteLine(_EndTime); + return (returnTime >= _EndTime) ? DateTime.MaxValue : returnTime; + } + + private DateTime NextRunTimeInt(DateTime time, bool AllowExact) + { + TimeSpan Span = time-_StartTime; + if (Span < TimeSpan.Zero) + return _StartTime; + if (ExactMatch(time)) + return AllowExact ? time : time + _Interval; + uint msRemaining = (uint)(_Interval.TotalMilliseconds - ((uint)Span.TotalMilliseconds % (uint)_Interval.TotalMilliseconds)); + return time.AddMilliseconds(msRemaining); + } + + private bool ExactMatch(DateTime time) + { + TimeSpan Span = time-_StartTime; + if (Span < TimeSpan.Zero) + return false; + return (Span.TotalMilliseconds % _Interval.TotalMilliseconds) == 0; + } + + private TimeSpan _Interval; + private DateTime _StartTime; + private DateTime _EndTime; + } +} \ No newline at end of file diff --git a/UtilidadesTSL4net.c/ScheduledItems/SingleEvent.cs b/UtilidadesTSL4net.c/ScheduledItems/SingleEvent.cs new file mode 100644 index 0000000..29ae869 --- /dev/null +++ b/UtilidadesTSL4net.c/ScheduledItems/SingleEvent.cs @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ +using System; + +namespace UtilidadesCSharp.Schedule +{ + /// Single event represents an event which only fires once. + public class SingleEvent : IScheduledItem + { + public SingleEvent(DateTime eventTime) + { + _EventTime = eventTime; + } + #region IScheduledItem Members + + public void AddEventsInInterval(DateTime Begin, DateTime End, System.Collections.ArrayList List) + { + if (Begin <= _EventTime && End > _EventTime) + List.Add(_EventTime); + } + + public DateTime NextRunTime(DateTime time, bool IncludeStartTime) + { + if (IncludeStartTime) + return (_EventTime >= time) ? _EventTime : DateTime.MaxValue; + else + return (_EventTime > time) ? _EventTime : DateTime.MaxValue; + } + private DateTime _EventTime; + + #endregion + } +} diff --git a/UtilidadesTSL4net.c/TimerJob.cs b/UtilidadesTSL4net.c/TimerJob.cs new file mode 100644 index 0000000..9e4d9c8 --- /dev/null +++ b/UtilidadesTSL4net.c/TimerJob.cs @@ -0,0 +1,181 @@ +/*************************************************************************** + * Copyright Andy Brummer 2004-2005 + * + * This code is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * This code may be used in compiled form in any way you desire. This + * file may be redistributed unmodified by any means provided it is + * not sold for profit without the authors written consent, and + * providing that this notice and the authors name is included. If + * the source code in this file is used in any commercial application + * then a simple email would be nice. + * + **************************************************************************/ + +using System; +using System.Collections; +using System.Reflection; +using System.Timers; + +namespace UtilidadesCSharp.Schedule +{ + /// + /// Timer job groups a schedule, syncronization data, a result filter, method information and an enabled state so that multiple jobs + /// can be managed by the same timer. Each one operating independently of the others with different syncronization and recovery settings. + /// + public class TimerJob + { + + + + + + + public TimerJob(IScheduledItem schedule, IMethodCall method) + { + Schedule = schedule; + Method = method; + _ExecuteHandler = new ExecuteHandler(ExecuteInternal); + } + public IScheduledItem Schedule; + public bool SyncronizedEvent = true; + public IResultFilter Filter; + public IMethodCall Method; +// public IJobLog Log; + public bool Enabled = true; + + public DateTime NextRunTime(DateTime time, bool IncludeStartTime) + { + if (!Enabled) + return DateTime.MaxValue; + return Schedule.NextRunTime(time, IncludeStartTime); + } + + public void Execute(object sender, DateTime Begin, DateTime End, ExceptionEventHandler Error) + { + if (!Enabled) + return; + + ArrayList EventList = new ArrayList(); + Schedule.AddEventsInInterval(Begin, End, EventList); + + if (Filter != null) + Filter.FilterResultsInInterval(Begin, End, EventList); + + foreach(DateTime EventTime in EventList) + { + if (SyncronizedEvent) + _ExecuteHandler(sender, EventTime, Error); + else + _ExecuteHandler.BeginInvoke(sender, EventTime, Error, null, null); + } + } + + private void ExecuteInternal(object sender, DateTime EventTime, ExceptionEventHandler Error) + { + try + { + TimerParameterSetter Setter = new TimerParameterSetter(EventTime, sender); + Method.Execute(Setter); + } + catch (Exception ex) + { + if (Error != null) + try { Error(this, new ExceptionEventArgs(EventTime, ex)); } catch {} + } + } + + private delegate void ExecuteHandler(object sender, DateTime EventTime, ExceptionEventHandler Error); + + private ExecuteHandler _ExecuteHandler; + } + + /// + /// Timer job manages a group of timer jobs. + /// + public class TimerJobList + { + public TimerJobList() + { + _List = new ArrayList(); + } + + public void Add(TimerJob Event) + { + _List.Add(Event); + } + + public void Clear() + { + _List.Clear(); + } + + /// + /// Gets the next time any of the jobs in the list will run. Allows matching the exact start time. If no matches are found the return + /// is DateTime.MaxValue; + /// + /// The starting time for the interval being queried. This time is included in the interval + /// The first absolute date one of the jobs will execute on. If none of the jobs needs to run DateTime.MaxValue is returned. + public DateTime NextRunTime(DateTime time) + { + DateTime next = DateTime.MaxValue; + //Get minimum datetime from the list. + foreach(TimerJob Job in _List) + { + DateTime Proposed = Job.NextRunTime(time, true); + next = (Proposed < next) ? Proposed : next; + } + return next; + } + + public TimerJob[] Jobs + { + get { return (TimerJob[])_List.ToArray(typeof(TimerJob)); } + } + + private ArrayList _List; + } + + /// + /// The timer job allows delegates to be specified with unbound parameters. This ParameterSetter assigns all unbound datetime parameters + /// with the specified time and all unbound object parameters with the calling object. + /// + public class TimerParameterSetter : IParameterSetter + { + /// + /// Initalize the ParameterSetter with the time to pass to unbound time parameters and object to pass to unbound object parameters. + /// + /// The time to pass to the unbound DateTime parameters + /// The object to pass to the unbound object parameters + public TimerParameterSetter(DateTime time, object sender) + { + _time = time; + _sender = sender; + } + public void reset() + { + } + public bool GetParameterValue(ParameterInfo pi, int ParameterLoc, ref object parameter) + { + switch(pi.ParameterType.Name.ToLower()) + { + case "datetime": + parameter = _time; + return true; + case "object": + parameter = _sender; + return true; + case "scheduledeventargs": + parameter = new ScheduledEventArgs(_time); + return true; + case "eventargs": + parameter = new ScheduledEventArgs(_time); + return true; + } + return false; + } + DateTime _time; + object _sender; + } +} diff --git a/UtilidadesTSL4net.c/UtilidadesTSL4net.c.csproj b/UtilidadesTSL4net.c/UtilidadesTSL4net.c.csproj new file mode 100644 index 0000000..2772a08 --- /dev/null +++ b/UtilidadesTSL4net.c/UtilidadesTSL4net.c.csproj @@ -0,0 +1,132 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {BF2447F5-65A1-4A47-A16E-F2227634E050} + Library + Properties + Utilidadescsharp + UtilidadesTSL4net.c + + + 3.5 + + + v2.0 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + x86 + AllRules.ruleset + + + + + pdbonly + true + bin\release\ + TRACE + prompt + 4 + x86 + AllRules.ruleset + + + + + + + + + + + + + + + + True + True + Settings.settings + + + + + + + + + + + + + UserControl + + + + + + Designer + ucImagen.cs + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/UtilidadesTSL4net.c/ucImagen.cs b/UtilidadesTSL4net.c/ucImagen.cs new file mode 100644 index 0000000..5be67ef --- /dev/null +++ b/UtilidadesTSL4net.c/ucImagen.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Windows.Forms; + +namespace UtilidadesCSharp.Imagen +{ + public enum SizeMode + { + Scrollable, + RatioStretch + } + /// + /// Summary description for Viewer. + /// + public class Imagen : System.Windows.Forms.UserControl + { + private System.Windows.Forms.PictureBox pictureBox1; + private System.ComponentModel.IContainer components; + private SizeMode sizeMode; + + public Imagen() + { + // This call is required by the Windows.Forms Form Designer. + InitializeComponent(); + this.ImageSizeMode = SizeMode.RatioStretch; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (components != null) + { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + public Image Image + { + get { return this.pictureBox1.Image; } + set + { + this.pictureBox1.Image = value; + this.SetLayout(); + //this.ChangeSize(); + } + } + public SizeMode ImageSizeMode + { + get { return this.sizeMode; } + set + { + this.sizeMode = value; + this.AutoScroll = (this.sizeMode == SizeMode.Scrollable); + this.SetLayout(); + } + } + + private void RatioStretch() + { + float pRatio = (float)this.Width / this.Height; + float imRatio = (float)this.pictureBox1.Image.Width / this.pictureBox1.Image.Height; + + if (this.Width >= this.pictureBox1.Image.Width && this.Height >= this.pictureBox1.Image.Height) + { + this.pictureBox1.Width = this.pictureBox1.Image.Width; + this.pictureBox1.Height = this.pictureBox1.Image.Height; + } + else if (this.Width > this.pictureBox1.Image.Width && this.Height < this.pictureBox1.Image.Height) + { + this.pictureBox1.Height = this.Height; + this.pictureBox1.Width = (int)(this.Height * imRatio); + } + else if (this.Width < this.pictureBox1.Image.Width && this.Height > this.pictureBox1.Image.Height) + { + this.pictureBox1.Width = this.Width; + this.pictureBox1.Height = (int)(this.Width / imRatio); + } + else if (this.Width < this.pictureBox1.Image.Width && this.Height < this.pictureBox1.Image.Height) + { + if (this.Width >= this.Height) + { + //width image + if (this.pictureBox1.Image.Width >= this.pictureBox1.Image.Height && imRatio >= pRatio) + { + this.pictureBox1.Width = this.Width; + this.pictureBox1.Height = (int)(this.Width / imRatio); + } + else + { + this.pictureBox1.Height = this.Height; + this.pictureBox1.Width = (int)(this.Height * imRatio); + } + } + else + { + //width image + if (this.pictureBox1.Image.Width < this.pictureBox1.Image.Height && imRatio < pRatio) + { + this.pictureBox1.Height = this.Height; + this.pictureBox1.Width = (int)(this.Height * imRatio); + } + else // height image + { + this.pictureBox1.Width = this.Width; + this.pictureBox1.Height = (int)(this.Width / imRatio); + } + } + } + this.CenterImage(); + } + private void Scrollable() + { + this.pictureBox1.Width = this.pictureBox1.Image.Width; + this.pictureBox1.Height = this.pictureBox1.Image.Height; + this.CenterImage(); + } + private void SetLayout() + { + if (this.pictureBox1.Image == null) + return; + if (this.sizeMode == SizeMode.RatioStretch) + this.RatioStretch(); + else + { + this.AutoScroll = false; + this.Scrollable(); + this.AutoScroll = true; + + } + } + private void CenterImage() + { + int top = (int)((this.Height - this.pictureBox1.Height) / 2.0); + int left = (int)((this.Width - this.pictureBox1.Width) / 2.0); + if (top < 0) + top = 0; + if (left < 0) + left = 0; + this.pictureBox1.Top = top; + this.pictureBox1.Left = left; + } + + #region Component Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.SuspendLayout(); + // + // pictureBox1 + // + this.pictureBox1.BackColor = System.Drawing.Color.Transparent; + this.pictureBox1.Cursor = System.Windows.Forms.Cursors.Default; + this.pictureBox1.Location = new System.Drawing.Point(24, 32); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(296, 208); + this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; + this.pictureBox1.TabIndex = 0; + this.pictureBox1.TabStop = false; + // + // Imagen + // + this.AutoScroll = true; + this.BackColor = System.Drawing.Color.Transparent; + this.Controls.Add(this.pictureBox1); + this.Name = "Imagen"; + this.Size = new System.Drawing.Size(352, 272); + this.Load += new System.EventHandler(this.Viewer_Load); + this.Resize += new System.EventHandler(this.Viewer_Resize); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.ResumeLayout(false); + + } + #endregion + + private void Viewer_Load(object sender, System.EventArgs e) + { + this.pictureBox1.Width = 0; + this.pictureBox1.Height = 0; + this.SetLayout(); + } + + private void Viewer_Resize(object sender, System.EventArgs e) + { + this.SetLayout(); + } + + } +} diff --git a/UtilidadesTSL4net.c/ucImagen.resx b/UtilidadesTSL4net.c/ucImagen.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/UtilidadesTSL4net.c/ucImagen.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ucImagen.cs b/ucImagen.cs new file mode 100644 index 0000000..5be67ef --- /dev/null +++ b/ucImagen.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Windows.Forms; + +namespace UtilidadesCSharp.Imagen +{ + public enum SizeMode + { + Scrollable, + RatioStretch + } + /// + /// Summary description for Viewer. + /// + public class Imagen : System.Windows.Forms.UserControl + { + private System.Windows.Forms.PictureBox pictureBox1; + private System.ComponentModel.IContainer components; + private SizeMode sizeMode; + + public Imagen() + { + // This call is required by the Windows.Forms Form Designer. + InitializeComponent(); + this.ImageSizeMode = SizeMode.RatioStretch; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (components != null) + { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + public Image Image + { + get { return this.pictureBox1.Image; } + set + { + this.pictureBox1.Image = value; + this.SetLayout(); + //this.ChangeSize(); + } + } + public SizeMode ImageSizeMode + { + get { return this.sizeMode; } + set + { + this.sizeMode = value; + this.AutoScroll = (this.sizeMode == SizeMode.Scrollable); + this.SetLayout(); + } + } + + private void RatioStretch() + { + float pRatio = (float)this.Width / this.Height; + float imRatio = (float)this.pictureBox1.Image.Width / this.pictureBox1.Image.Height; + + if (this.Width >= this.pictureBox1.Image.Width && this.Height >= this.pictureBox1.Image.Height) + { + this.pictureBox1.Width = this.pictureBox1.Image.Width; + this.pictureBox1.Height = this.pictureBox1.Image.Height; + } + else if (this.Width > this.pictureBox1.Image.Width && this.Height < this.pictureBox1.Image.Height) + { + this.pictureBox1.Height = this.Height; + this.pictureBox1.Width = (int)(this.Height * imRatio); + } + else if (this.Width < this.pictureBox1.Image.Width && this.Height > this.pictureBox1.Image.Height) + { + this.pictureBox1.Width = this.Width; + this.pictureBox1.Height = (int)(this.Width / imRatio); + } + else if (this.Width < this.pictureBox1.Image.Width && this.Height < this.pictureBox1.Image.Height) + { + if (this.Width >= this.Height) + { + //width image + if (this.pictureBox1.Image.Width >= this.pictureBox1.Image.Height && imRatio >= pRatio) + { + this.pictureBox1.Width = this.Width; + this.pictureBox1.Height = (int)(this.Width / imRatio); + } + else + { + this.pictureBox1.Height = this.Height; + this.pictureBox1.Width = (int)(this.Height * imRatio); + } + } + else + { + //width image + if (this.pictureBox1.Image.Width < this.pictureBox1.Image.Height && imRatio < pRatio) + { + this.pictureBox1.Height = this.Height; + this.pictureBox1.Width = (int)(this.Height * imRatio); + } + else // height image + { + this.pictureBox1.Width = this.Width; + this.pictureBox1.Height = (int)(this.Width / imRatio); + } + } + } + this.CenterImage(); + } + private void Scrollable() + { + this.pictureBox1.Width = this.pictureBox1.Image.Width; + this.pictureBox1.Height = this.pictureBox1.Image.Height; + this.CenterImage(); + } + private void SetLayout() + { + if (this.pictureBox1.Image == null) + return; + if (this.sizeMode == SizeMode.RatioStretch) + this.RatioStretch(); + else + { + this.AutoScroll = false; + this.Scrollable(); + this.AutoScroll = true; + + } + } + private void CenterImage() + { + int top = (int)((this.Height - this.pictureBox1.Height) / 2.0); + int left = (int)((this.Width - this.pictureBox1.Width) / 2.0); + if (top < 0) + top = 0; + if (left < 0) + left = 0; + this.pictureBox1.Top = top; + this.pictureBox1.Left = left; + } + + #region Component Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.SuspendLayout(); + // + // pictureBox1 + // + this.pictureBox1.BackColor = System.Drawing.Color.Transparent; + this.pictureBox1.Cursor = System.Windows.Forms.Cursors.Default; + this.pictureBox1.Location = new System.Drawing.Point(24, 32); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(296, 208); + this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; + this.pictureBox1.TabIndex = 0; + this.pictureBox1.TabStop = false; + // + // Imagen + // + this.AutoScroll = true; + this.BackColor = System.Drawing.Color.Transparent; + this.Controls.Add(this.pictureBox1); + this.Name = "Imagen"; + this.Size = new System.Drawing.Size(352, 272); + this.Load += new System.EventHandler(this.Viewer_Load); + this.Resize += new System.EventHandler(this.Viewer_Resize); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.ResumeLayout(false); + + } + #endregion + + private void Viewer_Load(object sender, System.EventArgs e) + { + this.pictureBox1.Width = 0; + this.pictureBox1.Height = 0; + this.SetLayout(); + } + + private void Viewer_Resize(object sender, System.EventArgs e) + { + this.SetLayout(); + } + + } +} diff --git a/ucImagen.resx b/ucImagen.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/ucImagen.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file