using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using System.IO; namespace Test_Merge { public partial class Main : Form { //private Reader Reader = null; private F1TVEmulator Emulator = null; private DataWrapper Wrapper = null; private bool cancelRequested = false; private SemaphoreSlim semaphore = new SemaphoreSlim(1); string ConfigFile = ""; string GpUrl = ""; //For the responsive content Size oldSize = new Size(); Size oldRankingSize = new Size(); Size oldLapTimesSize = new Size(); Size oldBattles = new Size(); Size oldPnlBattles = new Size(); Size oldPnlRankings = new Size(); Size oldPnlFastest = new Size(); Size oldPnlSlowest = new Size(); Point oldRankingPosition = new Point(); Point oldBattlePosition = new Point(); Point oldDriverInfoPosition = new Point(); Point olPnlFastestPosition = new Point(); Point oldPnlSlowestPosition = new Point(); public Main() { InitializeComponent(); } public async void RefreshUI() { if (Directory.Exists(ConfigurationTool.CONFIGS_FOLDER_NAME)) { lsbPresets.DataSource = null; lsbPresets.DataSource = Directory.GetFiles(ConfigurationTool.CONFIGS_FOLDER_NAME); } } private async void btnSettings_Click(object sender, EventArgs e) { if (Emulator != null) Emulator.ResetDriver(); Emulator = null; Wrapper = null; GC.Collect(); Settings settingsForm = new Settings(); settingsForm.ShowDialog(); RefreshUI(); //MessageBox.Show(settingsForm.GrandPrixUrl + Environment.NewLine + settingsForm.GrandPrixName + Environment.NewLine + settingsForm.GrandPrixYear); if (settingsForm.GrandPrixUrl != "" && settingsForm.SelectedConfigFile != "") { GpUrl = settingsForm.GrandPrixUrl; if (File.Exists(settingsForm.SelectedConfigFile)) { ConfigFile = settingsForm.SelectedConfigFile; } else { MessageBox.Show("The config file has not been found please return to the config and change it"); } } else { //WE dont care anymore, the user will choose its Grand Prix himself in the main program //MessageBox.Show("There is no URL for the Grand Prix you want to decode. Please return to the config and add a valid one"); } } private async void Form1_Load(object sender, EventArgs e) { //Those are the default values but they will need to be changed later when the configuration has been done ConfigFile = "./Presets/Clean_4K_2023.json"; GpUrl = "https://f1tv.formula1.com/detail/1000006688/2023-azerbaijan-grand-prix?action=play"; tbxGpUrl.Text = GpUrl; oldSize = this.Size; oldRankingSize = gpbxRanking.Size; oldLapTimesSize = gpbxLapTimes.Size; oldBattles = gpbxBattles.Size; oldPnlRankings = pnlLiveRanking.Size; oldPnlBattles = pnlBattles.Size; oldPnlFastest = pnlFastest.Size; oldPnlSlowest = pnlSlowest.Size; oldRankingPosition = gpbxRanking.Location; oldBattlePosition = gpbxBattles.Location; oldDriverInfoPosition = gpbxDriverInfos.Location; olPnlFastestPosition = pnlFastest.Location; oldPnlSlowestPosition = pnlSlowest.Location; tip1.SetToolTip(btnResetEmulator, "Starts or restarts the emulator. You need to start this to use the app"); tip1.SetToolTip(btnSettings, "Opens the configuration menu"); tip1.SetToolTip(tbxGpUrl, "Insert the URL of the Grand Prix you want to track. Dont forget the \"?action=play\" at the end"); tip1.SetToolTip(lsbPresets, "Select a configuration preset to use with the decoding"); tip1.SetToolTip(pbxResult,"A preview of what the program sees. You should see the DATA page of the F1TV here"); tip1.SetToolTip(lsbOvertakes,"A list of all the activity. You can scroll to see the most recent overtakes"); tip1.SetToolTip(gpbxBattles,"The four first battles in the field. A battle is two drivers less than 3 seconds apart"); tip1.SetToolTip(gpbxLapTimes,"The fastest and slowest drivers on track at the moment. It takes the average lapTime of the last 5 laps to choose who is the fastes or the slowest"); RefreshUI(); } private async void btnUpdate_Click(object sender, EventArgs e) { cancelRequested = false; if (Emulator != null && Wrapper != null) { // Disable UI controls to prevent re-entrancy btnResetEmulator.Enabled = false; btnStartDecoding.Enabled = false; btnStopUpdating.Enabled = true; btnSettings.Enabled = false; while (!cancelRequested) { await semaphore.WaitAsync(); try { // Start the time-consuming task on a separate thread await Task.Run(async () => { Stopwatch sw = new Stopwatch(); sw.Start(); Bitmap screen = Emulator.Screenshot(); screen.Save("HopefullyDataScreenshot.png"); Invoke((MethodInvoker)delegate { pbxResult.Image = (Bitmap)screen.Clone(); }); Wrapper.ChangeImage(screen); int errorCode = Wrapper.Refresh(); sw.Stop(); // Task completed Invoke((MethodInvoker)delegate { DisplayResults(errorCode, sw, screen); DisplayBattles(); DisplayDeltas(); DisplayOvertakes(); }); }); } finally { semaphore.Release(); } } // Re-enable UI controls btnStopUpdating.Text = "Stop"; btnStartDecoding.Enabled = true; btnStopUpdating.Enabled = false; btnResetEmulator.Enabled = true; btnSettings.Enabled = true; } } private void DisplayOvertakes() { Wrapper.DisplayOvertakes(lsbOvertakes); } private void DisplayBattles() { Wrapper.DisplayBattles(pnlBattles, this); } private void DisplayDeltas() { Wrapper.DisplayTimesDeltas(pnlFastest, pnlSlowest, this); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { if (Emulator != null) { Emulator.Stop(); } } private void DisplayResults(int errorCode, Stopwatch sw, Bitmap screen) { if (errorCode != 0) { cancelRequested = true; MessageBox.Show("An error has occured while trying to recover data from live feed. This can happen sometimes. I would advise you to restart a few times. If the problem persists check your configuration."); } else { Wrapper.DisplayLiveRanking(pnlLiveRanking, this); } } private void btnStopUpdating_Click(object sender, EventArgs e) { // Set the cancellation flag cancelRequested = true; btnStopUpdating.Enabled = false; btnResetEmulator.Enabled = false; btnStopUpdating.Text = "Stopping"; } private async void button1_Click(object sender, EventArgs e) { lsbOvertakes.Items.Clear(); btnResetEmulator.Text = "Launching"; btnResetEmulator.Enabled = false; btnSettings.Enabled = true; btnStartDecoding.Enabled = false; btnStopUpdating.Enabled = false; btnSettings.Enabled = false; int errorCode = -1; await Task.Run(async () => { if (Emulator != null) Emulator.ResetDriver(); Emulator = null; Wrapper = null; GC.Collect(); Emulator = new F1TVEmulator(GpUrl); errorCode = await Emulator.Start(); }); if (errorCode != 0) { //IMPLEMENT MORE SPECIFIC ERROR CODES !! MessageBox.Show("An error has occured while trying to start the driver."); btnResetEmulator.Enabled = true; btnSettings.Enabled = true; btnResetEmulator.Text = "Retry"; } else { Wrapper = new DataWrapper(ConfigFile, Emulator.Screenshot()); btnResetEmulator.Text = "Re launch"; btnResetEmulator.Enabled = true; btnSettings.Enabled = true; btnStartDecoding.Enabled = true; } } private void removeBorders(object sender, PaintEventArgs e) { GroupBox gpbx = (GroupBox)sender; using (Pen pen = new Pen(gpbx.BackColor, 50)) { e.Graphics.DrawRectangle(pen, 0, 0, gpbx.Width - 1, gpbx.Height - 1); e.Graphics.DrawRectangle(pen, 0, 0, gpbx.Width - 1, gpbx.Height - 1); } using (var brush = new SolidBrush(gpbx.ForeColor)) { var textPosition = new Point(5, 0); // Adjust the X and Y values as needed e.Graphics.DrawString(gpbx.Text, gpbx.Font, brush, textPosition); } } private void lsbPresets_SelectedIndexChanged(object sender, EventArgs e) { if (lsbPresets.SelectedIndex >= 0) ConfigFile = lsbPresets.Items[lsbPresets.SelectedIndex].ToString(); } private void textBox1_TextChanged(object sender, EventArgs e) { if (tbxGpUrl.Text != "") GpUrl = tbxGpUrl.Text; } public void btnDriver_Click(object sender, EventArgs e) { //Removes the cover if (pnlCover.Visible = true) pnlCover.Visible = false; //Happens when a driver button has been clicked //MessageBox.Show((sender as Button).Name + " has been selected"); Button btn = (sender as Button); string[] parts = btn.Name.Split('_'); DriverData driver = Wrapper.GetFullDriverData(parts[0], pnlCurrentDriverLapsHistory, this); lblCurrentDriverName.Text = driver.Name; lblCurrentDriverPosition.Text = driver.Position.ToString(); lblCurrentDriverGapToLeader.Text = Reader.ConvertMsToTime(driver.GapToLeader); lblCurrentDriverLapTime.Text = Reader.ConvertMsToTime(driver.LapTime); lblCurrentDriverTyreAge.Text = driver.CurrentTyre.NumberOfLaps.ToString(); if (driver.DRS) { lblCurrentDriverDRS.Text = "Open"; lblCurrentDriverDRS.ForeColor = Color.FromArgb(0, 164, 46); } else { lblCurrentDriverDRS.Text = "Closed"; lblCurrentDriverDRS.ForeColor = Color.Black; } switch (driver.CurrentTyre.Coumpound) { case Tyre.Type.Undefined: lblCurrentDriverTyreType.Text = "uuuuh..."; lblCurrentDriverTyreType.ForeColor = Color.Violet; break; case Tyre.Type.Hard: lblCurrentDriverTyreType.Text = "Hard"; lblCurrentDriverTyreType.ForeColor = Color.FromArgb(164, 165, 168); break; case Tyre.Type.Medium: lblCurrentDriverTyreType.Text = "Medium"; lblCurrentDriverTyreType.ForeColor = Color.FromArgb(245, 191, 0); break; case Tyre.Type.Soft: lblCurrentDriverTyreType.Text = "Soft"; lblCurrentDriverTyreType.ForeColor = Color.FromArgb(255, 0, 0); break; case Tyre.Type.Inter: lblCurrentDriverTyreType.Text = "Intermediate"; lblCurrentDriverTyreType.ForeColor = Color.FromArgb(0, 164, 46); break; case Tyre.Type.Wet: lblCurrentDriverTyreType.Text = "Wet"; lblCurrentDriverTyreType.ForeColor = Color.FromArgb(39, 96, 166); break; } } public void btnLapTime_Click(object sender, EventArgs e) { //Happens when a lapTime has been clicked Button btn = sender as Button; string[] parts = btn.Name.Split('_'); Wrapper.DisplayLapTimeInfos(parts[0], Convert.ToInt32(parts[1]), btn.Text); } private void Main_Resize(object sender, EventArgs e) { int xDiff = this.Width - oldSize.Width; int yDiff = this.Height - oldSize.Height; int padding = 10; //This will take half the newly created space gpbxRanking.Size = new Size(oldRankingSize.Width + xDiff / 2, oldRankingSize.Height + yDiff); gpbxRanking.Location = new Point(oldRankingPosition.X + xDiff / 2, gpbxRanking.Location.Y); //Will take half the new height and half the new height gpbxLapTimes.Size = new Size(oldLapTimesSize.Width + xDiff / 2, oldLapTimesSize.Height + yDiff / 2); //Will take half the new height and half the new width gpbxBattles.Size = new Size(oldBattles.Width + xDiff / 2, oldBattles.Height + yDiff / 2); gpbxBattles.Location = new Point(gpbxBattles.Location.X, oldBattlePosition.Y + yDiff / 2); //The infos wont change width but will need to be centerd Point startOfZone = new Point(gpbxOvertakes.Width + gpbxOvertakes.Location.X, gpbxOvertakes.Location.Y); Point endOfZone = new Point(gpbxRanking.Location.X, gpbxOvertakes.Location.Y); int totalWidth = endOfZone.X - startOfZone.X; gpbxDriverInfos.Location = new Point(startOfZone.X + (totalWidth / 2 - gpbxDriverInfos.Width / 2), oldDriverInfoPosition.Y + yDiff); //Now resizing internals pnlFastest.Size = new Size(oldPnlFastest.Width + xDiff / 4,oldPnlFastest.Height + yDiff / 4); pnlFastest.Location = new Point(olPnlFastestPosition.X,olPnlFastestPosition.Y + yDiff / 4); pnlSlowest.Size = new Size(oldPnlSlowest.Width + xDiff / 4, oldPnlSlowest.Height + yDiff / 4); pnlSlowest.Location = new Point(oldPnlSlowestPosition.X + xDiff / 4, oldPnlSlowestPosition.Y + yDiff / 4); pnlBattles.Size = new Size(oldPnlBattles.Width + xDiff / 2,oldPnlBattles.Height + yDiff / 2); pnlLiveRanking.Size = new Size(oldPnlRankings.Width + xDiff / 2,oldPnlRankings.Height + yDiff); } } }