@@ -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
@@ -689,6 +693,7 @@ public Train(Simulator simulator, BinaryReader inf)
689693 BrakeLine2PressurePSI = inf.ReadSingle();
690694 BrakeLine3PressurePSI = inf.ReadSingle();
691695 BrakeLine4 = inf.ReadSingle();
696+ TotalBrakePipeFlowM3pS = inf.ReadSingle();
692697 aiBrakePercent = inf.ReadSingle();
693698 LeadLocomotiveIndex = inf.ReadInt32();
694699 RetainerSetting = (RetainerSetting)inf.ReadInt32();
@@ -1037,6 +1042,7 @@ public virtual void Save(BinaryWriter outf)
10371042 outf.Write(BrakeLine2PressurePSI);
10381043 outf.Write(BrakeLine3PressurePSI);
10391044 outf.Write(BrakeLine4);
1045+ outf.Write(TotalBrakePipeFlowM3pS);
10401046 outf.Write(aiBrakePercent);
10411047 outf.Write(LeadLocomotiveIndex);
10421048 outf.Write((int)RetainerSetting);
@@ -1312,6 +1318,7 @@ public TrainCar GetNextCab()
13121318 LeadLocomotiveIndex = Math.Abs(nextCabIndex) - 1;
13131319 Trace.Assert(LeadLocomotive != null, "Tried to switch to non-existent loco");
13141320 TrainCar newLead = LeadLocomotive; // Changing LeadLocomotiveIndex also changed LeadLocomotive
1321+ SetDPUnitIDs(true); // DP IDs must be reprocessed when lead locomotive changes
13151322 ((MSTSLocomotive)newLead).UsingRearCab = nextCabIndex < 0;
13161323
13171324 if (oldLead != null && newLead != null && oldLead != newLead)
@@ -1370,6 +1377,7 @@ public void LeadNextLocomotive()
13701377 else if (coud > 1)
13711378 LeadLocomotiveIndex = firstLead;
13721379 TrainCar newLead = LeadLocomotive;
1380+ SetDPUnitIDs(true); // DP IDs must be reprocessed when lead locomotive changes
13731381 if (prevLead != null && newLead != null && prevLead != newLead)
13741382 newLead.CopyControllerSettings(prevLead);
13751383 }
@@ -1525,19 +1533,59 @@ public void ReverseCars()
15251533 /// </summary>
15261534 public void SetDPUnitIDs(bool keepRemoteGroups = false)
15271535 {
1536+ // List to keep track of new 'lead' DP units
1537+ // 'Lead' DP units follow the air brake commands of the master loco, 'trail' DP units do not
1538+ List<TrainCar> tempDPLead = new List<TrainCar>();
1539+
1540+ // List of each DP group's locomotives
1541+ List<List<TrainCar>> tempLocoGroups = new List<List<TrainCar>>();
1542+
1543+ var prevId = -1;
1544+
15281545 var id = 0;
15291546 foreach (var car in Cars)
15301547 {
15311548 //Console.WriteLine("___{0} {1}", car.CarID, id);
1532- if (car is MSTSLocomotive)
1549+ if (car is MSTSLocomotive loco )
15331550 {
1534- (car as MSTSLocomotive).DPUnitID = id;
1551+ loco.DPUnitID = id;
1552+
1553+ if (id != prevId && !tempDPLead.Contains(car)) // If this is a new ID, that means we found a 'lead' unit
1554+ {
1555+ tempDPLead.Add(car);
1556+
1557+ prevId = id;
1558+ }
1559+
15351560 if (car.RemoteControlGroup == 1 && !keepRemoteGroups)
15361561 car.RemoteControlGroup = 0;
15371562 }
15381563 else
15391564 id++;
15401565 }
1566+
1567+ foreach (TrainCar locoCar in tempDPLead)
1568+ {
1569+ // The train's lead unit should always be a DP lead unit, even if not at the front
1570+ // If a different locomotive in the lead loco's group has been declared DP lead, replace that loco with the lead loco
1571+ if (LeadLocomotive is MSTSLocomotive lead && locoCar is MSTSLocomotive loco)
1572+ if (loco.DPUnitID == lead.DPUnitID && locoCar != LeadLocomotive)
1573+ {
1574+ tempDPLead.Insert(tempDPLead.IndexOf(locoCar), LeadLocomotive);
1575+ tempDPLead.Remove(locoCar);
1576+ break; // foreach doesn't like it when the collection is modified during the loop, break to mitigate error
1577+ }
1578+ }
1579+
1580+ DPLeadUnits = tempDPLead;
1581+
1582+ foreach (TrainCar loco in DPLeadUnits)
1583+ {
1584+ // Find all locomotives connected to each DP unit
1585+ tempLocoGroups.Add(DetermineLocomotiveGroup(loco));
1586+ }
1587+
1588+ LocoGroups = tempLocoGroups;
15411589 }
15421590
15431591 /// <summary>
@@ -4217,6 +4265,76 @@ public TrainCar FindLeadLocomotive()
42174265 return null;
42184266 }
42194267
4268+ //================================================================================================//
4269+ /// <summary>
4270+ /// Find connected locomotives
4271+ /// <\summary>
4272+
4273+ // Finds all locomotives connected to the TrainCar reference provided as input,
4274+ // returning the connected locomotives* as a list of TrainCars.
4275+ // Returns null if there are no locomotives in the given group.
4276+ // *If the input is a steam locomotive, the output will instead be the steam locomotive and any tenders connected.
4277+ // Useful for determining locomotive brake propagation on groups of locomotives other than the lead group.
4278+
4279+ public List<TrainCar> DetermineLocomotiveGroup(TrainCar loco)
4280+ {
4281+ if (loco is MSTSLocomotive)
4282+ {
4283+ List<TrainCar> tempGroup = new List<TrainCar>();
4284+ int first;
4285+ int last;
4286+
4287+ first = last = Cars.IndexOf(loco);
4288+
4289+ // If locomotive is a steam locomotive, check for tenders only
4290+ if (first >= 0 && loco is MSTSSteamLocomotive)
4291+ {
4292+ if (first > 0 && Cars[first - 1].WagonType == TrainCar.WagonTypes.Tender)
4293+ first--;
4294+ if (last < Cars.Count - 1 && Cars[last + 1].WagonType == TrainCar.WagonTypes.Tender)
4295+ last++;
4296+ }
4297+ else // Other locomotive types
4298+ {
4299+ for (int i = last; i < Cars.Count && (Cars[i] is MSTSLocomotive && !(Cars[i] is MSTSSteamLocomotive)); i++)
4300+ last = i;
4301+ for (int i = first; i >= 0 && (Cars[i] is MSTSLocomotive && !(Cars[i] is MSTSSteamLocomotive)); i--)
4302+ first = i;
4303+ }
4304+
4305+ if (first < 0 || last < 0)
4306+ return null;
4307+ else
4308+ {
4309+ for (int i = first; i <= last; i++)
4310+ tempGroup.Add(Cars[i]);
4311+ return tempGroup;
4312+ }
4313+ }
4314+ else
4315+ return null;
4316+ }
4317+
4318+ //================================================================================================//
4319+ /// <summary>
4320+ /// Find connected DP lead locomotive
4321+ /// <\summary>
4322+
4323+ // Finds the DP lead locomotive to the TrainCar reference provided as input,
4324+ // returning the TrainCar reference to the DP lead unit.
4325+ // Returns null if there are no DP lead units controlling the given train car.
4326+
4327+ public TrainCar DetermineDPLeadLocomotive(TrainCar locoCar)
4328+ {
4329+ if (Cars.Contains(locoCar) && locoCar is MSTSLocomotive loco)
4330+ foreach (TrainCar dpLead in DPLeadUnits)
4331+ if (dpLead is MSTSLocomotive dpLeadLoco)
4332+ if (dpLeadLoco.DPUnitID == loco.DPUnitID)
4333+ return dpLead;
4334+
4335+ return null;
4336+ }
4337+
42204338 //================================================================================================//
42214339 /// <summary>
42224340 /// Propagate brake pressure
0 commit comments