Created
May 7, 2021 13:29
-
-
Save kdorsel/75617c638a8b35905b6691d8149481c6 to your computer and use it in GitHub Desktop.
FB_Hash
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<TcPlcObject Version="1.1.0.1"> | |
<POU Name="FB_Hash" Id="{19e70039-197b-0b95-2330-4461be85b2a8}" SpecialFunc="None"> | |
<Declaration><![CDATA[{attribute 'reflection'} | |
FUNCTION_BLOCK FB_Hash | |
VAR_INPUT | |
END_VAR | |
VAR_OUTPUT | |
END_VAR | |
VAR | |
_enDB : BOOL; | |
msg : FB_TcMessage; | |
dataSorted : ARRAY [1..HASHSIZE] OF POINTER TO ST_HashItem; | |
data : ARRAY [1..HASHSIZE] OF ST_HashItem; | |
size : UINT; | |
k : UINT; | |
// Keeps track of all the collisions of this FB | |
collisions : UDINT; | |
END_VAR | |
VAR CONSTANT | |
// To reduce collisions use a prime number as hash size | |
// 101, 251, 503, 1009, 2503, 5003, 10007 | |
HASHSIZE : UINT := 251; | |
EMPTYITEM : ST_HashItem := (); | |
END_VAR | |
]]></Declaration> | |
<Implementation> | |
<ST><![CDATA[/////////////////////////////////////////////////////////////////////////////// | |
// FB_Hash | |
// | |
// v1.0.0 | |
// | |
// Can be used for counter variables | |
// Also made persistent by making the FB definition inside global persistent | |
/////////////////////////////////////////////////////////////////////////////// | |
]]></ST> | |
</Implementation> | |
<Method Name="_get" Id="{fd7b5c8d-a8e6-0f1d-2715-ee5061e3b677}"> | |
<Declaration><![CDATA[METHOD PROTECTED _get : UDINT | |
VAR_INPUT | |
key : STRING(255); | |
ignore : BOOL; | |
END_VAR | |
VAR | |
ii : UINT; | |
END_VAR | |
]]></Declaration> | |
<Implementation> | |
<ST><![CDATA[ | |
IF key = '' THEN | |
msg.CreateEx(TC_Events.HashEventClass.EmptyKey, 0); | |
msg.Send(0); | |
ELSE | |
key := F_ToLCase(key); | |
FOR ii := 1 TO HASHSIZE DO | |
k := hash(key, ii-1); | |
IF data[k].key = key THEN | |
_get := data[k].value; | |
EXIT; | |
ELSIF data[k].key = '' THEN | |
IF NOT ignore THEN | |
msg.CreateEx(TC_Events.HashEventClass.KeyError, 0); | |
msg.ipArguments.Clear().AddString(key); | |
msg.Send(0); | |
END_IF | |
EXIT; | |
// ELSE | |
// Collision, hash again | |
END_IF | |
END_FOR | |
END_IF | |
]]></ST> | |
</Implementation> | |
</Method> | |
<Method Name="_sort" Id="{d29a3cdc-6b0c-0a80-2b67-e10b3788124b}"> | |
<Declaration><![CDATA[METHOD PROTECTED _sort : BOOL | |
VAR_INPUT | |
first : DINT; | |
last : DINT; | |
END_VAR | |
VAR | |
ii : DINT; | |
jj : DINT; | |
pivot : DINT; | |
temp : POINTER TO ST_HashItem; | |
END_VAR | |
]]></Declaration> | |
<Implementation> | |
<ST><![CDATA[ | |
IF first < last THEN | |
pivot := first; | |
ii := first; | |
jj := last; | |
WHILE ii < jj DO | |
WHILE dataSorted[ii]^.key <= dataSorted[pivot]^.key AND_THEN ii < last DO | |
ii := ii + 1; | |
END_WHILE | |
WHILE dataSorted[jj]^.key > dataSorted[pivot]^.key DO | |
jj := jj - 1; | |
END_WHILE | |
IF ii < jj THEN | |
temp := dataSorted[ii]; | |
dataSorted[ii] := dataSorted[jj]; | |
dataSorted[jj] := temp; | |
END_IF | |
END_WHILE | |
temp := dataSorted[pivot]; | |
dataSorted[pivot] := dataSorted[jj]; | |
dataSorted[jj] := temp; | |
THIS^._sort(first, jj - 1); | |
THIS^._sort(jj + 1, last); | |
END_IF | |
]]></ST> | |
</Implementation> | |
</Method> | |
<Method Name="clear" Id="{00478c4a-2509-0945-02b3-b32e47985d41}"> | |
<Declaration><![CDATA[METHOD clear : BOOL | |
VAR_INPUT | |
END_VAR | |
VAR | |
ii : UINT; | |
END_VAR]]></Declaration> | |
<Implementation> | |
<ST><![CDATA[ | |
size := 0; | |
FOR ii := 1 TO HASHSIZE DO | |
dataSorted[ii] := ADR(EMPTYITEM); | |
data[ii].key := ''; | |
data[ii].hash := 0; | |
data[ii].value := 0; | |
END_FOR | |
clear := TRUE; | |
]]></ST> | |
</Implementation> | |
</Method> | |
<Method Name="FB_init" Id="{2ad491ce-33ec-0a93-2215-14771b60ef7b}"> | |
<Declaration><![CDATA[METHOD FB_init : BOOL | |
VAR_INPUT | |
bInitRetains : BOOL; // if TRUE, the retain variables are initialized (warm start / cold start) | |
bInCopyCode : BOOL; // if TRUE, the instance afterwards gets moved into the copy code (online change) | |
enDB : BOOL; | |
END_VAR | |
VAR | |
ii : UINT; | |
END_VAR | |
]]></Declaration> | |
<Implementation> | |
<ST><![CDATA[ | |
_enDB := enDB; | |
FOR ii := 1 TO HASHSIZE DO | |
dataSorted[ii] := ADR(EMPTYITEM); | |
END_FOR | |
// Call release to help if the FB is made persistent | |
msg.Release(); | |
]]></ST> | |
</Implementation> | |
</Method> | |
<Method Name="get" Id="{c93e1584-0ffb-04a6-301c-678e5bbc582f}"> | |
<Declaration><![CDATA[METHOD get : UDINT | |
VAR_INPUT | |
key : STRING(255); | |
END_VAR | |
]]></Declaration> | |
<Implementation> | |
<ST><![CDATA[ | |
get := _get(key, FALSE); | |
]]></ST> | |
</Implementation> | |
</Method> | |
<Method Name="hash" Id="{cebc5374-33d9-0076-2122-7e9e0a18de97}"> | |
<Declaration><![CDATA[METHOD PROTECTED hash : UINT | |
VAR_INPUT | |
key : STRING(255); | |
off : UINT; | |
END_VAR | |
]]></Declaration> | |
<Implementation> | |
<ST><![CDATA[ | |
hash := F_DATA_TO_CRC16_CCITT(ADR(key), TO_UDINT(len(key)), off); | |
hash := hash MOD HASHSIZE; | |
]]></ST> | |
</Implementation> | |
</Method> | |
<Method Name="inc" Id="{eec7c809-359a-0238-0721-10093d099764}"> | |
<Declaration><![CDATA[METHOD inc : UDINT | |
VAR_INPUT | |
key : STRING(255); | |
END_VAR | |
]]></Declaration> | |
<Implementation> | |
<ST><![CDATA[ | |
inc := _get(key, TRUE); | |
IF inc = 0 THEN | |
set(key, 1); | |
inc := 1; | |
ELSE | |
inc := data[k].value := data[k].value + 1; | |
END_IF | |
]]></ST> | |
</Implementation> | |
</Method> | |
<Method Name="remove" Id="{4d62016b-dd7e-05c7-0d06-99c4e533c052}"> | |
<Declaration><![CDATA[METHOD remove : UDINT | |
VAR_INPUT | |
key : STRING(255); | |
END_VAR | |
VAR | |
ii : UINT; | |
END_VAR]]></Declaration> | |
<Implementation> | |
<ST><![CDATA[ | |
remove := get(key); | |
IF remove <> 0 THEN | |
size := size - 1; | |
// Find where this key was stored in the sorted keys | |
FOR ii := 1 TO HASHSIZE DO | |
IF dataSorted[ii] <> 0 AND_THEN dataSorted[ii]^.key = key THEN | |
EXIT; | |
END_IF | |
END_FOR | |
// Fill the empty spot if needed | |
IF ii < HASHSIZE THEN | |
MEMCPY(ADR(dataSorted[ii]), ADR(dataSorted[ii+1]), SIZEOF(dataSorted[1]) * size); | |
END_IF | |
dataSorted[HASHSIZE] := ADR(EMPTYITEM); | |
// Reset the entry | |
data[k].key := ''; | |
data[k].hash := 0; | |
data[k].value := 0; | |
END_IF | |
]]></ST> | |
</Implementation> | |
</Method> | |
<Method Name="reset" Id="{313d3096-4522-061a-1754-150b54087ec4}"> | |
<Declaration><![CDATA[METHOD reset : UDINT | |
VAR_INPUT | |
key : STRING(255); | |
END_VAR | |
]]></Declaration> | |
<Implementation> | |
<ST><![CDATA[ | |
reset := get(key); | |
IF reset <> 0 THEN | |
{IF defined (variable: DB.Pool)} | |
IF _enDB THEN | |
DB.Pool.exec2(ADR(DB.qryCounter), TRUE, F_STRING(data[k].key), F_UDINT(data[k].value)); | |
END_IF | |
{END_IF} | |
data[k].value := 0; | |
END_IF | |
]]></ST> | |
</Implementation> | |
</Method> | |
<Method Name="set" Id="{33b96d78-7423-0772-0df9-4a2fc258aaaa}"> | |
<Declaration><![CDATA[METHOD set : BOOL | |
VAR_INPUT | |
key : STRING(255); | |
val : UDINT; | |
END_VAR | |
VAR | |
//msg : FB_TcMessage; | |
ii : UINT; | |
END_VAR | |
VAR_INST | |
//msg : FB_TcMessage; | |
END_VAR | |
]]></Declaration> | |
<Implementation> | |
<ST><![CDATA[ | |
IF key = '' THEN | |
msg.CreateEx(TC_Events.HashEventClass.EmptyKey, 0); | |
msg.Send(0); | |
ELSE | |
key := F_ToLCase(key); | |
FOR ii := 1 TO HASHSIZE DO | |
k := hash(key, ii-1); | |
IF data[k].key = key THEN | |
data[k].value := val; | |
set := TRUE; | |
EXIT; | |
ELSIF data[k].key = '' THEN | |
data[k].key := key; | |
data[k].hash := k; | |
data[k].value := val; | |
size := size + 1; | |
IF size > HASHSIZE * 0.7 THEN | |
IF size = HASHSIZE THEN | |
msg.CreateEx(TC_EVENTS.HashEventClass.FillLimitError, 0); | |
ELSE | |
msg.CreateEx(TC_EVENTS.HashEventClass.FillLimitWarn, 0); | |
END_IF | |
msg.send(0); | |
END_IF | |
IF ii > 1 THEN | |
collisions := collisions + ii - 1; | |
msg.CreateEx(TC_EVENTS.HashEventClass.InsertCollision, 0); | |
msg.ipArguments.Clear().AddUInt(ii-1); | |
msg.send(0); | |
END_IF | |
set := TRUE; | |
EXIT; | |
// ELSE | |
// Collision | |
END_IF | |
END_FOR | |
END_IF | |
]]></ST> | |
</Implementation> | |
</Method> | |
<Method Name="sort" Id="{cdc9b76a-998a-0b4e-0cea-70aa0422368c}"> | |
<Declaration><![CDATA[METHOD PROTECTED sort : BOOL | |
VAR_INPUT | |
END_VAR | |
]]></Declaration> | |
<Implementation> | |
<ST><![CDATA[ | |
IF size > 1 THEN | |
sort := _sort(1, size - 1); | |
END_IF | |
]]></ST> | |
</Implementation> | |
</Method> | |
</POU> | |
</TcPlcObject> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment