Beispiel: Hash-Tabelle (FB_HashTableCtrl)
Die kompletten Sourcen finden Sie hier: HashTableExample.zip
Das Beispielprojekt besitzt zwei Programmteile:
- P_TABLE_OF_UDINT ist eine einfaches Beispielprogramm welches nur 32 bit Werte in der Hash-Tabelle bearbeitet.
- Im P_TABLE_OF_STRUCTDATA wird gezeigt wie andere Datentypen (z.B. strukturierte Datentypen) in der Hash-Tabelle bearbeitet werden können.
Die maximale Anzahl der Tabellenelemente kann zur Laufzeit nicht verändert werden und wird in dem Beispielprojekt durch die MAX_DATA_ELEMENTS begrenzt. Wenn Sie mehr Elemente benötigen, dann müssen Sie das table-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_TABLE_OF_UDINT
Im ersten SPS-Zyklus werden in der Tabelle als Beispiel Informationen über Artikelnummer und Artikelname abgelegt. Die Artikelnummer dient als Schlüssel und der Array-Index des Artikelnamens als Wert.
Über eine steigende Flanke am bLookup kann der Artikelname anhand der Artikelnummer gefunden werden.
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
Dieses Programmteil zeigt wie strukturierte Datensätze statt einfacher 32-Zahlen in der Tabelle bearbeitet werden können. Dabei wird der 32 bit Elementwert nur als Referenzpointer auf den tatsächlichen Elementwert 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_SpecialHashTableCtrl kann als eine spezialisierte Version des FB_HashTableCtrl-Funktionsbausteins bezeichnet werden. Der FB_HashTableCtrl-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_SpecialHashTableCtrl-Funktionsbaustein Veränderungen durchführen müssen.
Die Typdeklaration von 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
Wie werden die 32 bit Elementwerte zu Referenzpointern auf die ST_DataElement-Array-Instanzen?
Die max. Größe der Tabelle ist durch MAX_DATA_ELEMENTS-Konstante begrenzt. Folglich können in der Tabelle nur MAX_DATA_ELEMENTS-Referenzpointer gespeichert werden. Der FB_SpecialHashTableCtrl-Baustein besitzt intern eine ST_DataElement-Array-Variable mit derselben Array-Größe wie die T_HashTableEntry-Array-Varriable. Zur Vereinfachung sind die Array-Indizies in beiden Arrays gleich!
Jedes T_HashTableEntry-Array-Element kann nur einmal in die Tabelle "reingehängt" werden. Dabei sucht der FB_HashTableCtrl-Funktionsbaustein nach einem freien/unbenutzten T_HashTableEntry-Array-Element und fügt es bei Erfolg der Tabelle zu. Mit Hilfe der Aktion A_GetIndexAtPosPtr kann der verwendete Index des T_HashTableEntry-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 zweiten Aktionsaufruf: A_Add.
nodes[index].value := ADR( dataPool[index] )
Die Zuweisung wird z.B. in der FB_SpecialHashTableCtrl->A_Add-Aktion durchgeführt:
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