/// <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>
        /// 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();
            }
        }