Example: Linked list (FB_LinkedListCtrl)

Here you can unpack the complete sources: LinkedListExample.zip

 

The example project has two parts to the program:

The maximum number of node elements cannot be changed at runtime, and is limited in the example project by MAX_DATA_ELEMENTS. If you need more nodes, then you must increase the size of the node array accordingly (i.e. increase the value of the constant).

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

The handle of the linked list is initialised in the first PLC cycle. This handle is then passed as the VAR_IN_OUT variable to the FB_LinkedListCtrl function block when accessing the list. The linked list is manipulated through the action calls of the function block. This allows node elements to be added, removed, searched and so on. The desired action is executed in response to a rising edge of the associated boolean variable. When you run the program, all the operations are carried out once.

 

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        : UDINT;
    getValue        : UDINT;
    getPosPtr       : POINTER TO T_LinkedListEntry := 0;
    bInit       : BOOL := TRUE;
END_VAR
IF bInit THEN
    bInit   := FALSE;
    F_CreateLinkedListHnd( ADR( nodes ), SIZEOF( nodes ), hList );
    fbList.A_Reset( hList := 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 );
    IF NOT fbList.bOk THEN
        ;(* List overflow *)
    END_IF

    putValue := 11111;
    fbList.A_AddTailValue( hList := hList, putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
    IF NOT fbList.bOk THEN
        ;(* List overflow *)
    END_IF

    putValue := 12345;
    fbList.A_AddTailValue( hList := hList, putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
    IF NOT fbList.bOk THEN
        ;(* List overflow *)
    END_IF

    putValue := 67890;
    fbList.A_AddTailValue( hList := hList, putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
    IF NOT fbList.bOk THEN
        ;(* List overflow *)
    END_IF
END_IF

IF bAddHeadValue THEN
    bAddHeadValue := FALSE;
    putValue := 33333;
    fbList.A_AddHeadValue( hList := hList, putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
    IF NOT fbList.bOk THEN
        ;(* List overflow *)
    END_IF

    putValue := 44444;
    fbList.A_AddHeadValue( hList := hList, 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( hList := hList, getValue=>getValue, getPosPtr=>getPosPtr );
    IF fbList.bOk THEN
        sInfo := UDINT_TO_STRING( getValue );
        REPEAT
            fbList.A_GetPrev( hList := hList, putPosPtr := getPosPtr, getValue=>getValue, getPosPtr=>getPosPtr );
            IF fbList.bOk THEN
                sInfo := UDINT_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 := UDINT_TO_STRING( getValue );
        REPEAT
            fbList.A_GetNext( hList := hList, putPosPtr := getPosPtr, getValue=>getValue, getPosPtr=>getPosPtr );
            IF fbList.bOk THEN
                sInfo := UDINT_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 := UDINT_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 := UDINT_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 := UDINT_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

This section of the program illustrates how structured data sets can be manipulated in the list in place of simply 32-bit numbers. In this case, the 32-bit node value is only used as a reference pointer to the actual value of the node. The reference pointer is able to point to instances of structured variables or other data types. The functionality is encapsulated in a function block. This function block, FB_SpecialLinkedListCtrl, can be thought of as a specialised version of the function block FB_LinkedListCtrl. The FB_LinkedListCtrl block is also used internally by the specialised FB.

The DATAELEMENT_TO_STRING function is only used to permit visual output of the value of the node.

A structured variable of type ST_DataElement is used as an example. The highlight: You can add further member variables to the data type declaration of ST_DataElement without having to make any changes to the program or to the FB_SpecialLinkedListCtrl function block.

The type declaration for ST_DataElement:

TYPE ST_DataElement :
STRUCT
    number  : UDINT := 0;
    name    : STRING(MAX_NAME_LENGTH) := '';
    price   : REAL := 0.0;

    (* add additional member variables here *)
END_STRUCT
END_TYPE

 

A simple search function is implemented. You can search for nodes having a particular name, number or price.

How do the 32-bit node values become reference pointers to the instances of the ST_DataElement array?

The maximum size of the list is limited by the constant MAX_DATA_ELEMENTS. It follows that no more than MAX_DATA_ELEMENTS reference pointers can be stored in the list. The FB_SpecialLinkedListCtrl function block has an internal ST_DataElement array variable with the same size as the T_LinkedListEntry array variable. For the sake of simplicity, the array indices in the two arrays are identical!

Each T_LinkedListEntry array element can only be inserted into the list once. The FB_LinkedListCtrl function block therefore searches for a free/unused T_LinkedListEntry array element, and inserts it into the list if successful. The index of the T_LinkedListEntry being used can be determined through the action A_GetIndexAtPosPtr. In the next step, the 32-bit node value that has just been added is assigned the address of the same array element in the ST_DataElement array. In the example project, this is done by calling the action A_SetValueAtPosPtr.

nodes[index].value := ADR( dataPool[index] )

 

The assignment is, for instance, carried out in the FB_SpecialLinkedListCtrl->A_AddHeadValue action:

fbList.A_AddHeadValue( hList := hList, putValue := 16#00000000, getPosPtr=>getPosPtr, bOk=>bOk );
IF fbList.bOk THEN
    fbList.A_GetIndexAtPosPtr( hList := hList, putPosPtr := getPosPtr, getValue =>indexOfElem, bOk=>bOk );
    IF fbList.bOk THEN
        pRefPtr      := ADR( dataPool[indexOfElem] );
        pRefPtr^ := putValue;
        fbList.A_SetValueAtPosPtr( hList := hList, putPosPtr := getPosPtr, putValue := pRefPtr, bOk=>bOk );
        IF fbList.bOk THEN
            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