/// <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> /// 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> /// Create a new Dispatch and add to scheduling pool for new schedules. Otherwise swap existing dispatch out /// This is called after a schedule change or dispatch has completed /// </summary> /// <param name="ScheduleID"></param> internal static void PlanSchedulingPool(DpcHandle DPC) { SchedulingHandle schedulinghandle; Dispatch olddispatch; Dispatch newdispatch; Schedule schedule; Tool tool; CallbackDevice callbackdevice; bool inschedulingpool; DateTime nextdispatchdatetimeutc; DateTime currentdatetimeutc = DateTime.UtcNow; using (DispatchEngineContainer dec = new DispatchEngineContainer()) { schedule = dec.Schedules.Single(s => s.Id == DPC.ScheduleID); dec.Entry(schedule).Reference(r => r.Tool).Load(); // current implementation only supports one tool per schedule tool = schedule.Tool; nextdispatchdatetimeutc = GetTargetDispatchDateTimeUtc(schedule); newdispatch = new Dispatch() { CreatedDateTimeUtc = currentdatetimeutc, LastModifiedDateTimeUtc = currentdatetimeutc, StatusID = DispatchStatus.Pending, RetryCount = 0, ScheduleID = DPC.ScheduleID, StartDateTimeUtc = nextdispatchdatetimeutc, ToolID = schedule.Tool.Id }; // new schedule will not have a dispatch in pool inschedulingpool = dec.SchedulingPool.Any(dh => dh.ScheduleID == DPC.ScheduleID); if (inschedulingpool) { // check if dispatch is waiting for callback // there should always be a dispatch in pool if this is not a new schedule olddispatch = dec.Dispatches.Single(dh => dh.Id == DPC.DispatchID); if (olddispatch.StatusID != DispatchStatus.Callback) { schedulinghandle = dec.SchedulingPool.Single(dh => dh.ScheduleID == DPC.ScheduleID); // set old dispatch to cancel only if it is in pending status if (olddispatch.StatusID == DispatchStatus.Pending) { olddispatch.StatusID = DispatchStatus.Cancelled; olddispatch.LastModifiedDateTimeUtc = currentdatetimeutc; } // swap out old dispatch if schedule is dispatchable if (schedule.EndDateTimeUtc > nextdispatchdatetimeutc) { dec.Dispatches.Add(newdispatch); dec.SaveChanges(); dec.Entry(newdispatch).Reload(); schedulinghandle.DispatchID = newdispatch.Id; } } } else { // this case it is a new schedule, we need to add to the schedule pool as well dec.Dispatches.Add(newdispatch); dec.SaveChanges(); dec.Entry(newdispatch).Reload(); // add devices associated to schedule to CallbackDevices table that is specifically maintained by engine dec.Entry(schedule).Collection(c => c.Devices).Load(); if (schedule.Devices.Count > 0) { foreach (var device in schedule.Devices) { callbackdevice = new CallbackDevice() { DispatchID = newdispatch.Id, DeviceID = device.Id }; dec.CallbackDevices.Add(callbackdevice); } } schedulinghandle = new SchedulingHandle() { ScheduleID = DPC.ScheduleID, DispatchID = newdispatch.Id }; dec.SchedulingPool.Add(schedulinghandle); } // remove current DPC dec.DpcQueue.Attach(DPC); dec.DpcQueue.Remove(DPC); dec.SaveChanges(); } }
/// <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(); } }