@@ -76,6 +76,9 @@ namespace Orts.Simulation.Physics
7676 public class Train
7777 {
7878 public List<TrainCar> Cars = new List<TrainCar>(); // listed front to back
79+ public List<TrainCar> DPLeadUnits = new List<TrainCar>(); // list of all DP lead locomotives
80+ // list of connected locomotives, each element is a list of the connected locomotives themselves
81+ public List<List<TrainCar>> LocoGroups = new List<List<TrainCar>>();
7982 public int Number;
8083 public string Name;
8184 public string TcsParametersFileName;
@@ -138,6 +141,7 @@ public TrainCar LastCar
138141 public float BrakeLine4 = -1; // extra line just in case, ep brake control line. -2: hold, -1: inactive, 0: release, 0 < value <=1: apply
139142 public RetainerSetting RetainerSetting = RetainerSetting.Exhaust;
140143 public int RetainerPercent = 100;
144+ public float TotalBrakePipeFlowM3pS; // Total flow rate of air from all MR to BP
141145 public float TotalTrainBrakePipeVolumeM3; // Total volume of train brake pipe
142146 public float TotalTrainBrakeCylinderVolumeM3; // Total volume of train brake cylinders
143147 public float TotalTrainBrakeSystemVolumeM3; // Total volume of train brake system
@@ -509,6 +513,9 @@ public TrainCar LeadLocomotive
509513 //if (lead.EngineBrakeController != null)
510514 // lead.EngineBrakeController.UpdateEngineBrakePressure(ref BrakeLine3PressurePSI, 1000);
511515 }
516+
517+ // If lead locomotive changes, distributed power needs to be updated
518+ SetDPUnitIDs(true);
512519 }
513520 }
514521
@@ -691,6 +698,7 @@ public Train(Simulator simulator, BinaryReader inf)
691698 BrakeLine2PressurePSI = inf.ReadSingle();
692699 BrakeLine3PressurePSI = inf.ReadSingle();
693700 BrakeLine4 = inf.ReadSingle();
701+ TotalBrakePipeFlowM3pS = inf.ReadSingle();
694702 aiBrakePercent = inf.ReadSingle();
695703 LeadLocomotiveIndex = inf.ReadInt32();
696704 RetainerSetting = (RetainerSetting)inf.ReadInt32();
@@ -1039,6 +1047,7 @@ public virtual void Save(BinaryWriter outf)
10391047 outf.Write(BrakeLine2PressurePSI);
10401048 outf.Write(BrakeLine3PressurePSI);
10411049 outf.Write(BrakeLine4);
1050+ outf.Write(TotalBrakePipeFlowM3pS);
10421051 outf.Write(aiBrakePercent);
10431052 outf.Write(LeadLocomotiveIndex);
10441053 outf.Write((int)RetainerSetting);
@@ -1314,6 +1323,7 @@ public TrainCar GetNextCab()
13141323 LeadLocomotiveIndex = Math.Abs(nextCabIndex) - 1;
13151324 Trace.Assert(LeadLocomotive != null, "Tried to switch to non-existent loco");
13161325 TrainCar newLead = LeadLocomotive; // Changing LeadLocomotiveIndex also changed LeadLocomotive
1326+ SetDPUnitIDs(true); // DP IDs must be reprocessed when lead locomotive changes
13171327 ((MSTSLocomotive)newLead).UsingRearCab = nextCabIndex < 0;
13181328
13191329 if (oldLead != null && newLead != null && oldLead != newLead)
@@ -1372,6 +1382,7 @@ public void LeadNextLocomotive()
13721382 else if (coud > 1)
13731383 LeadLocomotiveIndex = firstLead;
13741384 TrainCar newLead = LeadLocomotive;
1385+ SetDPUnitIDs(true); // DP IDs must be reprocessed when lead locomotive changes
13751386 if (prevLead != null && newLead != null && prevLead != newLead)
13761387 newLead.CopyControllerSettings(prevLead);
13771388 }
@@ -1527,19 +1538,79 @@ public void ReverseCars()
15271538 /// </summary>
15281539 public void SetDPUnitIDs(bool keepRemoteGroups = false)
15291540 {
1541+ // List to keep track of new 'lead' DP units
1542+ // 'Lead' DP units follow the air brake commands of the master loco, 'trail' DP units do not
1543+ List<TrainCar> tempDPLeads = new List<TrainCar>();
1544+ TrainCar tempDPLead = null;
1545+ // Value judging how compatible this locomotive is to the lead locomotive for DP purposes
1546+ float dpRating = 0;
1547+
1548+ // List of each DP group's locomotives
1549+ List<List<TrainCar>> tempLocoGroups = new List<List<TrainCar>>();
1550+
1551+ var prevId = -1;
15301552 var id = 0;
1553+
15311554 foreach (var car in Cars)
15321555 {
1533- //Console.WriteLine("___{0} {1}", car.CarID, id);
1534- if (car is MSTSLocomotive)
1556+ if (car is MSTSLocomotive loco)
15351557 {
1536- (car as MSTSLocomotive).DPUnitID = id;
1558+ float thisDPRating = DetermineDPCompatibility(LeadLocomotive, car);
1559+
1560+ loco.DPUnitID = id;
1561+
1562+ if (id != prevId) // New locomotive group
1563+ {
1564+ // Add the most suitable unit from the previous group as a DP lead unit
1565+ if (tempDPLead != null && !tempDPLeads.Contains(tempDPLead))
1566+ tempDPLeads.Add(tempDPLead);
1567+
1568+ dpRating = thisDPRating;
1569+ tempDPLead = car;
1570+
1571+ prevId = id;
1572+ }
1573+ else // Same locomotive group
1574+ // Check to see if this locomotive is more compatible than previous ones
1575+ if (thisDPRating > dpRating)
1576+ {
1577+ dpRating = thisDPRating;
1578+ tempDPLead = car;
1579+ }
1580+
15371581 if (car.RemoteControlGroup == 1 && !keepRemoteGroups)
15381582 car.RemoteControlGroup = 0;
15391583 }
15401584 else
15411585 id++;
15421586 }
1587+
1588+ // Add final DP lead unit
1589+ if (tempDPLead != null && !tempDPLeads.Contains(tempDPLead))
1590+ tempDPLeads.Add(tempDPLead);
1591+
1592+ foreach (TrainCar locoCar in tempDPLeads)
1593+ {
1594+ // The train's lead unit should always be a DP lead unit, even if not at the front
1595+ // If a different locomotive in the lead loco's group has been declared DP lead, replace that loco with the lead loco
1596+ if (LeadLocomotive is MSTSLocomotive lead && locoCar is MSTSLocomotive loco)
1597+ if (loco.DPUnitID == lead.DPUnitID && locoCar != LeadLocomotive)
1598+ {
1599+ tempDPLeads.Insert(tempDPLeads.IndexOf(locoCar), LeadLocomotive);
1600+ tempDPLeads.Remove(locoCar);
1601+ break; // foreach doesn't like it when the collection is modified during the loop, break to mitigate error
1602+ }
1603+ }
1604+
1605+ DPLeadUnits = tempDPLeads;
1606+
1607+ foreach (TrainCar loco in DPLeadUnits)
1608+ {
1609+ // Find all locomotives connected to each DP unit
1610+ tempLocoGroups.Add(DetermineLocomotiveGroup(loco));
1611+ }
1612+
1613+ LocoGroups = tempLocoGroups;
15431614 }
15441615
15451616 /// <summary>
@@ -4231,6 +4302,127 @@ public TrainCar FindLeadLocomotive()
42314302 return null;
42324303 }
42334304
4305+ //================================================================================================//
4306+ /// <summary>
4307+ /// Find connected locomotives
4308+ /// <\summary>
4309+
4310+ // Finds all locomotives connected to the TrainCar reference provided as input,
4311+ // returning the connected locomotives* as a list of TrainCars.
4312+ // Returns null if there are no locomotives in the given group.
4313+ // *If the input is a steam locomotive, the output will instead be the steam locomotive and any tenders connected.
4314+ // Useful for determining locomotive brake propagation on groups of locomotives other than the lead group.
4315+
4316+ public List<TrainCar> DetermineLocomotiveGroup(TrainCar loco)
4317+ {
4318+ if (loco is MSTSLocomotive)
4319+ {
4320+ List<TrainCar> tempGroup = new List<TrainCar>();
4321+ int first;
4322+ int last;
4323+
4324+ first = last = Cars.IndexOf(loco);
4325+
4326+ // If locomotive is a steam locomotive, check for tenders only
4327+ if (first >= 0 && loco is MSTSSteamLocomotive)
4328+ {
4329+ if (last < Cars.Count - 1 && !loco.Flipped && Cars[last + 1].WagonType == TrainCar.WagonTypes.Tender)
4330+ last++;
4331+ else if (first > 0 && loco.Flipped && Cars[first - 1].WagonType == TrainCar.WagonTypes.Tender)
4332+ first--;
4333+ }
4334+ else // Other locomotive types
4335+ {
4336+ for (int i = last; i < Cars.Count && (Cars[i] is MSTSLocomotive && !(Cars[i] is MSTSSteamLocomotive)); i++)
4337+ last = i;
4338+ for (int i = first; i >= 0 && (Cars[i] is MSTSLocomotive && !(Cars[i] is MSTSSteamLocomotive)); i--)
4339+ first = i;
4340+ }
4341+
4342+ if (first < 0 || last < 0)
4343+ return null;
4344+ else
4345+ {
4346+ for (int i = first; i <= last; i++)
4347+ tempGroup.Add(Cars[i]);
4348+ return tempGroup;
4349+ }
4350+ }
4351+ else
4352+ return null;
4353+ }
4354+
4355+ //================================================================================================//
4356+ /// <summary>
4357+ /// Find connected DP lead locomotive
4358+ /// <\summary>
4359+
4360+ // Finds the DP lead locomotive to the TrainCar reference provided as input,
4361+ // returning the TrainCar reference to the DP lead unit.
4362+ // Returns null if there are no DP lead units controlling the given train car.
4363+
4364+ public TrainCar DetermineDPLeadLocomotive(TrainCar locoCar)
4365+ {
4366+ if (Cars.Contains(locoCar) && locoCar is MSTSLocomotive loco)
4367+ foreach (TrainCar dpLead in DPLeadUnits)
4368+ if (dpLead is MSTSLocomotive dpLeadLoco && dpLeadLoco.DPUnitID == loco.DPUnitID)
4369+ return dpLead;
4370+
4371+ return null;
4372+ }
4373+
4374+ //================================================================================================//
4375+ /// <summary>
4376+ /// Find compatibility of DP connection between two locomotives
4377+ /// <\summary>
4378+
4379+ // Returns a score judging the compatibility of a lead locomotive (first input as a TrainCar)
4380+ // and remote locomotive (second input as a TrainCar). The more capabilities the two locomotives
4381+ // share, the higher the number returned. There is an additional slight bias for remote
4382+ // locomotives having higher capabilities than the lead locomotive.
4383+ // Returns -1 if one of the input TrainCars isn't a locomotive
4384+
4385+ public float DetermineDPCompatibility(TrainCar leadCar, TrainCar remoteCar)
4386+ {
4387+ float score = 0;
4388+
4389+ if (leadCar is MSTSLocomotive lead && remoteCar is MSTSLocomotive remote)
4390+ {
4391+ if (remote.DPSyncTrainApplication)
4392+ {
4393+ if (lead.DPSyncTrainApplication)
4394+ score++;
4395+ else
4396+ score += 0.1f;
4397+ }
4398+ if (remote.DPSyncTrainRelease)
4399+ {
4400+ if (lead.DPSyncTrainRelease)
4401+ score++;
4402+ else
4403+ score += 0.1f;
4404+ }
4405+ if (remote.DPSyncEmergency)
4406+ {
4407+ if (lead.DPSyncEmergency)
4408+ score++;
4409+ else
4410+ score += 0.1f;
4411+ }
4412+ if (remote.DPSyncIndependent)
4413+ {
4414+ if (lead.DPSyncIndependent)
4415+ score++;
4416+ else
4417+ score += 0.1f;
4418+ }
4419+
4420+ return score;
4421+ }
4422+ else
4423+ return -1;
4424+ }
4425+
42344426 //================================================================================================//
42354427 /// <summary>
42364428 /// Propagate brake pressure
0 commit comments