/// <summary> /// Get an estimate of the future state based on the current state, redirector's action and the path to be followed. /// ATTENTION: This method is only used for simulation(planning)! A user's actual state is only updated by the tracking system. /// </summary> /// <param name="s"></param> the current state of the environment. /// <param name="a"></param> action to be taken by the redirector. /// <param name="seg"></param> the path that the user is walking on in the current stage. public RedirectionManager.State ApplyStateUpdate(RedirectionManager.State state, Action a, Segment seg) { RedirectionManager.State newState = new RedirectionManager.State(); if (seg is LineSegment) { LineSegment segment = (LineSegment)seg; Vector2 tangentDir = (segment.endPos - Utilities.FlattenedPos2D(state.pos)).normalized; Vector2 delta_p = this.redirectionManager.speedReal * this.stageDuration * tangentDir; // update virtual position and direction newState.pos = state.pos + Utilities.UnFlatten(delta_p); newState.dir = state.dir; // update the real world state according to the action if (a.type == ActionType.CURVATURE) { float s = delta_p.magnitude; //Debug.Log("s=" + s); float kr = a.gain; // the curvature gain rou(c) float ori_0 = Vector2.SignedAngle(Vector2.right, Utilities.FlattenedDir2D(state.dirReal)) * Mathf.Deg2Rad; //Debug.Log("angle:"+ Vector2.SignedAngle(Vector2.right, Utilities.FlattenedDir2D(state.dirReal))+" ori_0:" + ori_0); // The new real position depends on user's real orientation newState.posReal.x = (Mathf.Sin(ori_0 + kr * s) - Mathf.Sin(ori_0)) / kr + state.posReal.x; newState.posReal.z = (Mathf.Cos(ori_0) - Mathf.Cos(ori_0 + kr * s)) / kr + state.posReal.z; newState.dirReal = Utilities.UnFlatten(Utilities.RotateVector(Utilities.FlattenedDir2D(state.dirReal), s * kr * Mathf.Rad2Deg)); //Debug.Log("ppp " + newState.posReal); //Debug.Log("ddd " + newState.dirReal); } else if (a.type == ActionType.ZERO) { newState.posReal = state.posReal + state.dirReal * this.redirectionManager.speedReal * this.stageDuration; newState.dirReal = state.dirReal; } else if (a.type == ActionType.RESET) { newState.posReal = state.posReal - state.dirReal * this.redirectionManager.speedReal * this.stageDuration; newState.dirReal = -state.dirReal; } } else if (seg is ArcSegment) { ArcSegment segment = (ArcSegment)seg; // update virtual position and direction float s = this.redirectionManager.speedReal * this.stageDuration; float ori_v0 = Vector2.SignedAngle(Vector2.right, Utilities.FlattenedDir2D(state.dir)) * Mathf.Deg2Rad; newState.pos.x = (Mathf.Sin(ori_v0 + s / segment.radius) - Mathf.Sin(ori_v0)) * segment.radius + state.pos.x; newState.pos.z = (Mathf.Cos(ori_v0) - Mathf.Cos(ori_v0 + s / segment.radius)) / segment.radius + state.pos.z; newState.dir = Utilities.UnFlatten(Utilities.RotateVector(state.dir, s / segment.radius)); // update the real world state according to the action float ori_r0 = Vector2.SignedAngle(Vector2.right, Utilities.FlattenedDir2D(state.dirReal)) * Mathf.Deg2Rad; if (a.type == ActionType.CURVATURE) { float kr = 1 / segment.radius + a.gain; // the compound curvature gain 1/r + rou(c) // The new real position depends on user's real orientation newState.posReal.x = (Mathf.Sin(ori_r0 + kr * s) - Mathf.Sin(ori_r0)) / kr + state.posReal.x; newState.posReal.z = (Mathf.Cos(ori_r0) - Mathf.Cos(ori_r0 + kr * s)) / kr + state.posReal.z; newState.dirReal = Utilities.UnFlatten(Utilities.RotateVector(state.dirReal, s * kr)); } else if (a.type == ActionType.ZERO) { newState.posReal.x = (Mathf.Sin(ori_r0 + s / segment.radius) - Mathf.Sin(ori_r0)) * segment.radius + state.posReal.x; newState.posReal.z = (Mathf.Cos(ori_r0) - Mathf.Cos(ori_r0 + s / segment.radius)) / segment.radius + state.posReal.z; newState.dirReal = Utilities.UnFlatten(Utilities.RotateVector(state.dirReal, s / segment.radius)); } else if (a.type == ActionType.RESET) { ori_r0 = Vector2.SignedAngle(Vector2.right, Utilities.FlattenedDir2D(-state.dirReal)) * Mathf.Deg2Rad; newState.posReal.x = (Mathf.Sin(ori_r0 + s / segment.radius) - Mathf.Sin(ori_r0)) * segment.radius + state.posReal.x; newState.posReal.z = (Mathf.Cos(ori_r0) - Mathf.Cos(ori_r0 + s / segment.radius)) / segment.radius + state.posReal.z; newState.dirReal = Utilities.UnFlatten(Utilities.RotateVector(-state.dirReal, s / segment.radius)); } } else if (seg is RotationSegment) { RotationSegment segment = (RotationSegment)seg; // update virtual position and direction newState.pos = state.pos; float rotatedAngle = this.redirectionManager.angularSpeedReal * this.stageDuration * Mathf.Sign(segment.angle); newState.dir = Utilities.RotateVector(state.dir, rotatedAngle); if (a.type == ActionType.ROTATION) { newState.posReal = state.posReal; rotatedAngle = rotatedAngle * a.gain; newState.dirReal = Utilities.RotateVector(state.dirReal, rotatedAngle); } else if (a.type == ActionType.ZERO) { newState.posReal = state.posReal; newState.dirReal = Utilities.RotateVector(state.dirReal, rotatedAngle); } } return(newState); }
public void TestGenericShift() { // regular work week with holidays and breaks schedule = new WorkSchedule("Regular 40 hour work week", "9 to 5"); // holidays NonWorkingPeriod memorialDay = schedule.CreateNonWorkingPeriod("MEMORIAL DAY", "Memorial day", new LocalDateTime(2016, 5, 30, 0, 0, 0), Duration.FromHours(24)); schedule.CreateNonWorkingPeriod("INDEPENDENCE DAY", "Independence day", new LocalDateTime(2016, 7, 4, 0, 0, 0), Duration.FromHours(24)); schedule.CreateNonWorkingPeriod("LABOR DAY", "Labor day", new LocalDateTime(2016, 9, 5, 0, 0, 0), Duration.FromHours(24)); schedule.CreateNonWorkingPeriod("THANKSGIVING", "Thanksgiving day and day after", new LocalDateTime(2016, 11, 24, 0, 0, 0), Duration.FromHours(48)); schedule.CreateNonWorkingPeriod("CHRISTMAS SHUTDOWN", "Christmas week scheduled maintenance", new LocalDateTime(2016, 12, 25, 0, 30, 0), Duration.FromHours(168)); // each shift duration Duration shiftDuration = Duration.FromHours(8); LocalTime shift1Start = new LocalTime(7, 0, 0); LocalTime shift2Start = new LocalTime(15, 0, 0); // shift 1 Shift shift1 = schedule.CreateShift("Shift1", "Shift #1", shift1Start, shiftDuration); // breaks shift1.CreateBreak("10AM", "10 am break", new LocalTime(10, 0, 0), Duration.FromMinutes(15)); shift1.CreateBreak("LUNCH", "lunch", new LocalTime(12, 0, 0), Duration.FromHours(1)); shift1.CreateBreak("2PM", "2 pm break", new LocalTime(14, 0, 0), Duration.FromMinutes(15)); // shift 2 Shift shift2 = schedule.CreateShift("Shift2", "Shift #2", shift2Start, shiftDuration); // shift 1, 5 days ON, 2 OFF Rotation rotation1 = new Rotation("Shift1", "Shift1"); rotation1.AddSegment(shift1, 5, 2); // shift 2, 5 days ON, 2 OFF Rotation rotation2 = new Rotation("Shift2", "Shift2"); rotation2.AddSegment(shift2, 5, 2); LocalDate startRotation = new LocalDate(2016, 1, 1); Team team1 = schedule.CreateTeam("Team1", "Team #1", rotation1, startRotation); Team team2 = schedule.CreateTeam("Team2", "Team #2", rotation2, startRotation); // same day LocalDateTime from = startRotation.PlusDays(7).At(shift1Start); LocalDateTime to; Duration totalWorking = Duration.Zero; // 21 days, team1 Duration d = Duration.Zero; for (int i = 0; i < 21; i++) { to = from.PlusDays(i); totalWorking = team1.CalculateWorkingTime(from, to); int dir = team1.GetDayInRotation(to.Date); Assert.IsTrue(totalWorking.Equals(d)); TimePeriod period = rotation1.GetPeriods()[dir - 1]; if (period is Shift) { d = d.Plus(shiftDuration); } } Duration totalSchedule = totalWorking; // 21 days, team2 from = startRotation.PlusDays(7).At(shift2Start); d = Duration.Zero; for (int i = 0; i < 21; i++) { to = from.PlusDays(i); totalWorking = team2.CalculateWorkingTime(from, to); int dir = team2.GetDayInRotation(to.Date); Assert.IsTrue(totalWorking.Equals(d)); if (rotation1.GetPeriods()[dir - 1] is Shift) { d = d.Plus(shiftDuration); } } totalSchedule = totalSchedule.Plus(totalWorking); Duration scheduleDuration = schedule.CalculateWorkingTime(from, from.PlusDays(21)); Duration nonWorkingDuration = schedule.CalculateNonWorkingTime(from, from.PlusDays(21)); Assert.IsTrue(scheduleDuration.Plus(nonWorkingDuration).Equals(totalSchedule)); // breaks Duration allBreaks = Duration.FromMinutes(90); Assert.IsTrue(shift1.CalculateBreakTime().Equals(allBreaks)); // misc WorkSchedule schedule2 = new WorkSchedule(); Shift shift3 = new Shift(); shift3.Name = "Shift3"; Assert.IsTrue(shift3.WorkSchedule == null); Assert.IsTrue(shift3.CompareTo(shift3) == 0); Team team3 = new Team(); Assert.IsTrue(team3.WorkSchedule == null); RotationSegment segment = new RotationSegment(); segment.Sequence = 1; segment.StartingShift = shift2; segment.DaysOn = 5; segment.DaysOff = 2; Assert.IsTrue(segment.Rotation == null); Rotation rotation3 = new Rotation(); rotation3.Name = "Rotation3"; Assert.IsTrue(rotation3.CompareTo(rotation3) == 0); Assert.IsTrue(rotation3.RotationSegments.Count == 0); NonWorkingPeriod nwp = new NonWorkingPeriod(); Assert.IsTrue(nwp.WorkSchedule == null); Assert.IsTrue(team1.WorkSchedule.Equals(schedule)); Assert.IsTrue(!team1.IsDayOff(startRotation)); Assert.IsTrue(team1.CompareTo(team1) == 0); team3.Rotation = rotation1; Assert.IsTrue(!memorialDay.IsInPeriod(new LocalDate(2016, 1, 1))); runBaseTest(schedule, Duration.FromHours(40), Duration.FromDays(7), new LocalDate(2016, 1, 1)); }
public RotationCommand(Orientation rotationAxis, RotationSegment segment, int turnovers = 1) { RotationAxis = rotationAxis; Segment = segment; Turnovers = turnovers % 4; }