State-Maschine mit enums

  • VB.NET

Es gibt 19 Antworten in diesem Thema. Der letzte Beitrag () ist von Digtro_77.

    State-Maschine mit enums

    Moin !

    Ich habe eine gewaltige State-Machine mit mehr als 450 Schritten programmiert.

    Dafür habe ich einen Haupttimer laufen lassen, der über den jeweiligen Zustand die State-Machine bedient.

    Nun müssen allerdings noch Punkte in der Mitte zugefügt werden - und zack, müssen alle Cases nach unten hin korrigiert werden.
    Um dies zu vermeiden, wollte ich ein Enum benutzen, doch leider *hüstel* ist vb dann kaum noch bedienbar...es ruckelt und der Cpu-Verbrauch geht enorm in die Höhe.

    Ich habe dann die Enums aufgeteilt, in vier unterschiedliche Enums:

    Initialisierung, Geräteansteuerung, Messung, Prüfmodi.

    Deklariert habe ich jedes Enum als Byte, mit genügend Platz um zusätzliche Punkte inefügen zu können.

    Für die State-Machine benutze ich nun die Position im Enum und schaltet dieses weiter, was auch soweit gut funktioniert.


    Wie komme ich aber von einem Enum zum anderen Enum, wenn jeweils das letzte Enum durch +1 weitergezählt wird ?
    Wie kann ich z.B. von dem Enum "Geräteansteuerung" / Position 2 um z.B. 4 Positionen zum Enum davor zurückgehen ?


    Gibt es vielleicht einen besseren Ansatz, um die Größe von so vielen States zu handeln ?
    Sowas kann man machen:

    VB.NET-Quellcode

    1. Module EnumExtensions
    2. <Extension> Public Sub [Step](Of T As {IComparable, IConvertible, IFormattable, Structure})(ByRef enm As T, value As Integer)
    3. Dim i = DirectCast(DirectCast(enm, Object), Integer)
    4. enm = DirectCast(DirectCast(i + value, Object), T)
    5. End Sub
    6. End Module
    7. Public Class Bla
    8. Public Enum myStates As Integer : A : B : C : D : E : F : G : H : I : J : K : L : M : N : O : P : Q : R : S : T : End Enum
    9. Private Sub Test() ' Aufrufe-Sample
    10. Dim state = myStates.K
    11. Debug.WriteLine(state)
    12. state.Step(3)
    13. Debug.WriteLine(state)
    14. End Sub
    15. End Class

    Ansonsten sind Enums mit das effizienteste, wasses gibt - sie "erben" ja von Integer (oder Byte, was minimal langsamer ist).
    Kann mir nicht vorstellen, dass Performance-Probleme daran liegen - es sei denn, du machst ganz komische Sachen mit denen.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Boah...vielen Dank dafür - das werde ich morgen gleich probieren !

    Also du meinst, dass selbst ein Enum as Byte (max.255) oder Integer mit 500 + Einträgen keine Einbuße in der Performance darstellen sollte ?


    Also nur bei der Deklaration gab es keinerlei Probleme, allerdings habe ich in den Select-Cases folgendes getan:

    VB.NET-Quellcode

    1. Private State_Zustand_Grundgeraet As Grundgeraet_Statemachine
    2. Select Case State_Zustand_Grundgeraet
    3. Case Grundgeraet_Statemachine.Messung_UVP_3V45
    4. If Not Flag Then
    5. Flag = True
    6. ' Achtung - Max und Min vertauschen
    7. Meswert_bewerten(GK560_FP345_Meas, GK560_PF345_Min, GK560_PF345_Max, Rigol_DM_Adresse_Ini, ":MEAS:VOLT:DC?", "V")
    8. Flag = False
    9. End If


    Nach den ersten 30ig Einträgen konnte ich kaum noch Scrollen, CPU-Auslastung lag bei 80%...da ging nix mehr.

    Wieder alles rausgenommen, Enum in 4 Enums aufgeteilt und zack, ich konnte den Code wieder bearbeiten...

    Ich probiere mal deinen Vorschlag und wende ihn auf ein Kompaktes und die 4 einzelnen Enums an...

    Danke!



    Die Auflistung der Enums ist tatsächlich nicht das Problem, sondern die Code-Ana-yse im Hintergrund, welche die CPU-Auslastug enorm in die HÖHE treibt.

    Ich habe das Projekt mit der 2010#er Version geöffnet und siehe da, die CPU-Auslastung ist minimal.
    Auch läßt sich der Code bearbeiten im Editor.

    Wie kann man die Code-Analyse bei 2019 abschalten ?
    Die Anweisungen im Netz geben keinen vernünftigen Hinweis darauf.

    Hat jemand eine Idee???

    Beiträge zusammengefügt. ~Thunderbolt

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Thunderbolt“ ()

    @Digtro_77 Kannst Du mal das bereinigte (ohne bin-, obj- und vs-Verzeichnisse) gezippte Projekt anhängen?
    Erweiterte Antwort => Dateianhänge => hochladen
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Also erstmal ein dickes Dankeschön @ ErfinderDesRades...dein Vorschlag war wirklich brilliant !

    Prinzipiell kann die Frage als "Erledigt" gekennzeichnet werden - das Verhalten im Studio 2019 muss allerdings noch weiter ergründet werden.
    Im 2010'er treten keinerlei Probleme auf.

    Danke, Danke ! :)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Digtro_77“ ()

    Vielleicht solltest du dir nochmal grundsätzlich Gedanken zu deiner Architektur machen, oder dich in dem Bereich fortbilden, denn eine

    Digtro_77 schrieb:

    gewaltige State-Machine mit mehr als 450 Schritten
    kling so als sollte man das lieber ganz anders umsetzen. Außerdem hat .NET bereits State-Machine implementiert, durch IEnumerable<T> mit yield return. Musst du das wirklich selber machen?
    ich versteh kein Wort von dem was in dem Link steht. Bist du dir sicher, dass du sowas machen willst?
    ich weiss auch nicht, was du unter "state-machine" verstehst, und ob das dasselbe ist wie ich oder Bluespide.
    IEnumerable und Konsorten kann imo allenfalls als super-primitive State-machine aufgefasst werden: Es geht nur Schritt für Schritt vorwärts - Verzweigungen, Sprünge, Rücksprünge sind nicht vorgesehen.

    Aufgrund solcher Begriffs-Differenzen kann man sich irrsinnig vergallopieren.

    Eben aus der Not heraus, habe ich mir das mit einem Timer selbst gebastelt.

    Natürlich ist das enorm kompliziert geworden, weswegen mir deine Funktion wahnsinnig geholfen hat.(@EDR)

    Mit dieser, in Verbindung mit den Enums, kann ich vorwärts und rückwärts springen.
    Unterroutinen gibt es ebenfalls mit einer zusätzlichen wiederkehrenden State-Machines, Verzweigungen und Waits, welche auf dem Timer der Statemachine basieren, ebenso.
    Auch, obwohl man das ja nicht sollte, kann ich die State-Machine anhalten und bei speziellen Gegebenheiten wieder an einem bestimmten State starten lassen.

    Sicherlich ist das alle nicht Bombe programmiert, aber es funktioniert absolut stabil...(bis auf die Tatsache, dass 2019 dicke Backen macht).
    Abgesehen davon, entwickelt man, insofern man denn möchte, sich ja auch stetig weiter...

    Mal etwas ausgeholt:
    Ich habe einen 100ms Timer, den ich wann ich will starte.

    Im Elapsed prüfe ich, in welchem State er sich befindet und führe ggf. eine Aktion aus.
    Damit die Befehle nicht anhand des Taktfrequenz des Timers ausgeführt werden, verriegel ich quasi nach dem Einsprung in den Vergleich die Abfrage.
    (Ja - ist schon scheiße...einmal das Flag vergessen und die Kiste steht...aber wie besser ?)
    So kann ich die State-Machine laufen lassen und asynchron dazu zum Beispiel einen neuen Timer aufrufen, der mir eine weitere Aktion ausführt.
    Nach dessen Abschluss, setzte ich einen neuen, den nächsten oder vorherigen Punkt, gebe das Flag frei und bin wieder in der State-Machine.

    Keine Ahnung, aber davon eben viel.

    VB.NET-Quellcode

    1. Private Sub MyMaintimer_GK560_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles MyMaintimer_GK560.Elapsed
    2. Select Case State_Zustand_Statemachine
    3. Case Statemachine.Reserve_Start
    4. ' Abfragen, ob der Controller in dem Testadapter erreichbar ist
    5. State_Zustand_Statemachine = Statemachine.IC303_Signatur_ein
    6. MyMaintimer_GK560.Start()
    7. Automatik = True
    8. '-----------------------------------------------------------------------------
    9. Case Statemachine.Abfrage_Pinlänge_ein
    10. ' Hier startet Bildtimer für die wechselnde Anzeige
    11. If Not Flag Then
    12. Flag = True
    13. Bildtimer_Zähler = 0
    14. Pinlänge = True
    15. Bildtimer_GK560.Start()
    16. End If
    17. Case Statemachine.Abfrage_Pinlänge
    18. ' Hier die Buttons auf der Mainpage anzeigen
    19. Button_EingabeOK_anzeigen(True)
    20. Button_EingabeFalse_anzeigen(True)
    21. ' Auswertung ohne einen Messtimer laufen zu lassen
    22. If (Eingabe_OK Or Eingabe_False) And Not Flag Then
    23. Flag = True
    24. Manuelle_Eingabe_Bewerten(GK560_Pinlänge, "Pinlänge:")
    25. Flag = False
    26. End If
    27. Case Statemachine.Abfrage_Pinlänge_aus
    28. If Not Flag Then ' Nur 1x senden
    29. Bild_anzeigen("")
    30. If Automatik Then State_Zustand_Statemachine.Step(1) ' Zustandsmaschine weiterschalten
    31. End If


    Wenn ich z.B. warten will, so benutze ich den Haupttimer, um mir daraus eine Wartefunktion zu basteln.
    Diese ist auch an anderer Stelle mit einem Timeout (er geht dann einfach weiter zum Fehlerhandling) versehen.

    VB.NET-Quellcode

    1. Case Statemachine.U0_3V45_Wartezeit_Netzteil_abwarten
    2. If Not Flag Then
    3. Flag = True
    4. Wartezeit = 0 ' Wartezeit EINMAL auf 0 setzen - Statemachine läuft weiter !
    5. End If
    6. ' Timervariable hochzählen :)
    7. Wartezeit += 100 ' Zählung in [ms] - es wird alle 100ms getaktet
    8. If Wartezeit >= Netzteil_Einschaltzeit Then ' Ist die Einschaltzeit erreicht, dann weiter im State
    9. State_Zustand_Statemachine.Step(1)
    10. Flag = False
    11. Wartezeit = 0
    12. End If



    Mit den Enums ist das super:

    VB.NET-Quellcode

    1. Public Enum Statemachine As Integer
    2. Reserve_Start
    3. IC303_Signatur_ein
    4. Reserve_IC303_Signatur
    5. IC303_Signatur_aus
    6. IC303_Löschen_ein
    7. Reserve_IC303_Löschen
    8. IC303_Löschen_aus
    9. IC303_Fuses_ein
    10. Reserve_IC303_Fuses
    11. IC303_Fuses_aus
    12. IC303_Flash_ein
    13. Reserve_IC303_Flash
    14. IC303_Flash_aus
    15. IC303_Eeprom_ein
    16. Reserve_IC303_Eeprom


    Lösche ich hier ein Enum raus, so kann ich durch die Nachverfolgung auch (einfacher) die dazu gehörigen Routinen löschen.
    Natürlich, und das ist auch wieder kompliziert, muss beim Weiterschalten auf die Blockgröße geachtet werden (es gibt 3er, 4er und 5er Blöcke...)

    Vorher war alles mit Integer-Cases realisiert, d.h. eine Excel Tabelle daneben, in der die Zahlen zu der Funktion abgelegt waren.
    Ein Dreher, eine falsche Nummer und das Programm stand an einer Stelle, an der es auf eine Eingabe oder Wert von einem Messgerät gewartet hat, welches gar kein Messwert bereitstellen wollte.
    Nun kann ich bereits aus den hinterlegten Enums erkennen, ob die gewählte Funktion der gewünschten entspricht - zusätzlich dazu, habe ich einen Debug-Mode, der mir den aktuellen State in dem er sich befindet, anzeigt.
    Das hilft enrom bei der Fehlersuche.
    Und auch, was der Knaller ist, kann ich Cases in der Abfolge einfach nachtragen oder entfernen.



    Ich denke, dass diese grafische Geschichte für kleinere Geschichten gut (jiippiii, man darf zeichnen...), wie z.B. eine Ampelsteuerung ausreichend sind, wahrscheinlich war es das dann auch.
    Den Vorschlag von Bluespide werde ich trotzdem mal verfolgen und natürlich ist mein Link dazu, etwas anderes.

    CodeTags korrigiert ~VaporiZed

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „VaporiZed“ ()

    @Digtro_77 Antwort auf Deine PN:
    Deine Verweise Ivi.Visa, VB-PowerPacks und NI.Visa kann ich leider nicht bedienen, also compiliert Dein Code nicht bei mir.
    Wenn Du aus der C-Ecke kommst, solltest Du vielleicht nach C# wechseln, das ist für Dich dann einfacher zu lesen und zu schreiben.
    Installiere im Studio 2019dieses Pack:
    marketplace.visualstudio.com/i…DevelopTeam.CodeConverter
    mit dem kannst Du Deinen Code nach C# übersetzen.
    Keine Ahnung, was das mit dem Studio ist. Möglicherweise, weil Dein Projekt auf x86 eingestellt ist.
    Ich weiß nicht, was das für DLLs sind.
    Ich habe jetzt auch mehrere hardware-lastige Projekte von VS2010 auf VS2019 umgestellt, das lief einwanfrei.
    Allerdings sind meine Projekte für x86 und x64 (nicht AnyCPU) ausgelegt, alles funktioniert.
    ====
    Du sprichst von mehreren Hardwaren, Sonder-Modulen usw.
    Mache für jede Hardware einzeln ein Testprogramm:
    Test-GUI als WinForm-Application,
    die Hardware-Klassen in eine separate DLL dieser Projektmappe.
    Füge dem Testprogramm das Projekt der DLL (nicht aber die DLL selbst) als Verweis hinzu.
    Da hast Du diese Hardware perfekt gekapselt und Du kannst den DLL-Code debuggen.
    Den Rest übernimmt das Studio.
    Wenn Du alle Hardwaren einzeln fertig hast, füge deren DLL-Projekte dem neuen Hauptprogramm als Quelle hinzu und füge deren Projekte dem Hauptprogramm als Verweis hinzu.
    ====
    Fang an und verstehe und beherezige diesen Thread:
    Dialoge: Instanziierung von Forms und Aufruf von Dialogen
    Dein Code ist in diesem Sinne VB6-Code!

    OK, das sollte erst mal reichen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Vielen DANK - ich werde die Punkte durchgehen - was "etwas" dauern wird...

    Aber nur, um das nochmal genau einzuordnen:

    Die *ivi sind die Messgerätetreiber für externe Rigol-Geräte (Scope und Multimeter).
    (oder natürlich auch andere Messgeräte, welche diese Treiber benutzen...)

    ni.com/de-de/support/downloads…iance-package.html#409836

    Der Code funktioniert mit allen Treibern unter 2019 einwandfrei - nur dann, wenn ich die Enums einsetze, ist es so, dass der
    der Code kaum editierbar ist, eben wegen der hohen CPU-Auslastung.

    Führe ich den Code aus, als erstellte exe oder im Debug-Mode, so läuft alles.

    Mache ich im Edititor eine Änderung, dauert es 5-6 Minuten, bis die unten angezeigten Fehler verschwinden und der Rechner
    wieder auf die Maus / Scrollrad reagiert...

    Wie gesagt, ich durchforste deine Anmerkungen und werde bestimmt noch die eine, oder andere Frage dazu haben...

    Danke ! :thumbsup:

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Digtro_77“ ()

    Ich werde keinen IVI-Treiber installieren, um das zu untersuchen. Sorry.

    Digtro_77 schrieb:

    nur dann, wenn ich die Enums einsetze,
    Das kann ich mir eigentlich nicht vorstellen.
    Wo im Code ersetzt Du welche Variablen durch welches Enum?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Nein - war auch nicht so geimeint, dass du diese installieren sollst.

    Als Beispiel:

    VB.NET-Quellcode

    1. Case 1
    2. ' Hier startet Bildtimer für die wechselnde Anzeige
    3. If Not Flag Then
    4. Flag = True
    5. Bildtimer_Zähler = 0
    6. Pinlänge = True
    7. Bildtimer_GK560.Start()
    8. End If


    durch:

    VB.NET-Quellcode

    1. Case Statemachine.Abfrage_Pinlänge_ein
    2. ' Hier startet Bildtimer für die wechselnde Anzeige
    3. If Not Flag Then
    4. Flag = True
    5. Bildtimer_Zähler = 0
    6. Pinlänge = True
    7. Bildtimer_GK560.Start()
    8. End If


    Wobei

    VB.NET-Quellcode

    1. Statemachine.Abfrage_Pinlänge_ein
    durch den Eintrag im Enum ersetzt wurde...

    Ist das falsch ?

    Digtro_77 schrieb:

    Ist das falsch ?
    Das das derartige Auswirkungen haben soll, kann ich mir nicht vorstellen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @Bluespide Es ist ein sehr großes Projekt.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ja - ist es.

    Ich werde ersmal die Anweisungen von Rod befolgen.

    Dann auslagern...

    Ich tippe darauf, dass der Code mit mehr als 12.000 ausführbaren Zeilen einfach zu groß für die automatische Korrektur
    bei 2019 ist - wie gesagt, beim editieren dauert es Minuten, bis eine Änderung in der Fehlerliste erfolgt.

    Auf der anderen Seite läuft es mit 2010 einwandfrei... :?:
    Ja...genau das ist das Problem!
    backgroundcompiler unterkringelt mir nicht mehr Tippfehler und dergleichen.


    JA - wie dumm von mir...das trifft es !

    Deswegen fragte ich bereits weiter oben, wie man diese ganzen zusätzlichen Überwachungsfunktionen
    abschalten kann.
    Aber genau DAS kommt hin...

    Was machst du in diesem Fall ?
    Welche Version benutzt du dann ???

    *AUFATME*

    AHA:

    Performance Probleme VS2019 Community

    UPDATE:
    Mit der VS 2017 treten diese Probleme nicht auf !


    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Digtro_77“ ()