Laden...

Forenbeiträge von trib Ingesamt 709 Beiträge

19.06.2024 - 09:05 Uhr

Dazu gibt es einen Haken in den Einstellungen der Gruppe.

Man kommt daran, wenn man die Zeile markiert und dann mit der rechten Maustaste in das graue Kästchen neben der Tabelle klickt.
Oder über die Gruppen-Ebenen, wenn man sich das Fenster eingeblendet hat.

Hat aber jetzt nicht so viel mit C# zu tun, oder?

23.02.2024 - 08:59 Uhr

Eines der schlimmsten Beispiele ist in meinen Augen Excel.

Wenn man Formeln eindeutscht, wird´s echt kurios. Und die Möglichkeit etwas online zu recherchieren, senkt sich von weltweit auf den deutschsprachigen Raum.
Getoppt wird das nur noch von Microsoft PowerApps und PowerAutomate.

Daher bin ich auch dafür: Eigenständige, englische Begriffe beizubehalten.

16.02.2024 - 08:18 Uhr

Die embedded Geräte mit denen ich gearbeitet habe, hatten im worst case 128MB RAM und 512MB Speicher.

Da hat ein Log(-file) schnell den Platz belegt, wenn man nicht aufgepasst hat. Daher habe ich immer offline als Ringspeicher geloggt und sofern eine Netzwerkverbindung bestand, an eine Datenbank übertragen.

BTT: Persönlich finde ich es am Besten einmal im Konstruktor einen Logger mitzugeben. Ist dieser null, greift eine standard implementierung. Im Zweifel Abt´s ThrowAwayLogger, der nix tut.

So kann ich eine Library ohne viel Aufwand testen und ein Gefühl dafür bekommen ob sie das macht was ich brauche. Entspricht sie meiner Erwartung, programmiere ich den Logger aus.

Das senkt die Einstiegshürde. Es gibt nix nervigeres als erstmal 10 Klassen schreiben zu müssen um dann zu merken, dass die Library gar nicht tut was man sucht!

12.02.2024 - 08:40 Uhr

Moin,

wie kommst Du darauf dass Deine Berechnung Dir Zoll zurückliefert?

Du hast Width und Density. In diesem Fall gibt die Breite die Anzahl der Pixel an. Da die Screens heutzutage alle extrem hochauflösend sind, hättest Du nun bei allen Apps winzige Symbole und Schriften.
Um das zu kompensieren, gibt es die "Density". Also wie viele echte Pixel einem dargestellten Pixel entsprechen.

Das Ergebnis hat also nichts mit Inches zu tun.

27.11.2023 - 14:16 Uhr

Das hat ja nix mit dem Webservice an sich zu tun.

Du fängst einen etwaigen Error mit catch im Task ab. Aber nicht ob der Webservice selbst korrekt durchläuft.

Daher nehme ich an, erhältst Du nur den Fehler des Tasks.

Versuch mal sowas in der Art:

t = Task.Run(() =>
        {
            try{
                BuchblattService.Create(UserData.BuchblattAB, ref buchblatt);
                }
                catch(Exception ex)
                {
                	MessageBox.Show(ex.Message);
                }
        });

Problematisch an den Webservices, die automatisch per Webreferenz hinzufügen erstellt wurden, Du hast keinen Zugriff auf den echten WebResponse. Aber aus der o.g. Nachricht sollte sich schon was sinnvolleres extrahieren lassen!

27.11.2023 - 08:42 Uhr

In Navision/Business Central musst Du die Meldungen praktisch auf zwei Ebenen abprüfen:

Klassischer http-Error (403, 404, usw.) mit dessen Beschreibung. Nicht Authorisiert, nicht gefunden, etc.

Bei einem 500, bekommst Du per SOAP eine Antwort im Body mit. Und genau dort steht 1:1 die Fehlermeldung aus Nav.

Die muss man dann aber natürlich aus dem XML noch auspacken.

Sofern !HttpResponseMessage.IsSuccessStatusCode

Dann im Response auf den XML-Knoten "faultstring" prüfen.

14.04.2023 - 08:18 Uhr

Sicher dass das am Framework liegt und nicht am Wechsel von z.B. Windows Forms auf WPF?

In WPF kannst Du folgendes verwenden:

Mouse.OverrideCursor = Cursors.Wait;
//Reset
Mouse.OverrideCursor = null;
16.03.2023 - 09:04 Uhr

Danke für Deine Rückmeldung 🙂

Und schön, dass Du meinen ersten Eindruck von ScottPlott bestätigst.

10.03.2023 - 13:57 Uhr

Ich probiere morgen mal ScottPlot aus. vielleicht klappt das ja besser.

Das verwende ich in einem kleinen Projekt und ich bin begeistert! Das ist echt flott, einfach und zu verwenden.

Es gibt ein paar minimale Einschränkungen, wo man sich etwas mit Workarounds helfen kann/muss. Z.B. ist die Darstellung von Tooltips, so wie ich sie mir z.B. vorstelle, etwas kompliziert.
Funktioniert aber mit ein bisschen Programmierung.

111.822 Datenpunkte laden zügig (unter 1 Sekunde), siehe Anhang.

13.02.2023 - 12:41 Uhr

Okay, der Text ist in der Tat identisch zu den anderen Daten zu behandeln.
Einzelne Bytes werden in char gewandelt und können ausgegeben werden.

Durch die ganze Rumprobiererei mit den Encodings und den Anfangs vielversprechenden Ergebnissen, habe ich mich da aber offensichtlich in eine Sackgasse manövriert.

Des Rätsels Lösung ist tatsächlich:
Einlesen der Datei mit dem BinaryReader ohne Encoding.
Die Längen der "Texte" sind < 256, die kann ich unmittlebar so verwenden. Die Texte selbst, werden dann stumpf von Links nach Rechts ausgelesen und in char umgewandelt.

Dann kommen die Längenangaben, welche aus der Anzahl Bytes bestehen, gefolgt von dem Wert.
Hier war mein Irrweg 04 00 00 F4 01 .. als Block zu erwarten. Korrekt ist aber 04 00 ist die Byte-Angabe und F4 01 00 00 der Wert.
Und das Ganze als Big Endian zu lesen, also von Rechts nach Links.

Das erste Datenpaket ist dann ein Timestamp, welcher ebenfalls in BigEndian definiert ist. Offenbar kommt das alles aus der Applikation selbst.
Alle weiteren Daten sind dann wiederum in LittleEndian abgelegt, da diese auch so eingelesen werden.

Dadurch dass der Anfang der Daten für mich encodiert absolut Sinn gemacht hat, war ich da vernagelt die Werte einfach rückwerts zu berechnen. Die Daten kannte ich und auch da habe ich mich zu sehr drauf versteift...

Vielen Dank! Manchmal braucht man einen kleinen Reset im Kopf 🙂

13.02.2023 - 12:17 Uhr

Hi,

ich denke Du hast da zwei Möglichkeiten.
Neben dem DataReceived-Event, gibt es auch noch ErrorReceived.
Da bekommst Du z.B. mit, wenn jemand den Stecker zieht.

Erwartest Du aber in bestimmten Intervallen Deine Daten, würde ich mir überlegen ob nicht anstelle des Events ein Timer die bessere Wahl wäre.
Der Timer läuft mit einem Intervall und darin schaust Du ob die Daten im Buffer bereitstehen. Wenn ja, starte den Timer neu.
Wenn nicht, hat sich der Arduino aufgehangen oder reagiert nicht entsprechend.

Eine dritte Variante wäre, eine Prüfung mit einem Timeout. Das macht aber eigentlich nur bei einmaligen Anfragen Sinn:


        private readonly SerialPort _comPort;
        private StringBuilder _dataToRecieve = new StringBuilder();
        private DateTime _lastReceive = DateTime.MinValue;
        private ComPortData _comPortData;

        public string SendAndWaitForResponse(string command, bool lineBreak)
        {
            Send(command, lineBreak);            
            return WaitForResponse();
        }
        public string WaitForResponse()
        {            
            _lastReceive = DateTime.Now;
            while (DateTime.Now.Subtract(_lastReceive).TotalMilliseconds < _comPortData.TimeoutMS)
            {
                _dataToRecieve.Append(_comPort.ReadExisting());
                if (DataIsCompleted(_dataToRecieve.ToString()))
                {
                    break;
                }
            }
            return _dataToRecieve.ToString();
        }

In DataIsCompleted() kannst Du dann prüfen ob Deine 8 Byte angekommen sind oder ob ein Zeilenendzeichen im String vorkommt.

10.02.2023 - 22:42 Uhr

Hallo zusammen,

ich habe eine Datei vorliegen, welche von einem japanischen Programm erzeugt wird.
Die Eckpunkte sind mir bekannt: Es ist ein Kommentarfeld mit einer vorrausgehenden Länge enthalten.
Dann ein Feld mit der Anzahl der enthaltenen Datensätze, sowie die Datensätze selbst.
Ein Datensatz besitzt zusätzlich einen Kopf mit einer ID und der Länge der Daten. Länge der Daten * Anzahl Datensätze definiert dann die zu erwartenden Bytes.

Die Position der Felder ist auch teilweise dokumentiert, der Rest ergibt sich dann aus der darin enthaltenen Anzahl/Länge.
Alle Daten werden klassisch berechnet: 1 Byte entspricht 0-255. 2 Byte berechnen sich byte[0] * 256 + byte[1] und so weiter.
Maximal schein es 4 byte große Werte zu geben.

Gut, also einen BinaryReader gebaut, durch die Bytes iteriert und nix passte!
Also verschiedenste Encodings ausprobiert, bis die Längen zu passen schienen, die Texte machten aber keinen Sinn.

Dann habe ich eine Liste von Encodings gebaut und bin alle Kombinationen durchgegangen.
Aktuell scheint es so auszusehen, dass die Daten im Kopf (Texte & Anzahl) in Unicode [1200] geöffnet und in Unicode (Big-Endian) [1201] konvertiert werden müssen.
Sonst steht die Anzahl 300 nicht als 0x01 0x2C sondern 0x2C 0x01, also verkehrt herum bereit. Der Kommentar lässt sich einfach byte für byte in char wandeln und ergibt auch Sinn.

Die Daten hingegen machen erst Sinn, wenn ich bei Unicode [1200] bleibe. Sonst sind sie wiederum "verdreht".
Deutet auf nach wie vor das falsche Encoding hin, aber erstmal scheint es zu funktionieren!

Weiter im Takt und die Daten schauen bei Zahlen > 65535 komisch aus. Moment! Das ist doch 0xFF 0xFF.
Also schnell als Hex-String ausgegeben und siehe da:
Unicode (Big-Endian): (also konvertiert)
58 01 00 00 7F FF 00 00 59 01 00 00 3A 00 01 00

Unicode:
01 58 00 00 FF 7F 00 00 01 59 00 00 00 3A 00 01

Soll-Ergebnis:
Index 344, Wert 65407
Index 345, Wert 65594

Unicode sortiert den Index und Wert< 65535 korrekt. In beiden Fällen "rutscht" aber die 01 an die letzte Stelle, welche eigentlich das dritte Byte einläuten sollte.
Brauche ich UTF32? Teste ich ja bereits:


        static void TestAllEncodings(string filePath)
        {
            var encodings = new List<Encoding>();
            foreach (EncodingInfo ei in Encoding.GetEncodings())
                encodings.Add(ei.GetEncoding());
            //encodings.Add(Encoding.BigEndianUnicode);            
            //encodings.Add(Encoding.Unicode);
            //encodings.Add(Encoding.Default);
            //encodings.Add(Encoding.UTF32);
            //encodings.Add(Encoding.UTF8);
            //encodings.Add(Encoding.ASCII);
            //encodings.Add(Encoding.GetEncoding(932)); // Japanese
            //encodings.Add(Encoding.GetEncoding(50220)); // ISO JP
            //encodings.Add(Encoding.GetEncoding(50221)); //ISO JP 1
            //encodings.Add(Encoding.GetEncoding(50222)); //ISO JP 2
            //encodings.Add(Encoding.GetEncoding(65001)); //UTF-8

            foreach (var encoding in encodings)
            {
                foreach (var encodingDest in encodings)
                {
                    BinaryReader(filePath, encoding, encodingDest);
                }
            }
        }

        static void BinaryReader(string filePath, Encoding startEncoding, Encoding destinationEncoding)
        {
            StringBuilder hexTextConverted = new StringBuilder();
            StringBuilder hexTextUnconverted = new StringBuilder();            
            
            using (var stream = File.Open(filePath, FileMode.Open))
            {
                using (var reader = new BinaryReader(stream, startEncoding, false))
                {
                    //int length = (int)reader.BaseStream.Length;
                    int length = 12000;                    
                    var bytes = reader.ReadBytes(length);                    
                    var convertedBytes = Encoding.Convert(startEncoding, destinationEncoding, bytes);
                    for (var i = 0; i < convertedBytes.Length; i++)
                    {
                        hexTextConverted.Append(string.Format("{0:X2}", convertedBytes[i]) + ' ');
                    }
                    for (var i = 0; i < bytes.Length; i++)
                    {
                        hexTextUnconverted.Append(string.Format("{0:X2}", bytes[i]) + ' ');
                    }
                    Console.WriteLine($"{startEncoding.EncodingName} ({startEncoding.CodePage}) -> {destinationEncoding.EncodingName} ({destinationEncoding.CodePage})");
                    string converted = hexTextConverted.ToString();
                    string unconverted = hexTextUnconverted.ToString();
                    //Check for confirmed length values
                    if ((converted.Contains("01 2C") && converted.Contains("01 77") && converted.Contains("01 F4")) ||
                        unconverted.Contains("01 2C") && unconverted.Contains("01 77") && unconverted.Contains("01 F4"))
                    {                        
                        Console.WriteLine("Comment:\t" + ConvertToText(bytes, 64, 14));
                        TestData(convertedBytes);
                    }
                    hexTextConverted.Clear();
                    hexTextUnconverted.Clear();
                }
            }            
        }

Bleiben eigentlich nur noch zwei Möglichkeiten:

  • Habe einen massiven Denkfehler bei den Encodings
  • Bin zu blöd und muss einfach alle Zahlen rückwerts summieren byte[1] * 256 + byte[0]

Wobei die Texte/Kommentare vorwärts sind und auch die generelle Flussrichtung der Datei von Links nach Rechts geht.
Gegen eine Summierung rückwerts spricht auch, dass die Nullen 0x00 weiterhin führend, also ebenfalls links sind.

Bin mit meinem Laten und meinem Encoding am Ende.
Vielleicht hat ja Jemand eine Idee!

Die Datei darf ich so leider nicht unverändert bereitstellen. Und sobald ich darin rumfummele, verfälsche ich das Ergebnis 🙁

16.01.2023 - 09:17 Uhr

Moin,

es gibt natürlich etliche Möglichkeiten für ein Protokoll zur Übermittlung der Daten.
Tatsächlich kann man eine Bibliothek für Json auf den Arduino ziehen und damit Klassen serialisieren. Ist aber riesen groß (aus Sicht des Mikrocontrollers).

Daher würde ich das Ganze so simpel wie möglich halten und sowas bauen:
text|5,389|1\n\r

wobei der erste Teil Deinen string darstellt, der Zweite den Winkel und der Dritte den Status des Schalters (1 oder 0).
Als Trennzeichen bietet sich eines an, welches in den Daten nicht vorkommt.
Abgeschlossen wir das Ganze dann mit einem Zeilenumbruch.

Oder Du kreierst mehrere "Datenpakete" und kombinierst nur einen Indentifier und den Wert
w|32,5\n\r -> Winkel
s|0\n\r -> State des Schalters
t|HalloWelt\n\r -> Text

In #c splitttest Du den gesamten String mit der Pipe, prüfst den ersten Teil auf den Buchstaben und je nach dem was dort drin steht, wandelst Du den zweiten Teil um.

11.01.2023 - 08:35 Uhr

Moin,

anstelle von ReadChar kannst Du ein Event vom ComPort verwenden.
Dies wird aufgerufen, sobald Daten eingehen. Nun läuft das aber aynchron zu dem Main-Thread der Form, was Dir einen Fehler liefern würde.
Dazu musst Du Dich dann mit

Invoke

auseinandersetzen um den Wert an das Label zu übergeben.

Es gab mal ein Template, wo man das abschauen konnte...
Hier isses:
Template SerialPort

22.12.2022 - 18:30 Uhr

Könnnte es dann am Serial Port Monitor liegen?
Leuchtet denn die Rx-LED am Arduino, dass dort auch wirklich 2xDaten ankommen?
Kannst Du denn reproduzieren, dass das Verhalten nicht auftritt, wenn Du keinen Unittest machst?

Nach Port.Close(); passiert bei mir jedenfalls nix mehr.

22.12.2022 - 12:13 Uhr

Hi KonstanzGlocke,

ich mache auch viel mit dem Arduino und C#. Aber dieses Verhalte ist mir nicht bekannt!
Bei der allerersten Verbindung startet der Arduino ggf. neu, weil er checkt ob er in den Bootmode gehen soll.
Anschließend kann man den Com-Port aber so oft öffnen und schließen, wie man mag.

Wird der Code auch definitiv nicht zweimal durchlaufen? Breakpoint oder Debug.WriteLine probiert?

Weshalb wartest Du nach dem Empfang der Daten nochmal 5 Sek?


wait Task.Delay(5000);

07.12.2022 - 19:23 Uhr

Es gibt eine Projektmappe, welche (vereinfacht) aus folgenden Komponenten besteht:

  • Service
  • Plugin1
  • Plugin2
  • Plugin3
  • Installer

Der Installer referenziert die primäre Ausgabe des Service und allen Abhängigkeiten.
Nun sollen Plugin1 und Plugin2 standardmäßig mit ausgeliefert werden.
Entsprechend erzeugt der Installer ein Unterverzeichnis für die Plugins. Dort wird ebenfalls pro Plugin die primäre Projektausgabe referenziert.
Bisher aus meiner Sicht nichts ungewöhnliches oder "reserviertes".

Die Plugins haben allerdings als Targetframework* (warum auch immer, sie stammen ursprünglich nicht von mir)


netcoreapp3.1;net472

hinterlegt. Und das führt dazu, dass eben zwei Kompilate erzeugt werden:* \obj\Release\netcoreapp3.1\Plugin1.dll

  • \obj\Release\net472\Plugin1.dll

und als "primäre Ausgabe" referenziert der Installer automatisch die DLL aus dem "netcoreapp3.1"-Verzeichnis.
Damit kann der .net 4.7.2 Dienst natürlich nichts anfangen.

Aktuell habe ich anstelle dieser "primären Ausgabe", direkt die richtige DLL als Klassenbibliothek eingebunden. Dann geht´s, ist aber alles andere als sauber.
Vorallem, wenn man während der Entwicklungsphase auch mal den Dienst und alle Abhängigkeiten + Plugins als Debug installieren möchte.

*Das Targetframework mit beiden Zielen ist strange. Egal welche Art von Klassenbibliothek ich erstelle, ich habe das Framework, Standard oder Core. Nie eine Mehrfachauswahl!
Selbst wenn ich die csprj anpasse, sagt Visual Studio dann "unsupported". Keine Ahnung welcher Typ von Projekt das ist 😕

07.12.2022 - 13:03 Uhr

Hallo zusammen,

ich habe einen Dienst, welcher über ein Pluginsystem erweitert werden kann.
Ein paar Plugins werden standardmäßig mitgeliefert, weshalb ich deren Ausgabe direkt in das Zielverzeichnis referenzieren möchte.

Kurz einen Installer gebaut, den Dienst und alle Abhängigkeiten hinzugefügt und den Plugin-Ordner erstellt.
Dann dort die "Primäre Ausgabe" der Plugins eingefügt.

Er wird alles "korrekt" installiert, der Dienst stürzt beim laden der Plugins jedoch ab.
Nach etwas debuggen & recherchieren ist mir aufgefallen, dass immer das Verzeichnis "\obj\Release*netcoreapp3.1*\Plugin.dll" verwendet wird.
Da der Dienst auf 4.7.2 läuft, müsste er natürlich entsprechend "\obj\Release*net472*\Plugin.dll" verwenden. Es werden auch immer beide dll´s erzeugt.
Allerdings finde ich aktuell keine Einstellung, wie ich das ändern kann.

Der Installer hat sog. Launch Conditions, dort sind tatsächlich ".Net Core" und ".Net Framework" enthalten. Die kann ich aber weder entfernen, noch anders sortieren.

Eine Alternative wäre natürlich eine harte Referenzierung, das wäre aber nur mein letztes Mittel. Wobei diese dann automatisch alle Abhängigkeiten mit in das Zielverzeichnis kopiert, was auch nicht richtig wäre...

10.10.2022 - 08:35 Uhr

Im Android-Geräte-Manager gibt es das Gerät zwar immer noch nicht, aber beim debug-start wird es erkannt.

Der Gerätemanager zeigt doch nur die Emulatoren an. Deshalb auch ausschließlich die Nexus/Pixel Geräte von Google selbst.
Das per USB verbundene Handy sehe ich nur in der Dropdown Liste, neben dem "Debuggen"-Knopf.

14.04.2022 - 09:25 Uhr

Aufgeben ist in der IT keine Option 😉
Was ist denn genau Deine Idee mit dem Auslesen der Notifyicons? Wenn die alle in Deiner Applikation, in verschiedenen Forms liegen, hast Du diese doch bereits.
Möchtest Du die Icons von fremden Anwendungen haben, musst Du etwas anderes an die Sache herangehen.

Wo hapert es? Was klappt am Code nicht?

08.11.2021 - 09:15 Uhr

Hallo,
eine XLIFF ist ja nichts anderes als eine Datei mit XML-Struktur. Entsprechend kannst Du doch einfach weitere XML-Knoten hinzufügen, gemäß dem XLIFF-Format- Und dann wieder als *.xlf abspeichern.
Es gibt 2-3 gute XLIFF Editoren, die auch gratis zu verwenden sind, wo Du Dir das vielleicht abschauen könntest.

06.10.2021 - 09:32 Uhr

Das wären dann doch 18,75€ * 18S * 4w = 1.350€
Was ich für einen Berufseinsteiger nicht so schlecht finde, wenn man bedenkt, dass die nach der Ausbildung, je nach Standort, Firma, usw. ~2.000 - 2.500€ bekommen.
Für einen ITA oder Fachinformatiker, der dann eine 40 Stundenwoche hat. Zurückgerechnet bekämen die bestenfalls 15,63€.
Überschaubare Steuern, bzw. dank clever angewendetem Freibetrag, vielleicht sogar gar keine.

Ansonsten sehe ich das wie Stefan. Eine Beschäftigung zu haben ist immer gut! Eine Anstellung gibt Sicherheit.
Du sammelst dort Erfahrung, die Dir auf deinem weiteren Wege helfen wird.
Und es sieht im Lebenslauf auch immer besser aus, wenn dort keine Lücke zu sehen ist.

Finde den Stundenlohn also mehr als fair.

17.06.2021 - 09:59 Uhr

Ganz einfach:
Dieser "User" hat sich frisch in einem Programmierforum angemeldet und sein erster Beitrag hat mit Entwicklung nix zu tun?
Nur um im zweiten Post dann einen ominösen Link zu posten?
Mit schlecht automatisierter Übersetzung?

Also ich werde da zumindest nicht drauf klicken 😁

05.05.2021 - 08:36 Uhr

Hi,

da gibt es natürlich massenweise fertige "Pick by Light"-Lösungen.
Das selbst bauen zu wollen ist weniger ein programmatisches, als ein elektrotechnisches Problem!

Wie viel Volt und Watt haben die LED´s? Rot & Grün separat oder per Signal steuerbar? Wie weit sind diese auseinander? Werden die per Multiplexing mit einem Microcontroller angesprochen oder jeder mit einem eigenen Controller?
ESP32 / ESP8266 mit WiFi zur Steuerung oder Kabelgebunden (Arduino & Co)?
Seriell per COM-Port?

Also ich habe definitiv mehr Fragen als Antworten 😉

12.02.2021 - 11:38 Uhr

Nach dem immensen Feedback stelle ich die Lösung und den Weg dorthin mal bereit.

Die 9 Karten können auf 362880 verschiedene Arten angeordnet werden.
Also erstellen wir uns eine Liste mit allen Möglichen Sortierungen der Karten:


static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<T> list, int length)
{
    if (length == 1) return list.Select(t => new T[] { t });
    return GetPermutations(list, length - 1)
        .SelectMany(t => list.Where(e => !t.Contains(e)),
            (t1, t2) => t1.Concat(new T[] { t2 }));
}

Diese Möglichkeiten gehen wir nun alle* durch:


private static Card[] _cardTemp;
public static void Solve()
{
    //Add Solution here:    
    IEnumerable<IEnumerable<int>> permutations = GetPermutations(Enumerable.Range(0, 9), 9);    

    //Create a copy for easier sorting/rollback per permutation
    _cardTemp = new Card[Cards.Count];
    Array.Copy(Cards.ToArray(), _cardTemp, Cards.Count);

    foreach (var permutation in permutations)    
    {
        //Skip all excluded entries
        //if (IsBlacklisted(permutation))
        //    continue;
        if (CheckSolution(permutation))
            return;    
      }
}

*Alle Möglichkeiten? Besser nicht, möchte man da denken!
Also habe ich eine Blacklist geschaffen, die bekannte, nicht funktionierende Kombinationen überspringt.
Nehmen wir an, die Kombination der Karten 1235.... kann nicht funktionieren, weil Karte 3 und 5 keine passenden Teile haben (egal wie man sie dreht), speichert man eben diese Kombination ab.
Nun kann man prüfen ob die aktuelle Permutation in der Blacklist vorkommt und für alle folgenden Kombinationen rausspringen...

Preoptimization is root of all evil!
Hier lernt man am lebenden Objekt, dass dieser Spruch nach wie vor Gültigkeit besitzt!
Was passierte: Der Durchlauf bis zum ersten Treffer hat statt 1,3 Sekunden auf einmal 3 Sekunden gedauert.
Die Blacklist wuchs immer weiter an und speicherte 2 bis 8 stellige Permutationen.
Aufgrund der variablen Breite, erfolgt die Suche durch die Blacklist mit StartsWith(), was bekanntlich nicht sonderlich performant ist.
Also wurde alles mehr gebremst, als dass es geholfen hat. Auch "Optimierungen" auf ein Dictionary mit fester Breite hat nach wie vor für Verzögerungen gesorgt.
Tatsächlich ist es schneller alle Kombinationen durchzugehen, als bestimmte auszuschließen.

Also weiter im Takt!
In der CheckSolution() prüfe ich Karte für Karte ob es einen "Match" geben kann.

Dafür gibt es die IsHit() Methode. Die nimmt die Flugzeugnummer entgegen und prüft dieses gegen das vorige Flugzeug. Als weiteren Parameter kann man übergeben ob nur die Möglichkeit besteht (Also das passende Gegenstück lediglich enthalten ist) oder dieses auch schon korrekt gedreht ist und anliegt.


private static bool IsHit(int planeNo, bool onlyPossibility = false)
{
    switch (planeNo)
    {
        case 0:
            return true;
        case 8:
        case 7:
        case 5:
        case 4:
            return onlyPossibility?
                Cards[planeNo].ContainsCounterpart(Cards[planeNo - 3].BottomPlane) && Cards[planeNo].ContainsCounterpart(Cards[planeNo - 1].RightPlane):
                Cards[planeNo].TopPlane.Fits(Cards[planeNo - 3].BottomPlane) && Cards[planeNo].LeftPlane.Fits(Cards[planeNo - 1].RightPlane);
        case 2:
        case 1:
            return onlyPossibility ?
                Cards[planeNo].ContainsCounterpart(Cards[planeNo - 1].RightPlane) :
                Cards[planeNo].LeftPlane.Fits(Cards[planeNo - 1].RightPlane);
        case 3:
        case 6:
            return onlyPossibility ?
                Cards[planeNo].ContainsCounterpart(Cards[planeNo - 3].BottomPlane) :
                Cards[planeNo].TopPlane.Fits(Cards[planeNo - 3].BottomPlane);
        default:
            return false;
    }
}

Das bringt uns zum nächsten Helfer, der eine Karte so lange dreht, bis sie korrekt anliegt. Oder mit false herausspringt:


private static bool TurnPlane(int planeNo)
{
    if (planeNo == 0 || IsHit(planeNo))
        return true;            
    for (int rotation = 0; rotation < 3; rotation++)
    {
        Cards[planeNo].RotatePlanesRight();
        if (IsHit(planeNo))
            return true;
    }
    return false;
}

Das Ganze nun noch zusammenstricken.
Dabei gibt es allerdings noch **zwei **Eigenheiten:


private static bool CheckSolution(IEnumerable<int> permutation)
{
    //Change all cards, one by one
    for (int i = 0; i < Cards.Count; i++)
    {
        int perm = permutation.ElementAt(i);
        Cards[i] = _cardTemp[perm];                

        // If card [1] does not fit to [0], rotate [0] until it works
        if (i == 1 && !IsHit(i, true))
            for (int rotation = 0; rotation < 3; rotation++)
            {
                Cards[0].RotatePlanesRight();
                if (IsHit(i, true))
                    break;
            }
        //If first two cards still do not fit!
        if (i == 1 && !IsHit(i, true))
        {
            //Blacklist(permutation, i);
            return false;
        }

        //Now turn until the plane fits on all edges or blacklist it
        if (!TurnPlane(i))
        {            
            //Blacklist(permutation, i);
            break;
        }
        //If we made it until here, all cards fit!
        if (i == Cards.Count - 1)
            return true;
    }
    return false;
}

Und zwar ist es wichtig, dass schon die erste Karte so gedreht wird, dass sie zur zweiten Karte passen kann.
Ansonsten kann man die Kombination bereits verlassen.

Anschließend schnappt man sich die nächste Karte und dreht so lange daran herum, bis diese passt.
Oder bricht entsprechend ab.
Bei der 9. Karte angelangt, ist das Rätsel gelöst! Oder?

Nein, natürlich nicht! Denn so erhält man nicht alle 16 Lösungen.
Es gibt Kartenpaare, die mehr als eine Kombination enthalten. Wenn nun Variante 1 nicht funktioniert, wird zur nächsten Permutation gesprungen. Damit hätte man die potentielle Lösung ignoriert.

Daher kam noch dieser Code dazu:


//Now turn until the plane fits on all edges or blacklist it
if (!TurnPlane(i))
{
    //Check if there is a better way, turning the first card more over
    //(There might be [definitely are] more combinations with first & second card)
    if (i >= 1)
    {
        bool anotherPossibility = true;
        //Turn first card max 3 times to find a second match
        for (int rotation = 0; rotation < 3; rotation++)
        {
            anotherPossibility = true;
            Cards[0].RotatePlanesRight();
            for (int j = 1; j <= i; j++)
            {
                if (!TurnPlane(j))
                {
                    anotherPossibility = false;
                    break;
                }
            }
            if (anotherPossibility)
                break;
        }
        if (anotherPossibility)
        {
            i = 0;
            continue;
        }
    }
    //Blacklist(permutation, i);
    break;
}

Gibt es innerhalb der drei Drehversuche noch einen weiteren Match, so wird einfach der Counter i wieder auf 0 gesetzt.
Somit geht diese Permutation nochmals ins Rennen!

Mein unrepräsentatives Ergebnis, wenn man alle Möglichkeiten durchlaufen lässt:


1:	024658137	1,3719574s	Attempts: 4750
2:	062731854	0,1125595s	Attempts: 19959
3:	078536412	0,0506084s	Attempts: 26483
4:	160352784	0,1687565s	Attempts: 46273
5:	168352704	0,024541s	Attempts: 49306
6:	214635078	0,0997753s	Attempts: 58739
7:	214635870	6,77E-05s	Attempts: 58744
8:	407253861	0,4984146s	Attempts: 117594
9:	450137268	0,104993s	Attempts: 130416
10:	458137260	0,031965s	Attempts: 134889
11:	487253061	0,0901648s	Attempts: 145112
12:	731056428	0,6187311s	Attempts: 212564
13:	731856420	0,0051159s	Attempts: 213197
14:	824650137	0,2260807s	Attempts: 236826
15:	862731054	0,1372636s	Attempts: 252055
16:	870536412	0,0283649s	Attempts: 254831
Total: 3,6389175s

Die Performance hängt natürlich stark am verwendeten Rechner und Debug/Release-Mode.

Die Programmierung ist wahrlich kein Hexenwerk. Die Eigenheiten und Fallstricke zu erkennen war aber echt interessant.
Und für mich persönlich ein schöner Ausbruch aus der Alltagsprogrammierung 🙂

08.02.2021 - 09:46 Uhr

Hi,

gibt es einen Grund hierfür?


public string DateOfGrading {get;set;}

Wäre ein DateTime nicht eleganter und nimmt Dir im Zweifel Berechnungen in Schaltjahren ab. Oder wirft einen Fehler, wenn das Datum ungültig hinterlegt wird.
Und da Du von "App" schreibst, sollte auch die lokale Zeitzone vom Anwender Dir keinen Strich mehr durch die Rechnung machen.

02.02.2021 - 09:22 Uhr

Und man kann nur Seitenweise blättern, nicht zur letzten Seite springen.

Die doppelten Seitenzahlen sind aber als Bug bereits bekannt.

01.02.2021 - 08:48 Uhr

Vielen Dank an alle Mitwirkenden für die intensiven zwei Jahre Arbeit!

26.01.2021 - 08:39 Uhr

Nutzt einfach das Zeug richtig, wie es designed is

Vermutlich weil es exakt so designt ist.
Wenn jeder Entwickler eine eigene Umgebung hat, ist es State of the Art eine Launch.json inkl. der Zugänge lokal vorzuhalten.

So hat man mindestens eine eigene (Cloud-)Umgebung und kann seine Branches darauf entwickeln.

Kenne ich von einem großen Microsoft Produkt auch so und finde nichts verwerfliches daran. Wird auch so von MS dokumentiert und erwartet.
Da ziehe ich ein Docker-Image und gebe in meiner Entwicklungsumgebung den Pfad dort hin, samt Zugangsdaten an.

Ist also definitiv keine Team-File, da jedes Teammitglied eine eigene Datei, ego Umgebung hat.

21.01.2021 - 14:57 Uhr

Die Aufgabe ist auf der vorigen Seite zu finden.

Hier nun die Solution des Flugzeug-Spiels.

Viel Erfolg!

PS: aktuell habe ich 14 mögliche Lösungen und finde die 1. nach 479 Durchläufen.
Allerdings habe ich noch eine Unzulänglichkeit im Code, für die ich noch keine elegante Lösung gefunden habe.

Stand jetzt: ca. 70 Zeilen Code, inkl. Leerzeilen und Kommentaren.

21.01.2021 - 14:53 Uhr

Hallo zusammen,

durch Zufall bin ich auf einen Ordner aus meiner Ausbildung von 2008 gestoßen.

Dort war es Aufgabe das "verflixte Flugzeugspiel" umzusetzen.
Meine Lösung damals sollte natürlich auch eine automatische Auflösung beinhalten, die äußerst mäßig funktioniert hat...


Den Code möchte heutzutage auch niemand mehr sehen 😁

Aufgabe ist es, dass alle Karten in einem 3x3 Raster so sortiert & gedreht werden, dass an den Stoßkanten je eine Flugzeugspitze und ein Heck in der selben Farbe anliegen.

Dazu habe ich kurzum eine minimalistische WPF-Anwendung erstellt.
Per Drag&Drop können die Karten getauscht, per Rechtsklick um 90° gedreht werden.

In der Klasse "Game.cs" wird in der Solve()-Methode losprogrammiert:

public static void Solve()
{
    //Add Solution here:
    for (int i = 1; i < Cards.Count; i++)
    {
        var j = 0;
        if (i == 3 || i == 6)
            while (!Cards[i].ContainsCounterpart(Cards[i - 3].BottomPlane) && i+j < Cards.Count)
                Cards.Swap(i, i + j++);
        else
            while (!Cards[i].ContainsCounterpart(Cards[i - 1].RightPlane) && i + j < Cards.Count)
                Cards.Swap(i, i + j++);
    }

    for (int i = 1; i < Cards.Count; i++)
        if (i == 3 || i == 6)
        {
            if (Cards[i].ContainsCounterpart(Cards[i - 3].BottomPlane))
                while (!Cards[i].TopPlane.Fits(Cards[i - 3].BottomPlane))
                    Cards[i].RotatePlanesRight();
        }
        else
            if (Cards[i].ContainsCounterpart(Cards[i - 1].RightPlane))
            while (!Cards[i].LeftPlane.Fits(Cards[i - 1].RightPlane))
                Cards[i].RotatePlanesRight();            
}

(Exemplarische Herangehensweise)

Die Liste der Karten besteht aus 4 Flugzeugen, die wiederum angeben welche Farbe sie besitzen und ob es sich um die Front oder das Heck handelt.
Man kann sie über Top,Left,Right & Bottom oder im Uhrzeigersinn Planes[0]-Planes[3] erreichen.

Zur Hilfe gibt es die Methoden*ContainsCounterpart - Ob ein passendes Flugzeugteil auf der Karte enthalten ist *Swap - Tauscht eine Karte mit einer anderen *RotatePlanesRight - Dreht die Karte um 90° *Fits - Ob das Flugzeug zu einem anderen passt

Leider kann ich nur ein Bild oder ein ZIP hochladen. Daher ist die Solution im nächsten Beitrag: Das Programmier-Spiel: nette Übungsaufgaben für zwischendurch

PS: Das Projekt ist absichtlich so minimal gehalten wie möglich. Also bitte sonderlich auf die Codequalität achten. Oder mangelndes MVVM 👅

06.01.2021 - 10:04 Uhr

Vergleich doch mal den "Content-Type" im Header Deiner C# Anfrage, mit dem was im Postman im "Contrent-Type" steht.
Das ist schon mal definitiv unterschiedlich!

"Boundary" ist wieder ein eigener Header, denn das Semikolon trennt die Header von einander.
Sonst wäre der "Content-Type" auch ungültig! Lager das mal in eine eigene Zeile im Postman aus.

Und dann bau die Header in .Net genauso nach: content.Headers.Add().

21.12.2020 - 08:35 Uhr

Hallo Patrik,

ich würde davon abraten das Zeugnis nicht mitzuschicken.
Natürlich ist es blöd wenn das nicht so ganz optimal ist und Du erst nachher durchgestartet bist. Jedoch hast Du zum einen die Möglichkeit im Anschreiben Deinen Werdegang darzulegen und zum Anderen die Reihenfolge der Zeugnisse in der Mappe zu beeinflussen.
So kann jeder Deine Lernkurve erkennen und sehen, dass Du ambitioniert an die Sache herangehst und trotz des einen schlechten Zeugnisses eine tolle Ausbildung abliefern kannst.
Manch Anderer blättert gar nicht mehr so weit 😉

Kein Zeugnis wird entweder als unprofessionell oder Verschleierung gesehen.

Zu Git:
Die Idee ist an sich gut. Du möchtet zeigen, dass Du motiviert bist und Dich bereits privat mit der Thematik des entwickelns auseinandersetzt.
Das würde ich im Anschreiben auch so erklären. Genauso wie Deinen Kursus.

Git würde ich verwenden, wenn Du eigene, individuelle Projekte hast. Ein Code, welcher in zahlreichen Büchern oder teilweise Hausaufgaben auftaucht, könnte genauso gut zusammenkopiert werden.
Ein Personaler kann damit auch überhaupt nichts anfangen 😃

Also sprich es im Anschreiben an und setze Deine Erfolge (Zertifikat) in den Lebenslauf.

24.11.2020 - 17:10 Uhr
  1. Was ist der Host? Ist das das gateway sprich der router z. b.?

Nein, Du hast einen Sender und einen (oder mehrere) Empfänger.
Oder Master/Slave, Host/Client, wie auch immer man es nennen mag.

Sprich: Einer muss ja den Broadcast initiieren und jemand anderes diese Nachricht empfangen und beantworten.
In Deinem Fall wird die Software am PC der Host sein und die Mikrokontroller die Empfänger.
Der Router macht wie er heißt: Er routet den Weg zwischen den Geräten.

  1. Der port spielt doch keine Rolle, solange ich bei meinen Microcontrollern diesen port für Udp eingerichtet habe, oder?

Wenn Du den Port angibst, spielt er doch bereits eine Rolle!
Die Broadcast-Nachricht soll ja nicht alle Geräte im Netzwerk ansprechen, sondern nur Deine. Stell es Dir als eine Art Filter vor!

  1. Ich habe zwei UdpClients in meinem Programm, der eine zum Zuhören, der ander zum Schreiben, richtig?

Das ist etwas irreführend, da die Klasse UDPCLient heißt. Sie kann aber sowohl Sender als auch Empfänger sein.

Prinzipiell macht es das Beispiel von Microsoft ja genau so:
Nachricht an einen Port senden, optimaler weise mit einer IP ala 192.168.2.255 damit alle IP´s in der Range angesprochen werden.
Dann wartet dieser auf einen Reponse, also die Rückantwort. Diese enthält dann die IP des antwortenden Gerätes. Und was auch immer Du in die Nachricht schreibst.

Dann obliegt es Dir, nochmal eine weitere Direkt-Verbindung mit diesem einen Gerät aufzumachen oder ob Dir die Antwort schon reicht.

Du solltest dann natürlich berücksichtigen, dass Du bei mehreren Geräten im Netzwerk auch mehrere Antworten bekommen wirst.

PS: Bei den ATmega´s wird der Speicher natürlich schnell knapp.
Je nach dem was Du da überhaupt vor hast, würde ich ggf. einen anderen Weg einschlagen.

24.11.2020 - 14:41 Uhr

In Alf Ator´s Link steht es doch ganz gut verständlich:

Now, no maths function on Earth could give a transformation from the Clay Paky RGBW settings to the LEE values.

Jede Berechnung wäre nur eine Annäherung.
Und diese Annäherung würde von LED-Typ zu Typ variieren.

Wenn Du doch eine Formel hast, die den Weißanteil aus einem RGB-Farbwert extrahieren und dem W-Wert zuweisen kann, wieso nimmst Du nicht exakt diese Berechnung und stellst sie selbst um?

Anders wirst Du mit keiner Lösung aus dem Internet genau den Wert erhalten, den Du erwartest.

24.11.2020 - 14:20 Uhr

Aber halbfertig gibt es das.

Per UDP-Broadcast kannst Du alle Geräte in einem Netzwerk anfragen, welche in einer IP-Range liegen und auf einen Port lauschen.
.Net-Seitig z.B. der UdpClient.

Seitens des µC (ich nehme mal an ein ESP32 oder8266) gibt es WiFiUDP.

Dann sendest Du eine Nachricht raus und Deine Mikrocontroller antworten entsprechend darauf.

Oder man schaut sich z.B. an wie die OTA Update-Libraries das machen.
Dabei meldet sich auch jeder ESP im Netzwerk bei der Arduino-IDE und sie erscheinen in der COM-Port Liste (Mit MAC-Adresse und Namen).

11.11.2020 - 10:46 Uhr

Es wird nach wie vor alles bitweise gespeichert.
Sowohl die Funktionsweise einer Festplatte (HDD) oder einer Speicherkarte / SSD basiert auf dem Konzept "kleine Schalter" an oder aus zu schalten. Ob das nun über Magnetisierung oder Gatter + Transistoren funktioniert, spielt erstmal keine Rolle.
Auch CD´s, DVD´s oder Bluray´s speichern nur eine Folge von an & aus, welche von einem Laser abgetastet werden.

Werden diese Daten nun übertragen, egal ob per Kupfer oder Lichtwellenleiter, geschieht dies durch Impulse. Man kann schließlich keine "5" durch ein Kabel schicken, sondern nur Strom oder keinen Strom => Binär.

Das System dahinter "entscheidet" dann über den Clock und die Bitgröße wann eine Übertragung vollständig ist.
Der Clock gibt den Takt vor, damit man z.B. zwei aufeinanderfolgende Nullen (aus) oder Einsen (an) feststellen kann. Quasi die Zeit, die man jedem Bit zur Übertragung gibt.
Die Bitgröße legt dann fest, wie groß ein Datenpaket ist.
Wenn auf einem 64bit Computer "3 + 5" berechnet werden soll, werden trotzdem zwei 64bit große Zahlen an den Prozessor gesendet. Vorne dann eben mit Nullen aufgefüllt.

Es ist also nicht ein Vor- oder Nachteil auf das Binärsystem zu setzen, sondern eine physikalische Gegebenheit, wie Computer funktionieren.

Und das Binär, bzw. Hexadezimalsystem ist die logische Schlussfolgerung mit diesen Nullen & Einsen "unsere" lesbaren Zahlen darzustellen. Und zwar so optimal, dass sie so wenig Platz verbrauchen wie nötig.

Mit 8 bit, also 8 Nullen oder Einsen, kann ich alle Zahlen von 0 - 255 (also 256 Zustände) darstellen.
Und das System basiert nun mal auf der Verdopplung: 1, 2, 4, 8, 16, 32, 64, 128*.
Möchte ich nun eine " 5 " darstellen, hake ich binär die 1 und die 4 an: 1010000
Brauche ich eine " 44 " nehme ich die 4, 8 & 32: 00110100
Führe ich diese Reihe nun weiter, lande ich bei 1024 und eben nicht bei 1000.

*Natürlich wird das Binärsystem andersherum von 128 - 1 dargestellt!
Dementsprechend stellt eine 00000001 eine 1 dar und die 10000000 die 128.

20.10.2020 - 09:04 Uhr

Hallo Günther,

wenn die App abstürzen würde, gäbe es einen EventLog-Eintrag.
Klingt für mich mehr als würde sie geschlossen (von wem auch immer).
Unter Forms gab es die CloseReason, die beschreibt wodurch das Schließen ausgeführt wurde. Unter WPF ist das etwas anders:
CloseReason unter WPF

30.09.2020 - 09:34 Uhr

Eine Diskussion zu dem Thema finde ich begrüßenswert!
Die jahrzehntelange Unterdrückung hat sich in vielen Dingen manifestiert, nicht nur in der Sprache. Einiges nehmen wir gar nicht mehr wahr.

Dass es akut auch Berichterstattungen über sog. "Blackfacing"gibt, ist da ebenfalls nur richtig.
Auch wenn ich hier Unterschiede sehe, wenn Otto Waalkes billig einen "Bimbo" verkauft oder ob Hoecker und Kalkhofe eine Persönlichkeit auf die Schippe nehmen, dazu sich dem Stilmittel der Verkleidung bedienen, dem am nächsten zu kommen.
Und das bei jedem Charakter tun, ohne die Hautfarbe als solches eine Rolle oder gar Zielscheibe für den Gag bietet.

Diese Differenzierung wünsche ich mir bei Master/Slaves ebenfalls.
Wir können nicht darüber richten, ob der Gebrauch "Slave" verletzend ist und/oder die jahrzehntelange Sklaverei und Apartheid den betreffenden Leuten immer wieder vor Augen führt.

Die Verhinderung eines Wortes, löst dessen Geschichte/Problem/Assoziation nicht aus. Andererseits kann man durch minimale Veränderungen an mancher Wortfindung Vorbehalte von ethnischen Gruppen entsprechen.

Man sollte die Geschichte nicht vergessen!
Man muss Geschichte aber nicht wiederholen!

Umbenennen alter Branches halte ich daher für falsch. Künftig sich auf einen anderen Namen zu einigen, der das selbe bedeutet um Asoziationen zu verhindern, dürfte ja wohl keine große Hürde darstellen!

14.09.2020 - 09:02 Uhr

Da musste ich doch sehr schmunzeln:

Erinnert mich an meine C++ Zeit in der Schule mit nem Borland Editor, der damals schon 10 Jahre aus dem Support war.

Offenbar waren wir auf der selben Schule 😉

Wir haben in der Ausbildung begonnen C# im Notepad zu entwickeln und mit der csc.exe zu kompilieren. Erstmal die Funktionsweise verstehen und im späteren Schritt alles über die IDE automatisieren. Welche, das war uns als Schüler freigestellt zu wählen.

29.07.2020 - 08:56 Uhr

Das macht doch gar keinen Sinn!
Entweder Du bist in der Cloud und Jeder Kollege kann von überall (auch mit MFA) sich verbinden.
Oder Du hast einen Server im Keller stehen und benötigst einen VPN.

Für die Ursprüngliche App empfehle ich Dir mal Powerapps anzuschauen. Damit kannst Du einfach eine App erstellen und mit einer beliebigen Datenquelle verknüpfen. Das kann ein SQL-Server sein, aber auch eine Excel-Tabelle.
Die App steht dann automatisch als Web-App, IOS, Android & UWP Anwendung bereit.
Man kann dort die die Azure AD verwenden. Die Authentifizierung passiert aber ohnehin über O365 und auch nur Firmenintern.
Wenn das nicht reicht, kann man Funktionen mit Powerautomate auslagern. Dort wäre es dann möglich Emails zu verschicken, Worflows auszulösen, einträge in ToDo/Planner zu machen.

Und nun kommts: Alles ohne Programmiererfahrung für einen kleinen Taler (Wenn nicht schon komplett durch O365 Business abgedeckt).

Diese ganzen Tools, Funktionen und die Infrastruktur dahinter hat man inklusive. Da kommt man mit dem verstaubten Server im Keller nicht gegen an!

27.05.2020 - 22:22 Uhr

Erstmal sehr schön, dass es klappt!

Die bei Hyundai müssen irgendwas anders machen... Keine Ahnung.

OBD2 ist ein Standard. Der ist verpflichtend für jedes in den USA oder Europa zugelassenes Auto.
In Europa mittlerweile sogar für Motorräder vorgeschrieben, aber ausgesetzt, da die Hersteller das nicht gebacken kriegen.

Im Wiki findet man eigentlich alle nötigen Infos:
OBD-II PIDs

Vielleicht nutzt der Hyundai ein anderes Protokoll, welches auch der ELM327 für Dich automatisch suchen kann:
http://www.obdtester.com/elm-usb-commands

27.05.2020 - 16:26 Uhr

Ihh, Thread.Sleep()!!!

Das klappt natürlich so weit, als dass der Buffer weiterhin gefüllt wird und die Wahrscheinlichkeit erhöht, dass dort nun ein abschließender Zeilenumbruch enthalten ist.
Aber ne Programmierung sollte nicht auf dem Prinzip "Hoffnung" basieren, daher lege ich Dir nochmal den Link auf das SerialPort Template nahe. 😃

Dein erster Befehl läuft auf einen Fehler:
7F2112
7f Bedeutet immer Fehler
21 Deine angefragte SID
12 "Sub Function Not Supported - Invalid Format"

Lag vielleicht an mir, denn ich bringe gerne mal SID 01 mit 12 durcheinander, da ich in erster Linie mit dem KWP2000 arbeite, dem Vorgänger.

Hattest Du vorher "ATMA" ausprobiert? Dann bekommst Du nämlich ungefragt alle CAN-Bus Nachrichten ungefiltert angezeigt. Daher ggf. die X:-Nachrichten.

Soweit ich weiß, werden andere Steuergeräte anders Adressiert. Und zwar in dem Teil der Nachricht, den Dir der ELM327 vorenthält.
Die echte Nachricht beginnt nämlich mit einem Format (0x80 / 0x81) und dann dem Adressaten, Sender und der Länge der folgenden Nachricht.
Diesen Adressaten kannst Du wiederum nur mit AT-Befehlen ändern.
Für mich scheint die SID 21 eher eine Herstellerspezifische ServiceID zu sein, damit sie einfach über die Diagnoseschnittstelle den Status der Batterien abgreifen können.

27.05.2020 - 14:12 Uhr

Hallo zusammen,

leider bin ich absoluter Nerd in diesem Thema 😉

Es wird immer ein Carriage Return gesendet (char 13).
Ob noch ein Linefeed (char 10) folgt, kannst Du mit "ATL" + 0 oder 1 entsprechend de- oder aktivieren. Standardmäßig ist es aus.

Anschließend folgt noch der Abschluss des Responses und die Bereitschaft ein neues Kommando zu empfangen.
Dies geschieht über ein Prompt ">" (char 0x3E)

Ansonsten passiert die Kommunikation standardmäßig, aus Performancegründen, ohne Header und Checksumme. Darum kümmert sich dann der ELM327 (Der Chip im Bluetooth-Dongle).

Es ist auf jeden Fall wichtig, die Daten wie Martin schon schreibt, zu puffern. Dazu gibt es auch ein tolles Tutorial hier im Forum 😄
Template SerialPort

Dann kannst Du einfach Deine SID (0x21) mit der entsprechenden PID (z.B. 0x0C RPM) absenden.
Der Rückgabewert wird dann so aussehen:
61 0C 08 AE

Die 61 steht für die Service ID 0x21 + 0x40.
0C wiederholt die Parameter ID.
0x08 = 8
0xAE = 174
8 * 256 + 174 = 2222 Umdrehungen pro Minute.

Die PID 02, welche Du im Screenshot abprüfst steht für den Fehlerspeicher.
Wenn ich das im Wust der Daten richtig sehe, hast Du noch das "Echo" aktiviert. Also die Wiederholung Deiner Anfrage.
Stell das am besten ab: "ATE0"

Dann empfehle ich noch die Hex-Werte immer zweistellig auszugeben. Am besten noch mit einem Leerzeichen dazwischen. Sonst steigt man ja gar nicht mehr durch 😃

Zu guter Letzt:
Du bekommst keine CAN-Message. Du verwendest die Diagnose Schnittstelle nach OBD2. Der CAN-Bus ist die interne Übertragungsart der Steuergeräte im Auto.
Könnte z.B. auch K-Line sein. Das übernimmt aber der ELM327 für Dich.
Wenn Du expliziete CAN-Bus Nachrichten sehen möchtes, kannst Du ja mal den Befehl "ATMA" ausprobieren 😛

29.04.2020 - 09:10 Uhr

Es soll ein vollautomatisches Testsystem erstellt werden,
das immer den neuesten Build mit der exe in Zusammenhang ausführt und testet

Dann mach doch genau das 😃

Nach dem erfolgreichen Buld, setze einen Docker Container auf und schubse das Programm drauf.
Dann starte die Tests und melde das Ergebnis zurück an DevOps.
Nun lösche den Container wieder.
Alles aus DevOps gesteuert (per PowerShell), wo Du dann auch mit dem Testergebnis direkt weiter machen kannst.

So machen wir das nun schon seit einiger Zeit und ohne das erfolgreiche o.g. Prozedere können wir einen Pullrequest gar nicht abschließen.

Leider habe ich das System selbst nicht aufgesetzt, daher habe ich nur zwei Links parat:
Deploying test environments with Azure DevOps, EKS and ExternalDNS
oder
Docker : .NET Core container automated tests from Azure Pipelines

01.04.2020 - 09:48 Uhr

Homeoffice für alle die es können. Also praktisch jeder, in einem IT Unternehmen.
Nur ein paar Verwaltungsmitarbeiter halten wechselweise die Stellung.

Im Großen und Ganzen bleiben die Projekte bestehen, nur Neu-Projekte werden teilweise verschoben. Dann werden die Kollegen eben für die interne Produktentwicklung eingesetzt oder weitergebildet (Online Schulungen).
Was per Teams aber echt heftig ist! Die Aufmerksamkeitsspanne ist deutlich kürzer und trotz vieler Pausen muss der Tag deutlich verkürzt werden.

Dann habe ich noch das große Glück, dass mein Arbeitgeber nach der Wirtschaftskrise das Unternehmen massiv abgesichert hat! Finanziell stehen wir also stabil da.

Zieht man die Parallele zur Krise 2008/2009, hat es die IT-Branchen am wenigsten hart getroffen. Und sobald die Wirtschaft wieder anlief, haben wir uns vor Arbeit kaum retten können.
Bin davon überzeugt, dass es wieder so laufen wird!

Durch den Freundeskreis kenne ich aber auch viele Solo-Selbstständige, Kleinunternehmer, Handwerksbetriebe bis zum großen Automobilzulieferer. Dort stand von heute auf morgen alles still! Unfassbar was da abgeht!
Die Fix- und Lohnkosten fressen jegliche Rücklagen binnen von Tagen auf. Da ist an Monate gar nicht zu denken...

18.03.2020 - 11:21 Uhr

Schau doch mal was modulo überhaupt tut und Dir als Rückgabewert bei verschiedensten Konstellationen zurückgibt.

Deine if-Abfrage verhindert ja ein Ergebnis auszugeben, sofern die Seitenzahl nicht durch 8 teilbar ist.

Und auf den Quotient kannst Du auch verzichten 😃


Console.WriteLine("Die Abweichung der Seitenanzahl für den Druck beträgt " + seitenZahl % divisor  + " .");
09.03.2020 - 13:32 Uhr

Sofern ich Dein gewünschtes Ergebnis korrekt verstehe, hast Du zwei Aufgaben.
1.Gruppieren der Daten 1.Implementierung einer Suche

Wenn Du nun den Text in ein Dictionary einliest, darfst Du nur einen Eintrag pro Tier/Keyword haben.
Gibt es diesen noch nicht, legst Du ihn an. Existiert das Keyword bereits, addierst Du die Werte auf.

Dann kannst Du das Dictionary darstellen. Ob direkt an ein DGV gebunden oder mit dem Umweg der DataTable.
Letzteres ermöglicht Dir eine recht einfache Filterung im DataGridView. Ohne zu wissen was dann damit passiert, kann ich keine Empfehlung dazu aussprechen.
Es gibt etliche Möglichkeiten für die Suche/Filterung.

23.01.2020 - 09:39 Uhr

Hallo oehrle,

ein Schreibschutz wird Dich nicht davor schützen, dass das Programm ausgelesen wird.

Um das dekompilieren zu umgehen: Verwende eine Programmiersprache, die keinen IL Code generiert. C, C++, etc.

Zwar gibt es auch einige Tools für z.B. C++ Anwendungen, die Hürde ist aber um ein vielfaches höher!

PS: Ja, man kann USB-Sticks schreibschützen. Hilft an dieser Stelle aber nicht.

16.12.2019 - 09:52 Uhr

Hi,

bin eben darüber geflogen und für mich sieht es aus, dass es keine Alternative zu Xamarin darstellt. Denn Du kannst Xamarin Forms direkt als source verwenden!
Sprich: Du kannst durchaus mit Xamarin arbeiten und anschließend mit UNO eine WebApp daraus erstellen.
Für Einsteiger in X-Plattforms aber nicht gerade eine Vereinfachung.

Deine Anforderung klingt eher nach Power Apps!
Dort kann man NAV als Quelle angeben und sich Listen und Ansichten zusammen klicken. Einfache Programmierung ist im Stile von Excel möglich.
Jede App steht automatisch als WebApp bereit und hat für iOS, Android und UWP die entsprechende App.