diff --git a/RouteManager.UMM/info.json b/RouteManager.UMM/info.json index cdc34cc..c0a823e 100644 --- a/RouteManager.UMM/info.json +++ b/RouteManager.UMM/info.json @@ -1,6 +1,6 @@ { "Id": "RouteManager", - "Version": "2.1.0.0", + "Version": "2.1.0.1", "DisplayName": "Dispatcher", "Author": "Erabior", "AssemblyName": "RouteManager.UMM.dll", diff --git a/RouteManager/Properties/AssemblyInfo.cs b/RouteManager/Properties/AssemblyInfo.cs index 24a1e4e..8951ea5 100644 --- a/RouteManager/Properties/AssemblyInfo.cs +++ b/RouteManager/Properties/AssemblyInfo.cs @@ -38,6 +38,6 @@ internal class AppVersion { - public const string Version = "2.1.0.0"; + public const string Version = "2.1.0.1"; } diff --git a/RouteManager/RouteManager.csproj b/RouteManager/RouteManager.csproj index 0b7f53f..62c7350 100644 --- a/RouteManager/RouteManager.csproj +++ b/RouteManager/RouteManager.csproj @@ -118,10 +118,12 @@ + + diff --git a/RouteManager/v2/core/AutoEngineer.cs b/RouteManager/v2/core/AutoEngineer.cs index 1b309a3..f5a8781 100644 --- a/RouteManager/v2/core/AutoEngineer.cs +++ b/RouteManager/v2/core/AutoEngineer.cs @@ -10,10 +10,8 @@ using System.Linq; using UnityEngine; using RouteManager.v2.Logging; -using Network; using RollingStock; using Track; -using static Game.Reputation.PassengerReputationCalculator; namespace RouteManager.v2.core { @@ -582,8 +580,6 @@ private static void onArrival(Car locomotive) RouteManager.logger.LogToDebug("EXITING FUNCTION: onArrival", LogLevel.Trace); } - - //Check to see if passengers are unloaded private static bool wasCurrentStopServed(Car locomotive) { @@ -701,7 +697,6 @@ private static bool passStillWaiting(Car locomotive, PassengerMarker? marker) } - //Initial checks to determine if we can continue with the coroutine private bool needToExitCoroutine(Car locomotive) { @@ -807,8 +802,6 @@ private static bool GetDirection(Car locomotive, PassengerStop stop) ************************************************************************************************************/ - - public IEnumerator AutoEngineerControlRoutine_dev(Car locomotive) { //Trace Function @@ -824,34 +817,76 @@ public IEnumerator AutoEngineerControlRoutine_dev(Car locomotive) RouteManager.logger.LogToDebug(String.Format("Loco: {0} \t has ID: {1}", locomotive.DisplayName, locomotive.id), LogLevel.Debug); //Set some initial values - LocoTelem.closestStation[locomotive] = StationManager.GetClosestStation_dev(locomotive); //closest station will only return a station marked as a stop - LocoTelem.currentDestination[locomotive] = StationManager.getNextStation_dev(locomotive);//LocoTelem.closestStation[locomotive].Item1; - //StationManager.getInitialDestination(locomotive); + //closest station will only return a station marked as a stop + LocoTelem.closestStation[locomotive] = StationManager.GetClosestStation_dev(locomotive); + LocoTelem.currentDestination[locomotive] = StationManager.getNextStation_dev(locomotive); //set locomotive drive direction LocoTelem.locoTravelingForward[locomotive] = GetDirection(locomotive, LocoTelem.currentDestination[locomotive]); + //get route info - what are the switches on our route and what state do they need to be in? + List switchRequirements; + + + + //Dev/testing only to be replaced once logic is working + PassengerStop alarka = PassengerStop.FindAll().Where(stop => stop.identifier == "alarka").First(); + //End dev/testing code + + + //get all switches on our route from current location to end of line/end of branch line + if (DestinationManager.GetRouteSwitches(locomotive.LocationF, (Track.Location)alarka.TrackSpans.First().lower, out switchRequirements)) + { + //we have the total route to end of line/branch + // Check if the next station has multiple platforms and find the last common switch - this is used later if we find ourselves against a switch + DestinationManager.PlanNextRoute(locomotive.LocationF, LocoTelem.currentDestination[locomotive], ref switchRequirements); + } + else + { + //oh-oh we're flying blind! + } + + LocoTelem.routeSwitchRequirements[locomotive] = switchRequirements; + LocoTelem.closestStationNeedsUpdated[locomotive] = false; LocoTelem.CenterCar[locomotive] = TrainManager.GetCenterCoach(locomotive); //set initial passenger loading TrainManager.CopyStationsFromLocoToCoaches_dev(locomotive); + RouteManager.logger.LogToDebug($"Copy complete", LogLevel.Debug); + + RouteManager.logger.LogToDebug($"Closest station: {LocoTelem.closestStation.ContainsKey(locomotive)} Center Car: {LocoTelem.CenterCar.ContainsKey(locomotive)}", LogLevel.Debug); + //Give time for passenger loading/unloading if already at the station if (Vector3.Distance(LocoTelem.closestStation[locomotive].Item1.CenterPoint, LocoTelem.CenterCar[locomotive].GetCenterPosition(Graph.Shared)) <= 15f) //StationManager.isTrainInStation(LocoTelem.CenterCar[locomotive])) { + RouteManager.logger.LogToDebug($"We're close", LogLevel.Debug); while (!wasCurrentStopServed_dev(locomotive)) { yield return new WaitForSeconds(1); } - if (RouteManager.Settings.showDepartureMessage) - RouteManager.logger.LogToConsole(String.Format("{0} has departed for {1}", Hyperlink.To(locomotive), LocoTelem.currentDestination[locomotive].DisplayName.ToUpper())); - - LocoTelem.clearedForDeparture[locomotive] = true; + RouteManager.logger.LogToDebug($"Stop Served", LogLevel.Debug); } + + if (RouteManager.Settings.showDepartureMessage) + RouteManager.logger.LogToConsole(String.Format("{0} has departed for {1}", Hyperlink.To(locomotive), LocoTelem.currentDestination[locomotive].DisplayName.ToUpper())); + + RouteManager.logger.LogToDebug($"Clearing", LogLevel.Debug); + LocoTelem.clearedForDeparture[locomotive] = true; + RouteManager.logger.LogToDebug($"Cleared", LogLevel.Debug); + + + + /************************************************** + * + * Initialisation complete, start main routines + * + ***************************************************/ + //Feature Ehancement #30 LocoTelem.initialSpeedSliderSet[locomotive] = false; @@ -887,6 +922,7 @@ public IEnumerator AutoEngineerControlRoutine_dev(Car locomotive) yield break; } + //Locomotive Enroute to Destination public IEnumerator locomotiveTransitControl_dev(Car locomotive) { @@ -906,10 +942,16 @@ public IEnumerator locomotiveTransitControl_dev(Car locomotive) //TEMP LOGIC float distanceToStation = float.MaxValue; bool delayExecution = false; - //float olddist = float.MaxValue; - float trainVelocity = 0; + float trainVelocity; int stationPadding = 10; + //get initial data + RouteSwitchData nextSwitch; + float distanceToSwitch = float.MinValue; + bool stopForSwitch = false; + bool checkNextSwitch = true; + int nextSwitchIndex; + //Loop through transit logic while (LocoTelem.TransitMode[locomotive]) @@ -925,6 +967,137 @@ public IEnumerator locomotiveTransitControl_dev(Car locomotive) yield return null; } + /* + * Check all switches that are up to 400 metres away + */ + + //get the first switch in the list + nextSwitch = LocoTelem.routeSwitchRequirements[locomotive].FirstOrDefault(); + nextSwitchIndex = 0; + + if (nextSwitch != null) + { + distanceToSwitch = DestinationManager.GetDistanceToSwitch(locomotive, nextSwitch); + checkNextSwitch = true; + } + + while (checkNextSwitch) + { + //remove switche if it's beneath/behind us + if(distanceToSwitch <= 0 && nextSwitch != null) + { + //remove the switch + RouteManager.logger.LogToDebug($"Removing switch: {nextSwitch.trackSwitch.id}, Distance: {distanceToSwitch}", LogLevel.Debug); + LocoTelem.routeSwitchRequirements[locomotive].Remove(nextSwitch); + + //find the next switch and calculate the distance + nextSwitch = LocoTelem.routeSwitchRequirements[locomotive].FirstOrDefault(); + nextSwitchIndex = 0; + + //no more switches + if (nextSwitch == null) + break; + } + else + { + break; + } + + //check our next switch distance + distanceToSwitch = DestinationManager.GetDistanceToSwitch(locomotive, nextSwitch); + RouteManager.logger.LogToDebug($"Approaching: {nextSwitch.trackSwitch.id}, Distance: {distanceToSwitch}", LogLevel.Debug); + + if (distanceToSwitch <= 400) + { + //Check switch state vs requirements: Need normal and is reverse || need reverse and is normal + if (nextSwitch.requiredStateNormal && nextSwitch.trackSwitch.isThrown || + !nextSwitch.requiredStateNormal && !nextSwitch.trackSwitch.isThrown) + { + RouteManager.logger.LogToDebug($"Switch {nextSwitch.trackSwitch.id} state incorrect req normal: {nextSwitch.requiredStateNormal}, is reversed: {nextSwitch.trackSwitch.isThrown}", LogLevel.Debug); + //switch is not in the required position, is it marked as able to be routed around? + //Currently we are ony looking at passenger platforms but in the future, we might want to look at track segments for complex switch yards + if (nextSwitch.isRoutable) + { + RouteManager.logger.LogToDebug($"Switch is routable", LogLevel.Debug); + //check if we can leave on an alternate platform + TrackSpan[] ts = LocoTelem.currentDestination[locomotive].TrackSpans.ToArray(); + + /* + **** work in progress - check all platforms *** + if (LocoTelem.nextPassengerPlatform[locomotive] == null) + { + LocoTelem.nextPassengerPlatform[locomotive] = 0; + } + + while (LocoTelem.nextPassengerPlatform[locomotive] < ts.Length -1 ) + { + //Try the next plaform + LocoTelem.nextPassengerPlatform[locomotive]++; + Location pNext = (Location)ts[(int)LocoTelem.nextPassengerPlatform[locomotive]].lower; + + //can a route be found? + List mainRoute = LocoTelem.routeSwitchRequirements[locomotive]; + } + */ + + Location pNext = (Location)ts[1].lower; + List mainRoute = LocoTelem.routeSwitchRequirements[locomotive]; + + if (pNext == null || !DestinationManager.PlanRouteDeviation(ref mainRoute, nextSwitch, locomotive.LocationF, pNext)) + { + //we can't enter this platform come to a stop + stopForSwitch = true; + RouteManager.logger.LogToDebug($"Deviation unsuccessful", LogLevel.Debug); + } + else + { + RouteManager.logger.LogToDebug($"Locomotive {locomotive.DisplayName}: route updated for switch"); + } + } + else + { + stopForSwitch = true; + RouteManager.logger.LogToDebug($"Switch is unroutable", LogLevel.Debug); + } + } + + if (stopForSwitch) + { + RouteManager.logger.LogToConsole(String.Format("{0} is holding at a switch; required state: {1}", Hyperlink.To(locomotive), nextSwitch.requiredStateNormal ? "NORMAL" : "REVERSED")); + + //wait for the switch to clear + while (nextSwitch.requiredStateNormal == nextSwitch.trackSwitch.isThrown) + { + StateManager.ApplyLocal(new AutoEngineerCommand(locomotive.id, AutoEngineerMode.Road, LocoTelem.locoTravelingForward[locomotive], (int)0, null)); + yield return new WaitForSeconds(1); + } + + RouteManager.logger.LogToDebug($"Switch {nextSwitch.trackSwitch.id} cleared", LogLevel.Debug); + stopForSwitch = false; + } + } + else + { + RouteManager.logger.LogToDebug($"No switches within 400m", LogLevel.Debug); + checkNextSwitch = false; + break; + } + + //Get the switch after the current one + RouteManager.logger.LogToDebug($"Getting subsequent switch, index: {nextSwitchIndex}, count: {LocoTelem.routeSwitchRequirements[locomotive].Count() - 1}", LogLevel.Debug); + if (nextSwitchIndex < LocoTelem.routeSwitchRequirements[locomotive].Count() - 1) + { + nextSwitchIndex++; + + nextSwitch = LocoTelem.routeSwitchRequirements[locomotive][nextSwitchIndex + 1]; + distanceToSwitch = DestinationManager.GetDistanceToSwitch(locomotive, nextSwitch); + } + else + { + checkNextSwitch = false; + } + } + //Getting close to a station update some values... //Cheeky optimization to reduce excessive logging... if (distanceToStation != float.MaxValue) @@ -979,7 +1152,7 @@ public IEnumerator locomotiveTransitControl_dev(Car locomotive) //Try again in 5 seconds if (delayExecution) { - yield return new WaitForSeconds(5); + yield return new WaitForSeconds(1);//5); } /***************************************************************** @@ -1008,7 +1181,7 @@ public IEnumerator locomotiveTransitControl_dev(Car locomotive) RouteManager.logger.LogToDebug($"{locomotive.DisplayName} distance to station: {distanceToStation} Speed: {trainVelocity} Max speed: {(int)LocoTelem.RMMaxSpeed[locomotive]}"); generalTransit(locomotive); - yield return new WaitForSeconds(5); + yield return new WaitForSeconds(1);// 5); } //Entering Destination Boundary else if (distanceToStation <= 400 && distanceToStation > 300) diff --git a/RouteManager/v2/core/DestinationManager.cs b/RouteManager/v2/core/DestinationManager.cs index 01e7bd5..e6084bf 100644 --- a/RouteManager/v2/core/DestinationManager.cs +++ b/RouteManager/v2/core/DestinationManager.cs @@ -18,6 +18,13 @@ public static class DestinationManager "almond", "nantahala", "topton", "rhodo", "andrews" }; + public static readonly List orderedStations_dev = new List + { + "sylva", "dillsboro", "wilmot", "whittier", "ela", "bryson", "hemingway", "alarkajct", "cochran", "alarka", "cochran", + "almond", "nantahala", "topton", "rhodo", "andrews" + }; + + //Update the list of stations to stop at. public static void SetStopStations(Car car, List selectedStops) { @@ -84,8 +91,10 @@ public static float GetDistanceToStation(Car locomotive, PassengerStop station) Graph trackGraph = Graph.Shared; float shortestDistance = float.MaxValue; + + //Todo: use end of train in direction of travel, rather than locomotive Car centerCar = LocoTelem.CenterCar[locomotive]; - centerCar.GetCenterPosition(trackGraph); + //centerCar.GetCenterPosition(trackGraph); //Check all tracks associated with a station foreach (TrackSpan trackSpan in station.TrackSpans) @@ -120,6 +129,222 @@ public static float GetDistanceToStation(Car locomotive, PassengerStop station) return shortestDistance; } + //Returns True if node is a switch + //Returns False if node is not a switch + //switchNormal will be true if the traversal is the normal path, false if it's the reverse path and null if not traversable + // i.e. moving from normal to reversed branch + public static bool PathIsNormal(TrackSegment from, TrackSegment to, out TrackNode? trackSwitch, out bool? switchNormal) + { + trackSwitch = GetCommonNode(from,to) ?? throw new Exception($"No common node for From: {from.id}, To: {to.id}"); + + + RouteManager.logger.LogToDebug($"PathIsNormal: From: {from?.id}, {from?.name}, To: {to?.id}, {to?.name}, Node: {trackSwitch?.id}, {trackSwitch?.name}", LogLevel.Verbose); + + bool result = Graph.Shared.DecodeSwitchAt(trackSwitch, out TrackSegment enter, out TrackSegment normal, out TrackSegment reversed); + + if (result) + { + //we have switch, determine if we can go between from and to + if (from == enter && to == normal || from == normal && to == enter) + { + switchNormal = true; + } + else if (from == enter && to == reversed || from == reversed && to == enter) + { + switchNormal = false; + } + else + { + RouteManager.logger.LogToError($"PathIsNormal: Unable to traverse switch"); + RouteManager.logger.LogToDebug($"PathIsNormal: Result: {result}, Enter: {enter?.id}, Normal: {normal?.id}, Reversed: {reversed?.id}", LogLevel.Verbose); + + switchNormal = null; + } + } + else + { + //not a switch + switchNormal = null; + return false; + } + + return true; + } + + public static TrackNode? GetCommonNode(TrackSegment a, TrackSegment b) + { + + if (a == null || b == null) + return null; + + if (a.a == b.a || a.a == b.b) { + return a.a; + } + else if (a.b == b.a || a.b == b.b) + { + return a.b; + } + + return null; + } + + public static bool GetRouteSwitches(Location start, Location destination, out List switchRequirements) + { + switchRequirements = new List(); + + //alarka hack - if Alarka is on our list, we need to use this as our final destination first and plan the route in 2 segments + //will need to be updated to make a general solution for branch lines + + //TODO: put hack in + //PassengerStop alarka = PassengerStop.FindAll().Where(stop => stop.identifier == "alarka").First(); + //(Track.Location)alarka.TrackSpans.First().lower + //RouteManager.logger.LogToDebug($"Finding Route to Alarka (segments) {loco.DisplayName} to {alarka?.name}..."); + //RouteManager.logger.LogToDebug($"Current Location F: {loco.LocationF}, Location A: {loco.LocationA}, Location B: {loco.LocationB}"); + // + + List segmentSteps = Graph.Shared.FindRoute(start, destination); + + RouteManager.logger.LogToDebug($"Route found: {segmentSteps.Count} steps:"); + + for (int i = 0; i < segmentSteps.Count - 1; i++) + { + TrackSegment seg = segmentSteps[i]; + TrackSegment segNext = segmentSteps[i + 1]; + + bool? requiredSwitchState; + bool isSwitch = DestinationManager.PathIsNormal(seg, segNext, out TrackNode trackSwitch, out requiredSwitchState); + + if (isSwitch && requiredSwitchState != null) + { + //RouteManager.logger.LogToDebug($"\r\nSeg.a: {seg.a.id}, {seg.a.name}\r\nSeg.b: {seg.b.id}, {seg.b.name}\r\nSegNext.a: {segNext.a.id}, {segNext.a.name}\r\nSegNext.b: {segNext.b.id}, {segNext.b.name}", LogLevel.Debug); + //RouteManager.logger.LogToDebug($"\t\t\tSegment: {seg?.id}, {seg?.name}, {seg?.trackClass}, Node A: {seg?.a.name}, Node B: {seg?.b.name}, Desired switch pos normal: {requiredSwitchState}", LogLevel.Debug); + + switchRequirements.Add(new RouteSwitchData(trackSwitch, seg, segNext, (bool)requiredSwitchState)); + } + else if (isSwitch && requiredSwitchState == null) + { + RouteManager.logger.LogToError("Unable to resolve path!"); + return false; + } + + } + + return true; + } + + public static float GetDistanceToSwitch(Car locomotive, RouteSwitchData trackSwitch) + { + RouteManager.logger.LogToDebug($"Loco: {locomotive.DisplayName} getting distance to switch {trackSwitch.trackSwitch?.id}", LogLevel.Trace); + + Graph trackGraph = Graph.Shared; + + //use end of train, rather than centre car + Car leading = TrainManager.GetLeadingEnd(locomotive); + + + Location swLocation = new Location(trackSwitch.segmentFrom, 0f, trackSwitch.segmentFrom.EndForNode(trackSwitch.trackSwitch)); + + float distanceA = trackGraph.FindDistance(leading.LocationA, swLocation); + float distanceB = trackGraph.FindDistance(leading.LocationB, swLocation); + float closestEndDistance = Math.Min(distanceA, distanceB); + + float straightLineDistance = Vector3.Distance(leading.GetCenterPosition(trackGraph), swLocation.GetPosition()); + Vector3 heading = swLocation.GetPosition() - leading.GetCenterPosition(trackGraph); + + RouteManager.logger.LogToDebug($"Straight Line: {straightLineDistance}, Heading: {heading}, Heading.mag: {heading.magnitude}, Direction: {heading/heading.magnitude}"); + RouteManager.logger.LogToDebug($"Heading sign: {(heading.x >0 && (heading.x > -heading.y && heading.x mainRoute) + { + TrackSpan[] tracks = nextStation.TrackSpans.ToArray(); + + if (tracks.Length > 1) + { + PlanNextRoute(start, (Location)tracks[1].lower, ref mainRoute, out RouteSwitchData commonPoint); + } + } + + public static bool PlanNextRoute(Location start, Location nextPlatform, ref List mainRoute, out RouteSwitchData commonPoint) + { + commonPoint = null; + + List requirementsP2; + if (GetRouteSwitches(start, nextPlatform, out requirementsP2)) + { + //found a route to second platform + //find last common node + commonPoint = mainRoute.Intersect(requirementsP2, new RouteSwitchDataComparer()).Last(); + if (commonPoint != null) + { + //common point between leaving the station and the current main route + commonPoint.isRoutable = true; + //new route from P2 + mainRoute = requirementsP2; + } + else + { + //routes don't intersect + RouteManager.logger.LogToDebug("No intersection of routes to second platform!", LogLevel.Debug); + return false; + } + } + return true; + } + + public static bool PlanRouteDeviation(ref List mainRoute, RouteSwitchData nextSwitch, Location current, Location nextPlatform) + { + + //can we get to the next platform? + if (DestinationManager.PlanNextRoute(current, nextPlatform, ref mainRoute, out RouteSwitchData commonPoint1)) + { + //find the last location on the mainRoute + Location finalDestination = new Location(mainRoute.Last().segmentTo, 0, mainRoute.Last().segmentTo.EndForNode(mainRoute.Last().trackSwitch)); + + List newRoute = new List(mainRoute); + //can we get from the next platform to the final destination + if (DestinationManager.PlanNextRoute(nextPlatform, finalDestination, ref newRoute, out RouteSwitchData commonPoint2)) + { + //we can get in and out, without breaking our route, lets update the route + + //flip the current switch requirements at our common switches + commonPoint1.requiredStateNormal = !commonPoint1.requiredStateNormal; + commonPoint2.requiredStateNormal = !commonPoint2.requiredStateNormal; + + //merge the two routes + int index1 = mainRoute.IndexOf(commonPoint1); + int index2 = mainRoute.IndexOf(commonPoint2); + + int indexNewRoute = newRoute.IndexOf(commonPoint2); + + index1++; + + if(index1 != index2) { + //remove all elements between common points + mainRoute.RemoveRange(index1, index2 - index1); + + //insert any new elements + mainRoute.InsertRange(index1, newRoute.Take(indexNewRoute)); + } + + return true; + } + } + + + //can't deviate + return false; + + } /************************************************************************************************************************** * @@ -135,8 +360,8 @@ public static float GetDistanceToStation(Car locomotive, PassengerStop station) ***************************************************************************************************************************/ - //Determine if station is selected - public static bool IsStopStationSelected(PassengerStop stop, Car locomotive) + //Determine if station is selected + public static bool IsStopStationSelected(PassengerStop stop, Car locomotive) { //Trace Function //RouteManager.logger.LogToDebug("ENTERED FUNCTION: IsStopStationSelected", LogLevel.Trace); @@ -190,9 +415,8 @@ public static PassengerStop IsTransferStationSelected(PassengerStop transferFrom { //Trace Function //RouteManager.logger.LogToDebug("ENTERED FUNCTION: IsPickupStationSelected", LogLevel.Trace); - PassengerStop transferTo; - bool result = LocoTelem.UITransferStationSelections[locomotive].TryGetValue(transferFrom.identifier, out transferTo); + bool result = LocoTelem.UITransferStationSelections[locomotive].TryGetValue(transferFrom.identifier, out PassengerStop transferTo); //Trace Function //RouteManager.logger.LogToDebug("EXITING FUNCTION: IsPickupStationSelected", LogLevel.Trace); diff --git a/RouteManager/v2/core/StationManager.cs b/RouteManager/v2/core/StationManager.cs index 7527347..ab34f6d 100644 --- a/RouteManager/v2/core/StationManager.cs +++ b/RouteManager/v2/core/StationManager.cs @@ -1,5 +1,4 @@ -using Game.Events; -using Model; +using Model; using RollingStock; using RouteManager.v2.dataStructures; using RouteManager.v2.Logging; @@ -457,95 +456,6 @@ public static bool isStationRight(Car locomotive, PassengerStop station) ************************************************************************************************************/ - - public static (PassengerStop, float) GetClosestStation_dev(Car currentCar) - { - //Trace Logging - RouteManager.logger.LogToDebug("ENTERED FUNCTION: GetClosestStation_dev", LogLevel.Trace); - - //Debugging Output - RouteManager.logger.LogToDebug(String.Format("Car {0} calculating closest station...", currentCar.DisplayName), LogLevel.Debug); - - // Initialize variables; - PassengerStop closestStation = null; - float closestDistance = float.MaxValue; - Graph graph = Graph.Shared; - - //Get front of the locomotive's location - Location locoFront = currentCar.LocationA; - Location locoRear = currentCar.LocationB; - - // If locoFront is null then bail - if (locoFront == null) - { - RouteManager.logger.LogToError("Could not obtain locomotive's front position."); - return (null, 0); - } - - //Debugging Output - RouteManager.logger.LogToDebug(String.Format("Car {0} centerpoint {1} Facing right {2}", currentCar.DisplayName, locoFront.GetPosition(), currentCar.Orientation > 0), LogLevel.Verbose); - - /* - RouteManager.logger.LogToDebug("List Platform Locations", LogLevel.Verbose); - - foreach (PassengerStop station in PassengerStop.FindAll().Where(ps => !ps.ProgressionDisabled)) - { - RouteManager.logger.LogToDebug($"Station {station.name} neighbours: {string.Join(", ", station.neighbors.Select(ps => ps.identifier).ToArray())}"); - RouteManager.logger.LogToDebug($"Station {station.name} Lower: {((Location)station.TrackSpans.First().lower).GetPosition()}, Rot: {((Location)station.TrackSpans.First().lower).GetPositionRotation()}, Direction: {((Location)station.TrackSpans.First().lower).GetDirection()}"); - RouteManager.logger.LogToDebug($"Station {station.name} Upper: {((Location)station.TrackSpans.First().upper).GetPosition()}, Rot: {((Location)station.TrackSpans.First().upper).GetPositionRotation()}, Direction: {((Location)station.TrackSpans.First().upper).GetDirection()}"); - }*/ - - - // Iterate over each station that has been progress unlocked and station is a selected stop - foreach (PassengerStop station in PassengerStop.FindAll().Where(ps => !ps.ProgressionDisabled && LocoTelem.stopStations[currentCar].Contains(ps))) - { - float distance = 0; - - RouteManager.logger.LogToDebug($"Station {station.name} neighbours: {string.Join(", ", station.neighbors.Select(ps => ps.identifier).ToArray())}"); - - try - { - // Calculate the distance between the locomotive and the station's platform centre - float distanceF = (graph.FindDistance(locoFront, (Location)station.TrackSpans.First().lower) + graph.FindDistance(locoFront, (Location)station.TrackSpans.First().upper)) / 2; - float distanceR = (graph.FindDistance(locoRear, (Location)station.TrackSpans.First().lower) + graph.FindDistance(locoRear, (Location)station.TrackSpans.First().upper)) / 2; - - if (isStationRight(currentCar, station) && currentCar.Orientation > 0 || - !isStationRight(currentCar, station) && currentCar.Orientation < 0) - { - distance = distanceF; - } - else if (isStationRight(currentCar, station) && currentCar.Orientation < 0 || - !isStationRight(currentCar, station) && currentCar.Orientation > 0) - { - distance = distanceR; - } - - RouteManager.logger.LogToDebug($"Graph distance from {currentCar.DisplayName} to {station.identifier}: Front: {distanceF}, Rear: {distanceR}", LogLevel.Verbose); - - } - catch - { - RouteManager.logger.LogToDebug($"Error calculating graph distance", LogLevel.Verbose); - return (null, float.MaxValue); - } - - // Keep track of the closest station - if (distance < closestDistance) - { - closestDistance = distance; - closestStation = station; - } - } - - //Debug output - RouteManager.logger.LogToDebug(String.Format("Car {0} Closest Station was: {1}", currentCar.DisplayName, closestStation.identifier), LogLevel.Debug); - - //Trace Logging - RouteManager.logger.LogToDebug("EXITING FUNCTION: GetClosestStation_dev", LogLevel.Trace); - - return (closestStation, closestDistance); - } - public static PassengerStop getNextStation_dev(Car locomotive) { PassengerStop nextStop = null; @@ -556,7 +466,8 @@ public static PassengerStop getNextStation_dev(Car locomotive) if (!LocoTelem.currentDestination.ContainsKey(locomotive) || LocoTelem.currentDestination[locomotive] == default(PassengerStop)) { //No Destination set so for now, assume closest station. - currentStation = GetClosestStation_dev(locomotive).Item1; + LocoTelem.closestStation[locomotive] = GetClosestStation_dev(locomotive); + currentStation = LocoTelem.closestStation[locomotive].Item1; RouteManager.logger.LogToDebug(String.Format("Loco {0} does not have a destination. Defaulting to closest station {1}", locomotive.DisplayName, currentStation.identifier), LogLevel.Debug); } else @@ -652,67 +563,101 @@ public static PassengerStop getNextStation_dev(Car locomotive) return nextStop; } - - /* - public static PassengerStop getInitialDestination_dev(Car locomotive) + public static (PassengerStop, float) GetClosestStation_dev(Car currentCar) { + //Trace Logging + RouteManager.logger.LogToDebug("ENTERED FUNCTION: GetClosestStation_dev", LogLevel.Trace); - if (isTrainInStation(locomotive)) - { - return LocoTelem.closestStation[locomotive]; - } - else + //Debugging Output + RouteManager.logger.LogToDebug(String.Format("Car {0} calculating closest station...", currentCar.DisplayName), LogLevel.Debug); + + // Initialize variables; + PassengerStop closestStation = null; + float closestDistance = float.MaxValue; + Graph graph = Graph.Shared; + + //Get front of the locomotive's location + Location locoFront = currentCar.LocationA; + Location locoRear = currentCar.LocationB; + + // If locoFront is null then bail + if (locoFront == null) { + RouteManager.logger.LogToError("Could not obtain locomotive's front position."); + return (null, 0); } + //Debugging Output + RouteManager.logger.LogToDebug(String.Format("Car {0} centerpoint {1} Facing right {2}", currentCar.DisplayName, locoFront.GetPosition(), currentCar.Orientation > 0), LogLevel.Verbose); + /* - PassengerStop nextStation = getNextStation(locomotive); + RouteManager.logger.LogToDebug("List Platform Locations", LogLevel.Verbose); + + foreach (PassengerStop station in PassengerStop.FindAll().Where(ps => !ps.ProgressionDisabled)) + { + RouteManager.logger.LogToDebug($"Station {station.name} neighbours: {string.Join(", ", station.neighbors.Select(ps => ps.identifier).ToArray())}"); + RouteManager.logger.LogToDebug($"Station {station.name} Lower: {((Location)station.TrackSpans.First().lower).GetPosition()}, Rot: {((Location)station.TrackSpans.First().lower).GetPositionRotation()}, Direction: {((Location)station.TrackSpans.First().lower).GetDirection()}"); + RouteManager.logger.LogToDebug($"Station {station.name} Upper: {((Location)station.TrackSpans.First().upper).GetPosition()}, Rot: {((Location)station.TrackSpans.First().upper).GetPositionRotation()}, Direction: {((Location)station.TrackSpans.First().upper).GetDirection()}"); + }*/ - RouteManager.logger.LogToDebug(String.Format("Loco {0} determining initial destination", locomotive.DisplayName), LogLevel.Debug); - //Make sure a previous destination is set - if (LocoTelem.previousDestinations.ContainsKey(locomotive)) + // Iterate over each station that has been progress unlocked and station is a selected stop + foreach (PassengerStop station in PassengerStop.FindAll().Where(ps => !ps.ProgressionDisabled && LocoTelem.stopStations[currentCar].Contains(ps))) { - RouteManager.logger.LogToDebug(String.Format("Loco {0} has previous destinations", locomotive.DisplayName), LogLevel.Verbose); - //Compare Previous Destination - //If we have not visited the closest station - if (!LocoTelem.previousDestinations[locomotive].Contains(LocoTelem.closestStation[locomotive].Item1)) + float distance = 0; + + RouteManager.logger.LogToDebug($"Station {station.name} neighbours: {string.Join(", ", station.neighbors.Select(ps => ps.identifier).ToArray())}", LogLevel.Debug); + + try { - RouteManager.logger.LogToDebug(String.Format("Loco {0} has previous destinations not containing closeset station", locomotive.DisplayName), LogLevel.Verbose); - //If the closest station is selected.... - if (LocoTelem.stopStations[locomotive].Contains(LocoTelem.closestStation[locomotive].Item1)) + // Calculate the distance between the locomotive and the station's platform centre + float distanceF = (graph.FindDistance(locoFront, (Location)station.TrackSpans.First().lower) + graph.FindDistance(locoFront, (Location)station.TrackSpans.First().upper)) / 2; + float distanceR = (graph.FindDistance(locoRear, (Location)station.TrackSpans.First().lower) + graph.FindDistance(locoRear, (Location)station.TrackSpans.First().upper)) / 2; + + if (isStationRight(currentCar, station) && currentCar.Orientation > 0 || + !isStationRight(currentCar, station) && currentCar.Orientation < 0) { - RouteManager.logger.LogToDebug(String.Format("Loco {0} Initial destintion is the closest: {1}", locomotive.DisplayName, LocoTelem.closestStation[locomotive].Item1)); - return LocoTelem.closestStation[locomotive].Item1; + distance = distanceF; } + else if (isStationRight(currentCar, station) && currentCar.Orientation < 0 || + !isStationRight(currentCar, station) && currentCar.Orientation > 0) + { + distance = distanceR; + } + + RouteManager.logger.LogToDebug($"Graph distance from {currentCar.DisplayName} to {station.identifier}: Front: {distanceF}, Rear: {distanceR}", LogLevel.Verbose); + } - } - else - { - //If the closest station is selected.... - if (LocoTelem.stopStations[locomotive].Contains(LocoTelem.closestStation[locomotive].Item1)) - { - RouteManager.logger.LogToDebug(String.Format("Loco {0} Initial destintion is the closest: {1}", locomotive.DisplayName, LocoTelem.closestStation[locomotive].Item1)); - return LocoTelem.closestStation[locomotive].Item1; - } - else if (LocoTelem.stopStations[locomotive].Contains(LocoTelem.currentDestination[locomotive]) && !LocoTelem.previousDestinations[locomotive].Contains(LocoTelem.currentDestination[locomotive])) + catch { - RouteManager.logger.LogToDebug(String.Format("Loco {0} Initial destintion is the current: {1}", locomotive.DisplayName, LocoTelem.currentDestination[locomotive])); - return LocoTelem.currentDestination[locomotive]; + RouteManager.logger.LogToDebug($"Error calculating graph distance", LogLevel.Verbose); + return (null, float.MaxValue); } - else + + // Keep track of the closest station + if (distance < closestDistance) { - RouteManager.logger.LogToDebug(String.Format("Loco {0} Initial destintion is not the closest: {1}", locomotive.DisplayName, nextStation)); - return nextStation; + closestDistance = distance; + closestStation = station; } + } - //Worst case, Just default to the next station - RouteManager.logger.LogToDebug(String.Format("Loco {0} getInitialDestination reached default case! Station was: {1}", locomotive.DisplayName, nextStation), LogLevel.Error); - return nextStation; - - }*/ + //Debug output + RouteManager.logger.LogToDebug(String.Format("Car {0} Closest Station was: {1}", currentCar.DisplayName, closestStation.identifier), LogLevel.Debug); + + //Trace Logging + RouteManager.logger.LogToDebug("EXITING FUNCTION: GetClosestStation_dev", LogLevel.Trace); + return (closestStation, closestDistance); + } + + + //Future use - try to avoid lag when calculating closest station by + public static IEnumerator<(PassengerStop, float)> CalculateDistanceToStation() + { + yield return (null, float.MaxValue); + } } } diff --git a/RouteManager/v2/core/TrainManager.cs b/RouteManager/v2/core/TrainManager.cs index be497f5..b91e071 100644 --- a/RouteManager/v2/core/TrainManager.cs +++ b/RouteManager/v2/core/TrainManager.cs @@ -131,6 +131,23 @@ public static void RMbell(Car locomotive, bool IsBell) StateManager.ApplyLocal(new PropertyChange(locomotive.id, Control.Bell, IsBell)); } + public static Car GetLeadingEnd(Car locomotive) + { + //int index = locomotive.set.IndexOfCar(locomotive).GetValueOrDefault(0); + bool right = LocoTelem.locoTravelingEastWard[locomotive]; + bool forward = LocoTelem.locoTravelingForward[locomotive]; + + //!right && !forward || !right && forward **simplifies to** !right || !forward + if (!right || !forward) //locomotive.velocity < 0) + { + return locomotive.EnumerateCoupled(Car.LogicalEnd.A).Last(); + } + //right && forward || right && !forward **simplifies to** right + else + { + return locomotive.EnumerateCoupled(Car.LogicalEnd.A).First(); + } + } public static Car GetCenterCoach(Car locomotive) { var graph = Graph.Shared; diff --git a/RouteManager/v2/dataStructures/LocoTelem.cs b/RouteManager/v2/dataStructures/LocoTelem.cs index 98fd5b3..b6ff26f 100644 --- a/RouteManager/v2/dataStructures/LocoTelem.cs +++ b/RouteManager/v2/dataStructures/LocoTelem.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using UnityEngine.UIElements; - namespace RouteManager.v2.dataStructures { public class LocoTelem @@ -28,6 +27,8 @@ public class LocoTelem public static Dictionary currentDestination { get; private set; } = new Dictionary(); public static Dictionary> previousDestinations { get; private set; } = new Dictionary>(); public static Dictionary previousDestination { get; private set; } = new Dictionary(); + public static Dictionary> routeSwitchRequirements { get; private set; } = new Dictionary>(); + public static Dictionary nextPassengerPlatform { get; private set; } = new Dictionary(); public static Dictionary> lowFuelQuantities { get; private set; } = new Dictionary>(); diff --git a/RouteManager/v2/dataStructures/RouteSwitchData.cs b/RouteManager/v2/dataStructures/RouteSwitchData.cs new file mode 100644 index 0000000..6c38266 --- /dev/null +++ b/RouteManager/v2/dataStructures/RouteSwitchData.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using Track; + +namespace RouteManager.v2.dataStructures +{ + public class RouteSwitchData + { + public TrackNode trackSwitch; //switch we're interested in + public TrackSegment segmentFrom; //track we're coming from + public TrackSegment segmentTo; //track we're going to + public bool requiredStateNormal; //switch state to make the traversal + public bool isRoutable; //we can ignore the state of this switch and route around it + + + public RouteSwitchData(TrackNode trackSwitch, TrackSegment segmentFrom, TrackSegment segmentTo, bool requiredStateNormal) + { + this.trackSwitch = trackSwitch; + this.segmentFrom = segmentFrom; + this.segmentTo = segmentTo; + this.requiredStateNormal = requiredStateNormal; + } + + public override string ToString() + { + return $"Switch ID: {this.trackSwitch.id}, From: {segmentFrom.id}, To: {segmentTo.id}, Is Decision: {this.isRoutable}"; + } + } + + public class RouteSwitchDataComparer : IEqualityComparer + { + + #region IEqualityComparer Members + + + public bool Equals(RouteSwitchData x, RouteSwitchData y) + { + //no null check here, you might want to do that, or correct that to compare just one part of your object + return x.trackSwitch == y.trackSwitch; + } + + + public int GetHashCode(RouteSwitchData obj) + { + unchecked + { + var hash = 17; + //same here, if you only want to get a hashcode on a, remove the line with b + hash = hash * 23 + obj.trackSwitch.GetHashCode(); + hash = hash * 23 + obj.trackSwitch.GetHashCode(); + + return hash; + } + } + + #endregion + } +} diff --git a/RouteManager/v2/dataStructures/StationInformation.cs b/RouteManager/v2/dataStructures/StationInformation.cs index 4a22524..5b9829a 100644 --- a/RouteManager/v2/dataStructures/StationInformation.cs +++ b/RouteManager/v2/dataStructures/StationInformation.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; namespace RouteManager.v2.dataStructures { diff --git a/RouteManager/v2/dataStructures/StationMapData.cs b/RouteManager/v2/dataStructures/StationMapData.cs index 24dea0b..4e43588 100644 --- a/RouteManager/v2/dataStructures/StationMapData.cs +++ b/RouteManager/v2/dataStructures/StationMapData.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using UnityEngine; +using UnityEngine; namespace RouteManager.v2.dataStructures { diff --git a/RouteManager/v2/harmonyPatches/GraphPatch.cs b/RouteManager/v2/harmonyPatches/GraphPatch.cs new file mode 100644 index 0000000..51c0666 --- /dev/null +++ b/RouteManager/v2/harmonyPatches/GraphPatch.cs @@ -0,0 +1,85 @@ +using HarmonyLib; +using System; +using System.Reflection; +using Track; +using RouteManager.v2.Logging; + + + + +namespace RouteManager.v2.harmonyPatches +{ + + + [HarmonyPatch(typeof(Graph))] + public static class GraphPatch + { + public delegate void delSegmentsReachableFrom(TrackSegment segment, TrackSegment.End end, out TrackSegment normal, out TrackSegment reversed); + public delegate void delCheckSwitchAgainstMovement(TrackSegment seg, TrackSegment nextSegment, TrackNode node); + + public static delSegmentsReachableFrom SegmentsReachableFrom; + public static delCheckSwitchAgainstMovement CheckSwitchAgainstMovement; + + + [HarmonyPostfix] + [HarmonyPatch(typeof(Graph), "Awake")] + public static void Awake(Graph __instance) + { + + RouteManager.logger.LogToDebug($"Graph.Awake()", LogLevel.Trace); + + //set up delegates to access private methods + /* + SegmentsReachableFrom = (DelSegmentsReachableFrom)Delegate.CreateDelegate(typeof(DelSegmentsReachableFrom), + null, + Graph.Shared.GetType().GetMethod("SegmentsReachableFrom", + BindingFlags.NonPublic | BindingFlags.Instance) + ); + */ + + /* + MethodInfo dynMethod = Graph.Shared.GetType().GetMethod("SegmentsReachableFrom", + BindingFlags.NonPublic | BindingFlags.Instance); + dynMethod.Invoke(Graph.Shared, new object[] { segment, end, normal, reversed }); + */ + + SegmentsReachableFrom = (delSegmentsReachableFrom)Delegate.CreateDelegate(typeof(delSegmentsReachableFrom), + Graph.Shared, + Graph.Shared.GetType().GetMethod("SegmentsReachableFrom", + BindingFlags.NonPublic | BindingFlags.Instance) + ); + + CheckSwitchAgainstMovement = (delCheckSwitchAgainstMovement)Delegate.CreateDelegate(typeof(delCheckSwitchAgainstMovement), + Graph.Shared, + Graph.Shared.GetType().GetMethod("CheckSwitchAgainstMovement", + BindingFlags.NonPublic | BindingFlags.Instance) + ); + + RouteManager.logger.LogToDebug($"EXITING Graph.Awake()", LogLevel.Trace); + } + + /*public static void DelSegmentsReachableFrom(TrackSegment segment, TrackSegment.End end, out TrackSegment normal, out TrackSegment reversed) + { + SegmentsReachableFrom(segment, end, out normal, out reversed); + }*/ + + /* + [HarmonyPrefix] + [HarmonyPatch(typeof(Graph), "SegmentsReachableFrom")] + public static void SegmentsReachableFrom(Graph __instance) + { + RouteManager.logger.LogToDebug($"Graph.SegmentsReachableFrom()", LogLevel.Trace); + + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(Graph), "SegmentsReachableFrom")] + public static void SegmentsReachableFromPost(Graph __instance) + { + RouteManager.logger.LogToDebug($"Leaving Graph.SegmentsReachableFrom()", LogLevel.Trace); + + } + */ + } + +} \ No newline at end of file diff --git a/RouteManager/v2/harmonyPatches/RouteManagerUI.cs b/RouteManager/v2/harmonyPatches/RouteManagerUI.cs index 4a0a214..77295ea 100644 --- a/RouteManager/v2/harmonyPatches/RouteManagerUI.cs +++ b/RouteManager/v2/harmonyPatches/RouteManagerUI.cs @@ -15,6 +15,7 @@ using RouteManager.v2.Logging; using RouteManager.v2.UI; using UI.Common; +using Network; namespace RouteManager.v2.harmonyPatches @@ -210,6 +211,7 @@ static bool Prefix(CarInspector __instance, UIPanelBuilder builder) LocoTelem.RMMaxSpeed[car] = (int)(value * 5f); SetOrdersValue(null, null, (int) LocoTelem.RMMaxSpeed[car], null); }, 0f, num / 5, wholeNumbers: true); + builder.AddField("Max Speed", control); /********************************************************************************** @@ -250,7 +252,40 @@ static bool Prefix(CarInspector __instance, UIPanelBuilder builder) { RouteManagerWindow.Show(car); }); + builder.AddButtonSelectable("Test Reachable", placeHolder, delegate + { + Multiplayer.Broadcast($"Leading End: {TrainManager.GetLeadingEnd(car)?.DisplayName}"); + }); + /* + builder.AddButtonSelectable("Test Reachable", placeHolder, delegate + { + + PassengerStop alarka = PassengerStop.FindAll().Where(stop => stop.identifier == "alarka").First(); + + RouteManager.logger.LogToDebug($"Finding Route to Alarka (segments) {car.DisplayName} to {alarka?.name}..."); + RouteManager.logger.LogToDebug($"Current Location F: {car.LocationF}, Location A: {car.LocationA}, Location B: {car.LocationB}"); + + List segmentSteps = Graph.Shared.FindRoute(car.LocationF, (Track.Location)alarka.TrackSpans.First().lower); + + RouteManager.logger.LogToDebug($"Route found: {segmentSteps.Count} steps:"); + for (int i = 0; i < segmentSteps.Count -1; i++) + { + TrackSegment seg = segmentSteps[i]; + TrackSegment segNext = segmentSteps[i + 1]; + + bool? requiredSwitchState; + bool isSwitch = DestinationManager.PathIsNormal(seg, segNext, out TrackNode? node, out requiredSwitchState); + + if (isSwitch) + { + RouteManager.logger.LogToDebug($"\r\nSeg.a: {seg.a.id}, {seg.a.name}\r\nSeg.b: {seg.b.id}, {seg.b.name}\r\nSegNext.a: {segNext.a.id}, {segNext.a.name}\r\nSegNext.b: {segNext.b.id}, {segNext.b.name}", LogLevel.Debug); + RouteManager.logger.LogToDebug($"\t\t\tSegment: {seg?.id}, {seg?.name}, {seg?.trackClass}, Node A: {seg?.a.name}, Node B: {seg?.b.name}, Desired switch pos normal: {requiredSwitchState}",LogLevel.Debug); + } + + } + }); + */ builder.AddExpandingVerticalSpacer(); } else diff --git a/repository.json b/repository.json index 6d51120..a18eff2 100644 --- a/repository.json +++ b/repository.json @@ -2,7 +2,7 @@ "Releases": [ { "Id": "RouteManager", - "Version": "2.1.0.0" + "Version": "2.1.0.1" } ] }