da ich weder weiss was Visual C++, noch Memory Leaks oder Exceptions sind, werde ich meine Suche in Richtung Protothreads fortführen, wenn ich zu den Priority Queues keine leichtere Kost mehr finden sollte. Werde ich hierbei nicht fündig werden, werde ich im Anschluss versuchen mich an den freundlicherweise von Serenitly geposteten Codefetzen zu machen. Ob zusätzliche Suchvorgänge nach Mutex und Semaphoren zielführender sind muss ich ebenfalls noch prüfen.
Da ich Anfänger bin (der jedoch glücklicherweise relativ genau weiss was sein Ziel ist) fällt es mir zugegebenermaßer äußerst schwer mich in Gefilden abseits der Arduinoumgebung zu tummeln. Meistens kostet mich das nur Zeit und Nerven und endet oftmals dann in irgendwelchen C-Codes, welche ich nicht verstehe. Im Anschluss komme ich dann wieder bei der Frage an, ob ich C oder C++ lernen soll (keiner konnte mir bisher kurz und prägnant den Unterschied erklären).
Ich verstehe zudem nicht, warum hier teilweise vermutet wird, dass das was ich suche nichts Prioritätenmanagement zu tun hat. Ich denke, dass ich auf dem in Posting #11 geposteten Diagramm deutlich machen konnte, dass die eingehenden Befehle durchaus unterschiedliche Prioritäten haben und diese Prioritäten auch Einfluss auf die Reihenfolge haben in denen diese umgesetzt werden sollen.
Mein Problem ist nicht, dass irgendwelche Prozesse parallel laufen sollen. Mein Problem ist die Erstellung und Verwaltung der Warteschlange inkl. des Priroritätenmanagements.
Danke für Euren Input. Ohne ihn wäre ich gänzlich verloren. :o
Was du genau willst wird vielleicht nicht vollständig von einer fertigen Lösungen erschlagen. Und man kann Dinge unterschiedlich implementieren.
Bezüglich Priority Queues. Die werden in der Praxis häufig mit Bäumen implementiert und nicht mit Listen. Das ist aber unnötig hier und viel zu kompliziert für dich. Deshalb gilt gerade da, dass das was du findest wahrscheinlich nicht genau auf dich zutrifft.
Irgendeine FIFO Lösung ist wohl nichts wenn man sich das Diagramm ansieht und rein ein Mutex/eine Semaphore bestimmt nur wie man mit der aktuellen Task umgeht. Mal angenommen das Mutex meldet dass die Resource belegt ist und es kommen aber weiter Anfragen auf den Zugriff. Hier braucht man irgendeine Warteschlange um das zu verwalten.
Wenn man sowas auf dem PC hat bekommt man das nur nicht so mit, da der PC die Verwaltung der Threads übernimmt.
Wenn du selbst was programmieren willst/musst kommst du nicht drum herum ein paar Grundlagen zu lernen.
Chris72622:
Im Anschluss komme ich dann wieder bei der Frage an, ob ich C oder C++ lernen soll (keiner konnte mir bisher kurz und prägnant den Unterschied erklären).
C ist ist mehr oder weniger ein Subset von C++, bzw. C++ ist eine Erweiterung von C. C ist das Grundgerüst mit der ganzen elementaren Syntax. C++ erweitert das dann um zusätzliche Elemente. Der größte Unterschied zwischen C und C++ ist vielleicht dass es in C keine Klassen gibt.
Das ganze objektorientierte Zeug kommt ist C++. Dazu dann Dinge wie Templates, Namensräume, Operatoren zur dynamischen Speicherverwaltung, Überladen von Operatoren und vor allem die Standard Template Library (auf dem PC, wobei es da auch eine Arduino Implementierung gibt).
Wenn man es genau nimmt ist etwas komplexer, da es Sachen gibt die in C++ eingeführt wurden und dann aber von neueren C Standards übernommen wurden (z.B. inline Funktionen oder Datentypen wie bool).
Wegen Visual C++:
Das ist kein Muss, aber es kann bei allgemeinen Algorithmen viel angenehmer sein die erst mal auf dem PC zu entwickeln. Auch ohne Exceptions bekommst du da von der Runtime in manchen fällen auf die Finger geklatscht wenn du was falsch machst (nicht immer(!) aber es ist nicht so wie auf dem µC wo man nichts mitbekommt).
Und man kann natürlich Debuggen
Auch für die Arduino Entwicklung ist das mit diesem Plugin sehr schön: http://www.visualmicro.com/
Viel angenehmer als die primitive Arduino IDE
Ich verstehe zudem nicht, warum hier teilweise vermutet wird, dass das was ich suche nichts Prioritätenmanagement zu tun hat. Ich denke, dass ich auf dem in Posting #11 geposteten Diagramm deutlich machen konnte, dass die eingehenden Befehle durchaus unterschiedliche Prioritäten haben und diese Prioritäten auch Einfluss auf die Reihenfolge haben in denen diese umgesetzt werden sollen.
Mein Problem ist nicht, dass irgendwelche Prozesse parallel laufen sollen. Mein Problem ist die Erstellung und Verwaltung der Warteschlange inkl. des Priroritätenmanagements.
Prioritäten, im Sinne von Tasks usw. Haben was mit "Wichtigkeit" zu tun. Tasks mit "höherer" Wichtigkeit laufen also schneller, u.U. mehrfach schneller. Bekommen mehr Zeit.
Wenn ich also versuche herauszufinden was du suchst, dann sei mir nicht böse, dass ich die Begriffe, welche du verwendest, hinterfrage.
Aber im Grunde hast du natürlich Recht!
Woher sollte ich wissen, was du suchst...?
Aber bisher glaube ich daran, dass du Verriegelungen suchst und keine Priöritätsverwaltungen.
Suche doch mal in der Linuxecke nach "nice", dann ahnst du hoffentlich, was ich meine...
combie:
Woher sollte ich wissen, was du suchst...?
Aber bisher glaube ich daran, dass du Verriegelungen suchst und keine Priöritätsverwaltungen.
Das schließt sich nicht aus.
Man kann ja Befehle sortieren während die aktuelle Aktivität läuft. Also verhindern das der aktuelle laufende Vorgang unterbrochen wird und gleichzeitig eine Liste nachfolgender Befehle erstellen von denen dann einer ausgeführt wird wenn die Zeit vorbei ist.
Bezüglich Prioritäten:
er will das Befehle höherer Priorität zuerst kommen, aber auch dass ein Befehl gleicher Priorität den ersten ersetzt (das ist aber trivial, da man einfach den Inhalt eines Knotens überschreiben kann).
Serenifly:
er will das Befehle höherer Priorität zuerst kommen, aber auch dass ein Befehl gleicher Priorität den ersten ersetzt (das ist aber trivial, da man einfach den Inhalt eines Knotens überschreiben kann).
Zumindest weiss ich, wo sich momentan der größte Knoten befindet..
Man könnte es auch ohne dynamischen Speicher (new/delete) machen und daher ohne eine relativ aufwendige Liste.
Man nimmt ein Array aus structs um die Tasks zu verwalten. Das struct enthält ein Byte für die Priorität und eine Variable was ausgeführt werden soll (hier könnte man gleich einen Funktionszeiger nehmen).
Wenn die Priorität schon vorhanden ist, überschreibt man einfach den Inhalt des vorhandenen structs. Ansonsten schreibt man die neue Task hinter die die schon da sind.
Um die Reihenfolge einzuhalten, sortiert man dann das komplette Array mit der Priorität als Schlüssel. Das kostet zwar mehr Rechenzeit, aber ist einfach zu programmieren. Und dass es Zeit kostet wird hier gar nicht schlimm sein.
Sortier-Algorithmen gibt es fertig. Insert Sort wäre hier zu empfehlen:
Oder man spart sich das sortieren, aber dann muss man das Array komplett nach dem Element mit der niedrigsten höchsten Priorität durchsuchen. Sortieren kann man dagegen auch mit einem Teil des Arrays machen (d.h. nur so viele Elemente sortieren wie wirklich Tasks da sind) und man braucht zum Zugriff nur eine Variable auf das nächste Element (wie "head" bei einem Ringpuffer).
Was ich oben geschrieben habe funktioniert. Lediglich das am Ende stimmt nicht ganz. Gelesen wird immer von Index 0 und man braucht einen Index weiter hinten zum Schreiben (tail).
Und man muss auch nach dem Entfernen eines Elements sortieren, sonst geht der Platz im Array verloren bis man alle Elemente entfernt.
Die Länge des Arrays wird im Header festgelegt:
#define QUEUE_LENGTH 3
Das sollte man später auskommentieren:
#define DEBUG
Sonst schreibt er Debug-Ausgaben auf Serial
Test Code in der .ino Datei
Was noch ganz wichtig ist:
Eine höhere Zahl, bedeutet eine höhere Priorität! Das ist etwas anders als normal üblich (und wie auch in deiner Beschreibung), wo eine niedrige Zahl meistens eine höhere Priorität bedeutet. Liegt daran, dass das Array mit 0 initialisiert, ich 0 für "leere" Stellen im Array verwende und so diese Einträge automatisch nach hinten sortiert werden.
Das könnte man auch anders machen, aber es lohnt sich nicht wirklich.
Also 1 = niedrigste Priorität, 255 = höchste Priorität
Bin nun am C++ lernen.. Die cpp und die h-Datei gehören in einen Ordner namens PriorityQueue, welcher wiederum in den libraries-Ordner von Arduino gehört, oder?
ArduinoTest2.ino wäre dann der eigentliche Sketch und der Rest sind Bestandteile dieser Bibliothek, oder?
Ja, aber du kannst auch beides erst mal in den Ordner des Sketches kopieren. Funktioniert genauso. Wenn es im Libraries Ordner ist dann ist das in allen anderen Sketchen verfügbar. Was eigentlich nicht nötig ist.
Ob das genau ist was du willst weiß ich nicht. Man muss es auch abändern, aber der Code ist nicht kompliziert.
Im Moment speichere ich für die Nutzdaten einen Integer ab. Da musst du dir dann überlegen wie da die jeweilige Task ansprechen willst. Ein Funktionszeiger ist wie gesagt eine Option.
Eine Sache ist auch dass getNextElement() ein QueueElement struct zurück gibt. Das kann man so lassen, aber eigentlich will man ja nur die Daten-Variable. Nicht auch die Priorität. Da kann man auch die Daten direkt zurück geben.
dein geposteter Code hat mir insbesondere im Bezug auf das Verständnis für Klassen und Methoden wirklich wahnsinnig geholfen. Trotzdem habe ich noch zwei Fragen.
Warum hast Du das struct per typedef in QueueElement „umbenannt“?
Ist es so, dass das queue im Sketch nichts mit dem queue in der Klasse PriorityQueue zu tun hat, da mit PriorityQueue queue; lediglich eine Warteschlange namens queue initialisiert wird, während das queue innerhalb der Klasse als globales Grundgerüst für sämtliche zu erstellenden Warteschlangen zu betrachten ist?
Warum hast Du das struct per typedef in QueueElement „umbenannt"?
Mach das typedef mal weg. Dann merkst du was passiert. Ohne das typedef musst du vor jedes QueueElement das struct keyword hinschreiben
Ist es so, dass das queue im Sketch nichts mit dem queue in der Klasse PriorityQueue zu tun hat
Ja. Variablen können an unterschiedlichen Stellen den gleichen Namen haben.
Schon in einem normalen Sketch kannst du eine globale Variable var haben und die mit einer lokalen Variablen var verdecken. Wobei das an der Stelle kein so guter Stil ist.
während das queue innerhalb der Klasse als globales Grundgerüst für sämtliche zu erstellenden Warteschlangen zu betrachten ist
Das queue in der Klasse ist einfach das Array, dass die Warteschlange bildet.
Wenn man mehrere Queue Objekte erstellen würde, hätte jedes ihr eigenes Array
Der Sortier-Algorithmus muss ständig structs kopieren. Je weniger Daten man da also hat, desto schneller geht es. Das fällt zwar bei einer handvoll Elementen nicht wirklich ins Gewicht, aber ich verschwende da trotzdem nicht gerne Ressourcen.
Wenn mal über das Kopieren nachdenkt, wäre hier eigentlich SelectionSort besser als InsertionSort. Die sind beide sehr ähnlich, aber InsertionSort ist meistens besser, da im Durchschnitt weniger Vergleiche gebraucht werden. Dafür wird mehr kopiert. In diesem Fall sind Vergleiche allerdings billiger als Kopier-Operationen, daher wäre SelectionSort wahrscheinlich im Vorteil.
Aber lass es, da das bei so kurzen Arrays wohl sowieso keinen merkbaren Unterschied macht. Finde erst mal raus ob das überhaupt so funktioniert, dann kann man immer noch optimieren.