/// <summary> /// Generates high DPC for new schedule to create new dispatch /// This is called when a new schedule has been committed /// </summary> /// <param name="ScheduleIDs"></param> internal static void MarkNewSchedule(IrqHandle IRQ) { DpcHandle dpchandle; Schedule schedule; Int64 scheduleid = IRQ.ScheduleID; DateTime currentdatetimeutc = DateTime.UtcNow; using (DispatchEngineContainer dec = new DispatchEngineContainer()) { schedule = dec.Schedules.Single(s => s.Id == scheduleid); // we allow an exception to run once an expired adhoc schedule // current assumption is we rely on the scheduling commit method // to prevent same schedule from committed more than once if (schedule.EndDateTimeUtc > currentdatetimeutc || schedule.IsAdhoc) { dpchandle = new DpcHandle() { ScheduleID = scheduleid, Importance = Importance.High, TimeStamp = currentdatetimeutc }; dec.DpcQueue.Add(dpchandle); } // remove current interrupt dec.IrqQueue.Attach(IRQ); dec.IrqQueue.Remove(IRQ); dec.SaveChanges(); } }
/// <summary> /// Generates high DPC to trigger planning of dispatch for a given schedule /// Also removes handle from pool if schedule is expired. /// This is called when a schedule changed or when a dispatch has completed, failed & cancelled. /// </summary> /// <param name="Interrupt"></param> internal static void SweepSchedulingPool(IrqHandle IRQ) { SchedulingHandle schedulinghandle; DpcHandle dpchandle; Schedule schedule; Dispatch dispatch; Int64 scheduleid = IRQ.ScheduleID; Int64? dispatchid = IRQ.DispatchID; bool indpcqueue; DateTime currentdatetimeutc = DateTime.UtcNow; using (DispatchEngineContainer dec = new DispatchEngineContainer()) { // check if there is already a high DPC for the schedule in queue // we don't sweep the pool if there is outstanding work to be done for the schedule indpcqueue = dec.DpcQueue.Any(dpc => dpc.ScheduleID == scheduleid && dpc.Importance == Importance.High); if (!indpcqueue) { // check if dispatch is waiting on callback dispatch = dec.Dispatches.Single(dh => dh.Id == dispatchid); if (dispatch.StatusID != DispatchStatus.Callback) { schedule = dec.Schedules.Single(s => s.Id == scheduleid); // check if schedule has been removed from pool // this can happen when a dispatch is done after the schedule has been expired if (dec.SchedulingPool.Any(sh => sh.ScheduleID == scheduleid)) { schedulinghandle = dec.SchedulingPool.Single(sh => sh.ScheduleID == scheduleid); if (schedule.EndDateTimeUtc > currentdatetimeutc) { dpchandle = new DpcHandle() { ScheduleID = schedule.Id, DispatchID = schedulinghandle.DispatchID, Importance = Importance.High, TimeStamp = currentdatetimeutc }; dec.DpcQueue.Add(dpchandle); } else { dec.SchedulingPool.Remove(schedulinghandle); } } } } // remove current interrupt dec.IrqQueue.Attach(IRQ); dec.IrqQueue.Remove(IRQ); dec.SaveChanges(); } }
/// <summary> /// Swap out the existing dispatch in scheduling pool with the dispatch set for callback /// This is called when a tool calls back the engine to run at a later time /// </summary> /// <param name="IRQ"></param> internal static void RelocateCallbackDispatch(IrqHandle IRQ) { SchedulingHandle schedulinghandle; Dispatch olddispatch; Dispatch currentdispatch; Int64 scheduleid = IRQ.ScheduleID; Int64? olddispatchid = IRQ.DispatchID; DateTime currentdatetimeutc = DateTime.UtcNow; using (DispatchEngineContainer dec = new DispatchEngineContainer()) { // reset the retry & error fields of the old dispatch olddispatch = dec.Dispatches.Single(d => d.Id == olddispatchid); olddispatch.RetryCount = 0; olddispatch.ErrorCode = null; olddispatch.ErrorMsg = null; olddispatch.LastModifiedDateTimeUtc = currentdatetimeutc; olddispatch.StatusID = DispatchStatus.Pending; if (dec.SchedulingPool.Any(sh => sh.ScheduleID == scheduleid)) { // double check to make sure current DispatchID in scheduling pool // is NOT the same as the callback DispatchID if (dec.SchedulingPool.Any(sh => sh.DispatchID != olddispatch.Id)) { schedulinghandle = dec.SchedulingPool.Single(dh => dh.ScheduleID == scheduleid); // set the current dispatch to cancelled currentdispatch = dec.Dispatches.Single(d => d.Id == schedulinghandle.DispatchID); currentdispatch.LastModifiedDateTimeUtc = currentdatetimeutc; currentdispatch.StatusID = DispatchStatus.Cancelled; // swap the current dispatch out with the old one schedulinghandle.DispatchID = (long)olddispatchid; } } else { schedulinghandle = new SchedulingHandle() { ScheduleID = scheduleid, DispatchID = (long)olddispatchid }; dec.SchedulingPool.Add(schedulinghandle); } // remove current interrupt dec.IrqQueue.Attach(IRQ); dec.IrqQueue.Remove(IRQ); dec.SaveChanges(); } }
/// <summary> /// Generates high low DPC to trigger runing of a dispatch /// This is called after polling /// </summary> /// <param name="IRQ"></param> internal static void MarkDispatchReady(IrqHandle IRQ) { DpcHandle dpchandle; dpchandle = new DpcHandle() { ScheduleID = IRQ.ScheduleID, DispatchID = IRQ.DispatchID, Importance = Importance.Low, // this should be low to run dispatches TimeStamp = DateTime.UtcNow }; using (DispatchEngineContainer dec = new DispatchEngineContainer()) { dec.DpcQueue.Add(dpchandle); // remove current interrupt dec.IrqQueue.Attach(IRQ); dec.IrqQueue.Remove(IRQ); dec.SaveChanges(); } }
/// <summary> /// Generates an interrupt when schedule expired /// This is called within polling /// </summary> /// <returns></returns> internal static bool PollExpiredSchedule() { IrqHandle irqhandle; var currentdatetimeutc = DateTime.UtcNow; bool foundexpired = false; using (DispatchEngineContainer dec = new DispatchEngineContainer()) { // look for expired schedules var schedulesexpired = from sh in dec.SchedulingPool join s in dec.Schedules on sh.ScheduleID equals s.Id where currentdatetimeutc > s.EndDateTimeUtc select sh; if (schedulesexpired.Count() > 0) { foreach (var e in schedulesexpired) { irqhandle = new IrqHandle() { ScheduleID = e.ScheduleID, DispatchID = e.DispatchID, Level = IRQL.Passive, TimeStamp = currentdatetimeutc }; dec.IrqQueue.Add(irqhandle); } dec.SaveChanges(); foundexpired = true; } } return(foundexpired); }
/// <summary> /// Generates interrupt when dispatch is ready to run /// This is called within polling, interrupt & deferred procedure /// </summary> /// <returns></returns> internal static bool PollDispatchReady() { IrqHandle irqhandle; var currentdatetimeutc = DateTime.UtcNow; bool foundready = false; using (DispatchEngineContainer dec = new DispatchEngineContainer()) { var dispatchready = from sh in dec.SchedulingPool join d in dec.Dispatches on sh.DispatchID equals d.Id where currentdatetimeutc > d.StartDateTimeUtc && (d.StatusID == DispatchStatus.Pending) select sh; if (dispatchready.Count() > 0) { foreach (var dr in dispatchready) { irqhandle = new IrqHandle() { ScheduleID = dr.ScheduleID, DispatchID = dr.DispatchID, Level = IRQL.Dispatch, TimeStamp = currentdatetimeutc }; dec.IrqQueue.Add(irqhandle); } dec.SaveChanges(); foundready = true; } } return(foundready); }
/// <summary> /// Runs simplex/duplex tool and update dispatch status accordingly. Also generates IRQ to notify dispatch is done /// This is called when there are items in dispatchready pool /// </summary> /// <param name="DispatchID"></param> internal static void RunDispatch(DpcHandle DPC) { Schedule schedule; Dispatch dispatch; IrqHandle irqhandle; Tool tool; List <DeviceDTO> devices = new List <DeviceDTO>(); Int64 scheduleid = DPC.ScheduleID; Int64? dispatchid = DPC.DispatchID; DateTime currentdatetimeutc = DateTime.UtcNow; using (DispatchEngineContainer dec = new DispatchEngineContainer()) { dispatch = dec.Dispatches.Single(d => d.Id == dispatchid); schedule = dec.Schedules.Single(s => s.Id == scheduleid); tool = dec.Tools.Single(t => t.Id == dispatch.ToolID); if (dec.CallbackDevices.Any(cd => cd.DispatchID == dispatchid)) { devices = (from d in dec.Devices where (from cd in dec.CallbackDevices where cd.DispatchID == dispatchid select cd.DeviceID).Contains(d.Id) select new DeviceDTO() { Id = d.Id, Name = d.Name, IPAddress = d.IPAddress }).ToList(); } } // we check here to make sure schedule change or cancellation is honored // which would hvae set the dispatch to cancelled if (dispatch.StatusID != DispatchStatus.Cancelled) { RunTool(ref dispatch, tool, devices); } // prevent dispatch from further running if it has retried at least 5 times if (dispatch.RetryCount >= 5) { dispatch.StatusID = DispatchStatus.Failed; } // update the last modified of dispatch dispatch.LastModifiedDateTimeUtc = currentdatetimeutc; using (DispatchEngineContainer dec = new DispatchEngineContainer()) { dec.Entry(dispatch).State = System.Data.EntityState.Modified; // generate an interrupt to notify completion if (dispatch.StatusID == DispatchStatus.Completed || dispatch.StatusID == DispatchStatus.Failed) { irqhandle = new IrqHandle() { ScheduleID = dispatch.ScheduleID, DispatchID = dispatch.Id, Level = IRQL.Dispatch, TimeStamp = currentdatetimeutc }; dec.IrqQueue.Add(irqhandle); } // remove current DPC dec.DpcQueue.Attach(DPC); dec.DpcQueue.Remove(DPC); dec.SaveChanges(); } }