Array in Array schreiben

Chico02

Level-1
Beiträge
47
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
<Beckhoff> Hallo com,ich hoffe ihr könnt mir helfen.
Ich hatte einen FB Baustein erstellt mit einer Array deklarierung. Das Problem ist allerdings das ich den Datenbereich vom Array über Var Input beschreiben wollte dies aber nicht funktioniert da es eine Constante sein muss.
Jetzt hatte ich die Idee das ich dem Array fest einen Wert von 100 gebe. Den Inhalt mit Var Output rausführe und dann in ein Array von z.B 1100...1120 packe. Das die restlichen 80 Speicherbereiche verloren gehen wäre mir egal da ich sowieso nie alle beschreibe. Allerdings weiß ich nicht wie ich vom Array X[0..100] den Inhalt in Array Z[1100..1120] einfüge. Beim Array handelt es sich um ein String Array und um ein Pointer To String Array falls die Info benötigt wird. Bereits danke für ihre eure Hilfe
 
Zuletzt bearbeitet:
Hallo Chico,

ich bin mir nicht sicher, ob ich Dich richtig verstehe.
Ich verstehe Dich so, daß Du Arrays felxibler Länge übergeben möchtest.

Das geht am besten, indem Du einen Pointer übergibst und in einem weiteren Input-Parameter dann die Länge des Arrays, das bearbeitet werden soll.
Jetzt zeigt der Pointer auf das erste Element vom Array (den Pointer kannst Du dann ja auch auf Element 1100 zeigen lassen), und mittels Pointer-Arithemtik kannst Du Dich dann durch die Array-Elemente weiterhangeln, bis eben zur maximalen Länge, die Du zusätzllich übergeben hast.

Dadurch kannst Du natürlich auch auf unberechtigte Speicherbereiche zugreifen, wenn Du über die Länge des Arrays hinaus liest/schreibst, aber da muß man ja bei Pointern immer aufpassen.

Gruß
Jens
 
Hallo Jens, danke für deine Antwort. Ich würde dir gerne mal den Code zeigen den ich vor hatte der aber so nicht funktioniert. Ich denke es wird dann somit auch zum Problem wenn ich den Array nur bis 20 beschreibe er aber trotzdem 100 lang ist.
Ich würde mal gerne allgemein in die Problematik eingehen und was das Ganze überhaupt soll.
Ich möchte Texte aus einer textilste lesen. In dieser textliste stehen meine Fehlermeldungen. Da es verschiedene Fehlermeldungen gibt bzw error, warnings, messages und das von verschiedenen Bereichen sind diese auch mit Codes versehen.
dnWarningBaseIdentNo ist jetzt z.B 1200. in meiner textliste gebe ich somit ID 1200 ein und trage den dazugehörigen Text ein. Somit ist immer ganz klar zu wem welcher Text gehört. Die Anzahl von Fehlern ist immer unterschiedlich deswegen hat Gruppe A beispielsweise nur 4 Fehler und Gruppe B 20. deswegen wollte ich auch nWarningMaxIdx an das Array übergeben was aber nicht funktioniert da es eine Konstante sein muss. nWarningMaxIdx und base id Wurde einmal definiert und initialisiert und wird auch nicht mehr verändert. Es handelt sich auch um konstanten. Leider gibt es aber nicht die Möglichkeit einem FB ein Var Input konstante zu geben zumindest wüsste ich nix davon. Das macht mir Haufen Arbeit wenn das nicht möglich ist.

Außerhalb vom FB wird dies Definiert:
pTextWarning: ARRAY[Gr00Control.dnWarningBaseIdentNo..Gr00Control.dnWarningBaseIdentNo + GR00Control.nWarningMaxIdx] OF POINTER TO STRING;
sTextWarning: ARRAY[Gr00Control.dnWarningBaseIdentNo..Gr00Control.dnWarningBaseIdentNo + GR00Control.nWarningMaxIdx] OF STRING;

Und muss vom FB beschrieben werden.




FUNCTION_BLOCK PUBLIC FB_GetTextList
VAR_INPUT
sTextList :STRING;
MaxIdx:DINT;
BaseIdentNo :DINT;


END_VAR
VAR_OUTPUT
pText :ARRAY[BaseIdentNo..BaseIdentNo + MaxIdx] OF POINTER TO STRING;
sText: ARRAY[BaseIdentNo..BaseIdentNo + MaxIdx] OF STRING;
END_VAR

VAR
sTextListID :STRING;
i: DINT := 0;
END_VAR




FOR i := 0 TO MaxIdx BY 1 DO
sTextList := 'Port_851.Meldetexte';
sTextListID := DINT_TO_STRING(BaseIdentNo + i);
pText[BaseIdentNo + i] := VisuElems.cmpDynamictext.DynamicTextGetText(ADR(sTextList), ADR(sTextListID));
sText[BaseIdentNo + i] := pText[BaseIdentNo + i]^;
END_FOR
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Wie Du schon festgestellt hast: Array-Grenzen können nicht flexibel sein.

Was Du machen mußt:
Die Größe von POINTER OF STRING ermitteln und die Größe von STRING ermittlen. Dazu gibt es SIZEOF.
An die Funktion übergibst Du nur den Pointer auf das erste zu bearbeitende Element des Arrays.

Innerhalb der Funktion mußt Du nun den Pointer für jeden Zugriff neu berechnen:

p_pText := p_1st_Element_pText + i * _size_of_pointer_to_string;
p_sText := p_1st_Element_sText + i * _size_of_string;

Somit hast Du dann immer den Zugriff auf das i. Element.
 
Danke nochmal für deine Antwort.
Liefert Size of dann nur den beschriebenen Datenbereich? Sprich bei 0..100 wurden nur 0..20 beschrieben dann wäre das Ergebnis 20. stimmt dies ? Bzw wenn eben das Array aus einem Byte besteht also Array of Byte
Byte = String
 
Zuletzt bearbeitet:
Nein: Sizeof liefert die Größe des Datentyps, in Deinem Fall also die Größe eines Array-Elements. So daß bei der Addition auf die Pointer-Adresse immer genau um i Elemente weitergesprungen wird.
Datentypen können von Hersteller zu hersteller ggf. unterschiedlich groß sein, daher nie von einer festen Größe ausgehen, sondern immer mit SIZEOF abfragen.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich verstehe danke nochmal für deine Erklärung. Jaa das Problem das ich das ganze allerdings nicht innerhalb des FBs machen kann bleibt allerdings.
Es gibt danach noch einen weiteren Schritt wo ich ebenfalls auf die Problematik stoßen werde. Daher lohnt sich der Aufwand eigentlich garnicht. Da muss wohl einfach Fleißarbeit her. Ich hab allerdings dennoch eine Frage zu deinem Beispiel.
p_pText := p_1st_Element_pText + i * _size_of_pointer_to_string;

p_pText Ist ja ein Array von z.B 1100..1200
Würde dies bedeuten das ich mit
p_1st_Element_pText + i * _size_of_pointer_to_string;

Wenn i 0 bzw 1 ist die Daten auf 1100 schreibe? p_1st_Element_pText Gehe ich davon aus ist ein Array von 0..100

Sorry das ich viel Frage ich habe bisher noch nicht mit Pointer gearbeitet. Wenn du alle Variablen die du schreibst auch deklarierst kann ich es besser verstehen
 
Zuletzt bearbeitet:
pText im FB wäre auf 100 begrenzt.
pText außerhalb dem FB hat ein Bereich von 1100..1200. pText außerhalb dem FB wird immer unterschiedliche Speicherbereiche haben. Somit kann ich pText 1100 auch nicht adressieren. Korrigiere mich wenn ich falsch liege. Noch besser wäre wenn du zumindest nur für ein Datenbereich vom Array ein Beispiel machst wie du das meinst.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Nur zum Vorgang vereinfacht:

IM FB:
PText[0] ist beschrieben

Außerhalb FB: wartet APText[1100] auf die Daten vom PText[0] wo sich im FB befindet.

Sollte ich versuchen das der FB Output PText => APText beschreibt funktioniert es nicht. Es funktioniert nur wenn APText ebenfalls 0..100 wäre aber nicht 1100..1200 oder im realen Fall sogar 1100..1120.
 
Code:
VAR
pTextWarning: ARRAY[Gr00Control.dnWarningBaseIdentNo..Gr00Control.dnWarningBaseIdentNo + GR00Control.nWarningMaxIdx] OF POINTER TO STRING;
sTextWarning: ARRAY[Gr00Control.dnWarningBaseIdentNo..Gr00Control.dnWarningBaseIdentNo + GR00Control.nWarningMaxIdx] OF STRING;
sizeof_PtoString     : DINT;
sizeof_String         : DINT;

GetTextList            : FB_GetTextList;
END_VAR

// Größe eines Elements ermitteln, dieses kann man ggf. auch einmalig in einem Init-Schritt machen.
sizeof_PtoString := SIZEOF(pTextWarning[GR00Control.nWarningMaxIdx]);
sizeof_String     := SIZEOF(sTextWarning[GR00Control.nWarningMaxIdx]);

GetTextList(MaxIdx         := GR00Control.nWarningMaxIdx,
            BaseIdentNo := xyz,
            p_pText        := ADR(pTextWarning[1100]),
            p_sText        := ADR(sTextWarning[1100]),
            size_pText    := sizeof_PtoString,
            size_sText    := sizeof_String);
            
            
//####################################################
FUNCTION_BLOCK PUBLIC FB_GetTextList

VAR_INPUT
    MaxIdx        : DINT;
    BaseIdentNo    : DINT;
    p_pText        : POINTER TO DWORD;
    p_sText        : POINTER TO STRING;
    size_pText    : DINT;
    size_sText    : DINT;
END_VAR

VAR_TEMP
    sTextListID    : STRING;
    i            : DINT;
    pt_pText    : POINTER TO STRING;
    pt_sText    : POINTER TO STRING;
    pt_Dynamic    : POINTER TO STRING;
END_VAR

VAR CONSTANT
    sTextList    : STRING := 'Port_851.Meldetexte';
END_VAR

FOR i := 0 TO MaxIdx DO
    sTextListID := DINT_TO_STRING(BaseIdentNo + i);
    
    // Zeiger zum aktuellen Element ermitteln
    pt_pText := p_pText + DINT_TO_DWORD(i * size_pText);
    pt_sText := p_sText + DINT_TO_DWORD(i * size_sText);
    
    // hier bin ich mir nicht sicher: bleibt die hier ermittelte Adresse von DynamciText über die Laufzeit erhalten?
    pt_Dynamic:= VisuElems.cmpDynamictext.DynamicTextGetText(ADR(sTextList), ADR(sTextListID));  
    pt_pText^ := pt_Dynamic;
    pt_sText^ := pt_Dynamic^;
END_FOR
END_FUNCTIONBLOCK

ungetestet!
 
Was Hack sagt funktioniert tatsächlich und genau das hab ich gesucht. Vielen Dank!!
Ich hab es noch nicht zuende erprobt aber jetzt spuckt er mir schonmal den richtigen Text raus daher denke ich das es so funktioniert.
JSEngineering deins war allerdings auch nicht unnötig da konnte ich etwas Einblick in Pointer einsehen.
Vielen Dank euch beiden!

durch Array
[*] kann ich ein beliebiges Array einfügen vom gleichen Datentyp. Genau so wie ich es gebraucht habe

Code:
FUNCTION_BLOCK PUBLIC FB_GetTextListVAR_INPUT
    sTextList :STRING;
    MaxIdx:DINT;
    BaseIdentNo :DINT;
    


END_VAR
VAR_OUTPUT
    


END_VAR


VAR
    sTextListID :STRING;
    i: DINT := 0;
END_VAR


VAR_IN_OUT
    pText :ARRAY
[*] OF POINTER TO STRING;
    sText: ARRAY
[*] OF STRING;
END_VAR

FOR i := 0  TO MaxIdx BY 1 DO
sTextList := sTextList;
sTextListID := DINT_TO_STRING(BaseIdentNo + i);
pText[BaseIdentNo + i] := VisuElems.cmpDynamictext.DynamicTextGetText(ADR(sTextList), ADR(sTextListID));
sText[BaseIdentNo + i] := pText[BaseIdentNo + i]^;
END_FOR




AUFRUF:

GetTextFB(
    sTextList:= 'Port_851.Meldetexte' , 
    MaxIdx:=2 , 
    BaseIdentNo:=dnWarningBaseIdentNo , 
    pText:= pTextWarning , 
    sText:= sTextWarning);
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Jetzt habe ich ein Problem beim zweiten Teil wo ich die Texte zuweise.
hier wäre in einem FB eine IF abfrage z.B
FOR z := 0 TO GR00Control.nFatalErrorMaxIdx BY 1 DO
IF GVL_Gr00Alarm.stAlarm.stControl.arbFatalError[z].bActive = TRUE

Allerdings muss ich stControl.arbFatalError von außen verändern können. Da es auch stAuto.arbFatalError sein kann oder stControl.arbWarning.
Die initialisierung dazu wäre als Var Input stAlarm :ST_AlarmRef;
im ST_AlarmRef befindet sich das .Control oder .FatalError.
habt ihr diesbezüglich auch eine lösung?


Ich möchte im Prinzip in Var input GVL_Gr00Alarm.stAlarm.stControl.arbFatalError füttern oder je nachdem was ich in der IF abfrage haben möchte
 
Ich bin mir nicht sicher ob ich es richtig verstehe. Aber kannst du nicht ein Enum übergeben und entsprechend umschalten?
 
Ich bin mir nicht sicher ob ich es richtig verstehe. Aber kannst du nicht ein Enum übergeben und entsprechend umschalten?

ich ergänze es mal kurz:
GVL_Gr00Alarm.stAlarm.stControl.arbFatalError

stAlarm
hat diesen inhalt:

TYPE ST_AlarmRef :
STRUCT
stControl :ST_AlarmOut;
stManual :ST_AlarmOut;
//stMaintenance :ST_AlarmOut;
stHome :ST_AlarmOut;
stAuto :ST_AlarmOut;
END_STRUCT
END_TYPE


ST_AlarmOut hat diesen Inhalt:

TYPE ST_AlarmOut :
STRUCT
arbFatalError :ARRAY[0..nALARM_MAX_NO] OF ST_AlarmProperties;
arbError :ARRAY[0..nALARM_MAX_NO] OF ST_AlarmProperties;
arbWarning :ARRAY[0..nALARM_MAX_NO] OF ST_AlarmProperties;
arbMessage :ARRAY[0..nALARM_MAX_NO] OF ST_AlarmProperties;
arbHint :ARRAY[0..nALARM_MAX_NO] OF ST_AlarmProperties;
bFatalError :BOOL; (* Fatal error in group / Fataler Fehler in der Gruppe *)
bError :BOOL; (* Error in group / Fehler in der Gruppe *)
bWarning :BOOL; (* Warning in group / Warnung in der Gruppe *)
bMessage :BOOL; (* Message in group / Meldung in der Gruppe *)
bHint :BOOL; (* Hint in group / Hint in der Gruppe *)
END_STRUCT
END_TYPE


ST_AlarmProperties hat diesen Inhalt

TYPE ST_AlarmProperties :
STRUCT
bActive :BOOL; (* Alarm on *)
{attribute 'hide'}
dtDateTime :DATE_AND_TIME ; (* Alarm time stamp *)
{attribute 'hide'}
enType :ET_AlarmType; (* Alarm type *)
dnCode :DINT; (* Alarm code *)
END_STRUCT
END_TYPE


Jetzt wollte ich für jeden Punkt dazwischen (stAlarm.stControl.arbFatalError[z].bActive) das ganze von außen beschreiben.
 
Zurück
Oben