Skip to content

Commit c5711ad

Browse files
committed
First version of TrainInfo tab in Help window.
1 parent 3b951a0 commit c5711ad

File tree

4 files changed

+240
-2
lines changed

4 files changed

+240
-2
lines changed

Source/Orts.Simulation/Simulation/RollingStocks/TrainCar.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3528,6 +3528,10 @@ public LatLonDirection GetLatLonDirection()
35283528

35293529
return new LatLonDirection(latLon, directionDeg); ;
35303530
}
3531+
3532+
public int GetWagonNumAxles() { return WagonNumAxles; }
3533+
3534+
public float GetGravitationalAccelerationMpS2() { return GravitationalAccelerationMpS2; }
35313535
}
35323536

35333537
public class WheelAxle : IComparer<WheelAxle>
373 Bytes
Loading

Source/RunActivity/RunActivity.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,9 @@
537537
<Content Include="Content\TrackMonitorImages.png">
538538
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
539539
</Content>
540-
<Content Include="Content\TrainOperationsCoupler.png">
540+
<Content Include="Content\TrainInfoSprites.png">
541+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
542+
</Content> <Content Include="Content\TrainOperationsCoupler.png">
541543
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
542544
</Content>
543545
<Content Include="Content\TrainOperations\TrainOperationsMap.png">

Source/RunActivity/Viewer3D/Popups/HelpWindow.cs

Lines changed: 233 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
using System;
2121
using System.Collections.Generic;
2222
using System.IO;
23+
using System.Diagnostics;
2324
using System.Linq;
2425
using System.Windows.Forms;
26+
using LibGit2Sharp;
2527
using Microsoft.Xna.Framework;
2628
using Microsoft.Xna.Framework.Graphics;
2729
using Orts.Simulation;
@@ -30,6 +32,11 @@
3032
using ORTS.Common;
3133
using ORTS.Common.Input;
3234
using ORTS.Settings;
35+
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
36+
using Orts.Formats.Msts;
37+
using SharpFont;
38+
using static Orts.Viewer3D.WebServices.TrainCarOperationsWebpage.OperationsSend;
39+
using SharpDX.Direct3D9;
3340

3441
namespace Orts.Viewer3D.Popups
3542
{
@@ -62,6 +69,14 @@ public class HelpWindow : Window
6269
StreamWriter wDbfEval;//Debrief eval
6370
public static float DbfEvalDistanceTravelled = 0;//Debrief eval
6471

72+
// for Train Info tab
73+
private Train LastPlayerTrain = null;
74+
private int LastPlayerTrainCarCount = 0;
75+
private static Texture2D TrainInfoSpriteSheet = null;
76+
//private struct CarInfo { public float MassKg; public bool IsEngine; }
77+
//private CarInfo[] CarMass = null; // TODO: Does not need to persist beyond method. Use List<T>(size) instead.
78+
private class CarInfo { public readonly float MassKg; public readonly bool IsEngine; public CarInfo(float mass, bool isEng) { MassKg = mass; IsEngine = isEng; } }
79+
6580
List<TabData> Tabs = new List<TabData>();
6681
int ActiveTab;
6782

@@ -74,7 +89,7 @@ public void LogSeparator(int nCols)
7489
}
7590

7691
public HelpWindow(WindowManager owner)
77-
: base(owner, Window.DecorationSize.X + owner.TextFontDefault.Height * 37, Window.DecorationSize.Y + owner.TextFontDefault.Height * 24, Viewer.Catalog.GetString("Help"))
92+
: base(owner, Window.DecorationSize.X + owner.TextFontDefault.Height * 42, Window.DecorationSize.Y + owner.TextFontDefault.Height * 24, Viewer.Catalog.GetString("Help"))
7893
{
7994
Tabs.Add(new TabData(Tab.KeyboardShortcuts, Viewer.Catalog.GetString("Key Commands"), (cl) =>
8095
{
@@ -576,6 +591,22 @@ owner.Viewer.Simulator.PlayerLocomotive is MSTSLocomotive &&
576591
scrollbox.Add(new TextFlow(scrollbox.RemainingWidth, ((MSTSLocomotive)owner.Viewer.Simulator.PlayerLocomotive).EngineOperatingProcedures));
577592
}
578593
}));
594+
Tabs.Add(new TabData(Tab.TrainInfo, Viewer.Catalog.GetString("Train Info"), (cl) =>
595+
{
596+
var labelWidth = cl.TextHeight * 11;
597+
var scrollbox = cl.AddLayoutScrollboxVertical(cl.RemainingWidth);
598+
if (Owner.Viewer.PlayerTrain != null)
599+
{
600+
string name = Owner.Viewer.PlayerTrain.Name;
601+
if (!String.IsNullOrEmpty(Owner.Viewer.Simulator.conFileName))
602+
name += " (" + Viewer.Catalog.GetString("created from") + " " + Path.GetFileName(Owner.Viewer.Simulator.conFileName) + ")";
603+
var line = scrollbox.AddLayoutHorizontalLineOfText();
604+
line.Add(new Label(labelWidth, line.RemainingHeight, Viewer.Catalog.GetString("Name:"), LabelAlignment.Left));
605+
line.Add(new Label(line.RemainingWidth, line.RemainingHeight, name, LabelAlignment.Left));
606+
607+
if (Owner.Viewer.PlayerTrain.Cars != null) { AddAggregatedTrainInfo(Owner.Viewer.PlayerTrain, scrollbox, labelWidth); }
608+
}
609+
}));
579610
}
580611

581612
private void ReportEvaluation(WindowManager owner, ControlLayout cl, TrainCar locomotive, int nmissedstation, string labeltext, int noverspeedcoupling, int dbfstationstopsremaining, Train playerTrain, int colWidth, Label indicator, bool lcurvespeeddependent, bool lbreakcouplers, int ndbfEvalTaskAccomplished)
@@ -1152,6 +1183,198 @@ private void writeline()
11521183
if (!lDebriefEvalFile) wDbfEval.WriteLine();
11531184
}
11541185

1186+
/// <summary>
1187+
/// Add aggregated info to the Train Info layout.
1188+
/// Loops through the train cars and aggregates the info.
1189+
/// </summary>
1190+
private void AddAggregatedTrainInfo(Train playerTrain, ControlLayout scrollbox, int labelWidth)
1191+
{
1192+
string numEngines = ""; // using "+" between DPU sets
1193+
int numCars = 0;
1194+
int numAxles = 0;
1195+
var massKg = playerTrain.MassKg;
1196+
string sectionMass = ""; // using " + " between wagon sets
1197+
var lengthM = playerTrain.Length;
1198+
var maxSpeedMps = playerTrain.TrainMaxSpeedMpS;
1199+
float totPowerW = 0f;
1200+
string sectionTractiveForce = ""; // using " + " between DPU sets
1201+
float maxBrakeForceN = 0f;
1202+
float lowestCouplerStrengthN = 9.999e8f; // impossible high force
1203+
float lowestDerailForceN = 9.999e8f; // impossible high force
1204+
1205+
int engCount = 0; string countSeparator = ""; // when set, indicates that subsequent engines are in a separate block
1206+
float engForceN = 0f; string forceSeparator = ""; // when set, indicates that subsequent engines are in a separate block
1207+
float wagMassKg = 0f; string massSeparator = ""; // when set, indicates that subsequent engines are in a separate block
1208+
int numOperativeBrakes = 0;
1209+
bool isMetric = false; bool isUK = false; // isImperial* does not seem to be used in simulation
1210+
1211+
if (TrainInfoSpriteSheet == null) { TrainInfoSpriteSheet = SharedTextureManager.Get(Owner.Viewer.RenderProcess.GraphicsDevice, Path.Combine(Owner.Viewer.ContentPath, "TrainInfoSprites.png")); }
1212+
const int spriteWidth = 6; const int spriteHeight = 26;
1213+
var carInfoList = new List<CarInfo>(playerTrain.Cars.Count);
1214+
1215+
foreach (var car in playerTrain.Cars)
1216+
{
1217+
// ignore (legacy) EOT
1218+
if (car.WagonType == TrainCar.WagonTypes.EOT || car.CarLengthM < 1.1f) { continue; }
1219+
1220+
var wag = car is MSTSWagon ? (MSTSWagon)car : null;
1221+
var eng = car is MSTSLocomotive ? (MSTSLocomotive)car : null;
1222+
1223+
var isEng = (car.WagonType == TrainCar.WagonTypes.Engine && eng != null && eng.MaxForceN > 25000); // count legacy driving trailers as wagons
1224+
1225+
if (car.IsMetric) { isMetric = true; }; if (car.IsUK) { isUK = true; }
1226+
1227+
if (isEng)
1228+
{
1229+
engCount++;
1230+
numAxles += eng.LocoNumDrvAxles + eng.GetWagonNumAxles();
1231+
totPowerW += eng.MaxPowerW;
1232+
engForceN += eng.MaxForceN;
1233+
1234+
// hanlde transition from wagons to engines
1235+
if (wagMassKg > 0)
1236+
{
1237+
sectionMass += massSeparator + FormatStrings.FormatLargeMass(wagMassKg, isMetric, isUK);
1238+
wagMassKg = 0f; massSeparator = " + ";
1239+
}
1240+
}
1241+
else if (wag != null)
1242+
{
1243+
numCars++;
1244+
numAxles += wag.GetWagonNumAxles();
1245+
wagMassKg += wag.MassKG;
1246+
1247+
// handle transition from engines to wagons
1248+
if (engCount > 0 || (engCount == 0 && numCars == 0))
1249+
{
1250+
numEngines += countSeparator + engCount.ToString();
1251+
engCount = 0; countSeparator = "+";
1252+
sectionTractiveForce += forceSeparator + FormatStrings.FormatForce(engForceN, isMetric);
1253+
engForceN = 0f; forceSeparator = " + ";
1254+
}
1255+
}
1256+
1257+
// wag and eng
1258+
if (wag != null)
1259+
{
1260+
maxBrakeForceN += wag.MaxBrakeForceN;
1261+
var couplerStrength = GetMinCouplerStrenght(wag);
1262+
if (couplerStrength < lowestCouplerStrengthN) { lowestCouplerStrengthN = couplerStrength; }
1263+
var derailForce = GetDerailForce(wag);
1264+
if (derailForce < lowestDerailForceN) { lowestDerailForceN = derailForce; }
1265+
if (wag.MaxBrakeForceN > 0) { numOperativeBrakes++; }
1266+
1267+
carInfoList.Add(new CarInfo(wag.MassKG, isEng));
1268+
}
1269+
}
1270+
1271+
if (engCount > 0) { numEngines = numEngines + countSeparator + engCount.ToString(); }
1272+
if (String.IsNullOrEmpty(numEngines)) { numEngines = "0"; }
1273+
if (engForceN > 0) { sectionTractiveForce += forceSeparator + FormatStrings.FormatForce(engForceN, isMetric); }
1274+
if (wagMassKg > 0) { sectionMass += massSeparator + FormatStrings.FormatLargeMass(wagMassKg, isMetric, isUK); }
1275+
1276+
var line = scrollbox.AddLayoutHorizontalLineOfText();
1277+
line.Add(new Label(labelWidth, line.RemainingHeight, Viewer.Catalog.GetString("Number of Engines:"), LabelAlignment.Left));
1278+
line.Add(new Label(line.RemainingWidth, line.RemainingHeight, numEngines, LabelAlignment.Left));
1279+
1280+
line = scrollbox.AddLayoutHorizontalLineOfText();
1281+
line.Add(new Label(labelWidth, line.RemainingHeight, Viewer.Catalog.GetString("Number of Cars:"), LabelAlignment.Left));
1282+
line.Add(new Label(line.RemainingWidth, line.RemainingHeight, numCars.ToString(), LabelAlignment.Left));
1283+
1284+
line = scrollbox.AddLayoutHorizontalLineOfText();
1285+
line.Add(new Label(labelWidth, line.RemainingHeight, Viewer.Catalog.GetString("Number of Axles:"), LabelAlignment.Left));
1286+
line.Add(new Label(line.RemainingWidth, line.RemainingHeight, numAxles.ToString(), LabelAlignment.Left));
1287+
1288+
line = scrollbox.AddLayoutHorizontalLineOfText();
1289+
line.Add(new Label(labelWidth, line.RemainingHeight, Viewer.Catalog.GetString("Total Weight:"), LabelAlignment.Left));
1290+
string massValue = FormatStrings.FormatLargeMass(massKg, isMetric, isUK) + " (" + sectionMass + ")";
1291+
line.Add(new Label(line.RemainingWidth, line.RemainingHeight, massValue, LabelAlignment.Left));
1292+
1293+
line = scrollbox.AddLayoutHorizontalLineOfText();
1294+
line.Add(new Label(labelWidth, line.RemainingHeight, Viewer.Catalog.GetString("Total Length:"), LabelAlignment.Left));
1295+
line.Add(new Label(line.RemainingWidth, line.RemainingHeight, FormatStrings.FormatShortDistanceDisplay(lengthM, isMetric), LabelAlignment.Left));
1296+
1297+
line = scrollbox.AddLayoutHorizontalLineOfText();
1298+
line.Add(new Label(labelWidth, line.RemainingHeight, Viewer.Catalog.GetString("Maximum Speed:"), LabelAlignment.Left));
1299+
line.Add(new Label(line.RemainingWidth, line.RemainingHeight, FormatStrings.FormatSpeedLimit(maxSpeedMps, isMetric), LabelAlignment.Left));
1300+
1301+
scrollbox.AddHorizontalSeparator();
1302+
1303+
line = scrollbox.AddLayoutHorizontalLineOfText();
1304+
line.Add(new Label(labelWidth, line.RemainingHeight, Viewer.Catalog.GetString("Total Power:"), LabelAlignment.Left));
1305+
line.Add(new Label(line.RemainingWidth, line.RemainingHeight, FormatStrings.FormatPower(totPowerW, isMetric, false, false), LabelAlignment.Left));
1306+
1307+
line = scrollbox.AddLayoutHorizontalLineOfText();
1308+
line.Add(new Label(labelWidth, line.RemainingHeight, Viewer.Catalog.GetString("Max Tractive Effort:"), LabelAlignment.Left));
1309+
line.Add(new Label(line.RemainingWidth, line.RemainingHeight, sectionTractiveForce, LabelAlignment.Left));
1310+
1311+
if (!isMetric)
1312+
{
1313+
float hpt = massKg > 0f ? W.ToHp(totPowerW) / Kg.ToTUS(massKg) : 0f;
1314+
line = scrollbox.AddLayoutHorizontalLineOfText();
1315+
line.Add(new Label(labelWidth, line.RemainingHeight, Viewer.Catalog.GetString("Horespower per Ton:"), LabelAlignment.Left));
1316+
line.Add(new Label(line.RemainingWidth, line.RemainingHeight, string.Format("{0:0.0}", hpt), LabelAlignment.Left));
1317+
1318+
float tpob = numOperativeBrakes > 0 ? Kg.ToTUS(massKg) / numOperativeBrakes : 0;
1319+
line = scrollbox.AddLayoutHorizontalLineOfText();
1320+
line.Add(new Label(labelWidth, line.RemainingHeight, Viewer.Catalog.GetString("Tons per Operative Brake:"), LabelAlignment.Left));
1321+
line.Add(new Label(line.RemainingWidth, line.RemainingHeight, string.Format("{0:0}", tpob), LabelAlignment.Left));
1322+
}
1323+
1324+
line = scrollbox.AddLayoutHorizontalLineOfText();
1325+
line.Add(new Label(labelWidth, line.RemainingHeight, Viewer.Catalog.GetString("Lowest Coupler Strength:"), LabelAlignment.Left));
1326+
line.Add(new Label(line.RemainingWidth, line.RemainingHeight, FormatStrings.FormatForce(lowestCouplerStrengthN, isMetric), LabelAlignment.Left));
1327+
1328+
line = scrollbox.AddLayoutHorizontalLineOfText();
1329+
line.Add(new Label(labelWidth, line.RemainingHeight, Viewer.Catalog.GetString("Lowest Derail Force:"), LabelAlignment.Left));
1330+
line.Add(new Label(line.RemainingWidth, line.RemainingHeight, FormatStrings.FormatForce(lowestDerailForceN, isMetric), LabelAlignment.Left));
1331+
1332+
scrollbox.AddHorizontalSeparator();
1333+
1334+
// weight graph
1335+
line = scrollbox.AddLayoutHorizontalLineOfText();
1336+
line.Add(new Label(line.RemainingWidth, line.RemainingHeight, Viewer.Catalog.GetString("Car Weight (front on left):"), LabelAlignment.Left));
1337+
scrollbox.AddSpace(scrollbox.RemainingWidth, 2);
1338+
var hscrollbox = scrollbox.AddLayoutScrollboxHorizontal(SystemInformation.HorizontalScrollBarHeight + spriteHeight + 2);
1339+
var vbox = hscrollbox.AddLayoutVertical(carInfoList.Count * spriteWidth + 2);
1340+
var weightbox = vbox.AddLayoutHorizontal(spriteHeight + 2);
1341+
foreach (var car in carInfoList)
1342+
{
1343+
int spriteIdx = (int)Math.Floor(car.MassKg / 15000f);
1344+
if (spriteIdx < 0) { spriteIdx = 0; } else if (spriteIdx > 11) { spriteIdx = 11; }
1345+
var image = new Image(spriteWidth, spriteHeight);
1346+
if (car.IsEngine) { image.Source = new Rectangle(spriteIdx * spriteWidth, 0, spriteWidth, spriteHeight); }
1347+
else { image.Source = new Rectangle(spriteIdx * spriteWidth, spriteHeight, spriteWidth, spriteHeight); }
1348+
image.Texture = TrainInfoSpriteSheet;
1349+
weightbox.Add(image);
1350+
}
1351+
}
1352+
1353+
/// <summary>
1354+
/// Get the lowest coupler strength for a car.
1355+
/// </summary>
1356+
private float GetMinCouplerStrenght(MSTSWagon wag)
1357+
{
1358+
float couplerStrength = 1e10f; // default from TrainCar.GetCouplerBreak2N()
1359+
if (wag.Couplers.Count > 1 && wag.Couplers[1].Break2N < couplerStrength) { couplerStrength = wag.Couplers[1].Break2N; }
1360+
else if (wag.Couplers.Count > 0 && wag.Couplers[0].Break2N < couplerStrength) { couplerStrength = wag.Couplers[0].Break2N; }
1361+
if (couplerStrength < 99f) { couplerStrength = 1e10f; } // use default if near zero
1362+
return couplerStrength;
1363+
}
1364+
1365+
/// <summary>
1366+
/// Calculate the lowest force it takes to derail the car (on a curve).
1367+
/// It is equivalent to the vertical force at the wheel.
1368+
/// </summary>
1369+
private float GetDerailForce(MSTSWagon wag)
1370+
{
1371+
// see TotalWagonVerticalDerailForceN in TrainCar.cs
1372+
float derailForce = 2.0e5f; // 45k lbf on wheel
1373+
int numWheels = wag.GetWagonNumAxles() * 2;
1374+
if (numWheels > 2) { derailForce = wag.MassKG / numWheels * wag.GetGravitationalAccelerationMpS2(); }
1375+
return derailForce;
1376+
}
1377+
11551378
public override void TabAction()
11561379
{
11571380
ActiveTab = (ActiveTab + 1) % Tabs.Count;
@@ -1195,6 +1418,7 @@ enum Tab
11951418
ActivityEvaluation,
11961419
TimetableBriefing,
11971420
LocomotiveProcedures,
1421+
TrainInfo,
11981422
}
11991423

12001424
class TabData
@@ -1253,6 +1477,14 @@ public override void PrepareFrame(ElapsedTime elapsedTime, bool updateFull)
12531477
Layout();
12541478
}
12551479
}
1480+
else if (Tabs[ActiveTab].Tab == Tab.TrainInfo && Owner.Viewer.PlayerTrain != null)
1481+
{
1482+
if (LastPlayerTrain == null || Owner.Viewer.PlayerTrain != LastPlayerTrain || LastPlayerTrainCarCount != Owner.Viewer.PlayerTrain.Cars.Count)
1483+
{
1484+
LastPlayerTrain = Owner.Viewer.PlayerTrain; LastPlayerTrainCarCount = Owner.Viewer.PlayerTrain.Cars.Count;
1485+
Layout();
1486+
}
1487+
}
12561488
if (this.ActivityUpdated == true) //true value is set in ActivityWindow.cs
12571489
{
12581490
this.ActivityUpdated = false;

0 commit comments

Comments
 (0)