|
| 1 | +// COPYRIGHT 2019, 2020 by the Open Rails project. |
| 2 | +// |
| 3 | +// This file is part of Open Rails. |
| 4 | +// |
| 5 | +// Open Rails is free software: you can redistribute it and/or modify |
| 6 | +// it under the terms of the GNU General Public License as published by |
| 7 | +// the Free Software Foundation, either version 3 of the License, or |
| 8 | +// (at your option) any later version. |
| 9 | +// |
| 10 | +// Open Rails is distributed in the hope that it will be useful, |
| 11 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | +// GNU General Public License for more details. |
| 14 | +// |
| 15 | +// You should have received a copy of the GNU General Public License |
| 16 | +// along with Open Rails. If not, see <http://www.gnu.org/licenses/>. |
| 17 | + |
| 18 | +using Orts.Simulation.Physics; |
| 19 | +using Orts.Simulation.RollingStocks; |
| 20 | +using Orts.Simulation.RollingStocks.SubSystems.Brakes; |
| 21 | +using ORTS.Common; |
| 22 | +using ORTS.Common.Input; |
| 23 | +using System; |
| 24 | +using System.Collections.Generic; |
| 25 | +using System.Drawing; |
| 26 | +using System.Linq; |
| 27 | + |
| 28 | +namespace Orts.Viewer3D.WebServices |
| 29 | +{ |
| 30 | + public static class TrainDpuDisplay |
| 31 | + { |
| 32 | + /// <summary> |
| 33 | + /// A Train Dpu row with data fields. |
| 34 | + /// </summary> |
| 35 | + public struct ListLabel |
| 36 | + { |
| 37 | + public string FirstCol; |
| 38 | + public List<string> LastCol; |
| 39 | + public List<string> SymbolCol; |
| 40 | + } |
| 41 | + |
| 42 | + private static bool normalVerticalMode = true;// vertical window size |
| 43 | + private static int dieselLocomotivesCount = 0; |
| 44 | + |
| 45 | + /// <summary> |
| 46 | + /// Table of Colors to client-side color codes. |
| 47 | + /// </summary> |
| 48 | + /// <remarks> |
| 49 | + /// Compare codes with index.css. |
| 50 | + /// </remarks> |
| 51 | + private static readonly Dictionary<Color, string> ColorCode = new Dictionary<Color, string> |
| 52 | + { |
| 53 | + { Color.Yellow, "???" }, |
| 54 | + { Color.Green, "??!" }, |
| 55 | + { Color.Black, "?!?" }, |
| 56 | + { Color.PaleGreen, "?!!" }, |
| 57 | + { Color.White, "!??" }, |
| 58 | + { Color.Orange, "!!?" }, |
| 59 | + { Color.OrangeRed, "!!!" }, |
| 60 | + { Color.Cyan, "%%%" }, |
| 61 | + { Color.Brown, "%$$" }, |
| 62 | + { Color.LightGreen, "%%$" }, |
| 63 | + { Color.Blue, "$%$" }, |
| 64 | + { Color.LightSkyBlue, "$$$" }, |
| 65 | + }; |
| 66 | + |
| 67 | + private static class Symbols |
| 68 | + { |
| 69 | + public const string Fence = "\u2590"; |
| 70 | + } |
| 71 | + |
| 72 | + private static readonly Dictionary<string, string> FirstColToAbbreviated = new Dictionary<string, string>() |
| 73 | + { |
| 74 | + [Viewer.Catalog.GetString("Flow")] = Viewer.Catalog.GetString("FLOW"),// |
| 75 | + [Viewer.Catalog.GetString("Fuel")] = Viewer.Catalog.GetString("FUEL"),// |
| 76 | + [Viewer.Catalog.GetString("Load")] = Viewer.Catalog.GetString("LOAD"),// |
| 77 | + [Viewer.Catalog.GetString("Loco Groups")] = Viewer.Catalog.GetString("GRUP"), |
| 78 | + [Viewer.Catalog.GetString("Oil Pressure")] = Viewer.Catalog.GetString("OIL"),// |
| 79 | + [Viewer.Catalog.GetString("Power")] = Viewer.Catalog.GetString("POWR"),// |
| 80 | + [Viewer.Catalog.GetString("Remote")] = Viewer.Catalog.GetString("RMT"),// |
| 81 | + [Viewer.Catalog.GetString("RPM")] = Viewer.Catalog.GetString("RPM"),// |
| 82 | + [Viewer.Catalog.GetString("Reverser")] = Viewer.Catalog.GetString("REVR"),// |
| 83 | + [Viewer.Catalog.GetString("Status")] = Viewer.Catalog.GetString("STAT"),// |
| 84 | + [Viewer.Catalog.GetString("Temperature")] = Viewer.Catalog.GetString("TEMP"),// |
| 85 | + [Viewer.Catalog.GetString("Throttle")] = Viewer.Catalog.GetString("THRO"),// |
| 86 | + [Viewer.Catalog.GetString("Time")] = Viewer.Catalog.GetString("TIME"),// |
| 87 | + [Viewer.Catalog.GetString("Tractive Effort")] = Viewer.Catalog.GetString("TRACT")// |
| 88 | + }; |
| 89 | + |
| 90 | + private static readonly Dictionary<string, string> LastColToAbbreviated = new Dictionary<string, string>() |
| 91 | + { |
| 92 | + [Viewer.Catalog.GetString("Forward")] = Viewer.Catalog.GetString("Forw."), |
| 93 | + [Viewer.Catalog.GetString("Idle")] = Viewer.Catalog.GetString("Idle"), |
| 94 | + [Viewer.Catalog.GetString("Running")] = Viewer.Catalog.GetString("Runn") |
| 95 | + }; |
| 96 | + |
| 97 | + |
| 98 | + /// <summary> |
| 99 | + /// Sanitize the fields of a <see cref="ListLabel"/> in-place. |
| 100 | + /// </summary> |
| 101 | + /// <param name="label">A reference to the <see cref="ListLabel"/> to check.</param> |
| 102 | + private static void CheckLabel(ref ListLabel label, bool normalMode) |
| 103 | + { |
| 104 | + void CheckString(ref string s) => s = s ?? ""; |
| 105 | + CheckString(ref label.FirstCol); |
| 106 | + |
| 107 | + if (label.LastCol != null) |
| 108 | + { |
| 109 | + for (int i = 0; i < label.LastCol.Count; i++) |
| 110 | + { |
| 111 | + var LastCol = label.LastCol[i]; |
| 112 | + CheckString(ref LastCol); |
| 113 | + label.LastCol[i] = LastCol; |
| 114 | + } |
| 115 | + } |
| 116 | + |
| 117 | + if (label.SymbolCol != null) |
| 118 | + { |
| 119 | + for (int i = 0; i < label.SymbolCol.Count; i++) |
| 120 | + { |
| 121 | + var symbolCol = label.SymbolCol[i]; |
| 122 | + CheckString(ref symbolCol); |
| 123 | + label.SymbolCol[i] = symbolCol; |
| 124 | + } |
| 125 | + } |
| 126 | + |
| 127 | + if (!normalMode) |
| 128 | + { |
| 129 | + foreach (KeyValuePair<string, string> mapping in FirstColToAbbreviated) |
| 130 | + label.FirstCol = label.FirstCol.Replace(mapping.Key, mapping.Value); |
| 131 | + foreach (KeyValuePair<string, string> mapping in LastColToAbbreviated) |
| 132 | + { |
| 133 | + if (label.LastCol != null) |
| 134 | + { |
| 135 | + for (int i = 0; i < label.LastCol.Count; i++) |
| 136 | + { |
| 137 | + label.LastCol[i] = label.LastCol[i].Replace(mapping.Key, mapping.Value); |
| 138 | + } |
| 139 | + } |
| 140 | + } |
| 141 | + } |
| 142 | + } |
| 143 | + |
| 144 | + /// <summary> |
| 145 | + /// Retrieve a formatted list <see cref="ListLabel"/>s to be displayed as an in-browser Track Monitor. |
| 146 | + /// </summary> |
| 147 | + /// <param name="viewer">The Viewer to read train data from.</param> |
| 148 | + /// <returns>A list of <see cref="ListLabel"/>s, one per row of the popup.</returns> |
| 149 | + public static IEnumerable<ListLabel> TrainDpuDisplayList(this Viewer viewer, bool normalTextMode = true) |
| 150 | + { |
| 151 | + bool useMetric = viewer.MilepostUnitsMetric; |
| 152 | + var labels = new List<ListLabel>(); |
| 153 | + |
| 154 | + void AddLabel(ListLabel label) |
| 155 | + { |
| 156 | + CheckLabel(ref label, normalTextMode); |
| 157 | + labels.Add(label); |
| 158 | + } |
| 159 | + void AddSeparator() => AddLabel(new ListLabel |
| 160 | + { |
| 161 | + FirstCol = "Sprtr", |
| 162 | + }); |
| 163 | + |
| 164 | + TrainCar trainCar = viewer.PlayerLocomotive; |
| 165 | + Train train = trainCar.Train; |
| 166 | + MSTSLocomotive locomotive = (MSTSLocomotive)trainCar; |
| 167 | + var multipleUnitsConfiguration = locomotive.GetMultipleUnitsConfiguration(); |
| 168 | + List<string> lastCol; |
| 169 | + List<string> symbolCol; |
| 170 | + var notDpuTrain = false; |
| 171 | + |
| 172 | + // Distributed Power |
| 173 | + if (multipleUnitsConfiguration != null) |
| 174 | + { |
| 175 | + lastCol = new List<string>(); |
| 176 | + symbolCol = new List<string>(); |
| 177 | + char[] multipleUnits = multipleUnitsConfiguration.Replace(" ", "").ToCharArray(); |
| 178 | + symbolCol.Add("");//first symbol empty |
| 179 | + foreach (char ch in multipleUnits) |
| 180 | + { |
| 181 | + if (ch.ToString() != " ") |
| 182 | + { |
| 183 | + if (Char.IsDigit(ch)) |
| 184 | + { |
| 185 | + lastCol.Add(ch.ToString()); continue; |
| 186 | + } |
| 187 | + else |
| 188 | + symbolCol.Add(ch == '|' ? Symbols.Fence + ColorCode[Color.Green] : ch == '–' ? ch.ToString() : ""); |
| 189 | + } |
| 190 | + } |
| 191 | + |
| 192 | + // allows to draw the second fence |
| 193 | + lastCol.Add(""); |
| 194 | + symbolCol.Add(""); |
| 195 | + AddLabel(new ListLabel |
| 196 | + { |
| 197 | + FirstCol = Viewer.Catalog.GetString("Loco Groups"), |
| 198 | + SymbolCol = symbolCol, |
| 199 | + LastCol = lastCol |
| 200 | + }); |
| 201 | + AddSeparator(); |
| 202 | + } |
| 203 | + else |
| 204 | + { |
| 205 | + lastCol = new List<string>(); |
| 206 | + symbolCol = new List<string>(); |
| 207 | + lastCol.Add(""); |
| 208 | + symbolCol.Add(""); |
| 209 | + AddLabel(new ListLabel |
| 210 | + { |
| 211 | + FirstCol = Viewer.Catalog.GetString(" Distributed power management not available with this player train. "), |
| 212 | + SymbolCol = symbolCol, |
| 213 | + LastCol = lastCol |
| 214 | + }); |
| 215 | + notDpuTrain = true; |
| 216 | + } |
| 217 | + |
| 218 | + if (locomotive != null && !notDpuTrain) |
| 219 | + { |
| 220 | + int numberOfDieselLocomotives = 0; |
| 221 | + int maxNumberOfEngines = 0; |
| 222 | + for (var i = 0; i < train.Cars.Count; i++) |
| 223 | + { |
| 224 | + if (train.Cars[i] is MSTSDieselLocomotive) |
| 225 | + { |
| 226 | + numberOfDieselLocomotives++; |
| 227 | + maxNumberOfEngines = Math.Max(maxNumberOfEngines, (train.Cars[i] as MSTSDieselLocomotive).DieselEngines.Count); |
| 228 | + } |
| 229 | + } |
| 230 | + if (numberOfDieselLocomotives > 0) |
| 231 | + { |
| 232 | + var dieselLoco = MSTSDieselLocomotive.GetDpuHeader(normalVerticalMode, numberOfDieselLocomotives, maxNumberOfEngines).Replace("\t", ""); |
| 233 | + string[] dieselLocoHeader = dieselLoco.Split('\n'); |
| 234 | + string[,] tempStatus = new string[numberOfDieselLocomotives, dieselLocoHeader.Length]; |
| 235 | + var k = 0; |
| 236 | + var dpUnitId = 0; |
| 237 | + var dpUId = -1; |
| 238 | + for (var i = 0; i < train.Cars.Count; i++) |
| 239 | + { |
| 240 | + if (train.Cars[i] is MSTSDieselLocomotive) |
| 241 | + { |
| 242 | + if (dpUId != (train.Cars[i] as MSTSLocomotive).DPUnitID) |
| 243 | + { |
| 244 | + var status = (train.Cars[i] as MSTSDieselLocomotive).GetDpuStatus(normalVerticalMode).Split('\t'); |
| 245 | + var fence = ((dpUnitId != (dpUnitId = train.Cars[i].RemoteControlGroup)) ? "|" : " "); |
| 246 | + for (var j = 0; j < status.Length; j++) |
| 247 | + { |
| 248 | + // fence |
| 249 | + tempStatus[k, j] = fence + status[j]; |
| 250 | + } |
| 251 | + dpUId = (train.Cars[i] as MSTSLocomotive).DPUnitID; |
| 252 | + k++; |
| 253 | + } |
| 254 | + } |
| 255 | + } |
| 256 | + |
| 257 | + dieselLocomotivesCount = k;// only leaders loco group |
| 258 | + for (var j = 0; j < dieselLocoHeader.Count(); j++) |
| 259 | + { |
| 260 | + lastCol = new List<string>(); |
| 261 | + symbolCol = new List<string>(); |
| 262 | + |
| 263 | + for (int i = 0; i < dieselLocomotivesCount; i++) |
| 264 | + { |
| 265 | + symbolCol.Add(tempStatus[i, j] != null && tempStatus[i, j].Contains("|") ? Symbols.Fence + ColorCode[Color.Green] : " "); |
| 266 | + lastCol.Add(tempStatus[i, j]); |
| 267 | + } |
| 268 | + |
| 269 | + // allows to draw the second fence |
| 270 | + lastCol.Add(""); |
| 271 | + symbolCol.Add(" "); |
| 272 | + |
| 273 | + AddLabel(new ListLabel |
| 274 | + { |
| 275 | + FirstCol = dieselLocoHeader[j], |
| 276 | + SymbolCol = symbolCol, |
| 277 | + LastCol = lastCol |
| 278 | + }); |
| 279 | + } |
| 280 | + } |
| 281 | + AddLabel(new ListLabel()); |
| 282 | + } |
| 283 | + |
| 284 | + AddLabel(new ListLabel()); |
| 285 | + return labels; |
| 286 | + } |
| 287 | + } |
| 288 | +} |
0 commit comments