/// <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(); } }
internal static bool PushCommand(EngineControl control) { bool success = false; DispatcherControl dc = new DispatcherControl() { Cmd = control, TimeStamp = DateTime.Now }; using (DispatchEngineContainer dec = new DispatchEngineContainer()) { dec.DispatcherControls.Add(dc); dec.SaveChanges(); success = true; } return(success); }
/// <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> /// 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(); } }