Dynamische Arrays

Flo123

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

ich habe heute neu mit TwinCat 3 und Beckhoff gestartet, kenne mich aber schon etwas mit SPS aus.

Nun bin ich gerade dabei eine Matrix multiplikation zu erstellen. Für eine Matrix nehme ich zurzeit ein Zweidimensionales Array:

matrix1 : ARRAY[1..3,1..3] OF DINT
matrix2 : ARRAY[1..3,1..3] OF DINT

Nun würde ich gerne eine Funktion für die Multiplikation schreiben. Dies funktioniert auch für feste größen des Arrays (z.B: 3x3)
Für eine dynamische größe eines Arrays müsste ich an die Funktion ein Array mit beliebiger Größe übergeben.

FUNCTION fMultiplyMatrix : ARRAY[1..sizeArray,1..sizeArray] OF DINT
VAR_INPUT
A : ARRAY[1..sizeArray,1..sizeArray] OF DINT;
B : ARRAY[1..sizeArray,1..sizeArray] OF DINT;
END_VAR


Dies funktioniert aber leider nicht, gibt es da eine Möglichkeit? Oder ist dies ähnlich zu C nicht wirklich möglich?

Schöne Grüße und Danke im vorraus
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Flo123,

für die Vorgehensweise wie in deinem Beispiel beschrieben, musst du die Variablen für die Array-Grenzen als CONSTANT definieren.
Das ist dann natürlich nicht mehr zur Laufzeit änderbar....

Die für dich vielleicht bessere Variante ist der Vorschlag von Fx64: Verwendung von __NEW() um ein neues Array beliebiger Größe anzulegen.
Dies funktioniert aber nur mit skalaren Typen, d.h. Du kannst kein ARRAY OF MyStruct anlegen lassen, sondern nur mit Standard-IEC Typen.
Zurück liefert dir __NEW() einen Pointer auf die angeforderter Speicheradresse.
Dieser Pointer kann aber wie ein "normales" Array verwendet werden.
Alle sonst üblichen Mechanismen können also angewendet werden

Nicht vergessen: Du musst noch die Bibliothek Tc3_MemMan einbinden.

Gruß,
mac203
 
Wozu sollte man NEW und DELETE brauchen, wenn man lediglich verschieden große Arrays verarbeiten will?
Dafür braucht man "nur" eine Möglichkeit, verschieden große Arrays an eine Funktion übergeben zu können und in der Funktion die Größe zu erkennen.

@Flo123
Mußt Du wirklich zur Laufzeit verschieden große Arrays verarbeiten oder hast Du blos keine Lust, je Projekt einmal die (maximale) Arraygröße festzulegen? Um wieviel verschiedene Größen handelt es sich, wäre es eine Option ggf. mehrere Versionen der Funktion zu programmieren?

Harald
 
Der Sinn von dynamischen (zur Laufzeit änderbaren) Arrays auf einer SPS hat sich mir noch nie erschlossen.
Ich muss die SPS sowieso so auslegen, dass sie die größten Arrays verarbeiten kann. Also wo soll der Vorteil sein bzw. nennt mir mal einen Anwendungsfall wo es zwingend notwendig ist.

Gruß
Dieter
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Vielen dank für die vielen und schnellen Antworten

Die für dich vielleicht bessere Variante ist der Vorschlag von Fx64: Verwendung von __NEW() um ein neues Array beliebiger Größe anzulegen.
Dies funktioniert aber nur mit skalaren Typen, d.h. Du kannst kein ARRAY OF MyStruct anlegen lassen, sondern nur mit Standard-IEC Typen.
Zurück liefert dir __NEW() einen Pointer auf die angeforderter Speicheradresse.
Dieser Pointer kann aber wie ein "normales" Array verwendet werden.
Alle sonst üblichen Mechanismen können also angewendet werden

Leider bin ich mir nicht ganz sicher, ob die Funtkion __New mir etwas hilft. Ich kann ja auch mit
A : ARRAY[0..iSize] OF DINT
ein neues Array mit größe iSize anlegen. Mein Problem besteht darin, dass ich das Array mit variabler Größe keiner Funktion übergeben kann bzw. der Input Parameter der Funktion kann nicht A: ARRAY[0..iSize] lauten.
Oder meint ihr ich soll den Pointer übergeben und dazu noch die größe iSize und dann damit weiter rechnen?


VAR_IN_OUT
A: ARRAY
[*] OF INT;
END_VAR

DIes wäre eigentlich die perfekte Lösung. Nur leider zeigt es bei mir einen Fehler an wenn ich ein * in die Klammern setzte []. Muss ich dafür noch neue Referenzen einbinden?

Mußt Du wirklich zur Laufzeit verschieden große Arrays verarbeiten oder hast Du blos keine Lust, je Projekt einmal die (maximale) Arraygröße festzulegen? Um wieviel verschiedene Größen handelt es sich, wäre es eine Option ggf. mehrere Versionen der Funktion zu programmieren?

Maximale Arrayanzahl habe ich zurzeit implementiert. Habe aber gedacht, dass es vielleicht eine elegantere Lösung gibt :)
Mehrere Funktionen kommen wohl eher nicht in Betracht, da es sehr viele Funktionen werden würden.

Vielleicht habe ich mein Problem noch nicht gut genug beschrieben:
Ich habe einen Kalman Filter erstellt um Messwerte zu fusionieren. Die Messwerte kommen von einem Laserscanner auf einem autonomen fahrzeug. Der Kalman FIlter bekommt jeden Zyklus verschiedene Positionen von Landmarken, je nachdem wo sich das Fahrzeug befindet, hat man eine unterschiedliche Anzahl an Messwerten (Landmarken).
Wie vielleicht vom Kalman Filter bekannt, muss man nun viele Matrizen Berechnungen ausführen. Dabei ist die Größe abhängig von den Landmarken -> Matrizenberechnung immer unterschiedlich.
Nun habe ich verschiedene Funktionen für Multiplikation, Addition, Inverse usw geschrieben.
Das Array in denen die Landmarken sitzen ist jedoch immer unterschiedlich und wird also dynamisch an die Funktion übergeben.
Die größe ist jedoch bei jedem Zyklus bekannt!!!

Was ich bis jetzt versucht habe:
1) Arraygröße auf maximal Anzahl der Landmarken setzen. (Vielleicht gibt es eine elegantere Lösung?)
2) Mehrere Funktionen, angepasst z.B. auf (3x3 Matrix, 3x4, 4x3, 4x4 usw) -> werden aber super viele Funktionen da es bis 30x30 gehen kann

Schöne Grüße
Flo
 
Zuletzt bearbeitet:
Mein Problem besteht darin, dass ich das Array mit variabler Größe keiner Funktion übergeben kann bzw. der Input Parameter der Funktion kann nicht A: ARRAY[0..iSize] lauten.
Oder meint ihr ich soll den Pointer übergeben und dazu noch die größe iSize und dann damit weiter rechnen?

Ich mache das tatsächlich so, dass ich dann nur mit den Pointern arbeite und diese dann auch als Übergabeparameter habe.

Nur leider zeigt es bei mir einen Fehler an wenn ich ein * in die Klammern setzte []. Muss ich dafür noch neue Referenzen einbinden?

Kann nicht funktionieren, da (meines Wissens nach) nicht so in der IEC-61131-3 definiert.
Die
[*] - Variante ist ja der Weg, den Codesys gehen will.
Beckhoff hat da schon seit längerem den Weg über __NEW / __DELETE eingeschlagen.

Und um auf Blockmove nochmal einzugehen:
Wir sind viele Jahre ohne dynamische Arrays ausgekommen. Ich finde es aber dennoch ganz praktisch.
Ursächlich hierfür waren vermutlich die vielen Anfragen und Wünsche aus der Hochsprachenwelt.
Warum soll man denn neue Technologien nicht verwenden?
Wer nutzt denn heute noch Telefonzellen????;)

Gruß,
mac203
 
danke für die schnelle Antwort,

dann versuche ich das nun mit __NEW und __DELETE.

Wenn ich
Matrix = __NEW(LREAL,3);
schreibe
kann ich komischerweise die Elemente
Matrix[0], Matrix[1], Matrix[2] und Matrix[3] ansprechen. Es müssten doch aber eigentlich nur 3 sein. Welche sind denn die richtigen Indixes? 0-2 oder 1-3?

Schöne Grüße
 
Doch das geht bei mir schon

Main Programm:
VAR
pElem : POINTER TO LREAL;
END_VAR

pElem := __NEW(LREAL, 3);
pElem[0] := 1;
pElem[1] := 2;
pElem[2] := 3;

fInitMatrix(3,1,pElem,m=>test);

Funktion fInitMatrix
VAR_INPUT
nRows : UDINT;
nCols : UDINT;
pElem : POINTER TO LREAL;
END_VARS

pElem[0] := 10;
pElem[1] := 20;
usw.

Dabei kann die 3 in jedem zyklus im Main Programm verändert werden und somit nimmt die Funktion eine "variable" Größe des Arrays auf. Oder sehe ich das falsch?
 
Zuletzt bearbeitet:
danke für die schnelle Antwort,

dann versuche ich das nun mit __NEW und __DELETE.

Wenn ich
Matrix = __NEW(LREAL,3);
schreibe
kann ich komischerweise die Elemente
Matrix[0], Matrix[1], Matrix[2] und Matrix[3] ansprechen. Es müssten doch aber eigentlich nur 3 sein. Welche sind denn die richtigen Indixes? 0-2 oder 1-3?

Schöne Grüße

Der Speicher liegt im Bereich von Matrix[0..2].
Ein Zugriff ist auch auf Matrix[22] oder welcher Wert auch immer möglich.
Du als Programmierer bist selbst dafür verantwortlich, den allocierten Speicher "richtig" zu nutzen.

Den oben gezeigten "falschen" Zugriff kannst du mit einer Überpürfungsfunktion: "POU for implicit checks" erkennen und abfangen.
Hier weitere Informationen.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Und was nützt Dir das Spiel mit dem NEW/DELETE? :confused:
Main Programm:
VAR
pElem : POINTER TO LREAL;
END_VAR

pElem := __NEW(LREAL, 3);
pElem[0] := 1;
pElem[1] := 2;
pElem[2] := 3;

fInitMatrix(3,1,pElem,m=>test);

Funktion fInitMatrix
VAR_INPUT
nRows : UDINT;
nCols : UDINT;
pElem : POINTER TO LREAL;
END_VARS

pElem[0] := 10;
pElem[1] := 20;
usw.

Dabei kann die 3 in jedem zyklus im Main Programm verändert werden und somit nimmt die Funktion eine "variable" Größe des Arrays auf.
Ganz ohne NEW:
Code:
Main Programm:
VAR
 pElem : POINTER TO LREAL;
 matrix1 : ARRAY[1..3] OF LREAL;
 matrix2 : ARRAY[1..4,1..3] OF LREAL;
END_VAR

pElem := ADR(matrix1);
fInitMatrix(3,1,pElem,m=>test);

pElem := ADR(matrix2);
fInitMatrix(4,3,pElem,m=>test);

//vermutlich geht auch direkt
fInitMatrix(3,1,ADR(matrix1),m=>test);
fInitMatrix(4,3,ADR(matrix2),m=>test);

Harald
 
Sicherheit

Der Sinn von dynamischen (zur Laufzeit änderbaren) Arrays auf einer SPS hat sich mir noch nie erschlossen.
Ich muss die SPS sowieso so auslegen, dass sie die größten Arrays verarbeiten kann. Also wo soll der Vorteil sein bzw. nennt mir mal einen Anwendungsfall wo es zwingend notwendig ist.

Gruß
Dieter

Genau, es ist eine Frage der Sicherheit, damit schon im Moment des Programmdownloads klar ist, dass es keine Memory Probleme geben kann, weil irgendwann einmal eine Grenze überschritten wird oder dass das Memory durch ewige NEW/DELETE fragmentiert ist.

Der Programmierer soll und kann ja eine Maximal Grösse angeben und dann im Zuge der Anwendung nur einen Teil des Arrays benutzen.

Das Gleiche ist auch die Begründung für die Einschränkung, dass man aus einer Funktion heraus keinen Funktionsbaustein instanzieren kann.
 
Das Gleiche ist auch die Begründung für die Einschränkung, dass man aus einer Funktion heraus keinen Funktionsbaustein instanzieren kann.

Finde ich als Begründung nicht schlüssig, denn in einer Funktion ließe sich die Instanz auch auf dem Stack erzeugen und nicht auf dem Heap. Wie es bei C++ auch gemacht wird wenn ich ein Objekt mit z.B.
Object foo;
erzeuge und nicht über einen Zeiger und new.
Dann gibt es keine Fragmentierung des Speichers. Höchstens einen Stacküberlauf, aber den kann man sich ja auch einfangen in dem man z.B. große Strings oder sonstige große Variablen anlegt.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Wer programmiert die SPS ?

Finde ich als Begründung nicht schlüssig, .... Höchstens einen Stacküberlauf, aber den kann man sich ja auch einfangen in dem man z.B. große Strings oder sonstige große Variablen anlegt.

Im Prinzip kann ich auch eine SPS wohl leicht abschiessen, wenn ich z.B. recursiv ohne Ende eine Funktion aufrufe.

Bei der Entwicklung der Sprache ST wollte man gerade auf die Probleme eingehen, die durch "unerfahrene" C Programmierer überall entstanden sind. Speicherüberlauf, Fragmentierung, nicht wieder freigegebene Resourcen etc. Wem aus der SPS Welt sind schon Stack und Heap mit allen Konsequenzen klar, wer überblickt Millionen/Milliarden von ggf. gegenläufigen Speicheranforderungen und Freigaben?

Deshalb die Einschränkungen in ST, man hatte den Stndard SPS Programmierer im Blick:

Quereinsteiger aus dem Elektro Bereich,
Autodidakt ohne IT Ausbildung,
Einzelkämpfer ohne Test und Freigabe Abteilung,
Schnellschuss Fixer auf der Baustelle mit nervösem Chef im Rücken.
 
Im Prinzip kann ich auch eine SPS wohl leicht abschiessen, wenn ich z.B. recursiv ohne Ende eine Funktion aufrufe.

Bei der Entwicklung der Sprache ST wollte man gerade auf die Probleme eingehen, die durch "unerfahrene" C Programmierer überall entstanden sind. Speicherüberlauf, Fragmentierung, nicht wieder freigegebene Resourcen etc. Wem aus der SPS Welt sind schon Stack und Heap mit allen Konsequenzen klar, wer überblickt Millionen/Milliarden von ggf. gegenläufigen Speicheranforderungen und Freigaben?

Die statische Analyse wie des Aufrufbaums im Programm lässt sich problemlos durch den Compiler erledigen, und dann ggf. eine Warnmeldung erzeugen. Es macht auch keinen Unterschied ob ich eine FB-Instanz mit 200 Bytes Instanzdaten, oder einen String mit 200 Zeichen auf dem Temp-Bereich (also Stack) ablege. D.h. der Stack"verbrauch" steht schon zur Compilezeit fest.
Bei Siemens S7 ist das noch einfacher. Da ist keine Rekursion erlaubt (zumindest nicht ohne Umwege um den Compiler zu täuschen), die maximale Schachtelungstiefe ist auf einen festen Wert begrenzt, und es gibt eine feste größe an Lokaldaten(-stack) pro Baustein. Da kann bezüglich Speicher nichts schiefgehen.

Eine logische Erklärung warum eine FB-Instanz nicht in einer Funktion abgelegt werden kann wäre für mich, dass dann die Funktion der Instanz-Variablen nicht mehr der entspricht wie sie in der IEC-Norm vorgegeben ist. Mit unklarem Speicherverbrauch oder Fragmentierung hätte das nichts zu tun, weil es wenn dann auf dem Stack abgelegt würde.
 
Ganz ohne NEW:
Code:
Main Programm:
VAR
 pElem : POINTER TO LREAL;
 matrix1 : ARRAY[1..3] OF LREAL;
 matrix2 : ARRAY[1..4,1..3] OF LREAL;
END_VAR

pElem := ADR(matrix1);
fInitMatrix(3,1,pElem,m=>test);

pElem := ADR(matrix2);
fInitMatrix(4,3,pElem,m=>test);

//vermutlich geht auch direkt
fInitMatrix(3,1,ADR(matrix1),m=>test);
fInitMatrix(4,3,ADR(matrix2),m=>test);

So habe ich das nun auch gemacht. Finde ich die beste und einfachste Lösung!!

Vielen Dank :)
 
Zurück
Oben