4 Werte sortieren und Priorität ausgeben

rainer123

Level-1
Beiträge
5
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,

Ich muss ein FB erstellen an dem man an vier Eingängen vier REAL-Werte anlegen kann. Die vier Werte gehören jeweils zu einer Anlage.

Jetzt muss an 4 Ausgängen ein Integer-Wert ausgeben werden, der die Priorität ausgibt (1-4)

Umso kleiner die Zahl am Eingang desto höher die Priorität.

Beispiel

Eingang Ausgang

1 20,5 3
2 36,4 4
3 0,63 1
4 9,20 2

Gibt es da einen fertigen Baustein? Da ich nicht gut programmieren kann.

Vielen Dank für eure Hilfe
 

Anhänge

  • FB Priorität bestimmen.jpg
    FB Priorität bestimmen.jpg
    28,9 KB · Aufrufe: 40
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Rainer123,
es gibt unzählige Möglichkeiten:
Beispiele:
a) Du hast 3 Merker und gehst mit 3 verschachtelten if / and / and Abfragen durch die ersten 3 durch und suchst den Kleinsten und merkst dir das.
Anschl. durch die nächsten 2 mit beachtung des vorigen Merkers, das spart Rechenzeit.
Beim 3.ten angekommen ergibt sich der letzte durch Logik :)

b) Du nimmst dir den Array-Sort aus der OSCAT und fütterst ihn mit deinen Werten und Voila, fast fertig.

3) Du machst alles selber und "lernst" ggf. dabei

LG
Shrimps
 
Hi Rain123,
ich war etwas voreilig.
Du brauchst keine Merker sondern nur die Vergleichslogik.

Nachfolgend ein kleines funktionierendes Programm, welches ich leider nich kommentiert habe.
Wenn du es magst, baue das als FB um und fertig...
Die Variable bOnce ist als Schrittkette gedacht...

LG
Shrimps
Code:
PROGRAM MAIN
VAR
    rWert1 : REAL := 20.5;
    rWert2 : REAL := 36.4;
    rWert3 : REAL := 0.63;
    rWert4 : REAL := 9.2;
    iOut1    : BYTE;
    iOut2    : BYTE;
    iOut3    : BYTE;
    iOut4    : BYTE;
    bOnce    : BOOL := TRUE;
END_VAR

IF bOnce THEN

iOut1 := 4;
iOut2 := 4;
iOut3 := 4;
iOut4 := 4;

iOut1 := iOut1 -1 * BOOL_TO_BYTE(rWert1 < rWert2);
iOut1 := iOut1 -1 * BOOL_TO_BYTE(rWert1 < rWert3);
iOut1 := iOut1 -1 * BOOL_TO_BYTE(rWert1 < rWert4);

iOut2 := iOut2 -1 * BOOL_TO_BYTE(rWert2 < rWert1);
iOut2 := iOut2 -1 * BOOL_TO_BYTE(rWert2 < rWert3);
iOut2 := iOut2 -1 * BOOL_TO_BYTE(rWert2 < rWert4);

iOut3 := iOut3 -1 * BOOL_TO_BYTE(rWert3 < rWert1);
iOut3 := iOut3 -1 * BOOL_TO_BYTE(rWert3 < rWert2);
iOut3 := iOut3 -1 * BOOL_TO_BYTE(rWert3 < rWert4);

iOut4 := iOut4 -1 * BOOL_TO_BYTE(rWert4 < rWert1);
iOut4 := iOut4 -1 * BOOL_TO_BYTE(rWert4 < rWert2);
iOut4 := iOut4 -1 * BOOL_TO_BYTE(rWert4 < rWert3);


bOnce := FALSE;

END_IF
 
Zuviel Werbung?
-> Hier kostenlos registrieren
oder du machst dein FB gleich variabel, d.h. du packst die REAL - Werte in ein beliebig großes Array und nutzt zuerst einen Sortieralgorithmus (Bubblesort ist da der einfachste) und weißst danach die Prioritäten einfach auf- bzw absteigend zu

Code:
FUNCTION _SORT_AR_LREAL_AB : BOOL
VAR_INPUT
    PT            : POINTER TO ARRAY[1..500] OF LREAL;
    SIZE         : UINT;
END_VAR
VAR
    i            : UINT;
    stop            : UINT;
    temp1        : LREAL;
    changed        : BOOL;
END_VAR

    stop        :=SIZE/8;

    REPEAT
        changed        :=FALSE;

        FOR i:=1 TO (stop - 1) DO

            IF pt^[i] > pt^[i+1] THEN
                temp1    :=pt^[i];
                pt^[i]    :=pt^[i+1];
                pt^[i+1]    :=temp1;
                changed    :=TRUE;
            END_IF
        END_FOR
    UNTIL
        NOT changed
    END_REPEAT

    _SORT_AR_LREAL_AB := TRUE;

Aufruf dann nur über

Code:
_SORT_AR_LREAL_AB(ADR(DEIN_ARRAY), SIZEOF(DEIN_ARRAY));
 
Hallo StructuredTrash,
i made your day ist n schönes Dankeschön (Musste erstmal herzhaft lachen):)
Du hast ja recht: Von hinten durchs Knie...
Aber ich habe folgende Gründe gehabt:
- Es war mir unklar wie tief der TE in der ST-Sprache drinsteckt und wenn ich hier so mitlese kommen viele "Profis" mit extremen Boolschen daher...
(Fast alle Bücher zu Thema ST und Codesys haben "unendlich" viel Boolsche Logik drin und man merkt den Autoren das Fehlen von ST-Tiefe an...)
Dann fand ich es persönlich lustig, wenig Codezeilen zu schreiben...

- Mir unkar ist allerdings die Abarbeitungsgeschwindigkeit:
Hier würde ich gerne mehr erfahren wie der Compiler die Logik umsetzt und wo ggf. Vorrang herrscht ?
Gibts es da irgendwas zum nachlesen ?

Am liebsten hätte ich dem TE von Anfang an gerne die Sache mit dem Array und der Sortierlogik vorgegeben so wie der andere Kollege hier, aber dies erschien mir
als zu schwer für den TE ?!

So long
Macht Spaß hier
Shrimps
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich finde Deinen Lösungsansatz ganz gut, denn zum Sortieren müsste man die Daten in ein FB-internes Array kopieren, wenn man die FB-Schnittstelle nicht ändern will. Ausserdem ist die Sortierung eigentlich gar nicht die Aufgabe des FBs.
Aber warum versuchst Du, auf Teufel komm raus bedingte Programmbearbeitung zu vermeiden? Das ist doch gerade die Stärke von ST.
Um die Bearbeitungsgeschwindigkeit wirklich beurteilen zu können, müsste man sich wohl den Assemblercode anschauen. Ich gehe aber mal davon aus, dass ein Sprungbefehl bei heutigen CPUs nicht länger dauert als eine Subtraktion. Und davon hat man bei der bedingten Ausführung im gesamten FB eben nur 6 statt 12.
 
Ich habe mal reingeschaut wie groß die Unterschiede der beiden Varianten (Berechnung von shrimp vs. If-then) sind.

Von der reinen Anzahl der Anweisungen sind beide Varianten fast identisch.
Bei der If-Variante werden aber nur im ungünstigsten Fall alle Pfade durchlaufen. Dafür hat die Berechnungs-Variante eine konstante Laufzeit. Evtl. ist das sogar bezüglich besserer Ausnutzung der Prozessor-Pipeline schneller, das müsste man aber ausmessen.

Wenn das Zielsystem eine 32-Bit Architektur besitzt, spart man auch etwas wenn man anstelle des Datentyps Byte ein Integer verwendet.
Mal abgesehen davon, dass ich persönlich arithmetische Operationen auf dem Typ Byte nicht so gerne sehe.

Aber ich hätte nicht gedacht, dass BOOL_TO_... doch relativ effizient umgesetzt werden kann. Ich hätte rein von der Lesbarkeit auch zur If-Variante gegriffen.
 

Anhänge

  • asm-code1-berechnung.txt
    9,4 KB · Aufrufe: 34
  • asm-code2-if.txt
    10,2 KB · Aufrufe: 17
Hallo,
erstmal Klasse das du das ganze auf Assemblerebene durchleuchten kannst !

Das mit dem Byte versus Integer hatte ich mir schon gedacht, da Wortweise Verarbeitung meistens angesagt ist.
Ich hatte hier nur meinen Sparfuchs im Kopf (Komme aus den Datenbanken und war sparsam mit den Datentypen, heutzutage obsolet..)

Von daher nehme ich auf jeden Fall mit, das ich für Zähler, Schrittketten etc. immer INT nehme...

Btw: Wie effizient ist ein Enum im Code ?, optimiert der Compiler hier auch auf INT je nach Umgebung ?)

LG
Shrimps
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Das mit dem Byte versus Integer hatte ich mir schon gedacht, da Wortweise Verarbeitung meistens angesagt ist.
Ich hatte hier nur meinen Sparfuchs im Kopf (Komme aus den Datenbanken und war sparsam mit den Datentypen, heutzutage obsolet..)

Kommt drauf an wo man sparen muss. Wenn ich ein 10.000 Elemente großes Array habe, benötigt das mit Integer eben doppelt so viel Speicher. Bei Arrays gibt es ja kein Padding. Bei einzeln deklarierten Variablen müsste man mal nachsehen ob das mit Byte überhaupt Arbeitsspeicher spart.

Hauptsächlich würde ich mit dem Datentyp Byte auch nicht rechnen wollen. Du hast damit auf jeden Fall schonmal eine implizite Typkonvertierung enthalten. Bei wirklich strikter Typprüfung nach IEC müsste sowas wahrscheinlich angemäkelt werden.

Btw: Wie effizient ist ein Enum im Code ?, optimiert der Compiler hier auch auf INT je nach Umgebung ?)
Das habe ich mir noch nicht angeschaut. Ich schätze aber mal Integer, aber geschätzt hast du wohl auch schon selber.
 
Achso, im Assemblercode kann man die Adressen der Variablen ja sehen. Bei Byte gibt es demnach keine Padding-Bytes, denn die werden alle um 1 Byte versetzt im Speicher angesprochen. Ich dachte immer Zugriffe auf ungerade Adressen sind langsam.

Übersetzt habe ich das mit TwinCat 2.10 für PC (x86).
 
Meine letzten Assemblerprogramme liegen so weit zurück, dass ich bei den heutigen CPUs nicht mehr wirklich mitreden kann. Der Vergleich zeigt aber, dass man heute in der Regel die übersichtlichere Lösung wählen kann, ohne Gewissensbisse bezüglich der Performance zu bekommen.

Btw.: Enums sind immer UINT.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ein drittel der Anweisungen ist auch wohl für Debuggingzwecke, d.h. damit das Online-Beobachten möglich ist. Zumindest die ganzen "add al, 0" kann man sich wegdenken.

Mal mit dem GCC verglichen: Dieser erzeugt ca. die Hälfte an Assembler-Anweisungen bei gleicher Funktion. Steckt aber auch "etwas" mehr Manpower dahinter ;-)
 
Angehängte Dateien
asm-code1-berechnung.txt
asm-code2-if.txt
Warum weiß der Compiler die absoluten Adressen der Variablen (Funktionsparameter?) im ds-Segment (z.B. ds:1E4D0h)? Sind das die Adressen der außen angelegten Variablen? Werden die Variablenwerte per Referenz übergeben? Wie sieht der Aufruf der Funktion aus?
Oder wurde da ein lineares Programm compiliert?

Mehrfachzuweisungen an Baustein-Ausgänge und Rücklesen von Baustein-Ausgängen sind in Multitasking-Umgebungen gefährlich und werden einem z.B. bei TIA-SCL auch zu Recht um die Ohren gehauen.

Man sollte bei solchen Lösungen zunächst mit lokalen Variablen arbeiten und erst am Ende einmal auf die Baustein-Ausgänge kopieren.

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Warum weiß der Compiler die absoluten Adressen der Variablen (Funktionsparameter?) im ds-Segment (z.B. ds:1E4D0h)? Sind das die Adressen der außen angelegten Variablen? Werden die Variablenwerte per Referenz übergeben? Wie sieht der Aufruf der Funktion aus?
Oder wurde da ein lineares Programm compiliert?

Ich habe keine extra Funktion erstellt, sondern Code wie auch Varablen sind direkt im Main-Programm.

Auf die Segment Angabe kann man nichts geben. Zum Disassemblieren erzeuge ich mir ein Bootprojekt das ich in einen Disassembler lade. Anhand von eindeutigen Anweisungen bzw. Konstanten im ST-Code die ich dann im Bootprojekt wiedersuche, sage ich dem Disassembler, dass ab hier eine Code-Sektion folgt.
Darum weiß der Disassembler überhaupt nichts über andere Sektionen, bzw. ist für ihn alles Data-Segment. Weiter nachgeforscht wie so ein Bootprojekt aufgebaut ist habe ich noch nicht. Bisher wollte ich mir nur kurze Codeschnippsel mal im Assembler ansehen, und dafür reicht meine Vorgehensweise.
 
Weil VAR_INPUT/VAR_OUTPUT bei CoDeSys ganz normale statische Variablen des FBs sind. Mehrfachzuweisungen/Rücklesen sind deshalb technisch kein Problem, sondern nur eine Frage persönlicher Vorlieben.
Ich bezeichne Mehrfachzuweisungen/Rücklesen nicht als "persönliche Vorliebe" sondern als Programmierfehler.

Wenn eine FB-Instanz aufgerufen wird, und von einer höherprioren Task diese Instanz unterbrochen wird, und da auf die Ausgänge der unterbrochenen Instanz zugegriffen wird ala "x := Instanz.Out1" dann wird es fatal, wenn der FB zunächst mehrfach verschiedene Werte in Out1 schreibt, bevor der endgültige Wert zugewiesen wird.
Wer dokumentiert seine Bausteine so, daß nicht direkt auf die Ausgänge zugegriffen werden darf, sondern immer eine Variable an dem Ausgangsparameter angeschlossen werden muß?! Z.B. bei Timern erwartet auch jeder, daß der Zugriff auf Timer.Q jederzeit unproblematisch möglich ist und verwendet kaum jemand eine extra Variable am Ausgang.

Gerade FB sind für solch undurchdachtes falsches Vorgehen prädestiniert. Da kann man ruhig in dem FB mithelfen, daß dieser äußere Programmierfehler keine Auswirkungen hat.


Ich habe keine extra Funktion erstellt, sondern Code wie auch Varablen sind direkt im Main-Programm.
Achso. Für mich wäre es auch mal interessant zu sehen, wie aufwändig (mehrfach-)Zugriff auf per Referenz übergebene Strukturen aussieht. Im Vergleich zu temporären Arbeitsvariablen.

Harald
 
Wenn eine FB-Instanz aufgerufen wird, und von einer höherprioren Task diese Instanz unterbrochen wird, und da auf die Ausgänge der unterbrochenen Instanz zugegriffen wird ala "x := Instanz.Out1" dann wird es fatal, wenn der FB zunächst mehrfach verschiedene Werte in Out1 schreibt, bevor der endgültige Wert zugewiesen wird.
Das stimmt schon, aber wer macht so etwas? Der Verzicht auf Mehrfachzuweisungen löst das Problem ja nur bei einem FB mit einem einzelnen Ausgang. Hat er mehrere Ausgänge, kann die höherpriore Task nicht davon ausgehen, dass der gesamte FB-Ausgangsbereich konsistent ist. Für solche Anwendungen packe ich die von der höherprioren Task zu lesenden Daten in eine Strukturvariable, die von der niedrigerprioren Task gesperrt und freigegeben wird.
 
Zurück
Oben