Simulation von Digitalen Schaltungen

  • VB.NET
  • .NET (FX) 4.0

Es gibt 15 Antworten in diesem Thema. Der letzte Beitrag () ist von Niko Ortner.

    Simulation von Digitalen Schaltungen

    Ich versuche, digitale Schaltungen zu simulieren. Das ist ganz einfach gehalten. Es gibt keine Spannungspegel oder so, sondern nur ob eine Leitung aktiv ist oder nicht.
    Zur Veranschaulichung ein Beispiel:

    Simple Schaltung:
    Links oben ist eine Dauerversorgung. Dinge, die daran angschlossen sind, sind immer aktiv.
    Rechts die roten Kreise sind LEDs die den Zustand der Leitung, an die sie angeschlossen sind, wiederspiegeln.
    Ganz unten ist eine Verbindung zu was anderem in der Schaltung. Solche Verbindungen sind bidirektional.
    In der Mitte ein Relais mit einem Wechslerkontakt (wovon nur der Öffner verwendet wird).

    Man beachte, dass hier nirgends Masse-Leitungen eingezeichnet sind. Da alle Bauteile sowieso immer fix an Masse angeschlossen sind, ist es unnötig, diese Leitungen auch noch einzuzeichnen. Dass die LEDs eine andere Betriebsspannung als die Relais haben ist hier auch irrelevant. Es geht wie gesagt nur darum, festzustellen, ob eine Leitung bzw. ein Bauteil aktiv ist.
    Eine Leitung ist dann aktiv, wenn mindestens ein Bauteil ein Signal "auf die Leitung sendet". Und das ist auch nicht wie z.B. bei TTL, dass zwei Ausgänge nicht direkt verbunden werden dürfen da sonst bei unterschiedlichen Ausgangszuständen ein Kurzschluss entsteht. Wenn eine Leitung nicht aktiv ist, dann bedeutet das hier, dass sie einfach nicht mit der Versorgungsspannung verbunden ist.



    Hier wird links der Zustand der Schaltung gezeigt, wenn von der Verbindung unten kein Signal kommt. Die Leitungen C sind nicht aktiv, die rechte LED deshalb auch nicht, das Relais zeiht nicht an, verbindet dadurch die Leitungen A und B, es entsteht eine Verbindung von der Versorgung zur linken LED und somit ist diese aktiv.
    Rechts daneben das Gegenteil. Von der Verbindung unten kommt ein Signal zu C, die rechte LED ist aktiv, das Relais auch und zieht an, trennt dadurch A und B und dadurch ist die linke LED nicht mehr aktiv.

    Die Schaltung sollte "Schritt für Schritt" simulierbar sein. Das heißt, bei jedem Schritt wird zuerst der neue Zustand aller Bauteile und Leitungen berechnet und dann erst werden alle Zustände übernommen. Sinn der Sache ist der: Da auch "Rekursionen" wie diese vorkommen können:

    , würde die Simulation hier nie fertig werden und je nach Implementierung eine Endlosschleife oder einen StackOverflow zur Folge haben.
    Davon abgesehen möchte ich wissen, wie lange es dauert, bis sich in einer Schaltung keine Zustände mehr verändern.
    Das sollte aber nicht so weit gehen, dass einzelne Leitunssegmente pro Simulationsschitt berechnet werden:

    Es ist auch OK, wenn Leitungen sofort neu berechnet werden, sobald z.B. ein Relais einen Kontakt schaltet. Nur umgekehrt sollte es nicht sein: Bauteile sollten ihre Zustände nicht sofort verändern, wenn sich die Zustände von angeschlossenen Leitungen verändern.

    Wichtig ist für mich auch, dass unnötige Updates vermieden werden. Wenn z.B. eien Leitung bereits aktiv ist und durch eine Veränderung eine Weitere Verbindung zu einer Versorgung hergestellt wird, dann sollen nicht nochmal alle Teile der Leitung und alle angeschlossenen Bauteile neu berechnet werden. Das System sollte nämlich auch bei größeren Schaltungen um die 20000 Objekte nicht mit Updates explodieren.

    Eine Sache noch: Der gespeicherte Zustand soll möglichst nicht direkt in den Objekten, die die Bauteile und Leitungen repräsentieren, verpackt sein, sondern in separaten, klonbaren Datenstrukturen gespeichert sein. Sinn der Sache ist der: Ich möchte die Zustände zwischenspeichern können, ohne dabei alle Informationen der Bauteile mitzukopieren. Dadurch kann ich nach jedem Schritt prüfen, ob die neuen Zustände gleich den alten sind bzw. "Backups" von Zuständen machen und diese später wieder laden.
    Dabei wird hilfreich sein, dass jedes Bauteil bzw. jede Leitung und jeder Anschluss eine GUID besitzt. Diese wird bereits zum Abspeichern verwendet und kann sicher auch verwendet werden, um festzustellen, welche Simulationszustände zu welchem Bauteil gehören.

    Also wenn jemand Ideen hat, wie man sowas hinbekommt, dann immer her damit. Wenn irgendwas unklar bzw. nicht ausreichend erklärt ist, erkläre ich das gerne genauer.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Um Endlosschleifen zu vermeiden würde ich versuchen, jeden vorherigen Status zu speichern und dann zu überprüfen, ob sich einer von diesen wiederholt. Ist dies der Fall, liegt dann ja klar eine Endlosschleife vor.
    mathematisch gesehen sind solche Schaltungen Graphen.
    Graphen kann man als Veralgemeinerung von Bäumen auffassen. Ein Baum ist ja kreisfrei und gerichtet, von Root zu den Leafs hin, aber beim Graph sind Zirkelbezüge gültig.
    Deshalb kann man Graphen auch nicht im Treeview abbilden, sondern muss so UML-ähnliche Diagramme bemühen, die von Fall zu fall unterschiedlich ausfallen können.

    Egal.
    Inne Informatik kann man Graphen relational modellieren.
    Lies dir Wikipedia zu Graphne, Kanten und Knoten und Zeugs, das hat alles eine enge Verwandaschaft zu Tabellen und Relationen.

    Ich weiß jetzt nicht, was du im einzelnen ausrechnen wilslt, aber die Graphen-Theorie hält grundsätzlich unerhört möchtige Werkzeuge und Theorien bereit - ist aber vlt auch mit Kanonen auf Spatzen, zB mit Fourier-Transformation auf deine paar digitalen Bauteile da loszugehen.
    Aber vlt. auch nicht.
    @nafets
    Da ich sowieso diskrete Schritte haben möchte ergibt sich das Problem eigentlich.

    @ErfinderDesRades
    Die Bauteile ansich sind bereits da. Das hab ich wirklich vergessen zu erwähnen. Ich habe bereits ein Programm um die ganzen Schaltungen aufzubauen und ich möchte da jetzt die Simulation hinzufügen.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

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

    Anmerkung zu EDR: Die Datenstruktur Graph lässt sich relativ leicht über eine Adjazenzmatrix darstellen.

    Grüße
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:
    Zuerst möchte ich dazu sagen, dass ich wenig Ahnung von der Materie habe, aber das Thema an sich sehr interessant ist. Die Idee von @ErfinderDesRades ist gar nicht mal schlecht. Allerdings ist FFT evtl. ein bisschen (was genau ist größer als eine kanonenkugel und fliegt gut) auf Spatzen geschossen :). Ist dein Programm aufgebaut wie eine Schaltung an einer realen Schalttafel (die Dinger zum stecken)? Dann könntest du wahrscheinlich auf eine ganz simple Matrix (Matrizen) zurückgreifen, um alle Zustände zu prüfen (ggf. mit Vektoren etc). Da wäre die Anzahl der Bauteile dann ja vollkommen egal?

    Ist vielleicht nicht der ideale Ansatz, aber ich denke in die Richtung würde ich bei so einem "großem" Projekt recherchieren. Aber just my 2 Cents.
    @Gonger96
    So genau muss es nicht sein. Hier sind keine Masse-Leitungen eingezeichnet, weil alle Bauteile sowieso mit dem zweiten Anschluss (sofern zutreffend) an Masse hängen. Davon abgesehen sind Leitungen, die aktiv sind, mit der Versorgungsspannung verbunden (wie hoch auch immer die sein mag).Leitungen, die inaktiv sind, sind es einfach nicht. Also inaktive Leitungen sind nicht mit Masse verbunden, sondern einfach nicht mit der Versorgunsspannung verbunden (also das Äquivalent von hochohmigen Ausgängen bei diskreten Bauteilen).

    @ErfinderDesRades
    Im Startpost zeigt das 5. Bild ein Beispiel. Ignorier den Teil oben, wo "Falsch" steht, denn so sollte es nicht funktionieren. Darunter sind von links nach rechts 5 Zustände der schaltung gezeigt.
    Ganz links ist der Ausgangszustand.
    Dann wird ein Schritt berechnet und heraus kommt das rechts daneben. Also von irgendwo her kommt unten ein Signal und dadurch wechselt die Leitung von inaktiv zu aktiv.
    Dann wird der nächste Schritt berechnet und heraus kommt das, was eins weiter rechts gezeigt wird: Das Relais hat von inaktiv zu aktiv gewechselt und dadurch den Wechslerkontakt umgeschaltet. Wie das Umschalten genau gemacht werden soll weiß ich auch noch nicht. Die rechte LED hat ebenfalls von inaktiv zu aktiv gewechselt.
    Nächster Schritt: Die Leitung oben in der Mitte wechselt von aktiv zu inaktiv, weil sie jetzt nicht mehr mit der anderen, aktiven Leitung verbunden ist. Beachte auch, dass der andere andere Anschluss des Wechslerkontaktes jetzt aktiv ist, da er ja mit der anderen, aktiven Leitung verbunden ist.
    Und dann nochmal ein Schritt: Die LED wechselt von aktiv zu inaktiv, weil die Leitung, an der sie angeschlossen ist, inaktiv ist.

    Hast du da einen stabilen Zustand, und greifst dann da ein?
    Woraus sich dann lauter Folgerungen ergeben?

    Das würde nicht ohne weiteres funktionieren. Siehe dazu Bild #4 im Startpost. Würde man alle Folgerungen und deren Folgerungen und so weiter sofort berechnen, dann würde der Simulationsschritt bei dem Beispiel nie fertig werden.

    Also für die Simulation gelten folgende zwei Einschränkungen:

    Wenn sich der Zustand eines einzigen Teils der Leitung verändert, dann sollen alle Zustände aller Teile einer Leitung sofort berechnet werden. Also eben nicht so wie in Bild #5 bei "Falsch" gezeigt.
    Wenn eine Leitung an ein Bauteil angeschlossen ist und sich der Zustand der Leitung im aktuellen Simulationsschritt verändert, dann soll der Zustand des Bauteils erst im nächsten Simulationsschritt berechnet werden.
    Wenn eine Leitung an ein Bauteil angeschlossen ist und sich der Zustand des Bauteils im aktuellen Simulationsschritt verändert, dann soll der Zustand der Leitung erst im nächsten Simulationsschritt berechnet werden.


    Ich hab das Projekt mal angehängt. Vielleich hiltft das beim Verständnis, wie es aktuell aufgebaut ist. Projektmappe öffnen, Debuggen, oben links auf "Datei" klicken und die TestCircuit.xml-Datei öffnen, die in der Zip liegt.
    Die ganzen Bauteile sind im Components-Ordner. Die Vererbungshierarchie sieht so aus:

    VB.NET-Quellcode

    1. ScreenObject 'Irgendwas was angezeigt werden und mit der Maus interagieren kann.
    2. Clamp.Contact 'Eine Klemme kann mehrere Kontakte haben. Kontakte können Eingänge, Ausgänge oder bidirektional sein.
    3. Relay.Contact 'Ein Relais kann mehrere Kontakte haben. Alle Kontakte sind Wechsler.
    4. GrabPoint 'Für Maus-Interaktion zum Ziehen.
    5. WirePlug 'Man kann Leitungen dran anschließen. Jedes WirePlug-Objekt hat eine GUID.
    6. Component 'Irgendwas, was Teil des Schaltplans ist. Jedes Component-Objekt hat eine GUID.
    7. Board 'Ein Projekt kann aus mehreren Unterbereichen bestehen. Die Klasse erbt nur aus Bequemlichkeit von Component, ist aber eigentlich nicht wirklich Teil des Schaltplans.
    8. Wire 'Eine Leitung. Wire-Objekte sind immer an exakt zwei WirePlug-Objekten angeschlossen.
    9. Device 'Irgendwas, was ein simples Bauteil ist. Zeichnet sich in erster Linie dadurch aus, dass es rotierbar und mit nur einem GrabPoint-Objekt verschiebbar ist.
    10. CControl 'Im Grunde genommen ein 64k RAM-Chip.
    11. Clamp.Clamp 'Eine Klemme, an der externe Geräte angeschlossen werden können
    12. Connector 'Verbindet Leitungen, die sich auf unterschiedlichen Boards befinden, wenn für beide Connector-Objekte der gleiche BusName und PinName verwendet wird.
    13. Led 'Zeigt einen Zustand an.
    14. Relay.Relay 'Ein Relais halt. Kann mehrere Kontakte haben.
    15. Transistor 'Wird für den RAM-Chip benötigt. Basis ist Eingang, Kollektor ist Ausgang.
    16. VPlus 'Versorgungsspannung. Gibt immer ein Signal aus.
    17. WirePoint 'Wird verwendet, um Leitungen zu "verlegen".


    @Moderation
    Ich hoffe es ist OK, dass sich im Libs-Ordner in der Zip-Datei zwei Dlls befinden. Man soll ja außerhalb des Showrooms keine ausführbaren Dateien anhängen. Bin- und Obj-Ordner sind aber gelöscht.
    Dateien
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    also deine Simulation scheint mir ein Walkthrough durch den Graphen zu sein.

    Das kann man mit Async sogar recht übersichtlich gestalten, man sollte aber eine Breath-First-Traversion wählen, denn bei Rekursion wird Async bisserl komisch.

    BFT sagt dir was? das ist die iterative Alternative, um Bäume zu durchlaufen - dabei wird eine Queue auf- und ab-gebaut.

    Vlt. Sample dazu hier: codeproject.com/Articles/10296…ithout-any-additional-Lin

    Ganz am schluss hab ich Floodfill-Algos gebastelt, und einer der beiden ist BFT.
    Damit kann man auch auf deine Schaltungen losgehen.
    Um Zirkel-Wege zu vermeiden sollteste sogar eine weitere FloodFill-Anleihe machen: Nämlich bereits besuchte Knoten markieren, und in Folge nicht nochmal besuchen.

    (achso - und wenn dir der Artikel was bringt, rate ihn hoch. Ich zumindest finde ihn wichtig (das mittm Floodfill noch am wenigsten))
    So, habs geschafft.

    Die Lösung ist eine Mischung aus den Ansätzen von @Gonger96 und @ErfinderDesRades
    von + nach - alles durchgegangen

    Walkthrough durch den Graphen


    Ich mache im Grunde folgendes:
    1. Alle Objekte suchen, die von sich aus aktiv sind. Also beispielsweise so ein VPlus-Objekt. Oder irgendwelche aktiven Ausgänge von Bauteilen.
    2. Als temporärer Zustand für alle Objekte wird inaktiv festgelegt.
    3. Rekursiv, von den in Schritt 1 gefundenen Objekten ausgehend, den temporären Zustand des Objektes auf aktiv setzen und mit allen verbundenen Objekten das gleiche machen, und mit deren verbundenen Objekten und so weiter.
      Alle Objekte, die hier gefunden werden, sind mit der Versorgungsspannung verbunden, denn sie sind ja mit den Objekten verbunden, die in Schritt 1 gefunden wurden.
    4. Den aktuellen Zustand auf den temporären Zustand festlegen. Alle Objekte, die in Schritt 3 gefunden wurden, sind somit aktiv. Alle Objekte, die in Schritt 3 nicht gefunden wurden, sind nicht mit der Versorgungsspannung verbunden und somit inaktiv (der temporäre Zustand wurde nie auf aktiv gesetzt).


    Das funktioniert auch bei großen Schaltungen noch sehr schnell.

    Vereinfacht sieht das so aus

    Alle simulierbaren Objekte implementiern dieses Interface:

    VB.NET-Quellcode

    1. Public Interface ISimulatable
    2. 'Wird z.B. zum Speichern und Laden des Zustandes verwendet.
    3. ReadOnly Property Guid As Guid
    4. 'Der aktuelle Zustand des simulierbaren Objektes
    5. Property CurrentState As Boolean
    6. 'Setzt den temporären Zustand auf inaktiv.
    7. 'Wird rekursiv für untergeordnete Objekte aufgerufen.
    8. 'Fügt das Objekt der SimulationContext.AllSimulatables-Liste hinzu.
    9. 'Ist ein Objekt bereits vonsich aus aktiv (z.B. VPlus), dann wird es zusätzlich zur SimulationContext.InitiallyActiveSimulatables-Liste hinzugefügt.
    10. Sub PrepareSimulationStep(Context As SimulationContext)
    11. 'Setzt den temporären Zustand auf aktiv.
    12. 'Wird rekursiv für die Objekte aufgerufen, die mit dem aktuellen Objekt verbunden sind.
    13. Sub VisitActive(Context As SimulationContext, Source As ISimulatable)
    14. 'CurrentState wird auf den temporären Zustand festgelegt.
    15. 'Wenn VisitActive für dieses Objekt aufgerufen wurde, ist es über irgend einen Pfad mit der Versorgungsspannung verbunden.
    16. Sub ApplyCalculatedState()
    17. End Interface
    18. Public Class SimulationContext
    19. 'Alle simulierbaren Objekte.
    20. Public ReadOnly Property AllSimulatables As List(Of ISimulatable)
    21. 'Nur die simulierbaren Objekte, die von sich aus aktiv sind (z.B. VPlus)
    22. Public ReadOnly Property InitiallyActiveSimulatables As List(Of ISimulatable)
    23. 'Von allen Connector-Objekten diejenigen raussuchen, die mit dem angegebenen Connector-Objekt logisch verbunden sind.
    24. Public Function FindAllConnectors(SourceConnector As Connector) As IEnumerable(Of Connector)
    25. Return AllSimulatables.OfType(Of Connector).Where(Function(i) i.BusName = SourceConnector.BusName AndAlso i.PinName = SourceConnector.PinName)
    26. End Function
    27. End Class

    Die Relay-Klasse sieht dann so aus:

    VB.NET-Quellcode

    1. Partial Public Class Relay
    2. Inherits Device '-> Component -> ISimulatable
    3. 'Stark vereinfacht!
    4. Dim WithEvents CoilPlug As WirePlug
    5. Public ReadOnly Property Contacts As New List(Of Contact)
    6. 'WirePlug-Objekte lösen dieses Event aus, damit die Objekte, zu denen sie gehören, angeben können, mit welchen anderen Objekten sie verbunden sind.
    7. Private Sub CoilPlug_ConnectedSimulatablesRequested(sender As Object, e As ConnectedSimulatablesRequestedEventArgs) Handles CoilPlug.ConnectedSimulatablesRequested
    8. e.Simulatables.Add(Me) 'Ist direkt mit dem aktuellen Relay-Objekt verbunden.
    9. e.Handled = True
    10. End Sub
    11. 'Der tempräre Zustand.
    12. Dim ComputedState As Boolean
    13. Dim _CurrentState As Boolean = False
    14. Public Overrides Property CurrentState As Boolean
    15. Get
    16. Return _CurrentState
    17. End Get
    18. Set(value As Boolean)
    19. If _CurrentState <> value Then
    20. _CurrentState = value
    21. For Each i In Contacts
    22. i.SimulationState = value 'Teilt den Contact-Objekten mit, welche Anschlüsse verbunden sein sollen.
    23. Next
    24. End If
    25. End Set
    26. End Property
    27. Public Overrides Sub PrepareSimulationStep(Context As SimulationContext)
    28. Context.AllSimulatables.Add(Me)
    29. ComputedState = False 'Temporären Zustand auf inaktiv setzen.
    30. CoilPlug.PrepareSimulationStep(Context)
    31. For Each i In Contacts
    32. i.PrepareSimulationStep(Context)
    33. Next
    34. End Sub
    35. Public Overrides Sub VisitActive(Context As SimulationContext, Source As ISimulatable)
    36. ComputedState = True 'Das Relais ist mit der Versorgungsspannung verbunden -> Am Ende des Simulationsschrittes auf aktiv setzen.
    37. End Sub
    38. Public Overrides Sub ApplyCalculatedState()
    39. CurrentState = ComputedState
    40. End Sub
    41. End Class

    Und dazu passend die Contact-Klasse:

    VB.NET-Quellcode

    1. Public Class Contact
    2. Inherits ScreenObject
    3. Implements ISimulatable
    4. ' /---- PlugNC
    5. ' /
    6. ' PlugC ----/| ---- PlugNO
    7. ' |
    8. ' |
    9. ' ---
    10. ' | | --- CoilPlug
    11. ' ---
    12. Public ReadOnly Property Guid As Guid Implements ISimulatable.Guid
    13. Public ReadOnly Property PlugC As WirePlug
    14. Public ReadOnly Property PlugNC As WirePlug
    15. Public ReadOnly Property PlugNO As WirePlug
    16. Public Property SimulationState As Boolean
    17. 'Wenn das Relais angezogen hat (also aktiv ist), dann ist PlugC mit PlugNO (Normally Open) verbunden. Ansonsten mit PlugNC (Normally Closed).
    18. Private Sub PlugC_ConnectedSimulatablesRequested(sender As Object, e As ConnectedSimulatablesRequestedEventArgs) Handles PlugC.ConnectedSimulatablesRequested
    19. If SimulationState Then
    20. e.Simulatables.Add(PlugNO)
    21. Else
    22. e.Simulatables.Add(PlugNC)
    23. End If
    24. e.Handled = True
    25. End Sub
    26. 'PlugNO ist mit PlugC verbunden, wenn das Relais aktiv ist. Ansonsten mit nichts anderem.
    27. Private Sub PlugNO_ConnectedSimulatablesRequested(sender As Object, e As ConnectedSimulatablesRequestedEventArgs) Handles PlugNO.ConnectedSimulatablesRequested
    28. If SimulationState Then
    29. e.Simulatables.Add(PlugC)
    30. End If
    31. e.Handled = True
    32. End Sub
    33. Private Sub PlugNC_ConnectedSimulatablesRequested(sender As Object, e As ConnectedSimulatablesRequestedEventArgs) Handles PlugNC.ConnectedSimulatablesRequested
    34. If Not SimulationState Then
    35. e.Simulatables.Add(PlugC)
    36. End If
    37. e.Handled = True
    38. End Sub
    39. 'Die Contact-Objekte benötigen selbst keinen Zustand (wird von der Relay-Klasse mit SimulationState gesetzt).
    40. Public Property CurrentState As Boolean Implements ISimulatable.CurrentState
    41. Get
    42. Return False
    43. End Get
    44. Set(value As Boolean)
    45. End Set
    46. End Property
    47. 'Die Contact-Objekte arbeiten bei der Simulation nicht aktiv mit, sondern verbinden nur die WirePlug-Objekte.
    48. 'Deshalb wird nicht wie in der Relay-Klasse Context.AllSimulatables.Add(Me) aufgerufen, sondern der Aufruf nur an die WirePlug-Objekte weitergeleitet.
    49. Public Sub PrepareSimulationStep(Context As SimulationContext) Implements ISimulatable.PrepareSimulationStep
    50. PlugC.PrepareSimulationStep(Context)
    51. PlugNO.PrepareSimulationStep(Context)
    52. PlugNC.PrepareSimulationStep(Context)
    53. End Sub
    54. Public Sub VisitActive(Context As Simulation.SimulationContext, Source As Simulation.ISimulatable) Implements Simulation.ISimulatable.VisitActive
    55. End Sub
    56. Public Sub ApplyCalculatedState() Implements Simulation.ISimulatable.ApplyCalculatedState
    57. End Sub
    58. End Class

    Die WirePlug-Klasse sieht so aus:

    VB.NET-Quellcode

    1. Public Class WirePlug
    2. Inherits ScreenObject
    3. Implements ISimulatable
    4. Public ReadOnly Property Guid As Guid Implements ISimulatable.Guid
    5. Public ReadOnly Property AttachedWires As List(Of Wire)
    6. Public Event ConnectedSimulatablesRequested As EventHandler(Of ConnectedSimulatablesRequestedEventArgs)
    7. Private Function RequestConnectedSimulatables() As IEnumerable(Of ISimulatable)
    8. Dim Args As New ConnectedSimulatablesRequestedEventArgs(Me)
    9. RaiseEvent ConnectedSimulatablesRequested(Me, Args)
    10. If Not Args.Handled Then
    11. Throw New NopeException("ConnectedSimulatablesRequested muss behandelt werden.")
    12. End If
    13. Return Args.Simulatables
    14. End Function
    15. Dim ComputedState As Boolean
    16. 'Wird von übergeordneten Objekten auf True gesetzt, um Ausgänge aktiv zu schalten.
    17. Public Property ForceOutputActive As Boolean
    18. Dim _CurrentState As Boolean = False
    19. Public Property CurrentState As Boolean Implements ISimulatable.CurrentState
    20. Get
    21. If ForceOutputActive Then
    22. Return True
    23. End If
    24. Return _CurrentState
    25. End Get
    26. Set(value As Boolean)
    27. If _CurrentState <> value Then
    28. _CurrentState = value
    29. OnInvalidated()
    30. End If
    31. End Set
    32. End Property
    33. Public Sub PrepareSimulationStep(Context As SimulationContext) Implements ISimulatable.PrepareSimulationStep
    34. Context.AllSimulatables.Add(Me)
    35. ComputedState = False
    36. If ForceOutputActive Then
    37. Context.InitiallyActiveSimulatables.Add(Me)
    38. End If
    39. For Each i In AttachedWires
    40. i.PrepareSimulationStep(Context)
    41. Next
    42. End Sub
    43. Public Sub VisitActive(Context As SimulationContext, Source As ISimulatable) Implements ISimulatable.VisitActive
    44. ComputedState = True
    45. For Each i In AttachedWires
    46. If Not i Is Source Then 'Zur Optimierung wird immer mitgegeben, von wo aus das Signal kommt. Dadurch Muss VisitActive nicht nochmal an dem Quell-Objekt aufgerufen werden.
    47. i.VisitActive(Context, Me)
    48. End If
    49. Next
    50. For Each i In RequestConnectedSimulatables()
    51. If Not i Is Source Then
    52. i.VisitActive(Context, Me)
    53. End If
    54. Next
    55. End Sub
    56. Public Sub ApplyCalculatedState() Implements ISimulatable.ApplyCalculatedState
    57. CurrentState = ComputedState
    58. End Sub
    59. End Class

    Verwendet wird das Ganze so:

    VB.NET-Quellcode

    1. 'Im MainWindow:
    2. Private Sub ExecuteSimulationStep()
    3. Dim Context As New SimulationContext
    4. For Each i In CurrentProject.Boards.SelectMany(Function(b) b.BoardComponents)
    5. i.PrepareSimulationStep(Context)
    6. Next
    7. For Each i In Context.InitiallyActiveSimulatables
    8. i.VisitActive(Context, Nothing)
    9. Next
    10. For Each i In Context.AllSimulatables
    11. i.ApplyCalculatedState()
    12. Next
    13. End Sub

    Bilder
    • Unbenannt 2.png

      45,45 kB, 1.220×534, 110 mal angesehen
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

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

    Niko Ortner schrieb:

    Das funktioniert auch bei großen Schaltungen noch sehr schnell.
    Wenn das dein Ziel war, dann ist "Simulation" der falsche Begriff.
    Sondern du wolltest nur den "UnterSpannung"-Zustand von Schaltungs-Elementen berechnen, ausgehend von ein paar bekannten Elementen.

    Da hat dann Async auch nix verloren, und - ja, ein WalkThrough ist schnell.

    Eine Simulation wäre genau das Gegenteil von schnell - die hätte ja bei jedem Schritt des Walkthroughs innehalten müssen, und den neuen Zwischenzustand präsentieren.
    Ich denke, das kann man problemlos als Simulation bezeichnen.
    Ob ein Leitungs-Segment pro Schritt neu berechnet wird, oder viele, sehe ich da jetzt nicht als entscheidenden Faktor.
    Das sind die Ergebnisse aufeinanderfolgender Simulationsschritte eines (zugegebemermaßen viel zu aufwändigen) Volladdierers:

    Also man sieht schon auch, wie sich Veränderungen "durchhangeln".
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils