Skip to content

Commit 597dfc7

Browse files
authored
Merge pull request #1001 from twpol/feature/telemetry-menu
Telemetry collection and menu options #or-telemetry-system
2 parents 0b281d7 + 2ef2b6e commit 597dfc7

20 files changed

+1327
-535
lines changed

Source/Contrib/DataCollector/Program.cs

+7-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class Program
2929
static void Main(string[] args)
3030
{
3131
if (args.Contains("/system", StringComparer.OrdinalIgnoreCase))
32-
SystemInfo.WriteSystemDetails(Console.Out);
32+
CollectSystem();
3333
else if (args.Contains("/tile-terrtex", StringComparer.OrdinalIgnoreCase))
3434
CollectTileTerrtex(args);
3535
else
@@ -53,6 +53,12 @@ static void ShowHelp()
5353
Console.WriteLine(" /help Show help and usage information");
5454
}
5555

56+
static void CollectSystem()
57+
{
58+
Console.Error.WriteLine("Collecting information...");
59+
SystemInfo.WriteSystemDetails(Console.Out);
60+
}
61+
5662
struct TileTerrtexDirectory
5763
{
5864
public string Path;

Source/Contrib/SimulatorTester/UserSettings.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
// along with Open Rails. If not, see <http://www.gnu.org/licenses/>.
1717

1818
using System.Collections.Generic;
19-
using ORTS.Settings;
19+
using ORTS.Common;
2020

2121
namespace SimulatorTester
2222
{
Loading
Loading

Source/Documentation/Manual/options.rst

+34
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,40 @@ Otherwise, if the loco has an Overspeed Monitor specified in its ENG file, then
129129

130130
This monitor is enabled by checking the option.
131131

132+
133+
.. _telemetry:
134+
135+
Telemetry
136+
---------
137+
138+
.. image:: images/options-telemetry.png
139+
140+
These options let you choose which (if any) anonymous data collection types you would like to enable, preview the data that will be collected, and visit the `telemetry server and source code <https://telemetry.openrails.org>`_.
141+
142+
Each anonymous data collection type can be set to:
143+
144+
- **Undecided (off)** - (default) no data is collected or sent, but we may ask if you want to participate via :ref:`notifications`
145+
- **Off** - no data is collected or sent
146+
- **On** - data is collected and sent automatically
147+
148+
There is no option to allow you to check through the collected data before it is sent.
149+
150+
Application, runtime, operating system, and hardware properties
151+
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
152+
153+
This anonymous data collection type can be previewed to see what will be sent; for example::
154+
155+
Application = Open Rails U2025.01.31-1152 (X64)
156+
Runtime = .NET Framework 4.8.9290.0
157+
System = Microsoft Windows 11 Home 10.0.26100 (X64; en-GB; en-GB,en-US,ja-JP)
158+
Memory = 32,592 MB
159+
CPU = 12th Gen Intel(R) Core(TM) i7-1255U (GenuineIntel; 12 threads; 2,600 MHz)
160+
GPU = Intel(R) Iris(R) Xe Graphics (Intel Corporation; 128 MB)
161+
Direct3D = 12_1,12_0,11_1,11_0,10_1,10_0,9_3,9_2,9_1
162+
163+
This is also included at the start of every :ref:`log file <driving-logfile>`.
164+
165+
132166
Audio Options
133167
=============
134168

Source/Menu/MainForm.cs

+34-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// COPYRIGHT 2009, 2010, 2011, 2012, 2013, 2014, 2015 by the Open Rails project.
1+
// COPYRIGHT 2009 - 2024 by the Open Rails project.
22
//
33
// This file is part of Open Rails.
44
//
@@ -56,6 +56,7 @@ public enum UserAction
5656

5757
bool Initialized;
5858
UserSettings Settings;
59+
TelemetryManager TelemetryManager;
5960
List<Folder> Folders = new List<Folder>();
6061
public List<Route> Routes = new List<Route>();
6162
List<Activity> Activities = new List<Activity>();
@@ -149,6 +150,7 @@ void MainForm_Shown(object sender, EventArgs e)
149150
{
150151
var options = Environment.GetCommandLineArgs().Where(a => (a.StartsWith("-") || a.StartsWith("/"))).Select(a => a.Substring(1));
151152
Settings = new UserSettings(options);
153+
TelemetryManager = new TelemetryManager(Settings.Telemetry);
152154

153155
Cursor = Cursors.Default;
154156

@@ -266,6 +268,7 @@ orderby tool.Text
266268
ShowTimetableEnvironment();
267269

268270
CheckForUpdate();
271+
CheckForTelemetry();
269272

270273
if (!Initialized)
271274
{
@@ -345,6 +348,21 @@ public virtual void OnCheckUpdatesAgain(EventArgs e)
345348
CheckForUpdate();
346349
}
347350

351+
void CheckForTelemetry()
352+
{
353+
// DO NOT await this call as we want it to run in the background
354+
_ = TelemetryManager.SubmitIfDue(TelemetryType.System, () => new
355+
{
356+
SystemInfo.Application,
357+
SystemInfo.Runtime,
358+
SystemInfo.OperatingSystem,
359+
SystemInfo.InstalledMemoryMB,
360+
SystemInfo.CPUs,
361+
SystemInfo.GPUs,
362+
SystemInfo.Direct3DFeatureLevels
363+
});
364+
}
365+
348366
void LoadLanguage()
349367
{
350368
if (Settings.Language.Length > 0)
@@ -543,10 +561,15 @@ void testingToolStripMenuItem_Click(object sender, EventArgs e)
543561
}
544562

545563
void buttonOptions_Click(object sender, EventArgs e)
564+
{
565+
ShowOptionsDialog();
566+
}
567+
568+
public void ShowOptionsDialog()
546569
{
547570
SaveOptions();
548571

549-
using (var form = new OptionsForm(Settings, UpdateManager, BaseDocumentationUrl))
572+
using (var form = new OptionsForm(Settings, UpdateManager, TelemetryManager, BaseDocumentationUrl))
550573
{
551574
switch (form.ShowDialog(this))
552575
{
@@ -560,7 +583,15 @@ void buttonOptions_Click(object sender, EventArgs e)
560583
}
561584
}
562585
}
563-
586+
587+
public void ShowTelemetryDialog()
588+
{
589+
using (var telemetryForm = new TelemetryForm(TelemetryManager))
590+
{
591+
telemetryForm.ShowDialog(this);
592+
}
593+
}
594+
564595
void buttonDownloadContent_Click(object sender, EventArgs e)
565596
{
566597
using (var form = new ContentForm(Settings, BaseDocumentationUrl))

Source/Menu/Notifications/NotificationManager.cs

+38-41
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// COPYRIGHT 2009 - 2024 by the Open Rails project.
1+
// COPYRIGHT 2009 - 2024 by the Open Rails project.
22
//
33
// This file is part of Open Rails.
44
//
@@ -128,6 +128,7 @@ public void CheckNotifications()
128128
}
129129
catch (Exception ex)
130130
{
131+
AppendToLog(ex.ToString());
131132
Error = ex;
132133
}
133134
}
@@ -224,6 +225,10 @@ public void PopulatePage()
224225
{
225226
PopulateRetryPage();
226227
}
228+
else if (Notifications.NotificationList.Count == 0)
229+
{
230+
PopulateEmptyPage();
231+
}
227232
else
228233
{
229234
Settings.LastViewNotificationDate = $"{DateTime.Today:yyyy-MM-dd}";
@@ -268,6 +273,15 @@ private void PopulateRetryPage()
268273
Page.NDetailList.Add(new NRetryControl(Page, "Retry", 140, "Try again to fetch notifications", MainForm));
269274
}
270275

276+
private void PopulateEmptyPage()
277+
{
278+
NewPages.Count = 0;
279+
280+
// Reports no notifications.
281+
var today = DateTime.Now.Date;
282+
Page.NDetailList.Add(new NTitleControl(Panel, 1, 1, $"{today:dd-MMM-yy}", "No notifications"));
283+
}
284+
271285
#region Process Criteria
272286
private bool AreNotificationChecksMet(Notification notification)
273287
{
@@ -356,35 +370,12 @@ private bool CheckAllMatch(List<Criteria> criteriaList)
356370
{
357371
foreach (var c in criteriaList)
358372
{
359-
if (c is Contains) // other criteria might be added such as NoLessThan and NoMoreThan.
360-
{
361-
if (CheckContains(c, true) == false) return false;
362-
}
363-
if (c is NotContains)
364-
{
365-
if (CheckContains(c, false) == false) return false;
366-
}
373+
var result = c.IsMatch();
374+
AppendToLog($"Check: {result}: '{c.Property}' {c.GetType().Name} '{c.Value}'");
375+
if (!result) return false;
367376
}
368377
return true;
369378
}
370-
371-
/// <summary>
372-
/// Returns true if a match is found and sense = true. For NotContains, use sense = false.
373-
/// </summary>
374-
/// <param name="criteria"></param>
375-
/// <param name="sense"></param>
376-
/// <returns></returns>
377-
private bool CheckContains(Criteria criteria, bool sense)
378-
{
379-
// If Property was a parameter, then use the expansion
380-
var content = ParameterDictionary.ContainsKey(criteria.Property)
381-
? ParameterDictionary[criteria.Property]
382-
: criteria.Property;
383-
384-
var result = content.IndexOf(criteria.Value, StringComparison.OrdinalIgnoreCase) > -1 == sense;
385-
LogCheckContains(criteria.Value, sense, content, result);
386-
return result;
387-
}
388379
#endregion
389380

390381
private void AddItemToPage(NotificationPage page, Item item)
@@ -401,6 +392,10 @@ private void AddItemToPage(NotificationPage page, Item item)
401392
Page.NDetailList.Add(new NLinkControl(Page, item.Label, item.Indent, link.Value, MainForm, url));
402393
}
403394
}
395+
else if (item is Dialog dialog)
396+
{
397+
Page.NDetailList.Add(new NDialogControl(Page, item.Label, item.Indent, dialog.Value, MainForm, dialog.Form));
398+
}
404399
else if (item is Update update)
405400
{
406401
Page.NDetailList.Add(new NUpdateControl(Page, item.Label, item.Indent, update.Value, MainForm));
@@ -586,12 +581,20 @@ private bool ContainsParameter(string field)
586581
/// <returns></returns>
587582
string GetSetting(string settingText)
588583
{
589-
var nameArray = settingText.Split('.'); // 2 elements: "Settings.<property>", e.g. "SimpleControlPhysics"
590-
if (nameArray[0] == "Settings" && nameArray.Length == 2)
584+
// 2 elements: "Settings.<property>", e.g. "SimpleControlPhysics"
585+
// 3 elements: "Settings.<group>.<property>", e.g. "Telemetry.RandomNumber1000"
586+
var nameArray = settingText.Split('.');
587+
if (nameArray.Length < 2 || nameArray[0] != "Settings") return "";
588+
589+
var value = (object)Settings;
590+
for (var i = 1; i < nameArray.Length; i++)
591591
{
592-
return Settings.GetType().GetProperty(nameArray[1])?.GetValue(Settings).ToString() ?? "";
592+
var prop = value.GetType().GetProperty(nameArray[i]);
593+
if (prop == null) return "";
594+
value = prop.GetValue(value);
593595
}
594-
return "";
596+
if (value is DateTime dateTime) return dateTime.ToString("yyyy-MM-dd");
597+
return value?.ToString() ?? "";
595598
}
596599

597600
private string GetInstalledRoutes()
@@ -648,7 +651,7 @@ public void ChangePage(int step)
648651
}
649652

650653
#region Logging
651-
public void LogOverrideParameters()
654+
void LogOverrideParameters()
652655
{
653656
if (Log == false) return;
654657

@@ -660,7 +663,7 @@ public void LogOverrideParameters()
660663
}
661664
}
662665

663-
public void LogParameters()
666+
void LogParameters()
664667
{
665668
if (Log == false) return;
666669

@@ -672,18 +675,12 @@ public void LogParameters()
672675
}
673676
}
674677

675-
public void LogNotification(Notification n)
678+
void LogNotification(Notification n)
676679
{
677680
AppendToLog($"\r\nNotification: {n.Title}");
678681
}
679682

680-
public void LogCheckContains(string value, bool sense, string content, bool result)
681-
{
682-
var negation = sense ? "" : "NOT ";
683-
AppendToLog($"Check: {result} = '{value}' {negation}contained in '{content}'");
684-
}
685-
686-
public void AppendToLog(string record)
683+
void AppendToLog(string record)
687684
{
688685
if (Log == false) return;
689686

Source/Menu/Notifications/NotificationPage.cs

+30-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ private void AddPageCountAndArrows(NotificationManager manager)
8080
Panel.Controls.Add(pageCountControl);
8181
}
8282

83-
public class Arrow : Button {
83+
public class Arrow : Button
84+
{
8485
public Arrow(Panel panel, Image image, bool visible, bool enabled, int indentRight)
8586
{
8687
Margin = new Padding(0);
@@ -182,8 +183,10 @@ public class NButtonControl : NDetail
182183
{
183184
public static int ButtonCount = 0;
184185
public Button Button;
186+
protected MainForm MainForm;
185187
public NButtonControl(Panel panel, string legend, int width, string description, MainForm mainForm)
186188
{
189+
MainForm = mainForm;
187190
var buttonLeft = LeftPaddingIndented;
188191
Button = new Button
189192
{
@@ -244,6 +247,30 @@ public NLinkControl(NotificationPage page, string legend, int width, string desc
244247
ButtonCount++;
245248
}
246249
}
250+
public class NDialogControl : NButtonControl
251+
{
252+
public string Form;
253+
public NDialogControl(NotificationPage page, string legend, int width, string description, MainForm mainForm, string form)
254+
: base(page.Panel, legend, width, description, mainForm)
255+
{
256+
Form = form;
257+
page.ButtonDictionary.Add(ButtonCount, this);
258+
ButtonCount++;
259+
}
260+
261+
public void Show()
262+
{
263+
switch (Form)
264+
{
265+
case "OptionsForm":
266+
MainForm.ShowOptionsDialog();
267+
break;
268+
case "TelemetryForm":
269+
MainForm.ShowTelemetryDialog();
270+
break;
271+
}
272+
}
273+
}
247274
public class NUpdateControl : NButtonControl
248275
{
249276
public NUpdateControl(NotificationPage page, string legend, int width, string description, MainForm mainForm)
@@ -267,6 +294,8 @@ public void DoButton(UpdateManager updateManager, int key)
267294
var button = ButtonDictionary[key];
268295
if (button is NLinkControl)
269296
Process.Start(((NLinkControl)button).Url);
297+
else if (button is NDialogControl dialogControl)
298+
dialogControl.Show();
270299
else if (button is NUpdateControl)
271300
updateManager.Update();
272301
else if (button is NRetryControl)

0 commit comments

Comments
 (0)