public override Time CheckOverlap(Episode episode) { Time total = Time.Zero; for(int i = 0; i < this.EpisodeCount; i++) { if(this.Episodes[i].StartTime <= episode.StartTime && this.Episodes[i].EndTime >= episode.EndTime) { // this [i] completely covers the given episode total += episode.Duration; } else if(this.Episodes[i].StartTime >= episode.StartTime && this.Episodes[i].EndTime >= episode.EndTime) { // if the episode happens before we start, but we end after total += episode.EndTime - this.Episodes[i].StartTime; } else if(this.Episodes[i].StartTime <= episode.StartTime && this.Episodes[i].EndTime <= episode.EndTime) { //if we started before this episode, but we finished first total += this.Episodes[i].EndTime - episode.StartTime; } else if(this.Episodes[i].StartTime >= episode.StartTime && this.Episodes[i].EndTime <= episode.EndTime) { // if the episode is larger and 100% covering this [i] total += this.Episodes[i].Duration; } } return total; }
internal TravelEpisode(int id, TimeWindow timeWindow, Episode from, Episode to, ITashaPerson owner) : base(timeWindow, owner) { //TODO: verify this line: this.ActivityType = to.ActivityType; //----- this.From = from; this.To = to; }
public bool Insert(Episode ep, IZone location) { // you must be going somewhere if(location == null) { return false; } ep.Zone = location; /* * This is going to be very similar to the ProjectSchedule version * The only difference is that we are going to take into account travel times */ ConflictReport conflict = InsertCase(Owner, ep, true); switch(conflict.Type) { case ScheduleConflictType.NoConflict: { InsertAt(ep, conflict.Position); return true; } case ScheduleConflictType.CompleteOverlap: case ScheduleConflictType.Split: { // At this point work-business episodes have already been dealt with in the project schedule // Thus everything that splits an episode is in fact not allowed! return false; } case ScheduleConflictType.Posterior: { // the given position is the element we need to go after Time earlyTimeBound = Time.StartOfDay + FirstTripTime; Time lateTimeBound = Time.EndOfDay; Episode prior = (Episode)Episodes[conflict.Position]; Episode middle = ep; Episode post = (Episode)((conflict.Position < EpisodeCount - 1) ? Episodes[conflict.Position + 1] : null); // Make sure to bound the times with the padding of the travel times required if(conflict.Position >= 1) { earlyTimeBound = Episodes[conflict.Position - 1].EndTime + Scheduler.TravelTime(Owner, Episodes[conflict.Position - 1].Zone, prior.Zone, prior.StartTime); } if(EpisodeCount - conflict.Position > 2) { lateTimeBound = Episodes[conflict.Position + 2].StartTime - Scheduler.TravelTime(Owner, post.Zone, Episodes[conflict.Position + 2].Zone, post.EndTime); } if(Insert(earlyTimeBound, prior, middle, post, lateTimeBound)) { InsertAt(ep, conflict.Position + 1); return true; } return false; } case ScheduleConflictType.Prior: { // The given position is the element we need to go before Time earlyTimeBound = Time.StartOfDay + FirstTripTime; Time lateTimeBound = Time.EndOfDay; Episode prior = (Episode)(conflict.Position > 0 ? Episodes[conflict.Position - 1] : null); Episode middle = ep; Episode post = (Episode)Episodes[conflict.Position]; // Make sure to bound the times with the padding of the travel times required if(conflict.Position >= 2) { earlyTimeBound = Episodes[conflict.Position - 2].EndTime + Scheduler.TravelTime(Owner, Episodes[conflict.Position - 2].Zone, prior.Zone, prior.StartTime); } if(EpisodeCount - conflict.Position > 1) { lateTimeBound = Episodes[conflict.Position + 1].StartTime - Scheduler.TravelTime(Owner, post.Zone, Episodes[conflict.Position + 1].Zone, post.EndTime); } if(Insert(earlyTimeBound, prior, middle, post, lateTimeBound)) { InsertAt(ep, conflict.Position); return true; } return false; } default: throw new NotImplementedException("Unknown insert conflict type!"); } }
public abstract bool TestInsert(Episode episode);
private void IncreaseArraySize() { // if we don't have room create a new array of 2x the size var temp = new Episode[this.EpisodeCount * 2]; // copy all of the old data Array.Copy( this.Episodes, temp, this.EpisodeCount ); // and now use that larger array this.Episodes = temp; }
public override Time CheckOverlap(Episode episode) { throw new NotImplementedException(); }
private bool UnableToJustMoveToInsert(Time earlyTimeBound, Episode prior, Episode middle, Episode post, Time lateTimeBound) { // if the amount of time that we have to work with is larger then we are unable to just move to insert return((lateTimeBound - earlyTimeBound) < (prior != null ? prior.Duration : Time.Zero) + middle.Duration + (post != null ? post.Duration : Time.Zero)); }
private static void Relocate(Episode ep, Time startTime) { var dur = ep.Duration; ep.EndTime = (ep.StartTime = startTime) + dur; }
/// <summary> /// Run a quick check to see if it is at all possible to place all of the 3 episodes in the given time bounds /// </summary> /// <param name="earlyTimeBound">The earliest point</param> /// <param name="prior">the first episode in the batch (possibly null if there is no episode prior)</param> /// <param name="middle">the second episode in the batch</param> /// <param name="post">the last episode in the batch (possibly null if there is no episode post)</param> /// <param name="lateTimeBound">the latest point</param> /// <returns>If we can fit them all in properly</returns> private bool InitialInsertCheckPossible(Time earlyTimeBound, Episode prior, Episode middle, Episode post, Time lateTimeBound, ref Time travelFirst, ref Time travelSecond ) { Time minPrior = Time.Zero; Time minMid = Tasha.Scheduler.Scheduler.PercentOverlapAllowed * middle.OriginalDuration; Time minPost = Time.Zero; if(prior != null) { minPrior = Tasha.Scheduler.Scheduler.PercentOverlapAllowed * prior.OriginalDuration; travelFirst = Scheduler.TravelTime(Owner, prior.Zone, middle.Zone, prior.EndTime); } else { travelFirst = Scheduler.TravelTime(Owner, Owner.Household.HomeZone, middle.Zone, middle.StartTime); } if(post != null) { minPost = Tasha.Scheduler.Scheduler.PercentOverlapAllowed * post.OriginalDuration; travelSecond = Scheduler.TravelTime(Owner, middle.Zone, post.Zone, middle.EndTime); } else { travelSecond = Scheduler.TravelTime(Owner, middle.Zone, Owner.Household.HomeZone, middle.EndTime); } // it is possible to fit in if the bounds are larger than the minimum size of all three episodes return (lateTimeBound - earlyTimeBound) >= (minPrior + minMid + minPost + travelFirst + travelSecond); }
/// <summary> /// Return home from work creates a return home from work activity (return home from work may involve /// going to lunch at home or going to check up on something in the house during work). /// </summary> /// <param name="person"></param> /// <param name="schedule"></param> /// <param name="episode"></param> /// <returns></returns> private static bool ProcessReturnHomeFromWork(ITashaPerson person, Schedule schedule, Random random, Episode episode, int householdPD, int workPD, GenerationAdjustment[] generationAdjustments) { int freq = 0; //the current work schedule doesn't allow for a return from work activity if (episode.StartTime > Scheduler.MaxPrimeWorkStartTimeForReturnHomeFromWork || episode.Duration < Scheduler.MinPrimaryWorkDurationForReturnHomeFromWork) { return(false); } //End time of work to home activity Time endTime = episode.EndTime + new Time(0.3f) < Scheduler.ReturnHomeFromWorkMaxEndTime ? episode.EndTime + new Time(0.3f) : Scheduler.ReturnHomeFromWorkMaxEndTime; freq = TimeTable.GetFrequency(person, Activity.ReturnFromWork, random, 1, episode.StartTime + new Time(0.3f), endTime, householdPD, workPD, generationAdjustments); if (freq == 1) { IZone homeZone = person.Household.HomeZone; Time HalfAnHour = new Time() { Minutes = 30 }; Time MaxEndTime = ((episode.EndTime - HalfAnHour) < Scheduler.ReturnHomeFromWorkMaxEndTime) ? (episode.EndTime - HalfAnHour) : Scheduler.ReturnHomeFromWorkMaxEndTime; Time startTime; if (!TimeTable.GetStartTime(person, Activity.ReturnFromWork , freq , episode.StartTime + HalfAnHour , MaxEndTime, random, out startTime)) { return(false); } Time maxDuration = new Time(Math.Min((Scheduler.ReturnHomeFromWorkMaxEndTime - Time.OneHour).ToFloat(), (episode.EndTime - HalfAnHour - startTime).ToFloat())); Time duration; if (!TimeTable.GetDuration(person, Activity.ReturnFromWork, startTime, maxDuration, random, out duration)) { // reject return(false); } Episode returnFromWorkEpisode; returnFromWorkEpisode = new ActivityEpisode(0, new TimeWindow(startTime, startTime + duration), Activity.ReturnFromWork, person); returnFromWorkEpisode.Zone = homeZone; schedule.Insert(returnFromWorkEpisode, random); } return(true); }
/// <summary> /// Secondary work creates a secondary work activity for person /// </summary> /// <param name="person"></param> /// <param name="schedule"></param> /// <param name="primaryWorkEpisode"></param> private static void ProcessSecondaryWork(ITashaPerson person, Schedule schedule, Random random, Episode primaryWorkEpisode, int householdPD, int workPD, GenerationAdjustment[] generationAdjustments) { //can only work if finish primary work by 7:00PM if (primaryWorkEpisode.EndTime < Scheduler.SecondaryWorkThreshold) { int freq_R = 0; //getting earliest possible startTime Time HourAfterWork = primaryWorkEpisode.EndTime + Time.OneHour; Time MinStartTime = HourAfterWork > Scheduler.SecondaryWorkMinStartTime ? HourAfterWork : Scheduler.SecondaryWorkMinStartTime; freq_R = TimeTable.GetFrequency(person, Activity.SecondaryWork, random, 10, MinStartTime, Time.EndOfDay, householdPD, workPD, generationAdjustments); for (int i = 0; i < freq_R; i++) { //zone same as work zone IZone zone = primaryWorkEpisode.Zone.ZoneNumber == Scheduler.Tasha.ZoneSystem.RoamingZoneNumber ? Scheduler.LocationChoiceModel.GetLocationHomeBased(Activity.SecondaryWork, person.Household.HomeZone, random) : primaryWorkEpisode.Zone; //getting start time and duration of secondary work Time startTimeR; Time durationR; if (!TimeTable.GetStartTime(person, primaryWorkEpisode.ActivityType, freq_R, MinStartTime, Time.EndOfDay, random, out startTimeR)) { //TODO: We might want to reconsider this, skipping instead of just throwing an exception //throw new XTMFRuntimeException("Unable to find a start time for a primary work episode"); return; } if (!TimeTable.GetDuration(person, Activity.SecondaryWork, startTimeR, Time.EndOfDay - startTimeR, random, out durationR)) { //throw new XTMFRuntimeException("Unable to find a duration for a primary work episode"); return; } //inserting secondary work into schedule Episode secondaryWorkEpisode; secondaryWorkEpisode = new ActivityEpisode(0, new TimeWindow(startTimeR, startTimeR + durationR), Activity.SecondaryWork, person); secondaryWorkEpisode.Zone = zone; schedule.Insert(secondaryWorkEpisode, random); } } }
/// <summary> /// Checks to see if an episode can be inserted /// </summary> public abstract Time CheckOverlap(Episode episode);
/// <summary> /// Forces an episode to be inserted /// </summary> public abstract bool ForcedEpisodeInsert(Episode ep);
public abstract bool Insert(Episode ep, Random random);
public override bool Insert(Episode ep, Random random) { /* This is where episodes are first put near their other common types * RULES: * 1) Unless it is a Work Business episode, we are not allowed to have a "split" conflict type * 2) We are not allowed to squish things past the threshold allowed (50% by default) */ // Learn what type of case we are going to be in ConflictReport conflict = this.InsertCase(null, ep, false); switch (conflict.Type) { case ScheduleConflictType.NoConflict: { this.InsertAt(ep, conflict.Position); return(true); } case ScheduleConflictType.Split: { if ((ep.ActivityType != Activity.WorkBasedBusiness) & (ep.ActivityType != Activity.ReturnFromWork)) { return(false); } if (this.Episodes[conflict.Position].ActivityType != Activity.PrimaryWork) { return(false); } // Since it is a primary work episode we need to split it var postEp = new ActivityEpisode(0, new TimeWindow(ep.EndTime, this.Episodes[conflict.Position].EndTime), Activity.PrimaryWork, this.Episodes[conflict.Position].Owner); postEp.Zone = this.Episodes[conflict.Position].Zone; ((Episode)this.Episodes[conflict.Position]).EndTime = ep.StartTime; this.InsertAt(ep, conflict.Position + 1); this.InsertAt(postEp, conflict.Position + 2); return(true); } case ScheduleConflictType.Posterior: { // the given position is the element we need to go after Time earlyTimeBound = Time.StartOfDay; Time lateTimeBound = Time.EndOfDay; Episode prior = (Episode)this.Episodes[conflict.Position]; Episode middle = ep; Episode post = (Episode)((conflict.Position < this.EpisodeCount - 1) ? this.Episodes[conflict.Position + 1] : null); if (conflict.Position >= 1) { earlyTimeBound = this.Episodes[conflict.Position - 1].EndTime; } if (this.EpisodeCount - conflict.Position > 2) { lateTimeBound = this.Episodes[conflict.Position + 2].StartTime; } if (this.Insert(earlyTimeBound, prior, middle, post, lateTimeBound)) { this.InsertAt(ep, conflict.Position + 1); return(true); } return(false); } case ScheduleConflictType.Prior: { // The given position is the element we need to go before Time earlyTimeBound = Time.StartOfDay; Time lateTimeBound = Time.EndOfDay; Episode prior = (Episode)(conflict.Position > 0 ? this.Episodes[conflict.Position - 1] : null); Episode middle = ep; Episode post = (Episode)this.Episodes[conflict.Position]; if (conflict.Position >= 2) { earlyTimeBound = this.Episodes[conflict.Position - 2].EndTime; } if (this.EpisodeCount - conflict.Position > 1) { lateTimeBound = this.Episodes[conflict.Position + 1].StartTime; } if (this.Insert(earlyTimeBound, prior, middle, post, lateTimeBound)) { this.InsertAt(ep, conflict.Position); return(true); } return(false); } case ScheduleConflictType.CompleteOverlap: { // There are no cases where a complete overlap is allowed return(false); } default: { // We came across a type of conflict that we do not know how to handle! throw new NotImplementedException(String.Format("This conflict type \"{0}\" has not been coded for yet!", Enum.GetName(typeof(ScheduleConflictType), conflict.Type))); } } }
public override bool TestInsert(Episode episode) { throw new NotImplementedException(); }
private static void ProcessWorkBusiness(ITashaPerson person, Schedule workSchedule, Random random, Episode primWorkEpisode, int householdPD, int workPD, GenerationAdjustment[] generationAdjustments) { Time startTimeB; Time durationB; Time startTime = primWorkEpisode.StartTime; Time endTime = primWorkEpisode.EndTime; int freq = TimeTable.GetFrequency(person, Activity.WorkBasedBusiness, random, Scheduler.MaxFrequency, startTime, endTime, householdPD, workPD, generationAdjustments); for (int i = 0; i < freq; i++) { var attempt = 0; while (attempt < Scheduler.EpisodeSchedulingAttempts) { attempt++; if (!TimeTable.GetStartTime(person, Activity.WorkBasedBusiness, freq, startTime, endTime, random, out startTimeB)) { continue; } if (!TimeTable.GetDuration(person, Activity.WorkBasedBusiness, startTimeB, endTime - startTimeB, random, out durationB)) { continue; } Episode businessEpisode; businessEpisode = new ActivityEpisode(0, new TimeWindow(startTimeB, startTimeB + durationB), Activity.WorkBasedBusiness, person); if (workSchedule.Insert(businessEpisode, random)) { break; } } } }
private static bool MiddlePostInsert(ref Time earlyTimeBound, Episode middle, Episode post, ref Time lateTimeBound, ref Time firstTime, ref Time secondTime) { Time overlap = (middle.EndTime + secondTime) - post.StartTime; if(overlap <= Time.Zero) { return true; } // if we can move forward, move forward if((middle.StartTime - firstTime) - earlyTimeBound > overlap) { Relocate(middle, (middle.StartTime - overlap)); return true; } // if that is not enough, move as forward as we can Relocate(middle, (earlyTimeBound + firstTime)); overlap = (middle.EndTime + secondTime) - post.StartTime; Relocate(post, (post.StartTime + overlap)); return true; }
/// <summary> /// Return home from work creates a return home from work activity (return home from work may involve /// going to lunch at home or going to check up on something in the house during work). /// </summary> /// <param name="person"></param> /// <param name="schedule"></param> /// <param name="episode"></param> /// <returns></returns> private static bool ProcessReturnHomeFromWork(ITashaPerson person, Schedule schedule, Random random, Episode episode) { int freq = 0; //the current work schedule doesn't allow for a return from work activity if(episode.StartTime > Scheduler.MaxPrimeWorkStartTimeForReturnHomeFromWork || episode.Duration < Scheduler.MinPrimaryWorkDurationForReturnHomeFromWork) { return false; } //End time of work to home activity Time endTime = episode.EndTime + new Time(0.3f) < Scheduler.ReturnHomeFromWorkMaxEndTime ? episode.EndTime + new Time(0.3f) : Scheduler.ReturnHomeFromWorkMaxEndTime; freq = TimeTable.GetFrequency(person, Activity.ReturnFromWork, random, 1, episode.StartTime + new Time(0.3f), endTime); if(freq == 1) { IZone homeZone = person.Household.HomeZone; Time HalfAnHour = new Time() { Minutes = 30 }; Time MaxEndTime = ((episode.EndTime - HalfAnHour) < Scheduler.ReturnHomeFromWorkMaxEndTime) ? (episode.EndTime - HalfAnHour) : Scheduler.ReturnHomeFromWorkMaxEndTime; Time startTime; if(!TimeTable.GetStartTime(person, Activity.ReturnFromWork , freq , episode.StartTime + HalfAnHour , MaxEndTime, random, out startTime)) { return false; } Time maxDuration = new Time(Math.Min((Scheduler.ReturnHomeFromWorkMaxEndTime - Time.OneHour).ToFloat(), (episode.EndTime - HalfAnHour - startTime).ToFloat())); Time duration; if(!TimeTable.GetDuration(person, Activity.ReturnFromWork, startTime, maxDuration, random, out duration)) { // reject return false; } Episode returnFromWorkEpisode; returnFromWorkEpisode = new ActivityEpisode(0, new TimeWindow(startTime, startTime + duration), Activity.ReturnFromWork, person); returnFromWorkEpisode.Zone = homeZone; schedule.Insert(returnFromWorkEpisode, random); } return true; }
private bool CheckDurations(Time priorDuration, Episode prior) { if(prior != null) { return priorDuration >= prior.Duration; } return true; }
private bool UnableToJustMoveToInsert(Time earlyTimeBound, Episode prior, Episode middle, Episode post, Time lateTimeBound, ref Time travelFirst, ref Time travelSecond) { // if the amount of time that we have to work with is larger then we are unable to just move to insert var totalTime = (prior != null ? prior.Duration : Time.Zero) + middle.Duration + (post != null ? post.Duration : Time.Zero) + travelFirst + travelSecond; var boundTime = (lateTimeBound - earlyTimeBound); return(boundTime <= totalTime); }
private bool ShiftToInsert(ref Time earlyTimeBound, Episode prior, Episode middle, Episode post, ref Time lateTimeBound, ref Time firstTime, ref Time secondTime) { if(prior != null & post != null) { return AllThreeInsert(ref earlyTimeBound, prior, middle, post, ref lateTimeBound, ref firstTime, ref secondTime); } else if(prior != null) { return PriorMiddleInsert(ref earlyTimeBound, prior, middle, ref lateTimeBound, ref firstTime, ref secondTime); } else if(post != null) { return MiddlePostInsert(ref earlyTimeBound, middle, post, ref lateTimeBound, ref firstTime, ref secondTime); } throw new XTMFRuntimeException("Unexpected shift to insert case!"); }
/// <summary> /// Insert into our array data structure /// </summary> /// <param name="ep">The episode you want to insert</param> /// <param name="pos">The position you want to insert it into</param> public void InsertAt(Episode ep, int pos) { // if we are not adding it to the end if ( ( pos < 0 ) | ( pos > this.EpisodeCount ) ) { throw new XTMFRuntimeException( "Tried to insert into an schedule at position " + pos + " where there are currently " + this.EpisodeCount + " episodes." ); } if ( this.EpisodeCount + 1 >= this.Episodes.Length ) { // if we are assigning to the end, but it isn't large enough, expand IncreaseArraySize(); } if ( pos != this.EpisodeCount ) { Array.Copy( this.Episodes, pos, this.Episodes, pos + 1, this.EpisodeCount - pos ); } // take ownership of the episode ep.ContainingSchedule = this; this.Episodes[pos] = ep; this.EpisodeCount++; CheckEpisodeIntegrity(); }
/// <summary> /// Secondary work creates a secondary work activity for person /// </summary> /// <param name="person"></param> /// <param name="schedule"></param> /// <param name="primaryWorkEpisode"></param> private static void ProcessSecondaryWork(ITashaPerson person, Schedule schedule, Random random, Episode primaryWorkEpisode) { //can only work if finish primary work by 7:00PM if(primaryWorkEpisode.EndTime < Scheduler.SecondaryWorkThreshold) { int freq_R = 0; //getting earliest possible startTime Time HourAfterWork = primaryWorkEpisode.EndTime + Time.OneHour; Time MinStartTime = HourAfterWork > Scheduler.SecondaryWorkMinStartTime ? HourAfterWork : Scheduler.SecondaryWorkMinStartTime; freq_R = TimeTable.GetFrequency(person, Activity.SecondaryWork, random, 10, MinStartTime, Time.EndOfDay); for(int i = 0; i < freq_R; i++) { //zone same as work zone IZone zone = primaryWorkEpisode.Zone.ZoneNumber == Scheduler.Tasha.ZoneSystem.RoamingZoneNumber ? Scheduler.LocationChoiceModel.GetLocationHomeBased(Activity.SecondaryWork, person.Household.HomeZone, random) : primaryWorkEpisode.Zone; //getting start time and duration of secondary work Time startTimeR; Time durationR; if(!TimeTable.GetStartTime(person, primaryWorkEpisode.ActivityType, freq_R, MinStartTime, Time.EndOfDay, random, out startTimeR)) { //TODO: We might want to reconsider this, skipping instead of just throwing an exception //throw new XTMFRuntimeException("Unable to find a start time for a primary work episode"); return; } if(!TimeTable.GetDuration(person, Activity.SecondaryWork, startTimeR, Time.EndOfDay - startTimeR, random, out durationR)) { //throw new XTMFRuntimeException("Unable to find a duration for a primary work episode"); return; } //inserting secondary work into schedule Episode secondaryWorkEpisode; secondaryWorkEpisode = new ActivityEpisode(0, new TimeWindow(startTimeR, startTimeR + durationR), Activity.SecondaryWork, person); secondaryWorkEpisode.Zone = zone; schedule.Insert(secondaryWorkEpisode, random); } } }
/// <summary> /// Run a quick check to see if it is at all possible to place all of the 3 episodes in the given time bounds /// </summary> /// <param name="earlyTimeBound">The earliest point</param> /// <param name="prior">the first episode in the batch (possibly null if there is no episode prior)</param> /// <param name="middle">the second episode in the batch</param> /// <param name="post">the last episode in the batch (possibly null if there is no episode post)</param> /// <param name="lateTimeBound">the latest point</param> /// <returns>If we can fit them all in properly</returns> private bool InitialInsertCheckPossible(Time earlyTimeBound, Episode prior, Episode middle, Episode post, Time lateTimeBound) { Time minPrior = (prior != null ? Tasha.Scheduler.Scheduler.PercentOverlapAllowed * prior.OriginalDuration : Time.Zero); Time minMid = Tasha.Scheduler.Scheduler.PercentOverlapAllowed * middle.OriginalDuration; Time minPost = (post != null ? Tasha.Scheduler.Scheduler.PercentOverlapAllowed * post.OriginalDuration : Time.Zero); // it is possible to fit in if the bounds are larger than the minimum size of all three episodes return((lateTimeBound - earlyTimeBound) >= (minPrior + minMid + minPost)); }
private static void ProcessWorkBusiness(ITashaPerson person, Schedule workSchedule, Random random, Episode primWorkEpisode) { Time startTimeB; Time durationB; Time startTime = primWorkEpisode.StartTime; Time endTime = primWorkEpisode.EndTime; int freq = TimeTable.GetFrequency(person, Activity.WorkBasedBusiness, random, Scheduler.MaxFrequency, startTime, endTime); for(int i = 0; i < freq; i++) { var attempt = 0; bool success = false; while(attempt < Scheduler.EpisodeSchedulingAttempts) { attempt++; if(!TimeTable.GetStartTime(person, Activity.WorkBasedBusiness, freq, startTime, endTime, random, out startTimeB)) { continue; } if(!TimeTable.GetDuration(person, Activity.WorkBasedBusiness, startTimeB, endTime - startTimeB, random, out durationB)) { continue; } Episode businessEpisode; businessEpisode = new ActivityEpisode(0, new TimeWindow(startTimeB, startTimeB + durationB), Activity.WorkBasedBusiness, person); if(workSchedule.Insert(businessEpisode, random)) { success = true; break; } } } }
public ConflictReport InsertCase(ITashaPerson owner, Episode ep, bool travelTime) { ConflictReport report; report.Type = ScheduleConflictType.NoConflict; report.Position = this.EpisodeCount; for ( int i = 0; i < this.EpisodeCount; i++ ) { if ( this.Episodes[i].EndTime + this.Episodes[i].TravelTime < ep.StartTime ) continue; Time epEnd = ep.EndTime; Time ithEnd = this.Episodes[i].EndTime; if ( travelTime ) { ep.TravelTime = ( this.EpisodeCount - 1 > i ) ? Scheduler.TravelTime( owner, ep.Zone, this.Episodes[i + 1].Zone, ep.EndTime ) : Time.Zero; epEnd += ep.TravelTime; ithEnd += this.Episodes[i].TravelTime; } report.Position = i; // Check for Complete overlap of the ith position if ( this.Episodes[i].StartTime >= ep.StartTime && ( epEnd >= ithEnd || ep.EndTime >= this.Episodes[i].EndTime ) ) { report.Type = ScheduleConflictType.CompleteOverlap; } else if ( this.EpisodeCount - 1 > i && this.Episodes[i + 1].StartTime >= ep.StartTime && epEnd >= this.Episodes[i + 1].EndTime ) { report.Type = ScheduleConflictType.CompleteOverlap; } else if ( this.Episodes[i].StartTime < ep.StartTime && ep.EndTime < this.Episodes[i].EndTime ) { report.Type = ScheduleConflictType.Split; } else if ( this.Episodes[i].StartTime >= ep.StartTime && ep.EndTime < this.Episodes[i].EndTime ) { report.Type = ScheduleConflictType.Prior; } else if ( this.Episodes[i].StartTime < ep.StartTime && ep.EndTime >= this.Episodes[i].EndTime ) { report.Type = ScheduleConflictType.Posterior; } break; } return report; // There is no conflict }
public bool Insert(Episode ep, IZone location) { // you must be going somewhere if (location == null) { return(false); } ep.Zone = location; /* * This is going to be very similar to the ProjectSchedule version * The only difference is that we are going to take into account travel times */ ConflictReport conflict = InsertCase(Owner, ep, true); switch (conflict.Type) { case ScheduleConflictType.NoConflict: { InsertAt(ep, conflict.Position); return(true); } case ScheduleConflictType.CompleteOverlap: case ScheduleConflictType.Split: { // At this point work-business episodes have already been dealt with in the project schedule // Thus everything that splits an episode is in fact not allowed! return(false); } case ScheduleConflictType.Posterior: { // the given position is the element we need to go after Time earlyTimeBound = Time.StartOfDay + FirstTripTime; Time lateTimeBound = Time.EndOfDay; Episode prior = (Episode)Episodes[conflict.Position]; Episode middle = ep; Episode post = (Episode)((conflict.Position < EpisodeCount - 1) ? Episodes[conflict.Position + 1] : null); // Make sure to bound the times with the padding of the travel times required if (conflict.Position >= 1) { earlyTimeBound = Episodes[conflict.Position - 1].EndTime + Scheduler.TravelTime(Owner, Episodes[conflict.Position - 1].Zone, prior.Zone, prior.StartTime); } if (EpisodeCount - conflict.Position > 2) { lateTimeBound = Episodes[conflict.Position + 2].StartTime - Scheduler.TravelTime(Owner, post.Zone, Episodes[conflict.Position + 2].Zone, post.EndTime); } if (Insert(earlyTimeBound, prior, middle, post, lateTimeBound)) { InsertAt(ep, conflict.Position + 1); return(true); } return(false); } case ScheduleConflictType.Prior: { // The given position is the element we need to go before Time earlyTimeBound = Time.StartOfDay + FirstTripTime; Time lateTimeBound = Time.EndOfDay; Episode prior = (Episode)(conflict.Position > 0 ? Episodes[conflict.Position - 1] : null); Episode middle = ep; Episode post = (Episode)Episodes[conflict.Position]; // Make sure to bound the times with the padding of the travel times required if (conflict.Position >= 2) { earlyTimeBound = Episodes[conflict.Position - 2].EndTime + Scheduler.TravelTime(Owner, Episodes[conflict.Position - 2].Zone, prior.Zone, prior.StartTime); } if (EpisodeCount - conflict.Position > 1) { lateTimeBound = Episodes[conflict.Position + 1].StartTime - Scheduler.TravelTime(Owner, post.Zone, Episodes[conflict.Position + 1].Zone, post.EndTime); } if (Insert(earlyTimeBound, prior, middle, post, lateTimeBound)) { InsertAt(ep, conflict.Position); return(true); } return(false); } default: throw new NotImplementedException("Unknown insert conflict type!"); } }
private static void StoreEpisode(Episode e, StringBuilder builder) { builder.Append( "Activity -> " ); builder.Append( e.ActivityType.ToString() ); builder.Append( ", Start -> " ); builder.Append( e.StartTime.ToString() ); builder.Append( ", End -> " ); builder.Append( e.EndTime.ToString() ); builder.Append( ", TT -> " ); builder.Append( e.TravelTime.ToString() ); }
private static bool MiddlePostInsert(ref Time earlyTimeBound, Episode middle, Episode post, ref Time lateTimeBound, ref Time firstTime, ref Time secondTime) { Time overlap = (middle.EndTime + secondTime) - post.StartTime; if (overlap <= Time.Zero) { return(true); } // if we can move forward, move forward if ((middle.StartTime - firstTime) - earlyTimeBound > overlap) { Relocate(middle, (middle.StartTime - overlap)); return(true); } // if that is not enough, move as forward as we can Relocate(middle, (earlyTimeBound + firstTime)); overlap = (middle.EndTime + secondTime) - post.StartTime; Relocate(post, (post.StartTime + overlap)); return(true); }
public override bool Insert(Episode ep, Random random) { IZone zone = null; ep.ContainingSchedule = this; // if we are generating locations now is the time to do it if(random != null) { zone = Tasha.Scheduler.Scheduler.LocationChoiceModel.GetLocation(ep, random); // if we can not find a zone, then this episode is infeasible if(zone == null) { return false; } } return Insert(ep, zone); }
private static bool PriorMiddleInsert(ref Time earlyTimeBound, Episode prior, Episode middle, ref Time lateTimeBound, ref Time firstTime, ref Time secondTime) { Time overlap = (prior.EndTime + firstTime) - middle.StartTime; if (overlap <= Time.Zero) { return(true); } // if we can move back, move back if (lateTimeBound - (middle.EndTime + secondTime) > overlap) { Relocate(middle, (prior.EndTime + firstTime)); return(true); } // move back as far as we can Relocate(middle, (lateTimeBound - middle.Duration - secondTime)); // recalculate the overlap overlap = (prior.EndTime + firstTime) - middle.StartTime; Relocate(prior, prior.StartTime - overlap); return(true); }
private static bool FillInGaps(Episode middle, ref Time priorOverlap, ref Time postOverlap) { if(priorOverlap <= Time.Zero) { // check to see if there is enough time to move the middle closer to the prior if(postOverlap <= -priorOverlap) { Relocate(middle, (middle.StartTime - postOverlap)); return true; } else { // prior overlap < 0, so just add it Relocate(middle, (middle.StartTime + priorOverlap)); // subtract out the reduced time postOverlap += priorOverlap; } } if(postOverlap <= Time.Zero) { // check to see if there is enough time to move the middle closer to the prior if(priorOverlap <= -postOverlap) { Relocate(middle, (middle.StartTime + priorOverlap)); return true; } else { // prior overlap < 0, so subtract it Relocate(middle, (middle.StartTime - postOverlap)); // subtract out the reduced time priorOverlap += postOverlap; } } return false; }
private static bool PriorMiddleInsert(ref Time earlyTimeBound, Episode prior, Episode middle, ref Time lateTimeBound, ref Time firstTime, ref Time secondTime) { Time overlap = (prior.EndTime + firstTime) - middle.StartTime; if(overlap <= Time.Zero) { return true; } // if we can move back, move back if(lateTimeBound - (middle.EndTime + secondTime) > overlap) { Relocate(middle, (prior.EndTime + firstTime)); return true; } // move back as far as we can Relocate(middle, (lateTimeBound - middle.Duration - secondTime)); // recalculate the overlap overlap = (prior.EndTime + firstTime) - middle.StartTime; Relocate(prior, prior.StartTime - overlap); return true; }
private bool AllThreeInsert(ref Time earlyTimeBound, Episode prior, Episode middle, Episode post, ref Time lateTimeBound, ref Time firstTime, ref Time secondTime) { Time priorOverlap = (prior.EndTime + firstTime) - middle.StartTime; Time postOverlap = (middle.EndTime + secondTime) - post.StartTime; Time frontGap = prior.StartTime - earlyTimeBound; Time backGap = lateTimeBound - (post.EndTime + secondTime); // see if we can just fill in the gaps if (FillInGaps(middle, ref priorOverlap, ref postOverlap)) { return(true); } // push the episodes away from the middle with a proportion to what they overlap it with if (priorOverlap < Time.Zero) { priorOverlap = Time.Zero; } if (postOverlap < Time.Zero) { postOverlap = Time.Zero; } // make sure that there is actually an overlap var overlap = priorOverlap + postOverlap; if (overlap == Time.Zero) { return(true); } overlap = Time.Zero; var newPriorStartTime = prior.StartTime - priorOverlap; bool priorFailed = false; if (newPriorStartTime < earlyTimeBound) { overlap = earlyTimeBound - newPriorStartTime; newPriorStartTime = earlyTimeBound; priorFailed = true; } var newPostStartTime = post.StartTime + postOverlap; if (newPostStartTime + post.Duration > lateTimeBound) { overlap += (newPostStartTime + post.Duration) - lateTimeBound; newPostStartTime = lateTimeBound - post.Duration; } Relocate(prior, newPriorStartTime); Relocate(post, newPostStartTime); if (overlap == Time.Zero) { return(true); } if (priorFailed) { Relocate(middle, middle.StartTime + overlap); Relocate(post, middle.EndTime + secondTime); } else { Relocate(middle, middle.StartTime - overlap); Relocate(prior, middle.StartTime - firstTime - prior.Duration); } // Sanity Check if (post.EndTime > lateTimeBound) { throw new XTMFRuntimeException("We ended too late when inserting with 3 into a person schedule!\r\n" + Dump(this)); } else if (prior.StartTime < earlyTimeBound) { throw new XTMFRuntimeException("We started too early when inserting with 3 into a person schedule!\r\n" + Dump(this) + "\r\nFirst Time = " + firstTime.ToString() + "\r\nSecond Time = " + secondTime.ToString()); } return(true); }
private bool AllThreeInsert(ref Time earlyTimeBound, Episode prior, Episode middle, Episode post, ref Time lateTimeBound, ref Time firstTime, ref Time secondTime) { Time priorOverlap = (prior.EndTime + firstTime) - middle.StartTime; Time postOverlap = (middle.EndTime + secondTime) - post.StartTime; Time frontGap = prior.StartTime - earlyTimeBound; Time backGap = lateTimeBound - (post.EndTime + secondTime); // see if we can just fill in the gaps if(FillInGaps(middle, ref priorOverlap, ref postOverlap)) { return true; } // push the episodes away from the middle with a proportion to what they overlap it with if(priorOverlap < Time.Zero) priorOverlap = Time.Zero; if(postOverlap < Time.Zero) postOverlap = Time.Zero; // make sure that there is actually an overlap var overlap = priorOverlap + postOverlap; if(overlap == Time.Zero) return true; overlap = Time.Zero; var newPriorStartTime = prior.StartTime - priorOverlap; bool priorFailed = false; if(newPriorStartTime < earlyTimeBound) { overlap = earlyTimeBound - newPriorStartTime; newPriorStartTime = earlyTimeBound; priorFailed = true; } var newPostStartTime = post.StartTime + postOverlap; if(newPostStartTime + post.Duration > lateTimeBound) { overlap += (newPostStartTime + post.Duration) - lateTimeBound; newPostStartTime = lateTimeBound - post.Duration; } Relocate(prior, newPriorStartTime); Relocate(post, newPostStartTime); if(overlap == Time.Zero) return true; if(priorFailed) { Relocate(middle, middle.StartTime + overlap); Relocate(post, middle.EndTime + secondTime); } else { Relocate(middle, middle.StartTime - overlap); Relocate(prior, middle.StartTime - firstTime - prior.Duration); } // Sanity Check if(post.EndTime > lateTimeBound) { throw new XTMFRuntimeException("We ended too late when inserting with 3 into a person schedule!\r\n" + Dump(this)); } else if(prior.StartTime < earlyTimeBound) { throw new XTMFRuntimeException("We started too early when inserting with 3 into a person schedule!\r\n" + Dump(this) + "\r\nFirst Time = " + firstTime.ToString() + "\r\nSecond Time = " + secondTime.ToString()); } return true; }
private void FixDurationToInsert(ref Time earlyTimeBound, Episode prior, Episode middle, Episode post, ref Time lateTimeBound, ref Time firstTime, ref Time secondTime) { var priorDuration = Time.Zero; var middleDuration = middle.Duration; var postDuration = Time.Zero; // If we get here then we need to calculate the ratios and then assign the start times accordingly if(prior != null) { var duration = prior.Duration; priorDuration = prior.Duration; prior.StartTime = earlyTimeBound; prior.EndTime = earlyTimeBound + duration; } if(post != null) { var duration = post.Duration; postDuration = post.Duration; post.EndTime = lateTimeBound; post.StartTime = lateTimeBound - duration; } Time totalOriginalDuration = (prior != null ? prior.OriginalDuration : Time.Zero) + middle.OriginalDuration + (post != null ? post.OriginalDuration : Time.Zero); Time minPrior = (prior != null ? Tasha.Scheduler.Scheduler.PercentOverlapAllowed * prior.OriginalDuration : Time.Zero); Time minMid = Tasha.Scheduler.Scheduler.PercentOverlapAllowed * middle.OriginalDuration; Time minPost = (post != null ? Tasha.Scheduler.Scheduler.PercentOverlapAllowed * post.OriginalDuration : Time.Zero); Time remainder = (lateTimeBound - earlyTimeBound) - (minPrior + minMid + minPost + firstTime + secondTime); float ratioPrior = 0; float ratioMiddle = 0; if(prior != null) { ratioPrior = prior.OriginalDuration / totalOriginalDuration; prior.StartTime = earlyTimeBound; prior.EndTime = prior.StartTime + minPrior + (ratioPrior * remainder); middle.StartTime = prior.EndTime + firstTime; } else { middle.StartTime = earlyTimeBound + firstTime; } ratioMiddle = middle.OriginalDuration / totalOriginalDuration; middle.EndTime = middle.StartTime + minMid + (ratioMiddle * remainder); if(post != null) { // we do not need to include the ratio calculation for post since we know it needs to end at the end of the allowed time // and that it needs to start right after the middle var ratioPost = post.OriginalDuration / totalOriginalDuration; post.StartTime = middle.EndTime + secondTime; post.EndTime = lateTimeBound; } }
private void FixDurationToInsert(ref Time earlyTimeBound, Episode prior, Episode middle, Episode post, ref Time lateTimeBound, ref Time firstTime, ref Time secondTime) { var priorDuration = Time.Zero; var middleDuration = middle.Duration; var postDuration = Time.Zero; // If we get here then we need to calculate the ratios and then assign the start times accordingly if (prior != null) { var duration = prior.Duration; priorDuration = prior.Duration; prior.StartTime = earlyTimeBound; prior.EndTime = earlyTimeBound + duration; } if (post != null) { var duration = post.Duration; postDuration = post.Duration; post.EndTime = lateTimeBound; post.StartTime = lateTimeBound - duration; } Time totalOriginalDuration = (prior != null ? prior.OriginalDuration : Time.Zero) + middle.OriginalDuration + (post != null ? post.OriginalDuration : Time.Zero); Time minPrior = (prior != null ? Tasha.Scheduler.Scheduler.PercentOverlapAllowed * prior.OriginalDuration : Time.Zero); Time minMid = Tasha.Scheduler.Scheduler.PercentOverlapAllowed * middle.OriginalDuration; Time minPost = (post != null ? Tasha.Scheduler.Scheduler.PercentOverlapAllowed * post.OriginalDuration : Time.Zero); Time remainder = (lateTimeBound - earlyTimeBound) - (minPrior + minMid + minPost + firstTime + secondTime); float ratioPrior = 0; float ratioMiddle = 0; if (prior != null) { ratioPrior = prior.OriginalDuration / totalOriginalDuration; prior.StartTime = earlyTimeBound; prior.EndTime = prior.StartTime + minPrior + (ratioPrior * remainder); middle.StartTime = prior.EndTime + firstTime; } else { middle.StartTime = earlyTimeBound + firstTime; } ratioMiddle = middle.OriginalDuration / totalOriginalDuration; middle.EndTime = middle.StartTime + minMid + (ratioMiddle * remainder); if (post != null) { // we do not need to include the ratio calculation for post since we know it needs to end at the end of the allowed time // and that it needs to start right after the middle var ratioPost = post.OriginalDuration / totalOriginalDuration; post.StartTime = middle.EndTime + secondTime; post.EndTime = lateTimeBound; } }
private bool Insert(Time earlyTimeBound, Episode prior, Episode middle, Episode post, Time lateTimeBound) { if(earlyTimeBound < Time.StartOfDay) { throw new XTMFRuntimeException("An episode had an early time bound before the start of the day!"); } if(lateTimeBound > Time.EndOfDay) { throw new XTMFRuntimeException("An episode had a late time before after the end of the day!"); } Time firstTime = Time.Zero; Time secondTime = Time.Zero; var priorDuration = Time.Zero; var middleDuration = middle.OriginalDuration; var postDuration = Time.Zero; // Do a quick check to see if it is even possible to fit everything in together if(!InitialInsertCheckPossible(earlyTimeBound, prior, middle, post, lateTimeBound, ref firstTime, ref secondTime)) { return false; } if(firstTime < Time.Zero) { throw new XTMFRuntimeException("The First time in an insert is negative! " + firstTime.ToString() + ". Going from " + prior.Zone.ZoneNumber.ToString() + " to " + middle.Zone.ZoneNumber.ToString() + " at " + middle.StartTime.ToString() + "!"); } if(secondTime < Time.Zero) { throw new XTMFRuntimeException("The Second time in an insert is negative! " + secondTime.ToString() + ". Going from " + middle.Zone.ZoneNumber.ToString() + " to " + (post == null ? Owner.Household.HomeZone.ZoneNumber : post.Zone.ZoneNumber).ToString() + " at " + (post == null ? middle.EndTime : post.StartTime).ToString() + "!"); } // Assign the travel times to the episodes if(prior == null) { FirstTripTime = firstTime; } else { prior.TravelTime = firstTime; priorDuration = prior.OriginalDuration; } if(post != null) { postDuration = post.OriginalDuration; } middle.TravelTime = secondTime; // if we get here we know that there is a way to insert the episodes successfully if(UnableToJustMoveToInsert(earlyTimeBound, prior, middle, post, lateTimeBound, ref firstTime, ref secondTime)) { FixDurationToInsert(ref earlyTimeBound, prior, middle, post, ref lateTimeBound, ref firstTime, ref secondTime); } else { var ret = ShiftToInsert(ref earlyTimeBound, prior, middle, post, ref lateTimeBound, ref firstTime, ref secondTime); return ret; } return true; }
/// <summary> /// Run a quick check to see if it is at all possible to place all of the 3 episodes in the given time bounds /// </summary> /// <param name="earlyTimeBound">The earliest point</param> /// <param name="prior">the first episode in the batch (possibly null if there is no episode prior)</param> /// <param name="middle">the second episode in the batch</param> /// <param name="post">the last episode in the batch (possibly null if there is no episode post)</param> /// <param name="lateTimeBound">the latest point</param> /// <returns>If we can fit them all in properly</returns> private bool InitialInsertCheckPossible(Time earlyTimeBound, Episode prior, Episode middle, Episode post, Time lateTimeBound, ref Time travelFirst, ref Time travelSecond ) { Time minPrior = Time.Zero; Time minMid = Tasha.Scheduler.Scheduler.PercentOverlapAllowed * middle.OriginalDuration; Time minPost = Time.Zero; if (prior != null) { minPrior = Tasha.Scheduler.Scheduler.PercentOverlapAllowed * prior.OriginalDuration; travelFirst = Scheduler.TravelTime(Owner, prior.Zone, middle.Zone, prior.EndTime); } else { travelFirst = Scheduler.TravelTime(Owner, Owner.Household.HomeZone, middle.Zone, middle.StartTime); } if (post != null) { minPost = Tasha.Scheduler.Scheduler.PercentOverlapAllowed * post.OriginalDuration; travelSecond = Scheduler.TravelTime(Owner, middle.Zone, post.Zone, middle.EndTime); } else { travelSecond = Scheduler.TravelTime(Owner, middle.Zone, Owner.Household.HomeZone, middle.EndTime); } // it is possible to fit in if the bounds are larger than the minimum size of all three episodes return((lateTimeBound - earlyTimeBound) >= (minPrior + minMid + minPost + travelFirst + travelSecond)); }
private bool UnableToJustMoveToInsert(Time earlyTimeBound, Episode prior, Episode middle, Episode post, Time lateTimeBound, ref Time travelFirst, ref Time travelSecond) { // if the amount of time that we have to work with is larger then we are unable to just move to insert var totalTime = (prior != null ? prior.Duration : Time.Zero) + middle.Duration + (post != null ? post.Duration : Time.Zero) + travelFirst + travelSecond; var boundTime = (lateTimeBound - earlyTimeBound); return boundTime <= totalTime; }
public override bool ForcedEpisodeInsert(Episode ep) { throw new NotImplementedException(); }
private bool ShiftToInsert(ref Time earlyTimeBound, Episode prior, Episode middle, Episode post, ref Time lateTimeBound, ref Time firstTime, ref Time secondTime) { if (prior != null & post != null) { return(AllThreeInsert(ref earlyTimeBound, prior, middle, post, ref lateTimeBound, ref firstTime, ref secondTime)); } else if (prior != null) { return(PriorMiddleInsert(ref earlyTimeBound, prior, middle, ref lateTimeBound, ref firstTime, ref secondTime)); } else if (post != null) { return(MiddlePostInsert(ref earlyTimeBound, middle, post, ref lateTimeBound, ref firstTime, ref secondTime)); } throw new XTMFRuntimeException("Unexpected shift to insert case!"); }
private static bool AddHouseholdPass(Episode ep, ref TimeWindow feasible) { // Check to find the time that everyone is available at foreach ( var person in ep.People ) { // if we are make sure we can go to the event var pdata = ( person["SData"] as SchedulerPersonData ); if ( !pdata.Schedule.CheckEpisodeInsert( ep, ref feasible ) ) { // if we can not fit it in reject it return false; } } return true; }