<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"><channel><title>Ticket search results</title><link>https://forge.codesys.com/tol/iec-snippets/snippets/</link><description>You searched for true</description><language>en</language><lastBuildDate>Mon, 05 Feb 2024 17:09:15 -0000</lastBuildDate><item><title>Encrypt and Decrypt using AES_128_CFB algorithm</title><link>https://forge.codesys.com/tol/iec-snippets/snippets/9/</link><description>Based on [Crypto Example](https://forge.codesys.com/prj/codesys-example/crypto-example/home/Home/)

~~~ST
PROGRAM PLC_PRG
VAR
    Result1, Result2, Result3, Result4 : RTS_IEC_RESULT;

    _hCipher : RTS_IEC_HANDLE := CryptoGetAlgorithmById(ui32CryptoID:=RtsCryptoID.AES_128_CFB, pResult:=0);
    _szBlock : ULINT := 16; // Blocksize of ``_hCipher`` =&gt; 128 Bit for AES_128_CFB
    _sKey : SECRET; // 128 Bit Secret Key
    _szKey : ULINT := 16; // 128 Bit
    _sInitVector : MESSAGE; // Random Initial Value of Length ``_szBlock``
    sPlainText : MESSAGE := 'The red fox has an elongated body and relatively short limbs. The tail, which is longer than half the body length.';
    szPlainText : ULINT := TO_ULINT(Standard.LEN(sPlainText));
    sCipherText : MESSAGE;
    sDecipherText : MESSAGE;

    bsKey : RtsByteString := (ui32MaxLen:=SIZEOF(_sKey), ui32Len:=0, pByData:=ADR(_sKey));
    ckKey : RtsCryptoKey;
    bsInitVector : RtsByteString := (ui32MaxLen:=SIZEOF(_sInitVector), ui32Len:=0, pByData:=ADR(_sInitVector));
    bsPlainText : RtsByteString := (ui32MaxLen:=SIZEOF(MESSAGE), ui32Len:=TO_UDINT(szPlainText), pByData:=ADR(sPlainText));
    bsCipherText : RtsByteString := (ui32MaxLen:=SIZEOF(MESSAGE), ui32Len:=0, pByData:=ADR(sCipherText));
    bsDecipheredText : RtsByteString := (ui32MaxLen:=SIZEOF(MESSAGE), ui32Len:=0, pByData:=ADR(sDecipherText));

    Init: BOOL := TRUE;
END_VAR


IF Init THEN
    Result1 := CryptoGenerateRandomNumber(ui32NumOfRandomBytes:=TO_UDINT(_szKey), pRandom:=ADR(bsKey));
    Result2 := CryptoGenerateRandomNumber(ui32NumOfRandomBytes:=TO_UDINT(_szBlock), pRandom:=ADR(bsInitVector));
    ckKey.keyType := RtsCryptoKeyType.KeyType_Key;
    ckKey.key.byteString := bsKey;

    Result3 := CryptoSymmetricEncrypt(
        hAlgo:=_hCipher,
        pPlainText:=ADR(bsPlainText),
        key:=ckKey,
        pInitVector:=ADR(bsInitVector),
        xEnablePadding:=TRUE,
        pCipherText:=ADR(bsCipherText) );

    Result4 := CryptoSymmetricDecrypt(
        hAlgo:=_hCipher,
        pCipherText:=ADR(bsCipherText),
        key:=ckKey,
        pInitVector:=ADR(bsInitVector),
        xEnablePadding:=TRUE,
        pPlainText:=ADR(bsDecipheredText) );
    Init := FALSE;
END_IF;
~~~</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">i-campbell</dc:creator><pubDate>Mon, 03 Apr 2023 12:00:22 -0000</pubDate><guid isPermaLink="false">https://forge.codesys.com/tol/iec-snippets/snippets/9/</guid></item><item><title>Interface to manage mappings of "ANY" datatype</title><link>https://forge.codesys.com/tol/iec-snippets/snippets/8/</link><description>There is a nice article, called [The wonders o ANY](https://alltwincat.com/2018/03/21/the-wonders-of-any/). While I learnt how to use this datatype, I wanted to share an example interface with you.

The idea is, that I want to pass a list of variable mappings, containing informations about the variables, to a function block, that can process them.

use-cases:

* communication protocols
* parsers or generators for data exchange formats (XML, JSON, YAML, ...)

~~~
FUNCTION_BLOCK mappingEntry
VAR
	// the type of the actual parameter
    typeclass : __SYSTEM.TYPE_CLASS ;
	// the pointer to the actual parameter
    pvalue : POINTER TO BYTE;
	// the size of the data, to which the pointer points
    diSize : DINT;
END_VAR

METHOD FB_Init: BOOL
VAR_INPUT
    bInitRetains: BOOL; // TRUE: the retain variables are initialized (reset warm / reset cold)
    bInCopyCode: BOOL;  // TRUE: the instance will be copied to the copy code afterward (online change)   
	map : ANY;
END_VAR

typeClass := map.TypeClass;
pValue := map.pValue;
diSize := map.diSize;
~~~

~~~
FUNCTION_BLOCK MyMappingFB
METHOD FB_Init: BOOL
VAR_INPUT
    bInitRetains: BOOL; // TRUE: the retain variables are initialized (reset warm / reset cold)
    bInCopyCode: BOOL;  // TRUE: the instance will be copied to the copy code afterward (online change)   
	mapping : POINTER TO ARRAY [1..0] OF mappingEntry;
	size : DWORD;
END_VAR
~~~

And the usage can then be:
~~~
VAR_GLOBAL
	hugo : DWORD;
	egon : BYTE;
	balder : STRING;
END_VAR
~~~
~~~
PROGRAM PLC_PRG
VAR
	mappingList : ARRAY [0..2] OF mappingEntry[
		(GVL.hugo),
		(GVL.egon),
		(GVL.balder)
	];
	myMappingInst : MyMappingFB(ADR(mappingList), SIZEOF(mappingList) / SIZEOF(mappingEntry));
END_VAR

myMappingInst();
~~~

What you get in your "MyMappingFB" is:

* A list of ANY variables (type: mappingEntry)
* Each member contains info about:
    * Variable Type
    * Size
    * Pointer to the variable</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Ingo</dc:creator><pubDate>Mon, 05 Feb 2024 17:09:15 -0000</pubDate><guid isPermaLink="false">https://forge.codesys.com/tol/iec-snippets/snippets/8/</guid></item><item><title>Logmanager</title><link>https://forge.codesys.com/tol/iec-snippets/snippets/6/</link><description>**LogManager by @lmdejong**

~~~
PROGRAM LogManager
VAR
    POU_ERROR:          BOOL;
    ManagerState:       INT:=0;
    ErrorAtState:       INT:=0;
    State_eError:       FILE.ERROR;

    hFile:              FILE.CAA.HANDLE;
    filop:              FILE.Open;
    filcl:              FILE.Close;
    filwr:              FILE.Write;

    hDir:               FILE.CAA.HANDLE;
    fildop:             FILE.DirOpen;
    fildcl:             FILE.DirClose;
    fildcr:             FILE.DirCreate;

    LOG30:              TBLOG;
    LOG76:              TBLOG;

    LWD:                ARRAY [0..MaxID-1] OF TBTEST;

    sTarget:            STRING;
    sFileName:          STRING;
    TestID:             UINT;
    xFileExists:        BOOL;
    udiFileSize:        UDINT;
    szFileSize1:        FILE.CAA.SIZE := 0;
    udiCmpError:        UDINT;
    j:                  UINT;
    sData:              STRING(255);
    sRaw:               STRING(255);

    sYear:              STRING(4);
    sMonth:             STRING(2);
    sDay:               STRING(2);
    sHour:              STRING(2);
    sMinute:            STRING(2);
    sSecond:            STRING(2);
    sDaySep:            STRING(1) := '-';
    sTimSep:            STRING(1) := ':';

    xTrigLog30:         BOOL;
    xTrigLog76:         BOOL;
END_VAR
VAR CONSTANT
    MaxID:              UINT := 3;
    HeaderNames:        STRING(117) := 'DateTime;TestState;TestPaused;TestCounterA;RunTimeStrokeA;RunTimeTotalA;TestCounterB;RunTimeStrokeB;RunTimeTotalB$R$N';
END_VAR

//Save log section
LOG30.LTT(PT:=gvl_LogUpdateFreq);
LOG76.LTT(PT:=gvl_LogUpdateFreq);

CASE(ManagerState) OF
    0: // Initializing LogManager
        POU_ERROR := FALSE;
        fildop( xExecute:=FALSE);

        IF PLC_R.i_wSdCardStatus &lt;&gt; SEC.PLC_R_SDCARD_STATUS.SDCARD_READWRITE THEN //
            // SDCARD error
            ErrorAtState := ManagerState;
            State_eError := FILE.ERROR.NOT_EXIST;
            ManagerState := 90;
        ELSE
            ManagerState := 10; // Skip folder actions
        END_IF

    10: // SD-card in correct state check if folder exists
        fildop.sDirName := gvl_sLogDir;
        fildop( xExecute:=TRUE);
        IF fildop.xDone THEN
            hDir := fildop.hDir;
            IF NOT fildop.xError THEN
                ManagerState := 11;
            END_IF
        END_IF
        IF fildop.xError THEN 
            //IF fildop.eError = FILE.ERROR.NOT_EXIST THEN
                ManagerState := 15;
            (*ELSE
                State_eError := fildop.eError;
                ErrorAtState := ManagerState;
                ManagerState := 90;
            END_IF*)
        END_IF
        IF fildop.xDone THEN
            fildop( xExecute := FALSE );
        END_IF

    11: // Close directory
        fildcl.hDir := hDir;
        fildcl (xExecute := TRUE);

        IF fildcl.xDone = TRUE THEN
            fildcl (xExecute := FALSE);
            ManagerState := 20;
        END_IF
        IF fildcl.xError = TRUE THEN
            ErrorAtState := ManagerState;
            State_eError := fildcl.eError;
            ManagerState := 90;
        END_IF

    15: // Create directory
        fildcr.sDirName := gvl_sLogDir;
        fildcr( xExecute := TRUE );
        IF fildcr.xDone THEN
            fildcr.xExecute := FALSE;
            ManagerState := 0; // Return to check if file has been made
        END_IF
        IF fildcr.xError = TRUE THEN
            ErrorAtState := ManagerState;
            State_eError := fildcr.eError;
            ManagerState := 90;
        END_IF

    20: // Waiting for event
        // Resetting all FB's
        filop (xExecute := FALSE);
        filcl (xExecute := FALSE);
        filwr (xExecute := FALSE);
        fildop(xExecute := FALSE);
        fildcr(xExecute := FALSE);

        //Setting the timer to be linked to wether the test is active or not
        IF TB30.State = STATE.AUTO THEN
            LOG30.LTT(IN := TRUE);
        ELSE
            LOG30.LTT(IN := FALSE);
        END_IF
        IF TB76.State = STATE.AUTO THEN
            LOG76.LTT(IN := TRUE);
        ELSE
            LOG76.LTT(IN := FALSE);
        END_IF

        //Checking triggers
        IF LOG30.State &lt;&gt; TB30.State OR LOG30.Paused &lt;&gt; TB30.Paused OR LOG30.LTT.Q OR xTrigLog30 THEN
            LWD := TEST_30;

            ManagerState := 30;

            LOG30.State := TB30.State;
            LOG30.Paused := TB30.Paused;

            IF LOG30.LTT.Q THEN
                LOG30.LTT( IN := FALSE);
            END_IF

            IF xTrigLog30 THEN
                xTrigLog30 := FALSE;
            END_IF

        ELSIF LOG76.State &lt;&gt; TB76.State OR LOG76.Paused &lt;&gt; TB76.Paused OR LOG76.LTT.Q OR xTrigLog76 THEN
            LWD := TEST_76;

            ManagerState := 30;

            LOG76.State := TB76.State;
            LOG76.Paused := TB76.Paused;

            IF LOG76.LTT.Q THEN
                LOG76.LTT( IN := FALSE);
            END_IF

            IF xTrigLog76 THEN
                xTrigLog76 := FALSE;
            END_IF

        END_IF

    30: // Write start
        TestID := 0;

        ManagerState := 34;

    34: // Check if enabled and generate name
        IF LWD[TestID].Enabled THEN
            //Generate file name
            IF LWD[TestID].Product = '' THEN
                LWD[TestID].Product := 'UNKNOWN PRODUCT';
            END_IF
            IF LWD[TestID].ProductID = '' THEN
                LWD[TestID].ProductID := CONCAT('UNKNOWN PRODUCT ID ', UINT_TO_STRING(TestID));
            END_IF

            sFileName := CONCAT(LWD[TestID].Product, ' - ');
            sFileName := CONCAT(sFileName, LWD[TestID].ProductID);
            sFileName := CONCAT(sFileName, '.csv');

            sTarget := CONCAT(gvl_sLogDir, sFileName);
            //sTarget  := sFileName;
            //sTarget := 'TestFile.txt';

            ManagerState := 36;
        ELSE
            ManagerState := 70;
        END_IF

    36: // Check if file exists
        udiFileSize := FILE.SysFileGetSize(sTarget, ADR(udiCmpError));

        IF udiFileSize = 0 THEN
            //File doesnt exist or is empty
            //Header should be created
            ManagerState := 40;
        ELSIF udiCmpError = ERR_OK THEN
            //File exists
            //File should be directly opened in Append mode
            ManagerState := 45;
        ELSE
            State_eError := FILE.ERROR.ERROR_UNKNOWN;
            ErrorAtState := ManagerState;
            ManagerState := 90;
        END_IF

    40: // File doenst exist - Create file
        filop.sFileName := sTarget;
        filop.eFileMode := FILE.MODE.MRDWR;
        filop.xExclusive:= TRUE;
        filop (xExecute := TRUE );

        IF filop.xDone THEN
            hFile := filop.hFile;
            ManagerState := 42;
        END_IF

        IF filop.xError THEN
            State_eError := filop.eError;
            ErrorAtState := ManagerState;
            ManagerState := 90;
        END_IF

    42: // New file created - Write header      
        filwr.hFile:=hFile;
        filwr.pBuffer:=ADR(HeaderNames);
        filwr.szSize := INT_TO_USINT(Len(HeaderNames));
        filwr.udiTimeOut:=100000;    (* 100ms Timeout *)
        filwr( xExecute:=TRUE);

        IF filwr.xDone THEN
            ManagerState := 49;
            filwr (xExecute := FALSE);
        END_IF

        IF filwr.xError THEN
            State_eError := filwr.eError;
            ErrorAtState := ManagerState;
            ManagerState := 90;
        END_IF

    45: // File exists open in MAPPD
        filop.sFileName := sTarget;
        filop.eFileMode := FILE.MODE.MAPPD;
        filop (xExecute := TRUE );

        IF filop.xDone THEN
            hFile := filop.hFile;
            ManagerState := 50;
        END_IF

        IF filop.xError THEN
            State_eError := filop.eError;
            ErrorAtState := ManagerState;
            ManagerState := 90;
        END_IF

    49: // Reset write FB
        filwr(xExecute := FALSE);
        IF NOT filwr.xBusy AND NOT filwr.xDone THEN
            ManagerState := 50;
        END_IF 

    50: // Generate data
        sYear := Mid(gvl_DT_string, 4, 4);
        sMonth:= Mid(gvl_DT_string, 2, 9);
        sDay  := Mid(gvl_DT_string, 2, 12);

        sHour := Mid(gvl_DT_string, 2, 15);
        sMinute:=Mid(gvl_DT_string, 2, 18);
        sSecond:=Mid(gvl_DT_string, 2, 21);     

        sRaw := '';
        sRaw := CONCAT(sDay, sDaySep);
        sRaw := CONCAT(sRaw, sMonth);
        sRaw := CONCAT(sRaw, sDaySep);
        sRaw := CONCAT(sRaw, sYear);
        sRaw := CONCAT(sRaw, ' ');
        sRaw := CONCAT(sRaw, sHour);
        sRaw := CONCAT(sRaw, sTimSep);
        sRaw := CONCAT(sRaw, sMinute);
        sRaw := CONCAT(sRaw, sTimSep);
        sRaw := CONCAT(sRaw, sSecond);
        sRaw := CONCAT(sRaw,';');

        sRaw := CONCAT(sRaw,StateText(LWD[TestID].TestState));
        sRaw := CONCAT(sRaw,';');
        sRaw := CONCAT(sRaw,BOOL_TO_STRING(LWD[TestID].TestPaused));
        sRaw := CONCAT(sRaw,';');
        sRaw := CONCAT(sRaw,INT_TO_STRING(LWD[TestID].TestCounterA));
        sRaw := CONCAT(sRaw,';');
        sRaw := CONCAT(sRaw,TIME_TO_STRING(LWD[TestID].RunTimeStrokeA));
        sRaw := CONCAT(sRaw,';');
        sRaw := CONCAT(sRaw,TIME_TO_STRING(LWD[TestID].RunTimeTotalA));
        sRaw := CONCAT(sRaw,';');
        sRaw := CONCAT(sRaw,INT_TO_STRING(LWD[TestID].TestCounterB));
        sRaw := CONCAT(sRaw,';');
        sRaw := CONCAT(sRaw,TIME_TO_STRING(LWD[TestID].RunTimeStrokeB));
        sRaw := CONCAT(sRaw,';');
        sRaw := CONCAT(sRaw,TIME_TO_STRING(LWD[TestID].RunTimeTotalB));
        sData := CONCAT(sRaw,'$R$N');

        ManagerState := 55;

    55: // Write data
        filwr.hFile:=hFile;
        filwr.pBuffer:=ADR(sData);
        filwr.szSize := INT_TO_USINT(Len(sData));
        filwr.udiTimeOut:=100000;    (* 100ms Timeout *)
        filwr( xExecute:=TRUE);

        IF filwr.xDone THEN
            ManagerState := 60;
            filwr (xExecute := FALSE);
        END_IF

        IF filwr.xError THEN
            State_eError := filwr.eError;
            ErrorAtState := ManagerState;
            ManagerState := 90;
        END_IF

    60: // Writing done, close file
        filcl.hFile := hFile;
        filcl( xExecute := TRUE );

        IF filcl.xDone THEN
            ManagerState := 61;
        END_IF

        IF filcl.xError THEN
            State_eError := filcl.eError;
            ErrorAtState := ManagerState;
            ManagerState := 90;
        END_IF

    61: // Round up file writing
        filop(xExecute := FALSE);
        filcl(xExecute := FALSE);
        filwr(xExecute := FALSE);

        IF NOT filop.xBusy AND NOT filop.xDone AND
         NOT filcl.xBusy AND NOT filcl.xDone AND
         NOT filwr.xBusy AND NOT filwr.xDone THEN
            ManagerState := 70;
        END_IF

    70: // Next item state or end cycle
        TestID := TestID + 1;
        IF TestID &lt; MaxID THEN
            ManagerState := 34;
        ELSE
            ManagerState := 20;
        END_IF

    90: // Error state
        POU_ERROR := TRUE;
END_CASE
~~~


</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">aliazzz</dc:creator><pubDate>Mon, 08 Jun 2020 07:08:24 -0000</pubDate><guid isPermaLink="false">https://forge.codesys.com/tol/iec-snippets/snippets/6/</guid></item><item><title>UDP Send and Receive with Net Base Services SP16</title><link>https://forge.codesys.com/tol/iec-snippets/snippets/5/</link><description>Requries Library: NetBaseSrv = Net Base Services, 3.5.16.0 using the Namespace NBS

~~~ST
PROGRAM PLC_PRG
VAR
	//Control
	xStart : BOOL; //Use this to toggle the system ON
	xSend  : BOOL; //Send data one time. needs a false to true transition
	
	//Settings
	uiSendToPort : UINT := 6454;
	uiReceivePort : UINT := 6454;
	theirIP : NBS.IPv4Address := (ipAddress := '10.0.0.10');
	myIP : NBS.IPv4Address := (ipAddress := '10.0.0.9');
	MySendData : ARRAY [0..529] OF BYTE := [TO_BYTE('A'),TO_BYTE('r'),TO_BYTE('t'),TO_BYTE('-'),
                                       TO_BYTE('N'),TO_BYTE('e'),TO_BYTE('t'),00,
                                       00,16#50,0,14,
                                       0,0,0,0,
                                       0,2,
                                       512(0)]; //SOME RANDOM DATA
	NumberOfBytesToSend : UDINT := 20;
	
	//Receive Data
	MyReceiveData : ARRAY [0..529] OF BYTE;
	WhoSentThisData : NBS.IIPAddress;
	
	//Just some function Blocks
	myUDP_Peer : NBS.UDP_Peer;
	myUDP_Send : NBS.UDP_Send;
	myUDP_Receive : NBS.UDP_Receive;
END_VAR
~~~


~~~ST
myUDP_Peer(
	xEnable:= xStart, 
	xBusy=&gt; , 
	xError=&gt; , 
	eErrorID=&gt; , 
	itfAsyncProperty:= , 
	itfIPAddress:= myIP, 
	uiPort:= uiReceivePort, 
	itfMulticast:= , 
	xActive=&gt; , 
	itfPeer=&gt; );

myUDP_Send(
	xExecute:= xSend, 
	udiTimeOut:= 1000000, // 1second timeout
	xDone=&gt; , 
	xBusy=&gt; , 
	xError=&gt; , 
	eErrorID=&gt; , 
	itfTSNContext:= , 
	itfPeer:= myUDP_Peer.itfPeer, 
	itfIPAddress:= theirIP, 
	uiPort:= uiSendToPort, 
	pData:= ADR(MySendData), 
	udiSize:= MIN(NumberOfBytesToSend,SIZEOF(MySendData)), 
	udiCount=&gt; );

xSend := FALSE;
	
myUDP_Receive(
	xEnable:= myUDP_Peer.xActive, 
	xBusy=&gt; , 
	xError=&gt; , 
	eErrorID=&gt; , 
	itfPeer:= myUDP_Peer.itfPeer, 
	pData:= ADR(MyReceiveData), 
	udiSize:= SIZEOF(MyReceiveData), 
	xReady=&gt; , 
	itfIPAddressFrom=&gt; , 
	uiPortFrom=&gt; , 
	udiCount=&gt; );
~~~</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">i-campbell</dc:creator><pubDate>Mon, 20 Dec 2021 12:32:15 -0000</pubDate><guid isPermaLink="false">https://forge.codesys.com/tol/iec-snippets/snippets/5/</guid></item></channel></rss>