Example: Hash table (FB_HashTableCtrl)

Here you can unpack the complete sources: HashTableExample.zip


The example project has two parts to the program:

The maximum number of table 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 table 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_TABLE_OF_UDINT

In the first PLC cycle the article number and article name are stored in the table. The article number serves as key and the array index of the article name as value. Via a rising edge at bLookup the article name can be found via the article number.

PROGRAM P_TABLE_OF_UDINT
VAR
    sInfo       : T_MaxString := '';
    bAdd        : BOOL := TRUE;
    bLookup     : BOOL := TRUE;
    bRemove     : BOOL := TRUE;
    bEnum       : BOOL := TRUE;
    bCount      : BOOL := TRUE;

    search      : UDINT := 11111;(* article number *)

    fbTable         : FB_HashTableCtrl;(* basic hash table control function block *)
 hTable      : T_HHASHTABLE;(* hash table handle *)
    table       : ARRAY[0..MAX_DATA_ELEMENTS] OF T_HashTableEntry;(* Max. number of hash table entries. The value of hash table entry = 32 bit integer *)
    names       : ARRAY[0..MAX_DATA_ELEMENTS] OF STRING(MAX_NAME_LENGTH);
    bInit       : BOOL := TRUE;
END_VAR

 

IF bInit THEN
    bInit := FALSE;
    F_CreateHashTableHnd( ADR( table ), SIZEOF( table ), hTable );(* Intialize table handle *)
 fbTable.A_Reset( hTable := hTable );
END_IF

IF bAdd THEN
    bAdd := FALSE;

    (* Fill table. Article number is the key. Array index number is the value (article name) *)
    names[0] := 'Chair';
    fbTable.A_Add( key := 12345, putValue := 0(* array index*), hTable := hTable );
    IF NOT fbTable.bOk THEN
        ;(* Table overflow *)
    END_IF

    names[1] := 'Table';
    fbTable.A_Add( key := 67890, putValue := 1, hTable := hTable );
    IF NOT fbTable.bOk THEN
        ;(* Table overflow *)
    END_IF

    names[2] := 'Couch';
    fbTable.A_Add( key := 11111, putValue := 2, hTable := hTable );
    IF NOT fbTable.bOk THEN
        ;(* Table overflow *)
    END_IF

    names[3] := 'TV set';
    fbTable.A_Add( key := 22222, putValue := 3, hTable := hTable );
    IF NOT fbTable.bOk THEN
        ;(* Table overflow *)
    END_IF
END_IF

IF bLookup THEN(* search for the article name by article number *)
 bLookup := FALSE;
    sInfo := '';
    fbTable.A_Lookup( key := search, hTable := hTable );
    IF fbTable.bOk THEN
        sInfo := names[fbTable.getValue];
    ELSE
        ;(* Entry not found *)
    END_IF
END_IF

IF bRemove THEN
    bRemove := FALSE;
    sInfo := '';
    fbTable.A_Remove( key := articleNo, hTable := hTable );
    IF fbTable.bOk THEN
        sInfo := names[fbTable.getValue];
    ELSE
        ;(* Entry not found *)
    END_IF
END_IF

IF bEnum THEN(* enumerate table entries *)
    bEnum := FALSE;
    sInfo := '';

    fbTable.A_GetFirst( putPosPtr := 0, hTable := hTable );
    IF fbTable.bOk THEN
        sInfo := names[fbTable.getValue];

        REPEAT
            fbTable.A_GetNext( putPosPtr := fbTable.getPosPtr , hTable := hTable );
            IF fbTable.bOk THEN
                sInfo := names[fbTable.getValue];
            END_IF
        UNTIL NOT fbTable.bOk
        END_REPEAT

    END_IF
END_IF

IF bCount THEN(* count entries in the table *)
    bCount := FALSE;
    sInfo := UDINT_TO_STRING( hTable.nCount );
END_IF


PROGRAM P_TABLE_OF_STRUCTDATA

This section of the program illustrates how structured data sets can be manipulated in the table in place of simply 32-bit numbers. In this case, the 32-bit element value is only used as a reference pointer to the actual value of the element. 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_SpecialHashTableCtrl, can be thought of as a specialised version of the function block FB_HashTableCtrl. The FB_HashTableCtrl 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 element.

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_SpecialHashTableCtrl 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

 

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

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

Each T_HashTableEntry array element can only be inserted into the table once. The FB_HashTableCtrl function block therefore searches for a free/unused T_HashTableEntry array element, and inserts it into the table if successful. The index of the T_HashTableEntry 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_Add again.

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

 

The assignment is, for instance, carried out in the FB_SpecialHashTableCtrl->A_Add action:

fbTable.A_Add( hTable := hTable, key := key, putValue := 16#00000000, getPosPtr=>getPosPtr, bOk=>bOk );
IF fbTable.bOk THEN
     fbTable.A_GetIndexAtPosPtr( hTable := hTable, putPosPtr := getPosPtr, getValue =>indexOfElem, bOk=>bOk );
    IF fbTable.bOk THEN
         pRefPtr := ADR( dataPool[indexOfElem] );

        pRefPtr^ := putValue;

         fbTable.A_Add( hTable := hTable, key := key, putValue := pRefPtr, bOk=>bOk );
        IF fbTable.bOk THEN
            getValue := putValue;
        END_IF
    END_IF
END_IF

 

PROGRAM P_TABLE_OF_STRUCTDATA
VAR
    sInfo   : T_MaxString := '';
    bAdd    : BOOL := TRUE;
    bLookup     : BOOL := TRUE;
    bRemove     : BOOL := TRUE;
    bEnum   : BOOL := TRUE;
    bCount  : BOOL := TRUE;

    search  : UDINT := 11111;(* article number *)

    fbTable     : FB_SpecialHashTableCtrl;(* Specialized hash table control function block *)
    putValue    : ST_DataElement;
    getValue    : ST_DataElement;
    getPosPtr : POINTER TO T_HashTableEntry := 0;
    bInit   : BOOL := TRUE;
END_VAR
IF bInit THEN
    bInit := FALSE;
    fbTable.A_Reset();(* reset / initialize table *)
END_IF


IF bAdd THEN
    bAdd := FALSE;

    (* Fill table. Article number is the key and data structure is the value *)
    putValue.number := 12345;
    putValue.name := 'Chair';
    putValue.price := 44.98;
    fbTable.A_Add( key := 12345, putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
    IF NOT fbTable.bOk THEN
        ;(* Table overflow *)
    END_IF

    putValue.number := 67890;
    putValue.name := 'Table';
    putValue.price := 99.98;
    fbTable.A_Add( key := 67890, putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
    IF NOT fbTable.bOk THEN
        ;(* Table overflow *)
    END_IF

    putValue.number := 11111;
    putValue.name := 'Couch';
    putValue.price := 99.98;
    fbTable.A_Add( key := 11111, putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
    IF NOT fbTable.bOk THEN
        ;(* Table overflow *)
    END_IF

    putValue.number := 22222;
    putValue.name := 'TV set';
    putValue.price := 99.98;
    fbTable.A_Add( key := 22222, putValue := putValue, getPosPtr=>getPosPtr, getValue=>getValue );
    IF NOT fbTable.bOk THEN
        ;(* Table overflow *)
    END_IF
END_IF

IF bLookup THEN(* search for the article name by article number *)
    bLookup := FALSE;
    sInfo := '';
    fbTable.A_Lookup( key := search, getPosPtr=>getPosPtr, getValue=>getValue );
    IF fbTable.bOk THEN
        sInfo := DATAELEMENT_TO_STRING( getValue );
    ELSE
        ;(* Entry not found *)
    END_IF
END_IF

IF bRemove THEN(* remove one entry from the table *)
    bRemove := FALSE;
    sInfo := '';
    fbTable.A_Remove( key := search, getPosPtr=>getPosPtr, getValue=>getValue );
    IF fbTable.bOk THEN
        sInfo := DATAELEMENT_TO_STRING( getValue );
    ELSE
        ;(* Entry not found *)
    END_IF
END_IF

IF bEnum THEN(* enumerate table entries *)
    bEnum := FALSE;
    sInfo := '';

    fbTable.A_GetFirst( putPosPtr := 0, getPosPtr=>getPosPtr, getValue=>getValue );
    IF fbTable.bOk THEN
        sInfo := DATAELEMENT_TO_STRING( getValue );

        REPEAT
            fbTable.A_GetNext( putPosPtr := fbTable.getPosPtr , getPosPtr=>getPosPtr, getValue=>getValue );
            IF fbTable.bOk THEN
                sInfo := DATAELEMENT_TO_STRING( getValue );
            END_IF
        UNTIL NOT fbTable.bOk
        END_REPEAT
    END_IF
END_IF

IF bCount THEN(* count entries in the table *)
    bCount := FALSE;
    fbTable.A_Count();
    IF fbTable.bOk THEN
        sInfo := UDINT_TO_STRING( fbTable.nCount );
    END_IF
END_IF