Laden...

Forenbeiträge von PierreDole Ingesamt 74 Beiträge

08.12.2019 - 18:46 Uhr

Bin über meinem Projekt zu Screen Scraping gekommen. Naja, das Ermitteln von Informationen mittels Screenshoting hat mein Programm ziemlich verlangsamt. Jetzt will einfach aus Neugier wissen, wie viel schneller es in C++ ginge, also quasi ob diese Methode überhaupt irgendwo taugt.
Vom Assembler weiß ich, daß er der Schnellste ist. Von daher, da ein Vergleichswert zu bekommen, wäre auch ganz nett.
Daß das ganz nicht 100% genau ist, ist nicht weiter schlimm. Es soll nur als ein Richtwert dienen.

08.12.2019 - 14:37 Uhr

In Release Modus ändern sich die Ergebnisse auch nur etwas in den Nachkommastellen.

Mir kam aber die Idee Thread.Sleep zu umgehen und das ganze mit einem Timer zu versuchen. Dieses Mal habe ich nur einen 1p x 1p Screenshot zum Testen genommen, da es sonst etwas zu lang dauern würde.

DispatcherTimer timer = new DispatcherTimer();
        float eTime = 0f;
        float eTimeMin = float.MaxValue;
        float eTimeMax = 0f;
        float timerTicks = 0f;

        public MainWindow()
        {
            InitializeComponent();

            timer.Interval = TimeSpan.FromMilliseconds(50);
            timer.Tick += OnTimerTick;
        }

        private long CreateScreenshot(int width, int height)
        {

            Bitmap bmp = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(bmp);

            this.stopwatch.Reset();
            this.stopwatch.Start();

            g.CopyFromScreen(0, 0, 0, 0, new System.Drawing.Size(width, height));

            this.stopwatch.Stop();

            g.Dispose();

            return stopwatch.ElapsedMilliseconds;
        }

        private void StartButton_Click(object sender, RoutedEventArgs e)
        {
            timer.Start();
        }

        private void OnTimerTick(object sender, EventArgs e)
        {
            this.timerTicks++;

            float tmpTime = CreateScreenshot(1, 1);
            this.eTime += tmpTime;

            if(this.eTimeMin > tmpTime)
                this.eTimeMin = tmpTime;

            if(this.eTimeMax < tmpTime)
                this.eTimeMax = tmpTime;


            this.TextOutput.Text = "Minimum: " + this.eTimeMin + "ms\nAverage: " + Math.Round(this.eTime / this.timerTicks, 2) + 
                "ms\nMax: " + this.eTimeMax + "ms\nTimerTicks: " + this.timerTicks;

            if(timerTicks == 1000)
                timer.Stop();
        }

Die Ergebnisse sind jetzt alle fast gleich.

5ms:
Minimum: 1ms
Average: 9,8ms
Max: 19ms
TimerTicks: 1000

10ms:
Minimum: 1ms
Average: 9,74ms
Max: 19ms
TimerTicks: 1000

20ms:
Minimum: 1ms
Average: 9,94ms
Max: 19ms
TimerTicks: 1000

50ms:
Minimum: 1ms
Average: 9,91ms
Max: 20ms
TimerTicks: 1000

Und noch zum Schluß noch mit einem 100x100 Screenshot und im 10ms Intervall:
Minimum: 1ms
Average: 9,96ms
Max: 20ms
TimerTicks: 1000

Das ganze jetzt nochmal in C++. 😉 Und am liebsten auch noch im Assembler, den kann ich aber nicht. 😁

08.12.2019 - 12:39 Uhr

Moin,
ich wollte wissen, wie lange C# für die Erstellung eines Screenshot braucht. Dafür habe ich folgenden Test geschrieben:

 private long CreateScreenshot(int width, int height)
        {
            Bitmap bmp = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(bmp);

            this.stopwatch.Reset();
            this.stopwatch.Start();

            g.CopyFromScreen(0, 0, 0, 0, new System.Drawing.Size(width, height));

            this.stopwatch.Stop();
            
            g.Dispose();

            return stopwatch.ElapsedMilliseconds;
        }

        private float Measurement(int width, int height)
        {
            float t = 0;

            for(int i = 0; i < 100; i++)
            {
                t += CreateScreenshot(width, height);
            }
            return t / 100;
        }

        private void StartButton_Click(object sender, RoutedEventArgs e)
        {
            this.TextOutput.Text = "1 x 1 - generated in: " + Measurement(1, 1) + "ms\n";
            this.TextOutput.Text += "1 x 2 - generated in: " + Measurement(1, 2) + "ms\n";
            this.TextOutput.Text += "10 x 10 - generated in: " + Measurement(10, 10) + "ms\n";
            this.TextOutput.Text += "100 x 100 - generated in: " + Measurement(100, 100) + "ms\n";
            this.TextOutput.Text += "1000 x 1000 - generated in: " + Measurement(1000, 1000) + "ms\n";
        }

Da die Länge schwankt, habe ich die Tests jeweils 100 Mal durchlaufen lassen und dann den Durchschnittswert ermittelt.

Das Ergebnis:
1 x 1 - generated in: 15,98ms
1 x 2 - generated in: 15,97ms
10 x 10 - generated in: 16ms
100 x 100 - generated in: 16ms
1000 x 1000 - generated in: 19,56ms

Das kam mir etwas zu langsam vor. Habe als nächstes Thread.Sleep(10); in die for-Schleife getan um zu schauen, ob sich was am Ergebnis ändert.


            for(int i = 0; i < 100; i++)
            {
                t += CreateScreenshot(width, height);
                Thread.Sleep(10);
            }

Ergebnis:
1 x 1 - generated in: 5,53ms
1 x 2 - generated in: 5,66ms
10 x 10 - generated in: 5,59ms
100 x 100 - generated in: 5,49ms
1000 x 1000 - generated in: 20,85ms

Ok, sieht schon besser aus. Wird es noch schneller, wenn ich den Thread 20ms schlafen lasse?

Ergebnis mit 20ms:
1 x 1 - generated in: 12,22ms
1 x 2 - generated in: 12,2ms
10 x 10 - generated in: 12,22ms
100 x 100 - generated in: 12,19ms
1000 x 1000 - generated in: 23,05ms

An diese Stelle war ich schon etwas überrascht, da ich mit gleichbleibenden oder gar kürzeren Zeiten gerechnet habe. Habe den Test natürlich mehrfach wiederholt und bis auf kleine Schwankungen in den Nachkommastellen, tat sich nichts.

Aber gut, schauen wir mal, wie es mit 5ms aussieht:
1 x 1 - generated in: 10,5ms
1 x 2 - generated in: 10,5ms
10 x 10 - generated in: 10,56ms
100 x 100 - generated in: 10,43ms
1000 x 1000 - generated in: 24,43ms

Schneller als mit 20ms, aber langsamer als mit 10ms. Die Frage aller Fragen lautet jetzt: Wie bekomme ich ein akkurates Meßergebnis hin? 😃

Und dann wäre noch eine Sache mit dem Garbage Collector. Nach ca 13 Sekunden war der Testdurchlauf vorbei. Auf dem Screenshot im Anhang sieht man, daß der GC erst nach ca 53 Sekunden aufgeräumt hat. Das sind 40 Sekunden. Ist das normal? Ich meine, da ist fast ein 1GB im Speicher, der nicht gebraucht wird und der GC braucht 40 Sekunden um aufzuräumen. Kommt mir etwas lang vor.

01.12.2019 - 14:07 Uhr

Ja, genau, das erlebe ich sehr oft. Ich sehe Beispiele, die vereinfacht einen Aufbau darstellen und ich mich dann nur frage: wie wende ich das jetzt auf meinen Code an?

Ich habe ein Hud für iRacing erstellt, welcher einige Telemetriedaten anzeigt. Geschwindigkeit, RPMs, Fuel level, Laps, usw. Das Ding berechnet auch die Schaltpunkte und hat einen Spotter (Anzeige, die mir Bescheid gibt wenn ein Auto neben mir fährt, damit ich ihn nicht überm Haufen fahre).
Das ist tatsächlich ein sehr kleines Projekt, da ich alle Daten von einem Wrapper bekomme und sie quasi nur formatiert anzeigen lassen muss. Paar Berechnungen und "Spielereien" sind noch drin und das wars dann auch. Eigentlich wollte ich noch viel mehr reintun, aber dann habe ich die Lust dran verloren, eben wegen dem Code.
Sicherlich könnte man hier vieles in andere Klassen auslagern, mit fehlen aber halt die Ideen und Ansätze wie ich das machen könnte. Ich meine, ich könnte es machen, aber das bringt mich nicht weiter, da ich mich dann frage, ob es in der Form richtig ist oder ich totalen Unsinn fabriziert habe.

Es war nicht arrogant gemeint, vielleicht habe ich mich unglücklich ausgedrückt. Ich habe bei meinen Versuchen über die besagte Sehwelle zu kommen, einige solcher Tutorials angefangen zu lesen. Es kam nur Frust auf.
Ich bin am Programmieren und merke ich komme so nicht weiter, bzw, das ist nicht gut, wie ich es mache und versuche dann Informationen zu finden, die mir weiterhelfen. Wenn ich dann seitenweise lese, was nicht einmal annähend in die Richtung geht, wo sich mein Problem befindet, und ich schon erahne, daß die nächsten zig Seiten auch nicht in diese Richtung gehen werden, dann wird es mit der Zeit für mich demotivierend. Da denk ich mir auch nur: nee, das ist nicht das, was ich suche. Und stelle mir vor, daß es irgendwo im Netz einen Artikel geben muss, der sich genau mit diesem Problem auseinandersetzt. Ich meine, ich bin bestimmt nicht der einzige damit.

01.12.2019 - 11:50 Uhr

Moin,
ich bin auf der Suche nach guter Lektüre zum Thema Code-Gestaltung. Ich sags mal direkt: ich hasse meinen Code. Ich habe die prozedurale Programmierung gelernt (angefangen in den 90ern und Mitte der 2000er aufgehört) und tue mir heute mit der OOP schwer. Ich komme irgendwie nicht aus der Denkweise / den Mustern der damals erlernten prozeduralen Programmierung heraus. Ich neige dazu alles in einer langen Klasse zu schreiben, und wenn ich eine zweite erstelle, dann wird die genauso lang. 80% der Zeit bin ich am Überlegen, wie ich den Code besser gestalten kann, anstatt zu programmieren. Wie verschachtele ich die Klassen? Wie spreche ich sie an? Von außen! Nein, lieber eine Methode in der Klasse, die mir das fertige Ergebnis der Klasse liefert..... ist zum Verzweifeln

Ich habe schon viele Anläufe hinter mir OOP zu verinnerlichen, komme aber nicht über diese eine Schwelle. Zum einen sind Tutorials sehr demotivierend, da sie immer mit dem Basics anfangen (Was sind Klassen? Was ist Vererbung? usw.) und ich das schon alles kenne (habe allen Ernstes mal ein Java Zertifikat gemacht). Zum anderen finde ich nichts, was mir eben diese eine Frage verständlich beantwortet.
Ich habe mein Projekt ruhen lassen und einige Artikel zum Thema Software Architecture gelesen, die zwar interessant waren, aber auch nur Allgemeinplätzchen gewesen sind.

Und ganz ehrlich, ich weiß nicht mal wonach ich suche. Suche ich nach Software Architecture, nach Design Pattern, Clean Code, oder was ganz anderen?

Ich hoffe, jemand kennt paar Antworten.

01.12.2019 - 11:07 Uhr

Vielen Dank,

VS hat System.Drawing nicht zum Projekt hinzugefügt. Musste es per Hand machen. Nun gehts.

29.11.2019 - 12:54 Uhr

Moin,
gibt es die Bitmap Klasse in WPF? Ich möchte mit meiner App Screenshots machen können. Alle Beispiele, die ich im Netz finde beinhalten die Bitmap Klasse, selbst die Beispiele auf den Seiten von MS.
Das Ding ist, bei mir unterstricht VS 2017 Bitmap rot und als Quick Action bekomme ich es nur die Optionen eine neue Klasse zu generieren, aber nicht etwas zum "using" hinzuzufügen. Auch Beispiele, in denen die usings schon drin sind, funktionieren nicht. Z.B. wird Imaging in using System.Drawing.Imaging; rot unterstrichen.

Alles sieht danach aus, als müsste es diese Klasse geben, aber mein VS kennt sie nicht.

20.11.2019 - 11:59 Uhr

Ich bin sehr überrascht, daß diese Datenmengen nicht viele sind. Ich hatte aber auch nie wirklich davor damit zu tun gehabt. 😃

Vielen Dank für eure Einschätzung und Vorschläge. Ich werde mich erstmal in diese Datenbanken einlesen und mir einen Überblick verschaffen.

20.11.2019 - 01:53 Uhr

Moin,
ich möchte große Datenmengen speichern und bin mir nicht sicher, wie ich das anstellen soll. Es handelt sich um Telemetriedaten einer Rennsimulation. Es sind zwar nicht viele Daten auf einmal, 10 Datensätze mit ca 10 Zeichen pro Satz, aber ich bekomme sie 60 mal in der Sekunde, bei einer Renndauer von ca 30 Minuten. Das wären 108.000 Einträge, und zwar pro Rennen!

Was eignet sich am besten zum Speichern dieser Daten? Eine Datenbanktabelle wird im Nu riesengroß. Nach 10 Rennen hätte die Tabelle über eine Million Einträge. Oder sollte ich für jedes Rennen eine neue Tabelle anlegen? Dann wird aber die Datenbank irgendwann sehr groß und mit der statistischen Auswertung wäre es auch kompliziert. Da muss es eine bessere Möglichkeit geben.

Apropos Statistik: Wie verfahre ich da am besten? Macht es Sinn die Statistik zur Laufzeit zu berechnen und separat zu speichern?

Und dann würde ich noch gerne wissen, wie ich diese Daten speichere. Daß ich nicht 60 Mal in der Sekunde auf den Speicherort zugreifen sollte, ist mir bewusst (korrigiert mich, wenn ich falsch liege). Es muss also ein Datenpool her. Wie groß sollte er sein, bzw wie oft sollte ich speichern? Ich meine, je öfter ich speichere, desto mehr arbeitet der Rechner und je seltener ich speichere, desto größer die Datenmenge im Speicher. Gibt es da eine Faustformel für ein Optimum?

17.11.2019 - 14:46 Uhr

Ja, genau das wars! double.Parse() hat einfach den Punkt weggeparset. Mensch, das Ding macht Sachen... 😄

Mit double.Parse(seconds, CultureInfo.InvariantCulture) funktioniert das jetzt wunderbar. Es macht zwar aus dem Punkt ein Komma, aber das kann mir egal sein, wird ja umformatiert. 😃

Danke euch!

17.11.2019 - 13:54 Uhr

Moin,
ich steh gerade komplett aufm Schlauch. TimeSpan.FromSeconds gibt mir ein Ergebnis zurück, das nicht stimmt und ich komme nicht dahinter, woran es liegen kann. Nicht mal eine Nacht drüber schlafen hat geholfen.

Das ist der Code. An einer anderen Stelle funktioniert er wunderbar, habs von dort copy/pastet. (Der Code kommt aus einer for-Schleife.)


                    string tmpTime_ = TimeSpan.FromSeconds(double.Parse(query["Position", i]["FastestTime"].GetValue())).ToString(@"dd\:hh\:mm\:ss\:fff");
                    Console.WriteLine(query["Position", i]["FastestTime"].GetValue() + " | " + tmpTime_);

Als Ergebnis bekomme ich Folgendes:

124.7740 | 14:10:35:40:000
125.1115 | 14:11:31:55:000
125.5584 | 14:12:46:24:000
125.6810 | 14:13:06:50:000
125.8750 | 14:13:39:10:000
125.9621 | 14:13:53:41:000
126.6092 | 14:15:41:32:000
127.0831 | 14:17:00:31:000
127.2126 | 14:17:22:06:000
127.2812 | 14:17:33:32:000
128.2107 | 14:20:08:27:000
128.5625 | 14:21:07:05:000
135.2246 | 15:15:37:26:000

Erste Spalte sind die Sekunden und die zweite Spalte ist das formatierte Ergebnis. Man sieht schon mit bloßem Auge, daß 124.x Sekunden etwas mehr als 2 Minuten sind. Eigentlich brauche ich das auch nur in Minuten, Sekunden und Tausendstel. Die Tage und Stunden habe ich reingetan, da das Ergebnis seltsam aussah.

Wieso bekomme ich da über zwei Wochen heraus? Ideen, woran das liegen könnte?

09.11.2019 - 16:59 Uhr

Ich bin begeistert! Vielen Dank für die Vorschläge, ich habe sie alle beherzigt und den Code umgeschrieben. Dank den Timern und vor allem dem ViewModel läuft alles sehr viel flüssiger, da nicht alles ständig neu gezeichnet wird. Sogar der Spotter funktioniert auf Anhieb. 🙂 Die CPU-Auslastung ging von 10% auf 0.0% - 0.2% runter (abhängig von der Fahreranzahl auf der Strecke). Wie gesagt, ich bin begeistert! 😁

Noch bin ich nicht ganz fertig. Einiges muss ich noch ändern und ergänzen. Bin aber zumindest auf einen besseren Weg.

Eine Kleinigkeit hätte ich noch. Die ViewModel Datei ist etwas lang geraten und es kommt noch viel mehr rein. Ist das normal, oder kann man die irgendwie auf mehrere Dateien aufteilen, z.B. die Daten, die in den Labels angezeigt werden in eine Datei und alle Farben für Schriften und Hintergründe in eine andere?

namespace iRacingHud.ViewModel
{
    class HudViewModel : BaseViewModel
    {
        private string currentLapTime;
        private string lastLapTime;

        private double airTemperature;
        private double trackTemperature;

        private double throttleBarHeight;
        private double brakeBarHeight;
        private double clutchBarHeight;

        private string rpmLED01Color;
        private string rpmLED02Color;
        private string rpmLED03Color;
        private string rpmLED04Color;
        private string rpmLED05Color;
        private string rpmLED06Color;
        private string rpmLED07Color;
        private string rpmLED08Color;
        private string rpmLED09Color;

        private string shiftFlashlightColor;
        private bool isShiftFlashlightVisible;

        private string gear;

        private int speed;
        private float rpm;

        private string position;
        private string laps;

        private double fuelLevel;
        private double maxFuel;
        private string fuelUsage;

        private bool isSpotterVisible;

        private bool isHudPanelVisible;


        // Methods
        public string CurrentLapTime
        {
            get { return currentLapTime; }
            set
            {
                if(value != currentLapTime)
                {
                    currentLapTime = value;
                    OnPropertyChanged("CurrentLapTime");
                }
            }
        }
        public string LastLapTime
        {
            get { return lastLapTime; }
            set
            {
                if(value != lastLapTime)
                {
                    lastLapTime = value;
                    OnPropertyChanged("LastName");
                }
            }
        }
        

        public double AirTemperature
        {
            get { return airTemperature; }
            set
            {
                if(value != airTemperature)
                {
                    airTemperature = value;
                    OnPropertyChanged("AirTemperature");
                    OnPropertyChanged("AirTrackTemperature");
                }
            }
        }
        public double TrackTemperature
        {
            get { return trackTemperature; }
            set
            {
                if(value != trackTemperature)
                {
                    trackTemperature = value;
                    OnPropertyChanged("TrackTemperature");
                    OnPropertyChanged("AirTrackTemperature");
                }
            }
        }
        public string AirTrackTemperature
        {
            get { return string.Format("{0}°C / {1}°C", AirTemperature, TrackTemperature); }
        }


        public double ThrottleBarHeight
        {
            get { return throttleBarHeight; }
            set
            {
                if(value != throttleBarHeight)
                {
                    throttleBarHeight = value;
                    OnPropertyChanged("ThrottleBarHeight");
                }
            }
        }
        public double BrakeBarHeight
        {
            get { return brakeBarHeight; }
            set
            {
                if(value != brakeBarHeight)
                {
                    brakeBarHeight = value;
                    OnPropertyChanged("BrakeBarHeight");
                }
            }
        }
        public double ClutchBarHeight
        {
            get { return clutchBarHeight; }
            set
            {
                if(value != clutchBarHeight)
                {
                    clutchBarHeight = value;
                    OnPropertyChanged("ClutchBarHeight");
                }
            }
        }


        public string RpmLED01Color
        {
            get { return rpmLED01Color; }
            set
            {
                if(value != rpmLED01Color)
                {
                    rpmLED01Color = value;
                    OnPropertyChanged("RpmLED01Color");
                }
            }
        }
        public string RpmLED02Color
        {
            get { return rpmLED02Color; }
            set
            {
                if(value != rpmLED02Color)
                {
                    rpmLED02Color = value;
                    OnPropertyChanged("RpmLED02Color");
                }
            }
        }
        public string RpmLED03Color
        {
            get { return rpmLED03Color; }
            set
            {
                if(value != rpmLED03Color)
                {
                    rpmLED03Color = value;
                    OnPropertyChanged("RpmLED03Color");
                }
            }
        }
        public string RpmLED04Color
        {
            get { return rpmLED04Color; }
            set
            {
                if(value != rpmLED04Color)
                {
                    rpmLED04Color = value;
                    OnPropertyChanged("RpmLED04Color");
                }
            }
        }
        public string RpmLED05Color
        {
            get { return rpmLED05Color; }
            set
            {
                if(value != rpmLED05Color)
                {
                    rpmLED05Color = value;
                    OnPropertyChanged("RpmLED05Color");
                }
            }
        }
        public string RpmLED06Color
        {
            get { return rpmLED06Color; }
            set
            {
                if(value != rpmLED06Color)
                {
                    rpmLED06Color = value;
                    OnPropertyChanged("RpmLED06Color");
                }
            }
        }
        public string RpmLED07Color
        {
            get { return rpmLED07Color; }
            set
            {
                if(value != rpmLED07Color)
                {
                    rpmLED07Color = value;
                    OnPropertyChanged("RpmLED07Color");
                }
            }
        }
        public string RpmLED08Color
        {
            get { return rpmLED08Color; }
            set
            {
                if(value != rpmLED08Color)
                {
                    rpmLED08Color = value;
                    OnPropertyChanged("RpmLED08Color");
                }
            }
        }
        public string RpmLED09Color
        {
            get { return rpmLED09Color; }
            set
            {
                if(value != rpmLED09Color)
                {
                    rpmLED09Color = value;
                    OnPropertyChanged("RpmLED09Color");
                }
            }
        }

        public string ShiftFlashlightColor
        {
            get { return shiftFlashlightColor; }
            set
            {
                if(value != shiftFlashlightColor)
                {
                    shiftFlashlightColor = value;
                    OnPropertyChanged("ShiftFlashlightColor");
                }
            }
        }
        public bool IsShiftFlashlightVisible
        {
            get { return isShiftFlashlightVisible; }
            set
            {
                if(value != isShiftFlashlightVisible)
                {
                    isShiftFlashlightVisible = value;
                    OnPropertyChanged("IsShiftFlashlightVisible");
                }
            }
        }

        public string Gear
        {
            get { return gear; }
            set
            {
                if(value != gear)
                {
                    gear = value;
                    OnPropertyChanged("Gear");
                }
            }
        }

        public int Speed
        {
            get { return speed; }
            set
            {
                if(value != speed)
                {
                    speed = value;
                    OnPropertyChanged("Speed");
                }
            }
        }
        public float RPM
        {
            get { return rpm; }
            set
            {
                if(value != rpm)
                {
                    rpm = value;
                    OnPropertyChanged("RPM");
                }
            }
        }


        public string Position
        {
            get { return position; }
            set
            {
                if(value != position)
                {
                    position = value;
                    OnPropertyChanged("Position");
                }
            }
        }

        public string Laps
        {
            get { return laps; }
            set
            {
                if(value != laps)
                {
                    laps = value;
                    OnPropertyChanged("Laps");
                }
            }
        }


        public double FuelLevel
        {
            get { return fuelLevel; }
            set
            {
                if(value != fuelLevel)
                {
                    fuelLevel = value;
                    OnPropertyChanged("FuelLevel");
                    OnPropertyChanged("FuelDisplay");
                }
            }
        }
        public double MaxFuel
        {
            get { return maxFuel; }
            set
            {
                if(value != maxFuel)
                {
                    maxFuel = value;
                    OnPropertyChanged("MaxFuel");
                    OnPropertyChanged("FuelDisplay");
                }
            }
        }
        public string FuelDisplay
        {
            get { return string.Format("Fuel\n{0} l / {1} l", FuelLevel, MaxFuel); }
        }
        public string FuelUsage
        {
            get { return fuelUsage; }
            set
            {
                if(value != fuelUsage)
                {
                    fuelUsage = value;
                    OnPropertyChanged("FuelUsage");
                }
            }
        }


        public bool IsSpotterVisible
        {
            get { return isSpotterVisible; }
            set
            {
                if(value != isSpotterVisible)
                {
                    isSpotterVisible = value;
                    OnPropertyChanged("IsSpotterVisible");
                }
            }
        }


        public bool IsHudPanelVisible
        {
            get { return isHudPanelVisible; }
            set
            {
                if(value != isHudPanelVisible)
                {
                    isHudPanelVisible = value;
                    OnPropertyChanged("IsHudPanelVisible");
                }
            }
        }
    }
}

08.11.2019 - 11:41 Uhr

Quellcode anzeigen wollte ich eigentlich vermeiden, da ihr dann wohl kreischend wegrennt. 😄

Zuerst speichere ich einige Daten in Properties, da ich nicht weiß, was der Wrapper im Hintergrund tut und ich eveltuelle Neuberechnungen vermeiden will. In diesen Properties (car, player, driver, track und session) passiert nichts weiter.

Ok, hier das gefeuerte Event:


private void OnTelemetryUpdate(object sender, SdkWrapper.TelemetryUpdatedEventArgs e)
        {
            car.Speed = e.TelemetryInfo.Speed.Value;
            car.Engine.CurrentRPM = (int)e.TelemetryInfo.RPM.Value;
            car.Gearbox.CurrentGear = e.TelemetryInfo.Gear.Value;

            car.Pedals.ThrottleLevel = e.TelemetryInfo.Throttle.Value;
            car.Pedals.BrakeLevel = e.TelemetryInfo.Brake.Value;
            car.Pedals.ClutchLevel = e.TelemetryInfo.Clutch.Value;

            car.Tank.FuelLevel = e.TelemetryInfo.FuelLevel.Value;
            
            session.PlayerPosition = wrapper.GetTelemetryValue<int>("PlayerCarPosition").Value;
            session.PlayerClassPosition = wrapper.GetTelemetryValue<int>("PlayerCarClassPosition").Value;
            session.PlayerLapCount = e.TelemetryInfo.Lap.Value;
            session.PlayerLapDistPct = e.TelemetryInfo.CarIdxLapDistPct.Value[(int)car.Id];

            player.LapDistance = e.TelemetryInfo.CarIdxLapDistPct.Value[(int)player.CarId];
            player.EstimatedTime = e.TelemetryInfo.CarIdxEstTime.Value[(int)player.CarId];
            player.IsOnPitRad = e.TelemetryInfo.CarIdxOnPitRoad.Value[(int)player.CarId];
            player.IsOnTrack = e.TelemetryInfo.IsOnTrack.Value;

            // tmp - zeige Abstände von den Fahrern zu mir
            telemetryTextBox.Text = "";
            float playerLapDistance = player.LapDistance * track.Length;
            foreach(Driver driver in session.Drivers)
            {
                driver.LapDistance = e.TelemetryInfo.CarIdxLapDistPct.Value[(int)driver.CarId];
                driver.EstimatedTime = e.TelemetryInfo.CarIdxEstTime.Value[(int)driver.CarId];
                driver.IsOnPitRad = e.TelemetryInfo.CarIdxOnPitRoad.Value[(int)driver.CarId];

                if(driver.LapDistance != -1)
                    telemetryTextBox.Text += "pd: " + Math.Round(playerLapDistance) + "/" + Math.Round(driver.LapDistance * track.Length) + " : " + Math.Round(Math.Abs(playerLapDistance - (driver.LapDistance * track.Length))) + "\n";
            }
            
            

            if(player.IsOnTrack)
            {
                this.RPMControl();
                this.GearControl();
                this.PedalControl();
                this.DisplayControl();
                this.SpotterControl();
                kmhLabel.Content = Math.Round(car.Speed * 3.6);

                // show Hud Panel if on track
                if(hudPanel.Visibility == Visibility.Hidden)
                    hudPanel.Visibility = Visibility.Visible;

                //  dient der Berechnung des Verbrauchs pro Runde
                if(shouldUpdateFuelLevel)
                {
                    fuelLevelNewLap = e.TelemetryInfo.FuelLevel.Value;
                    shouldUpdateFuelLevel = false;
                }
            }
            else
            {
                // hide Hud Panel if not on track
                if(hudPanel.Visibility == Visibility.Visible)
                    hudPanel.Visibility = Visibility.Hidden;

                shouldUpdateFuelLevel = true;
            }
        }

Um es vollständig zu machen, hier noch die Control-Methoden:

#region Hud Control

        int currentGear = 0;
        private void RPMControl()
        {
            currentGear = car.Gearbox.CurrentGear == -1 ? 0 : car.Gearbox.CurrentGear;

            rpmLabel.Content = Math.Round((float)car.Engine.CurrentRPM / 1000, 1);

            rpmLED01.Fill = (car.Engine.CurrentRPM > (70 * car.Engine.ShiftRPM / 100)) ? this.rpmLedOnColor : new SolidColorBrush(this.rpmLedOffColor);
            rpmLED02.Fill = (car.Engine.CurrentRPM > (75 * car.Engine.ShiftRPM / 100)) ? this.rpmLedOnColor : new SolidColorBrush(this.rpmLedOffColor);
            rpmLED03.Fill = (car.Engine.CurrentRPM > (80 * car.Engine.ShiftRPM / 100)) ? this.rpmLedOnColor : new SolidColorBrush(this.rpmLedOffColor);
            rpmLED04.Fill = (car.Engine.CurrentRPM > (85 * car.Engine.ShiftRPM / 100)) ? this.rpmLedOnColor : new SolidColorBrush(this.rpmLedOffColor);
            rpmLED05.Fill = (car.Engine.CurrentRPM > (90 * car.Engine.ShiftRPM / 100)) ? this.rpmLedOnColor : new SolidColorBrush(this.rpmLedOffColor);
            rpmLED06.Fill = (car.Engine.CurrentRPM > (95 * car.Engine.ShiftRPM / 100)) ? this.rpmLedOnColor : new SolidColorBrush(this.rpmLedOffColor);

            if(car.Engine.CurrentRPM >= car.Engine.ShiftRPM && car.Engine.CurrentRPM < car.Engine.BlinkRPM)
            {
                rpmLED07.Fill = new SolidColorBrush(this.rpmLedShiftColor);
                rpmLED08.Fill = new SolidColorBrush(this.rpmLedShiftColor);
                rpmLED09.Fill = new SolidColorBrush(this.rpmLedShiftColor);
                rpmShiftFlash.Fill = rpmLedShiftFlashColor;
            }
            else if(car.Engine.CurrentRPM > car.Engine.BlinkRPM)
            {
                rpmLED07.Fill = new SolidColorBrush(this.rpmLedMaxColor);
                rpmLED08.Fill = new SolidColorBrush(this.rpmLedMaxColor);
                rpmLED09.Fill = new SolidColorBrush(this.rpmLedMaxColor);
                rpmShiftFlash.Fill = rpmLedShiftFlashColor;
            }
            else if(car.Gearbox.CurrentGear > 1 && car.Engine.CurrentRPM < car.Engine.ShiftDownSafeRPM[currentGear] && car.Engine.CurrentRPM > car.Engine.ShiftDownLastRPM[currentGear])
            {
                rpmShiftFlash.Fill = new SolidColorBrush(Color.FromRgb(255, 191, 0));
            }
            else if(car.Gearbox.CurrentGear > 1 && car.Engine.CurrentRPM < car.Engine.ShiftDownLastRPM[currentGear])
            {
                rpmShiftFlash.Fill = new SolidColorBrush(Color.FromRgb(166, 124, 0));
            }
            else
            {
                rpmLED07.Fill = new SolidColorBrush(this.rpmLedOffColor);
                rpmLED08.Fill = new SolidColorBrush(this.rpmLedOffColor);
                rpmLED09.Fill = new SolidColorBrush(this.rpmLedOffColor);
                rpmShiftFlash.Fill = Brushes.Transparent;
            }

        }

        private void GearControl()
        {
            if(gearLabel.Content.ToString() != car.Gearbox.CurrentGear.ToString())
            {
                if(car.Gearbox.CurrentGear == -1)
                    gearLabel.Content = "R";
                else if(car.Gearbox.CurrentGear == 0)
                    gearLabel.Content = "N";
                else
                    gearLabel.Content = car.Gearbox.CurrentGear.ToString();
            }
        }

        // Pedalwerte gehen von 0 bis 1
        private void PedalControl()
        {
            throttleBar.Height = Math.Abs(Math.Round((throttlePanel.Height - 4) * car.Pedals.ThrottleLevel));
            brakeBar.Height = Math.Abs(Math.Round((brakePanel.Height - 4) * car.Pedals.BrakeLevel));
            clutchBar.Height = Math.Abs(Math.Round((clutchPanel.Height - 4) * (1f - car.Pedals.ClutchLevel)));
        }


        private TimeSpan tmpTime;
        private bool fuelLevelUpdate = true;
        private float fuelUsage = 0f;

        private void DisplayControl()
        {
            // TODO: Start/Ziel-Überfahrt per LapCount bestimmen
            if(session.PlayerLapDistPct > 0.9999 && this.fuelLevelUpdate)
            {
                fuelUsage = fuelLevelNewLap - car.Tank.FuelLevel;
                fuelLevelNewLap = car.Tank.FuelLevel;
                fuelLevelUpdate = false;
            }
            else if(session.PlayerLapDistPct < 0.1 && !this.fuelLevelUpdate)
                fuelLevelUpdate = true;

            fuelLevelLabel.Content = "Fuel:\n" + Math.Round(car.Tank.FuelLevel, 2) + " l / " + Math.Round(car.Tank.MaxFuel, 2) + " l";
            fuelUsageLabel.Content = "Fuel Usage:\n" + (Math.Round(fuelUsage, 2) == 0 ? "-" : Math.Round(fuelUsage, 2) + " l");


            tmpTime = TimeSpan.FromMilliseconds(double.Parse(wrapper.GetTelemetryValue<float>("LapCurrentLapTime").Value.ToString()) * 1000);
            LapTimeLabel.Content = tmpTime.ToString(@"mm\:ss\:fff") == "00:00:000" ? "-:--" : tmpTime.ToString(@"mm\:ss\:fff");

            // TODO: muss nur einmal pro Runde gesetzt werden
            tmpTime = TimeSpan.FromMilliseconds(double.Parse(wrapper.GetTelemetryValue<float>("LapLastLapTime").Value.ToString()) * 1000);
            LastLapTimeLabel.Content = tmpTime.ToString(@"mm\:ss\:fff") == "00:00:000" ? "-:--" : tmpTime.ToString(@"mm\:ss\:fff");

            LapsLabel.Content = session.PlayerLapCount + " / " + (session.Laps == 0 ? "-" : session.Laps.ToString());


            PositionLabel.Content = (session.PlayerClassPosition == 0 ? "-" : session.PlayerClassPosition.ToString()) + " / " + session.Drivers.Count;

            temperatureLabel.Content = Math.Round(track.AirTemperature, 1) + "°C / " + Math.Round(track.TrackTemperature, 1) + "°C";
        }

        float playerLapDistance;
        private void SpotterControl()
        {
            if(!player.IsSpactator)
            {
                playerLapDistance = player.LapDistance * track.Length;
                foreach(Driver driver in session.Drivers)
                {
                    if(driver.CarId != player.CarId && driver.LapDistance != -1 && Math.Abs(playerLapDistance - (driver.LapDistance * track.Length)) < 4 && spotterLeft.Fill != spotterOnColor)
                    {
                        spotterLeft.Fill = spotterOnColor;
                        spotterRight.Fill = spotterOnColor;
                        break;
                    }
                    else if(spotterLeft.Fill != spotterOffColor)
                    {
                        spotterLeft.Fill = spotterOffColor;
                        spotterRight.Fill = spotterOffColor;
                    }
                }
            }
        }

        #endregion

08.11.2019 - 01:44 Uhr

Ja, bei meinem Code gibt es bestimmt viele andere Stellen, die optimiert werden könnten und müssten. Ich setzt halt da an, wo ich einen Ansatz sehe. Bloße Gewißheit reicht leider nicht aus. 😃

Es handelt sich um ein .NET Event. Das Ganze ist ein iRacing SDK, bzw ein Wrapper, der auf diesem SDK basiert. Ich bekomme (fast) alle Informationen, die auf der Rennstrecke erzeugt werden. Woran es hackt, ist der Spotter, bzw, die Abstandsmessung zwischen mir und den anderen Fahrzeugen. Ich bekomme von dem Event den prozentuellen Abstand aller Fahrzeuge von der Start/Ziel-Linie aus gesehen und berechne daraus den Abstand der anderen Fahrzeuge zu meinem Fahrzeug. Das Ding ist, das Fahrzeug ist keine vier Meter lang, die Strecke aber 6.4km. Vier Meter in dem prozentuellen Wert sind fast gar nichts (auch, wenn der prozentuelle Wert sechsstellig ist).
Wenn ich mir alle berechneten Abstände in einer Liste live anzeigen lasse, hacken die manchmal, quasi sie laggen. Nicht gut, wenn es um jede Tausendstel geht (naja, eigentlich um 1/60 Sekunde). Ich schiebe das jetzt auf meinen nicht performanten Programmierstil. Aber wenn Millionen Daten in einer Sekunde verarbeitet werden können, dann frage ich mich jetzt, woran es sonst liegen könnte.

07.11.2019 - 21:42 Uhr

Moin, ich möchte Daten, die ich von einem Event bekomme, in anderen Intervallen verarbeiten als das Event ausgelöst wird. Das Event feuert 60 Mal in der Sekunde. Das ist fix und kommt von einem Framework. Da es sich um sehr viele Daten handelt, und mein Programm die CPU mittlerweile zu 10% auslastet, suche ich nach Möglichkeiten alles etwas performanter zu gestalten.
Einige Daten könnte ich z.B. 10 Mal die Sekunde verarbeiten, andere sogar nur einmal in der Sekunde. Ich habe nur leider keine Idee, wie ich das anstelle. Hat jemand eine Idee?

07.06.2019 - 12:45 Uhr

Vielen Dank! Damit geht funktioniert es sehr gut. 😃

06.06.2019 - 16:08 Uhr

Moin,

ich komme bei meinem kleinen Vorhaben nicht weiter und bräuchte etwas Hilfestellung. Ich möchte mehrere Countdowns hintereinander ausführen. Momentan rast das Programm durch alle Zeitsetzungen durch. Wie halte ich es an, ohne es in einer While-Schleife gefangen zu halten? Ich habe es mit einer While-Schleife versucht, anstatt des Timers, aber dann wurde die GUI nicht aktualisiert.

Momentan sieht das alles wie folgt aus:

using System;

namespace Training_Timer
{
    class Training
    {
        // class vars
        private System.Windows.Forms.Timer aTimer;
        private System.Media.SoundPlayer timeOutSignal;
        private int timerCounter;

        private TrainingPlan plan;
        private System.Windows.Forms.Label uiLabel;


        public Training()
        {
            _init();

            InitCountdown();
        }

        // init
        private void _init()
        {
            aTimer = new System.Windows.Forms.Timer();
            timeOutSignal = new System.Media.SoundPlayer(@"F:\workspace\Visual Studio 2017\Projects\Training Timer\signal01.wav");

            timerCounter = 0;

            // avoid crash
            plan = new TrainingPlan();
            uiLabel = new System.Windows.Forms.Label();
        }

        public void Start()
        {
            foreach (TrainingExercise exercise in plan)
            {
                for (int i = 0; i < exercise.Sets; i++)
                {
                    Countdown(exercise.Duration);

                    if (exercise.Pause != 0)
                        Countdown(exercise.Pause);
                }
            }
        }


        // timer
        private void InitCountdown(int interval)
        {
            aTimer.Tick += new EventHandler(OnTimedEvent);
            aTimer.Interval = interval;
        }

        private void InitCountdown()
        {
            InitCountdown(1000);
        }

        private void Countdown(int duration)
        {
            //UpdateUITimer(duration);
            timerCounter = duration;
            aTimer.Enabled = true;
        }





        private void UpdateUITimer(int counter)
        {
            uiLabel.Text = TimeSpan.FromSeconds(counter).ToString("mm':'ss");
        }



        // events
        private void OnTimedEvent(object source, EventArgs e)
        {
            if(timerCounter == 0)
            {
                timeOutSignal.Play();
                aTimer.Enabled = false;
            }
            UpdateUITimer(timerCounter);
            timerCounter--;
        }



        // getter & setter
        public void SetPlan(TrainingPlan trainingPlan)
        {
            plan = trainingPlan;
        }

        public void SetUILabel(System.Windows.Forms.Label label)
        {
            uiLabel = label;
        }
    }
}

04.11.2017 - 18:51 Uhr

Moin,
ich schreibe gerade eine Art Input-Monitoring für mein Gamepad. Das Programm visualisiert an einem grafischen Gamepad alle Eingaben, die ich über mein physisches Gamepad tätige.
Soweit so gut. Das Einlesen der Daten vom Gamepad war nicht wirklich schwer. Nun möchte ich auch, daß das visualisierte Gamepad vibriert, wenn mein Gamepad es auch tut. Leider finde ich keine dll, die diese Information liefert. Kennt sich jemand damit aus und könnte mir einen Tip geben, wie ich die Vibration eines Gamepads erkenne?

26.07.2017 - 15:37 Uhr

Ja, das gesamte Spielfeld samt Checkpoints ist vorher bekannt. Ich habe blöderweise vergessen zu erwähnen, daß das Spielfeld nicht zwingen rechteckig sein muss. Es hat aber niemals Inseln.

A* war doch ein Pathfinding Algorithmus, oder habe ich was falsches in Erinnerung? Frage, da alle Checkpoints durchlaufen werden sollen.

26.07.2017 - 10:53 Uhr

Moin,
ich stehe vor einer kleinen Herausforderung und bin mir nicht sicher, ob ich die bestmögliche Lösung gefunden habe.

Ich habe ein zufällig großes Grid, mit zufällig vielen und zufällig angeordneten Checkpoints und einer zufälligen Startposition, wobei die Startposition sich immer außerhalb des Grids befindet und nicht fix ist.
Ich möchte den schnellstmöglichen Weg durch die Checkpoints berechnen. Meine Idee dazu ist, sich zu einer der Ecken zu bewegen, um ein Feld versetzt in das Feld mit der Analyse hineingehen und dann jeweils ein Feld links und rechts nach den Checkpoints zu scannen, die dann in eine Liste eingetragen werden. Diese Liste kann ich dann einfach ablaufen (Siehe Anhang). Ist das das Optimum?

Und wenn jetzt sich mal der Fall ergibt, daß sich die Checkpoints z.B. oben konzentrieren und unten nur beim Einstieg ein paar Checkpoints sind und ich einer davon sich zwar auch unten neben dem Einstieg befindet, aber zwei Felder von mir entfernt, dann ginge ich also zuerst nach oben um dann wieder für einen Checkpoint runtergehen zu müssen, anstatt ihn gleich mit auf dem Weg nach oben mitzunehmen um dort anschließend zu bleiben.
Da bin ich mir auch nicht sicher, wie ich vorgehen soll. In Kauf nehmen, da die Wahrscheinlichkeit vielleicht so gering ist, daß sich der Aufwand die Weganalyse zu ändern nicht lohnt?

Ein anderer Punkt wäre noch der Einstieg. Das Grid kann sehr groß werden, meistens bestimmt das 10-fache von der Beispielgrafik, wenn nicht noch größer. Wenn ich in der Mitte zwischen den Ecken starte, habe ich einen weiten Weg. Den Weg zu einer der Ecken in Kauf nehmen, oder einen Weg durch die Mitte des Grids suchen und versuchen irgendwie dann alle Checkpoints mitzunehmen?

26.07.2017 - 10:02 Uhr

Danke für eure Ratschäge. Hat mir sehr geholfen! Und sorry, daß ich erst jetzt Feedback gebe. Ich war so sehr in das Probieren und Programmieren vertieft, daß ich glatt vergessen habe zu antworten. 😃

10.07.2017 - 20:42 Uhr

Moin,
ich möchte eine Art Mausbewegungs-Viewer schreiben und verzweifle an der Berechnung. Die Aufgabe ist ganz einfach: wenn ich die Maus bewege, soll es in einer kleinen Grafik angezeigt werden. Ich habe ein Kreis von 150px Durchmesser, mit einer kleinen grafischen Maus in der Mitte, und wenn ich meine Maus z.B. nach oben bewege, bewegt sich die grafische Maus nach oben an den Rand des Kreises. Je nach dem wie ich relativ zum letzten Punkt (Stillstand) meine Maus bewege, soll sich die grafische Maus in diesem Kreis bewegen.
Ich habe zwei Koordinaten: die Ausgangskoordinate und die Aktuelle. Und jetzt kommt Mathe... wo wir bei meinem Problem wären: es ist über 20 Jahre her, als ich Mathe-Unterricht hatte und ich finde einfach keinen Ansatz. Mache ich das irgendwie mit Winkeln oder doch lieber mit Verhältnissen(x zu y)? Oder doch völlig anders? Wäre sehr dankbar, wenn mir jemand einen kleinen Denkanstoß geben könnte.

10.07.2017 - 16:51 Uhr

Also "Datenbindung" musste ich erstmal googlen. 😃

Habs mit Directory gelöst. Das ging dann reibungslos. Und is/as habe ganz vergessen gehabt. Schade, hätte zumindest gesehen, ob es geklappt hätte. Aber jetzt bin ich mit meinem Programmchen fertig und wurstle nicht mehr dran.

Vielen Dank für die Hilfe.

10.07.2017 - 14:04 Uhr

Moin,
ich habe eine Hashtable mit Daten gefüttert, die ich selbstverständlich wieder auslesen möchte. Nun, das klappt nicht so, wie ich mir es vorstelle, eigentlich klappt es gar nicht.

Das sind die Daten. Erstmal testweise unvollständig erstellt.


// 0 = Location, 1 = Size
			this.keyboardLayout.Add("D1", new Point[2] { new Point(25, 32), new Point(23, 24) } );
			this.keyboardLayout.Add("D2", new Point[2] { new Point(51, 32), new Point(22, 24) } );
			this.keyboardLayout.Add("D3", new Point[2] { new Point(74, 32), new Point(23, 24) } );

Das hier ist der Ausleseversuch. Die Fehlermeldungen habe ich jeweils in die Zeilen als Kommentar eingefügt.

foreach(DictionaryEntry de in this.keyboardLayout)
	        {
				// create new picturebox obj
				PictureBox pb = new PictureBox();
				
				// init pb obj
				((System.ComponentModel.ISupportInitialize)(pb)).BeginInit();
				
				// define picturebox
				pb.BackColor = System.Drawing.Color.Transparent;
				Object[] p = de.Value; // Der Typ 'object' kann nicht implizit in 'object[]' konvertiert werden. Es ist bereits eine explizite Konvertierung vorhanden. (Möglicherweise fehlt eine Umwandlung.) (CS0266)
				pb.Location = (Point)de.Value[0]; // Indizierung mit [] kann nicht auf einen Ausdruck vom Typ 'object' angewendet werden. (CS0021)
				pb.Margin = new Padding(0);
				pb.Name = (string)de.Key;
				pb.Size = (Size)de.Value; 
				// pb.TabIndex = 4;
				pb.TabStop = false;
				
				
				Debug.WriteLine("Key = {0}, Value = {1}", de.Key, de.Value);
	        }	

Egal wie ich es drehe und wende, de.Value wird nicht als Array erkannt - und habe schon viel daran gedreht und gewendet. Habs hin und her gecastet, habs über Deklarationsumwege versucht, keine Ahnung, kriege es irgendwie nicht hin.