TwinCAT Debug function



TwinCAT PLC Debug Function

(Using System Check function)

[前言]
TwinCAT PLC提供 System Check Function , 在 PLC 專案中引用  “CheckBounds,” “CheckDivByte,” 等Check Function時,編譯器會在相關聯運算時自動將該函式一同引入作編譯,以達到先設法避免TwinCAT PLC 出現執行過程的出錯;或避免執行過程變數超出定義邊界造成記憶體重疊,進而利於協助開發人員定位出錯誤碼的位置並修正。

[如何使用System Check function]

* 在TwinCAT 2的環境,宣告POU 名為"Checkxxx" 的Function 例如 CheckBound ; CheckDivByte ; CheckDivReal ;CheckDivWord ; CheckDivDWord ...
https://infosys.beckhoff.com/content/1033/tcplccontrol/html/tcplcctrl_checkbounds.htm?id=4661841464054853992
並依說明檔之範例定義VAR_INPUT & VAR_OUTPUT 層級變數,並將範例程式複製到函式的程式碼區塊中即可,不需特定自行呼叫,編譯器檢查到有此名稱之POU函數,即會自動在該運算時加入Check功能

[Foreword]

TwinCAT provides system check functions in the TwinCAT PLC compiler. When a PLC project includes POU, “CheckBounds,” “CheckDivByte,” etc., the PLC compiler produces additional program code internally in certain conditions, such as index variable access or division operation, and prevents the system from crashing or memory overlapping.

[How to use system function]

l   In TwinCAT2, it simply declares “Checkxxx function” in a PLC project. After copying the sample code and compiling, “Checkxxx function” will be embedded internally in program code.

l   In TwinCAT3, it can be directly activated by Add>POU for implicit checks in PLC project.

l   Do not change the declaration part. To maintain the functionality of the monitoring functions, the declaration part must not be modified. The only exception is to add local variables.

l   Check function may cause increased system load. Please use Check function only for test purposes.

[TwinCAT 2]

[Sample for CheckBounds]

The “CheckBounds” function is only performed with a variable array index. A consistently faulty array index results in a compiler error. TwinCAT calls the function implicitly as soon as an ARRAY variable is assigned values.

Caution! This is an incorrect sample code. Run this code will cause system no response or crash!


PROGRAM
MAIN

VAR

iData                : ARRAY [0..9] OF INT;

diData              : DINT;

i                       : INT;

bTest                : BOOL;

END_VAR

IF bTest THEN

            FOR i := 0 TO 15 BY 1 DO

                        iData[i] := i-1;

            END_FOR

            bTest := FALSE;

END_IF

In this sample, “i” exceeds the variable index range, which makes the value of “diData” unpredictable*1. Index variable “i” not only creates an infinite loop*2 but consumes high real-time resource and makes no response from PLC.



To avoid index exceeding the boundary, “CheckBounds” function could be used by simply putting in the PLC project as bellow.


FUNCTION CheckBounds : DINT

VAR_INPUT

    index, lower, upper: DINT;

END_VAR

IF index < lower THEN

    CheckBounds := lower;

ELSIF index > upper THEN

    CheckBounds := upper;

ELSE

    CheckBounds := index;

END_IF

 

After putting this function in a project, the check code generated by the function avoids index exceeding the variable boundary and memory overlap.


However, the correctness of data is a big issue. In the example, the value of “iData[9]” is incorrect. Besides avoiding memory overlap, programmers require to modify the incorrect code. To detect the incorrect situation, “ADSLOGDINT”* could be applied to display the error or warning messages depending on the setting. In this sample, several windows pop up when the index is out of boundary.

*Note: ADSLOGDINT is illustrated in the Appendix of this document.



FUNCTION CheckBounds : DINT

VAR_INPUT

            index, lower, upper: DINT;

END_VAR

VAR

            sMessage: STRING;

            diData: DINT;

END_VAR

IF index < lower THEN

            CheckBounds := lower;

            sMessage := 'Out of boundary! Lower boundary = ';

            sMessage := CONCAT(sMessage , DINT_TO_STRING(lower));

            sMessage := CONCAT(sMessage , '. Upper boundary = ');

            sMessage := CONCAT(sMessage , DINT_TO_STRING(upper));

            sMessage := CONCAT(sMessage , '. Current Index = %d');

            diData := ADSLOGDINT(msgCtrlMask := ADSLOG_MSGTYPE_LOG, msgFmtStr := sMessage , dintArg := index);

ELSIF index > upper THEN

            CheckBounds := upper;

            sMessage := 'Out of boundary! Lower boundary = ';

            sMessage := CONCAT(sMessage , DINT_TO_STRING(lower));

            sMessage := CONCAT(sMessage , '. Upper boundary = ');

            sMessage := CONCAT(sMessage , DINT_TO_STRING(upper));

            sMessage := CONCAT(sMessage , '. Current Index = %d');

            diData := ADSLOGDINT(msgCtrlMask := ADSLOG_MSGTYPE_LOG, msgFmtStr := sMessage , dintArg := index);

 

ELSE

            CheckBounds := index;

END_IF;

Result:

The log can be display on “Logger output” in System manager.



1.    The log can also be recorded on “Event Viewer” in Windows. (Event Viewer is not included in CE system.)



2.    For CE system, check “Enable Log File,” set size and path in the tab of “TwinCAT settings” in “CX configuration” in control panel. The log file named “tcsyslog” is generated in the assigned folder.


Sample log format

[Sample for CheckDivxxxx]

The functions “CheckDivByte, CheckDivReal, CheckDivWord and CheckDivDword” can be used to avoid division by 0. If you integrate these functions in the PLC project, they are called before each division that occurs in the code. It is recommended that all of these “CheckDivxxxx” functions are applied in the program due to different data types.

In the sample below, the divisor is 0(799/0 =?). The divisor will be set to 1 by “CheckDivReal” function before the compiler goes wrong. Therefore, the result is 799(799/1 = 799). User can also detect the situation in the log simply.

PROGRAM       MAIN

VAR

            bTest                : BOOL;

 

            fResult   : REAL;

            fDivident : REAL := 799;

            fDivisor  : REAL := 0;

END_VAR

FUNCTION CheckDivReal : REAL

VAR_INPUT

            divisor : REAL;

END_VAR

 

VAR

            sMessage: STRING;

            diData: DINT;

END_VAR

IF bTest THEN

 

            fResult := fDivident / fDivisor;

            bTest := FALSE;

 

END_IF;

IF divisor = 0 THEN

            CheckDivReal := 1;

 

            sMessage := 'The divisor is %d! ';

            diData := ADSLOGDINT(msgCtrlMask :=  ADSLOG_MSGTYPE_LOG, msgFmtStr := sMessage , dintArg := 0 );

 

ELSE

            CheckDivReal := divisor;

END_IF;

Result:

The log can be display on “Logger output” in System manager.


[Sample for CheckRangexxxx]

In order to check for observance of range boundaries at runtime, the functions “CheckRangeSigned” and “CheckRangeUnsigned” must be introduced. They are implicitly called as soon as a variable is written as belonging to a subrange type constructed from a signed or unsigned type.

In the following example, variable “y” is initially set to 10000. If “bCheckRange” is true, the variable “Value” is limited to 4095. However, when “bCheckRange” is false, the variable “Value” is 10000. According to the example, the “CheckRangexxxx” function is required to be called when variables are needed to be checked whether they are in the subranges.

Caution: If a function CheckRangeSigned or CheckRangeUnsigned is implemented, a continuous loop is created with the use of subrange in a FOR loop. This occurs exactly if the area indicated for the FOR loop is as large or larger than that for the subrange.

PROGRAM       MAIN

VAR

            (* Variables for CheckRange function *)

            Value    : DINT;

            y          : DINT := 10000;

            bCheckRange: BOOL;

 

END_VAR

FUNCTION CheckRangeSigned : DINT

VAR_INPUT

            value, lower, upper: DINT;

END_VAR

VAR

            sMessage: STRING;

            diData: DINT;

END_VAR

(* CheckRange *)

 

IF bCheckRange THEN

Value := CheckRangeSigned(y, -4095, 4095);

ELSE

Value := y;

END_IF;

IF (value < lower) THEN

            CheckRangeSigned := lower;

 

            sMessage := 'Out of range! Lower range = ';

            sMessage := CONCAT(sMessage , DINT_TO_STRING(lower));

            sMessage := CONCAT(sMessage , '. Upper range = ');

            sMessage := CONCAT(sMessage , DINT_TO_STRING(upper));

            sMessage := CONCAT(sMessage , '. Current value = %d');

            diData := ADSLOGDINT(msgCtrlMask :=  ADSLOG_MSGTYPE_LOG, msgFmtStr := sMessage , dintArg := value);

 

ELSIF(value > upper) THEN

            CheckRangeSigned := upper;

 

            sMessage := 'Out of range! Lower range = ';

            sMessage := CONCAT(sMessage , DINT_TO_STRING(lower));

            sMessage := CONCAT(sMessage , '. Upper range = ');

            sMessage := CONCAT(sMessage , DINT_TO_STRING(upper));

            sMessage := CONCAT(sMessage , '. Current value = %d');

            diData := ADSLOGDINT(msgCtrlMask := ADSLOG_MSGTYPE_LOG, msgFmtStr := sMessage , dintArg := value);

 

ELSE

            CheckRangeSigned := value;

END_IF;

Result:

The log can be display on “Logger output” in System manager.

[TwinCAT 3]

It is more user-friendly to apply “Check functions” in the program in TwinCAT3. Right click on POUs à Select Add à Select POU for implicit checks… à Check the functions needed. The standard functions are included in the program automatically. 




        

[Appendix]

[ADSFUNCTION ADSLOGDINT]

This function issues when called a message box holding a specifiable text on the screen, and writes an entry into the system’s log. Using the ADSLOGDINT function, a DINT value (4 byte signed integer) can be inserted in the text to be output at a point specified by the user. For this purpose the stored format string must contain the characters ‘%d’ at the desired location. The result value contains the function error code, or, if successful, 0.

msgCtrlMask : Control mask which determines the type and effect of the message output (see separate table). “OR” can be used for the desired combination.

Constant

Description

ADSLOG_MSGTYPE_HINT

Message type is advice.

ADSLOG_MSGTYPE_WARN

Message type is warning.

ADSLOG_MSGTYPE_ERROR

Message type is error.

ADSLOG_MSGTYPE_LOG

Message is written into the log.

ADSLOG_MSGTYPE_MSGBOX

Message is output to a message box.

ADSLOG_MSGTYPE_STRING

Message is a directly given string (default).

msgFmtStr : Contains the message to be issued. It can contain the formatting code ‘%d’ for the output of a DINT value at any position.

dintArg : Contains the numerical value to be inserted into the message.

https://infosys.beckhoff.com/english.php?content=../content/1033/tcplclibsystem/html/tcplclibsys_adslogdint.htm&id=4109655218661787585


留言

這個網誌中的熱門文章

[補充資料] EtherCAT Slave 定址 & Protocol 解析

Access TwinCAT EtherCAT CoE / SoE Object via ADS

ESC SII usage memo(Load Fail)