Beispiel: Verkettete-Liste (FB_LinkedListCtrl)
Hier können Sie die kompletten Sourcen entpacken: LinkedListExample.zip
Das Beispielprojekt besitzt zwei Programmteile:
- P_LIST_OF_UDINT ist ein einfaches Beispielprogramm welches nur 32 bit Werte in der verketteten Liste bearbeitet.
- Im P_LIST_OF_STRUCTDATA wird gezeigt, wie andere Datentypen (z.B. strukturierte Datentypen) in der verketteten Liste bearbeitet werden können.
Die maximale Anzahl der Knotenelemente kann zur Laufzeit nicht verändert werden und wird in dem Beispielprojekt durch die MAX_DATA_ELEMENTS begrenzt. Wenn Sie mehr Knoten benötigen, dann müssen Sie das nodes-Array entsprechend vergrößern ( d.h. den Wert der Konstante erhöhen).
VAR_GLOBAL CONSTANT
MAX_DATA_ELEMENTS : UDINT := 100;(* Max. number of elements in the list *)
MAX_NAME_LENGTH : UDINT := 30;(* Max. length of article name *)
END_VAR
PROGRAM P_LIST_OF_UDINT
Im ersten SPS-Zyklus wird das Handle der verketteten Liste initialisiert. Dieses Handle wird dann bei den Zugriffen auf die Liste als VAR_IN_OUT-Variable an den FB_LinkedListCtrl-Funktionsbaustein übergeben. Die verkettete Liste wird über die Aktionsaufrufe des FBs manipuliert. Auf diese Weise können Knotenelemente hinzugefügt/entfernt/durchsucht werden. Bei einer steigenden Flanke an der entsprechenden boolschen Variable wird die gewünschte Aktion ausgeführt. Wenn Sie das Programm starten werden alle Operationen einmalig ausgeführt.
PROGRAM P_LIST_OF_UDINT
VAR
sInfo : T_MaxString := '';
bAddTailValue : BOOL := TRUE;
bAddHeadValue : BOOL := TRUE;
bGetTail : BOOL := TRUE;
bGetHead : BOOL := TRUE;
bFind : BOOL := TRUE;
bRemoveHeadValue : BOOL := TRUE;
bRemoveTailValue : BOOL := TRUE;
bCount : BOOL := TRUE;
search : UDINT := 12345;
fbList : FB_LinkedListCtrl;(* basic linked list control function block *)
hList : T_HLINKEDLIST;(* linked list handle *)
nodes : ARRAY[0..MAX_DATA_ELEMENTS] OF T_LinkedListEntry;(* Max. number of linked list nodes. The value of list node = 32 bit integer *)
putValue : PVOID;(* Pointer or integer value (x86=>32bit, x64=>64bit)*)
getValue : PVOID;(* Pointer or integer value (x86=>32bit, x64=>64bit)*)
getPosPtr : POINTER TO T_LinkedListEntry := 0;
bInit : BOOL := TRUE;
END_VAR
IF bInit THEN
bInit := FALSE;
F_CreateLinkedListHnd( ADR( nodes ), SIZEOF( nodes ), hList );
END_IF
IF bAddTailValue THEN(* add some nodes to the list *)
bAddTailValue := FALSE;
putValue := 22222;
fbList.A_AddTailValue( hList := hList, putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
putValue := 11111;
fbList.A_AddTailValue( hList := hList, putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
putValue := 12345;
fbList.A_AddTailValue( hList := hList, putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
putValue := 67890;
fbList.A_AddTailValue( hList := hList, putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
END_IF
IF bAddHeadValue THEN
bAddHeadValue := FALSE;
putValue := 33333;
fbList.A_AddHeadValue( hList := hList, putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
putValue := 44444;
fbList.A_AddHeadValue( hList := hList, putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
END_IF
IF bGetTail THEN(* enumerate all nodes in list (start at tail node) *)
bGetTail := FALSE;
sInfo := '';
fbList.A_GetTail( hList := hList, getValue=>getValue, getPosPtr=>getPosPtr );
IF fbList.bOk THEN
sInfo := PVOID_TO_STRING( getValue );
REPEAT
fbList.A_GetPrev( hList := hList, putPosPtr := getPosPtr, getValue=>getValue, getPosPtr=>getPosPtr );
IF fbList.bOk THEN
sInfo := PVOID_TO_STRING( getValue );
ELSE
EXIT;
END_IF
UNTIL NOT fbList.bOk
END_REPEAT
END_IF
END_IF
IF bGetHead THEN(* enumerate all nodes in list (start at head node) *)
bGetHead := FALSE;
sInfo := '';
fbList.A_GetHead( hList := hList, getValue=>getValue, getPosPtr=>getPosPtr );
IF fbList.bOk THEN
sInfo := PVOID_TO_STRING( getValue );
REPEAT
fbList.A_GetNext( hList := hList, putPosPtr := getPosPtr, getValue=>getValue, getPosPtr=>getPosPtr );
IF fbList.bOk THEN
sInfo := PVOID_TO_STRING( getValue );
ELSE
EXIT;
END_IF
UNTIL NOT fbList.bOk
END_REPEAT
END_IF
END_IF
IF bFind THEN(* search for node in the list by node value*)
bFind := FALSE;
getPosPtr := 0;(* start from first node element *)
sInfo := '';
REPEAT
fbList.A_FindNext( hList := hList, putPosPtr := getPosPtr, putValue := search, getValue=>getValue, getPosPtr=>getPosPtr );
IF fbList.bOk THEN
sInfo := PVOID_TO_STRING( getValue );
ELSE
EXIT;
END_IF
UNTIL NOT fbList.bOk
END_REPEAT
END_IF
IF bRemoveTailValue THEN(* remove tail node from node list *)
bRemoveTailValue := FALSE;
sInfo := '';
fbList.A_RemoveTailValue( hList := hList, getValue=>getValue, getPosPtr=>getPosPtr );
IF fbList.bOk THEN
sInfo := PVOID_TO_STRING( getValue );
END_IF
END_IF
IF bRemoveHeadValue THEN(* remove head node from node list *)
bRemoveHeadValue := FALSE;
sInfo := '';
fbList.A_RemoveHeadValue( hList := hList, getValue=>getValue, getPosPtr=>getPosPtr );
IF fbList.bOk THEN
sInfo := PVOID_TO_STRING( getValue );
END_IF
END_IF
IF bCount THEN(* count nodes in list *)
bCount := FALSE;
sInfo := UDINT_TO_STRING( hList.nCount );
END_IF
PROGRAM P_LIST_OF_STRUCTDATA
Dieses Programmteil zeigt, wie strukturierte Datensätze statt einfacher 32-Zahlen in der Liste bearbeitet werden können. Dabei wird der 32 bit Knotenwert nur als Referenzpointer auf den tatsächlichen Knotenwert verwendet. Der Referenzpointer kann dann auch auf Instanzen von strukturierten Variablen oder anderen Datentypen zeigen. Die Funktionalität wurde in einem Funktionsblock gekapselt. Dieser Funktionsblock FB_SpecialLinkedListCtrl kann als eine spezialisierte Version des FB_LinkedListCtrl-Funktionsbausteins bezeichnet werden. Der FB_LinkedListCtrl-Baustein wird auch intern von dem spezialisierten FB verwendet.
Die Funktion DATAELEMENT_TO_STRING wird nur verwendet um eine visuelle Ausgabe der Knotenwerte zu ermöglichen.
Als Beispiel wird die Strukturierte Variable vom Typ: ST_DataElement verwendet. Der Clou: Sie können die Datentypdeklaration von ST_DataElement um weitere Membervariablen erweitern, ohne dass Sie an dem Programm oder dem FB_SpecialLinkedListCtrl-Funktionsbaustein Veränderungen durchführen müssen.
Die Typdeklaration von ST_DataElement:
(* Structured application data entry *)
TYPE ST_DataElement :
STRUCT
(* Adapt this structure to match your application needs *)
number : UDINT := 0;
name : STRING(MAX_NAME_LENGTH) := '';
price : REAL := 0.0;
END_STRUCT
END_TYPE
Es wurde eine einfache Suchfunktion implementiert. Sie können nach Knoten mit einem bestimmten name, number oder price suchen.
Wie werden die 32 bit Knotenwerte zu Referenzpointern auf die ST_DataElement-Array-Instanzen?
Die max. Größe der Liste ist durch MAX_DATA_ELEMENTS-Konstante begrenzt. Folglich können in der Liste nur MAX_DATA_ELEMENTS-Referenzpointer gespeichert werden. Der FB_SpecialLinkedListCtrl-Baustein besitzt intern eine ST_DataElement-Array-Variable mit derselben Größe wie die T_LinkedListEntry-Array-Varriable. Zur Vereinfachung sind die Array-Indizies in beiden Arrays gleich!
Jedes T_LinkedListEntry-Array-Element kann nur einmal in die Liste "reingehängt" werden. Dabei sucht der FB_LinkedListCtrl-Funktionsbaustein nach einem freien/unbenutzten T_LinkedListEntry-Array-Element und fügt es bei Erfolg der Liste zu. Mit Hilfe der Aktion A_GetIndexAtPosPtr kann der verwendete Index des T_LinkedListEntry-Arrays ermittelt werden. Im nächsten Schritt wird dem zuletzt hinzugefügten 32 bit Knotenwert die Adresse desselben Arrayelements vom ST_DataElement-Array zugewiesen. Im Beispielprojekt durch den Aktionsaufruf: A_SetValueAtPosPtr.
nodes[index].value := ADR( dataPool[index] )
Die Zuweisung wird z.B. in der FB_SpecialLinkedListCtrl->A_AddHeadValue-Aktion durchgeführt:
(* Adds head to the node list *)
MEMSET( ADR( getValue ), 0, SIZEOF( getValue ) );
getPosPtr := 0;
fbList.A_AddHeadValue( hList := hList, putValue := 16#00000000(* we will set this value later *), getPosPtr=>getPosPtr, bOk=>bOk );(* Add new element to the list, getPosPtr points to the new list node *)
IF fbList.bOk THEN(* Success *)
fbList.A_GetIndexAtPosPtr( hList := hList, putPosPtr := getPosPtr, getValue =>indexOfElem, bOk=>bOk );(* Get array index of getPosPtr *)
IF fbList.bOk THEN(* Success *)
pRefPtr := ADR( dataPool[indexOfElem] );(* Get pointer to the data element *)
pRefPtr^ := putValue;(* set element value *)
fbList.A_SetValueAtPosPtr( hList := hList, putPosPtr := getPosPtr, putValue := pRefPtr, bOk=>bOk );(* Assign the node value = pointer to the data element *)
IF fbList.bOk THEN(* Success *)
getValue := putValue;
END_IF
END_IF
END_IF
PROGRAM P_LIST_OF_STRUCTDATA
VAR
sInfo : T_MaxString := '';
bAddTailValue : BOOL := TRUE;
bAddHeadValue : BOOL := TRUE;
bGetTail : BOOL := TRUE;
bGetHead : BOOL := TRUE;
bFind : BOOL := TRUE;
bRemoveHeadValue : BOOL := TRUE;
bRemoveTailValue : BOOL := TRUE;
bCount : BOOL := TRUE;
search : ST_DataElement := ( name := 'Couch', price := 99.98, number := 12345 );(* search value ( by name, by price or by number ) *)
eSearch : E_SEARCH_CRITERIA := eSEARCH_BY_NAME;(* / eSEARCH_BY_PRICE / eSEARCH_BY_NUMBER *)
fbList : FB_SpecialLinkedListCtrl;(* Specialized linked list control function block *)
putValue : ST_DataElement;
getValue : ST_DataElement;
getPosPtr : POINTER TO T_LinkedListEntry := 0;
bInit : BOOL := TRUE;
END_VAR
IF bInit THEN
bInit := FALSE;
fbList.A_Reset();(* reset / initialize list *)
END_IF
IF bAddTailValue THEN(* add some nodes to the list *)
bAddTailValue := FALSE;
putValue.number := 22222;
putValue.name := 'TV set';
putValue.price := 99.98;
fbList.A_AddTailValue( putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
IF NOT fbList.bOk THEN
;(* List overflow *)
END_IF
putValue.number := 11111;
putValue.name := 'Couch';
putValue.price := 99.98;
fbList.A_AddTailValue( putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
IF NOT fbList.bOk THEN
;(* List overflow *)
END_IF
putValue.number := 12345;
putValue.name := 'Chair';
putValue.price := 44.98;
fbList.A_AddTailValue( putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
IF NOT fbList.bOk THEN
;(* List overflow *)
END_IF
putValue.number := 67890;
putValue.name := 'Table';
putValue.price := 99.98;
fbList.A_AddTailValue( putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
IF NOT fbList.bOk THEN
;(* List overflow *)
END_IF
END_IF
IF bAddHeadValue THEN
bAddHeadValue := FALSE;
putValue.number := 33333;
putValue.name := 'Couch';
putValue.price := 199.98;
fbList.A_AddHeadValue( putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
IF NOT fbList.bOk THEN
;(* List overflow *)
END_IF
putValue.number := 44444;
putValue.name := 'Couch';
putValue.price := 299.98;
fbList.A_AddHeadValue( putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
IF NOT fbList.bOk THEN
;(* List overflow *)
END_IF
END_IF
IF bGetTail THEN(* enumerate all nodes in list (start at tail node) *)
bGetTail := FALSE;
sInfo := '';
fbList.A_GetTail( getValue=>getValue, getPosPtr=>getPosPtr );
IF fbList.bOk THEN
sInfo := DATAELEMENT_TO_STRING( getValue );
REPEAT
fbList.A_GetPrev( putPosPtr := getPosPtr, getValue=>getValue, getPosPtr=>getPosPtr );
IF fbList.bOk THEN
sInfo := DATAELEMENT_TO_STRING( getValue );
ELSE
EXIT;
END_IF
UNTIL NOT fbList.bOk
END_REPEAT
END_IF
END_IF
IF bGetHead THEN(* enumerate all nodes in list (start at head node) *)
bGetHead := FALSE;
sInfo := '';
fbList.A_GetHead( getValue=>getValue, getPosPtr=>getPosPtr );
IF fbList.bOk THEN
sInfo := DATAELEMENT_TO_STRING( getValue );
REPEAT
fbList.A_GetNext( putPosPtr := getPosPtr, getValue=>getValue, getPosPtr=>getPosPtr );
IF fbList.bOk THEN
sInfo := DATAELEMENT_TO_STRING( getValue );
ELSE
EXIT;
END_IF
UNTIL NOT fbList.bOk
END_REPEAT
END_IF
END_IF
IF bFind THEN(* search for node in the list by node value (name, price, number... )*)
bFind := FALSE;
getPosPtr := 0;(* start from first node element *)
sInfo := '';
REPEAT
fbList.A_Find( eSearch := eSearch, putPosPtr := getPosPtr, putValue := search, getValue=>getValue, getPosPtr=>getPosPtr );
IF fbList.bOk THEN
sInfo := DATAELEMENT_TO_STRING( getValue );
ELSE
EXIT;
END_IF
UNTIL NOT fbList.bOk
END_REPEAT
END_IF
IF bRemoveTailValue THEN(* remove tail node from node list *)
bRemoveTailValue := FALSE;
sInfo := '';
fbList.A_RemoveTailValue( getValue=>getValue, getPosPtr=>getPosPtr );
IF fbList.bOk THEN
sInfo := DATAELEMENT_TO_STRING( getValue );
END_IF
END_IF
IF bRemoveHeadValue THEN(* remove head node from node list *)
bRemoveHeadValue := FALSE;
sInfo := '';
fbList.A_RemoveHeadValue( getValue=>getValue, getPosPtr=>getPosPtr );
IF fbList.bOk THEN
sInfo := DATAELEMENT_TO_STRING( getValue );
END_IF
END_IF
IF bCount THEN(* count nodes in list *)
bCount := FALSE;
sInfo := '';
fbList.A_Count( );
IF fbList.bOk THEN
sInfo := UDINT_TO_STRING( fbList.nCount );
END_IF
END_IF
Voraussetzungen
Entwicklungsumgebung | Zielplattform | Einzubindende SPS-Bibliotheken (Kategoriegruppe) |
---|---|---|
TwinCAT v3.1.0 | PC oder CX (x86, x64, Arm®) | Tc2_Utilities (System) |