Metrics - overview and description

Title in the “Standard metrics” view

Description

Code size

Code size [number of bytes]

Variables size

Variables size [number of bytes]

Stack size

Stack size [number of bytes]

Calls

Number of calls

Tasks

Called in tasks

Globals

Number of global variables used

IOs

Number of address accesses

Locals

Number of local variables

Inputs

Number of input variables

Outputs

Number of output variables

NOS

Number Of Statements (NOS)

Comments

Percentage of comment

McCabe

Complexity (McCabe)

Complexity

Cognitive complexity

DIT

Depth of Inheritance Tree (DIT)

NOC

Number Of Children (NOC)

RFC

Response For Class (RFC)

CBO

Coupling Between Objects (CBO)

Elshof

Complexity of reference (Elshof)

LCOM

Lack of Cohesion Of Methods - LCOM

SFC branches

Number of SFC branches

SFC steps

Number of SFC steps

 

Detailed description

Code size [number of bytes]

Title short form

Code size

Categories

Informative, efficiency

Definition

Number of bytes that a function block contributes to the application code

Further information

The number also depends on the code generator. For example, the code generator for Arm® processors generally generates more bytes than the code generator for x86 processors.

 

Variables size [number of bytes]

Title short form

Variables size

Categories

Informative, efficiency

Definition

Size of the static memory used by the object

Further information

For function blocks, this is the size used for an instance of this function block (which may also contain memory gaps depending on the memory alignment). For programs, functions and global variable lists, this is the sum of the size of all static variables.

Sample:

FUNCTION F_Sample : INT
VAR_INPUT
    a,b   : INT;
END_VAR
VAR
    c,d   : INT;
END_VAR
VAR_STAT
    f,g,h : INT;
END_VAR

The function has three static variables of type INT (f, g, h), each of which requires 2 bytes of memory. F_Sample therefore has a variable size of 6 bytes.

 

Stack size [number of bytes]

Title short form

Stack size

Categories

Informative, efficient, reliable

Definition

Number of bytes required to call a function or function block

Further information

Input variables and output variables are aligned with the memory. This can create a gap between these variables and the local variables. This gap is counted.

Return values from called functions that do not fit into a register are pushed onto the stack. The largest of these values determines the additionally allocated memory, which is also counted. Functions or function blocks that are called within the POUs under consideration have their own stack frame. Therefore, the memory for such calls does not count.

Depending on the code generator used, intermediate results of calculations also use the stack. These results are not counted.

Sample:

FUNCTION F_Sample : INT
VAR_INPUT
    a,b   : INT;
END_VAR
VAR
    c,d,e : INT;
END_VAR
VAR_STAT
    f,g,h : INT;
END_VAR
c := b;
d := a;
e := a + b;

Assumption: The "TwinCAT RT (x86)" solution platform is used for the calculation. The device has a stack alignment of 4 bytes, which can result in gaps between the variables.

The total stack size of F_Sample is 16 bytes and consists of:

VAR_STAT is not stored on the stack and therefore does not increase the stack size of a POU.

 

Number of calls

Title short form

Calls

Categories

Informative

Definition

Number of calls to the program organization unit (POU) within the application

Further information

If a program is called in a task, this call is also counted.

 

Called in tasks

Title short form

Tasks

Categories

Maintainability, reliability

Definition

Number of tasks in which the program organization unit (POU) is called up

Further information

For function blocks, the number of tasks in which the function block itself or any function block in the function block's inheritance tree is called is counted.

For methods and actions, the number of tasks in which the (higher-level) function block is called is displayed.

Sample:

FUNCTION_BLOCK FB1
FUNCTION_BLOCK FB2 EXTENDS FB1
FUNCTION_BLOCK FB3 EXTENDS FB2

Each function block is instantiated and called in a separate program. In addition, each program is called in a separate task.

The metric Called in tasks therefore results:

 

Number of global variables used

Title short form

Globals

Categories

Maintainability, reusability

Definition

Number of different global variables used in the program organization unit (POU)

 

Number of address accesses

Title short form

IOs

Categories

Reusability, maintainability

Definition

Number of address accesses in the implementation of the object

Sample:

PROGRAM MAIN
VAR
    bVar        : BOOL;
    bIn   AT%I* : BOOL;
    bOut  AT%Q* : BOOL;
END_VAR
bVar := TRUE;
bOut := bIn;
bOut := NOT bOut AND bIn;

The number of address accesses for the program MAIN is 5 and is made up of 2 write and 3 read accesses.

 

Number of local variables

Title short form

Locals

Categories

Informative, efficiency

Definition

Number of variables declared in the VAR area of the program organization unit (POU)

Further information

Inherited local variables are not counted.

 

Number of input variables

Number of input variables of the function block (VAR_INPUT).

Title short form

Inputs

Categories

Maintainability, reusability

Definition

Number of variables declared in the VAR_INPUT area of the program organization unit (POU)

Further information

Inherited input variables are not counted.

Standard upper limit for the associated rule SA0166

10

 

Number of output variables

Number of output variables of the function block (VAR_OUTPUT).

Title short form

Outputs

Categories

Maintainability, reusability

Definition

Number of variables declared in the VAR_OUTPUT area of the program organization unit (POU)

Further information

For function blocks, this is the number of user-defined output variables (VAR_OUTPUT). For methods and functions, this is the number of user-defined output variables (VAR_OUTPUT) plus one if they have a return value. The return value is also counted.

Inherited output variables are not counted.

A high number of output variables is a sign of a violation of the principle of clear responsibility.

Standard upper limit for the associated rule SA0166

10

Sample:

METHOD METH : BOOL
VAR_OUTPUT
    a : INT;
    b : LREAL;
END_VAR

The method METH has three outputs:

METHOD METH1
VAR_OUTPUT
    a : ARRAY[0..10] OF INT;
    b : LREAL;
END_VAR

The method METH1 has two outputs:

 

Number Of Statements (NOS)

Title short form

NOS

Categories

Informative

Definition

Number of executable statements in the implementation of a function block, function or method

Further information

NOS = Number Of executable Statements

Statements in the declaration, empty statements or pragmas are not counted.

Sample:

FUNCTION_BLOCK FB_Sample
VAR_OUTPUT
    nTest  : INT;
    i      : INT;
END_VAR
VAR
    bVar   : BOOL;
    c      : INT := 100;    // statements in the declaration are not counted
END_VAR
IF bVar THEN                //if statement: +1
    nTest := 0;             // +1
END_IF
 
WHILE nTest = 1 DO          //while statement: +1
    ;                       // empty statements do not add to the statement count
END_WHILE
 
FOR c := 0 TO 10 BY 2 DO    //for statement: +1
    i := i+i;               // +1
END_FOR
 
{text 'simple text pragma'} //pragmas are not counted
nTest := 2;                 //+1

The sample has six statements.

 

Percentage of comment

Title short form

Comments

Categories

Maintainability

Definition

Percentage of comments in the source code

This number is calculated using the following formula:

Percentage = 100 * <letters in comments> / <letters in source code and comments together>

Further information

Multiple consecutive spaces in the source code are counted as one space, which prevents a high weighting of indented source code. A percentage of 0 is returned for empty objects (no source code and no comments).

The statements also include declaration statements, for example.

 

Complexity (McCabe)

Title short form

McCabe

Categories

Testability

Definition

Number of binary branches in the control flow of the POU

(for example, the number of branches for IF and CASE statements and loops)

Further information

McCabe's cyclomatic complexity is a measure of the readability and testability of source code. It is calculated by counting the number of binary branches in the control flow of the POU. Cyclomatic complexity penalizes high branching because high branching increases the number of test cases required for high test coverage.

Recommended upper limit

10

The following samples show how complexity is calculated according to McCabe.

Sample: IF statement

// every POU has an initial cyclomatic complexity of 1, since it has at least 1 branch
IF b1 THEN                // +1 for the THEN branch
    ;
ELSIF b2 THEN             // +1 for the THEN branch of the IF inside the ELSE
    ;
ELSE
    IF b3 OR b4 THEN      // +1 for the THEN branch
        ;
    END_IF
END_IF

The code snippet has a cyclomatic complexity of 4.

Sample: CASE statement

// every POU has an initial cyclomatic complexity of 1, since it has at least 1 branch
CASE a OF
    1:     ;              // +1
    2:     ;              // +1
    3,4,5: ;              // +1
ELSE                      // the ELSE statement does not increase the cyclomatic complexity
    ;
END_CASE

The code snippet has a cyclomatic complexity of 4.

Sample: Loop statement

// every POU has an initial cyclomatic complexity of 1, since it has at least 1 branch
WHILE b1 DO               // +1 for the WHILE loop
    ;
END_WHILE
 
REPEAT                    // +1 for the REPEAT loop
    ;
UNTIL b2
END_REPEAT
 
FOR a := 0 TO 100 BY 2 DO // +1 for the REPEAT loop
    ;
END_FOR

The code snippet has a cyclomatic complexity of 4.

Sample: Other statements

The following statements also lead to an increase in cyclomatic complexity:

FUNCTION FUN : STRING
VAR_INPUT
    bReturn  : BOOL;
    bJump    : BOOL;
END_VAR
// every POU has an initial cyclomatic complexity of 1, since it has at least 1 branch
JMP(bJump) lbl;           //Conditional jumps increase the cyclomatic complexity by 1
 
FUN := 'u';
RETURN(condition_return); //Conditional returns increase the cyclomatic complexity by 1, too
 
lbl:
    FUN := 't';

The code snippet has a cyclomatic complexity of 3.

 

Cognitive complexity

Title short form

Cognitive complexity

Categories

Maintainability

Definition

Sum of partial complexities resulting, for example, from branches in the control flow of the POU and from sophisticated Boolean expressions

Further information

Cognitive complexity is a measure of the readability and comprehensibility of source code introduced by Sonarsource™ in 2016. It penalizes a strong nesting of the control flow and sophisticated Boolean expressions. The cognitive complexity is only calculated for structured text implementations.

Standard upper limit for the associated rule SA0178

20

Metrics - overview and description 1:

Tip

You can also use Command 'Show cognitive complexity for current editor' to display the increments for Structured Text directly in the editor.

The following samples show how cognitive complexity is calculated.

Sample: Control flow

Statements that manipulate the control flow increase the cognitive complexity by 1.

IF TRUE THEN              //+1 cognitive complexity
    ;
END_IF
 
WHILE TRUE DO             //+1 cognitive complexity
    ;
END_WHILE
 
FOR i := 0 TO 10 BY 1 DO  //+1 cognitive complexity
    ;
END_FOR
 
REPEAT                    //+1 cognitive complexity
    ;
UNTIL TRUE
END_REPEAT

The code snippet has a cognitive complexity of 4.

 

Sample: Nesting of the control flow

When nesting the control flow, an increment of 1 is added for each level of nesting.

IF TRUE THEN                      //+1 cognitive complexity
    WHILE TRUE DO                 //+2 (+1 for the loop itself, +1 for the nesting inside the IF)
        FOR i := 0 TO 10 BY 1 DO  //+3 (+1 for the FOR loop itself, +2 for the nesting inside the WHILE and the IF)
            ;
        END_FOR
    END_WHILE

    REPEAT                        //+2 (+1 for the loop itself, +1 for the nesting inside the IF)
        ;
    UNTIL TRUE
    END_REPEAT
END_IF

The code snippet has a cognitive complexity of 8.

 

Sample: Boolean expression

Since Boolean expressions play a major role in understanding source code, they are also taken into account when calculating cognitive complexity.

Understanding Boolean expressions that are connected with the same Boolean operator is not as difficult as understanding a Boolean expression that contains alternating Boolean operators. Therefore, each chain of equal Boolean operators in an expression increases the cognitive complexity.


b := b1;                             //+0: a simple expression, containing no operators, has no increment

The simple expression without operator has an increment of 0.

 

b := b1 AND b2;                      //+1: one chain of AND operators

The expression with an AND operation has an increment of 1.

 


b := b1 AND b2 AND b3;               //+1: one more AND, but the number of chains of operators does not change

The expression has an AND more. But since it is the same operator, the number of chains formed with identical operators does not change.

 

b := b1 AND b2 OR b3;                //+2: one chain of AND operators and one chain of OR operators

The expression has a chain of AND operators and a chain of OR operators. This results in an increment of 2.

 


b := b1 AND b2 OR b3 AND b4 AND b5;  //+3: one chain of AND operators, one chain of OR operators and another chain of AND operators

The code snippet has an increment of 3.

 


b := b1 AND NOT b2 AND b3;           //+1: the unary NOT operator is not considered in the cognitive complexity

The unary operator NOT is not taken into account in the cognitive complexity.

 

Sample: Further statements with increment

Structured Text has additional statements and expressions that change the control flow.

The following statements are penalized with an increment in cognitive complexity:

aNewLabel:
    x := MUX(i, a,b,c);  //+1 for MUX operator
    y := SEL(b, i,j);    //+1 for SEL operator
JMP aNewLabel;           //+1 for JMP to label

EXIT and RETURN statements do not increase cognitive complexity.

 

Depth of Inheritance Tree (DIT)

Title short form

DIT

Categories

Maintainability

Definition

Number of inheritances until a function block is reached that does not extend any other function block

Further information

DIT = Depth of Inheritance Tree

 

Sample:

FUNCTION_BLOCK FB_Base
FUNCTION_BLOCK FB_Sub EXTENDS FB_Base
FUNCTION_BLOCK FB_SubSub EXTENDS FB_Sub

The metric Depth of Inheritance Tree is:

 

Number Of Children (NOC)

Title short form

NOC

Categories

Reusability, maintainability

Definition

Number of function blocks that extend the given basic function block. Function blocks that indirectly extend a basic function block are not counted.

Further information

NOC = Number Of Children

 

Sample:

FUNCTION_BLOCK FB_Base
FUNCTION_BLOCK FB_Sub EXTENDS FB_Base
FUNCTION_BLOCK FB_SubSub1 EXTENDS FB_Sub
FUNCTION_BLOCK FB_SubSub2 EXTENDS FB_Sub

The metric Number Of Children is:

 

Response For Class (RFC)

Title short form

RFC

Categories

Maintainability, reusability

Definition

Number of different POUs, methods or actions that can be called by a POU

Further information

RFC = Response For Class

The value is used for measuring the complexity (in terms of testability and maintainability). All possible direct and indirect method calls can be reached via associations are taken into account. These can be used to respond to an incoming message or to respond to an event that has occurred.

Sample:

Function block FB1:

FUNCTION_BLOCK FB1
VAR
    d,x,y : INT;
END_VAR
x := METH(d+10);
y := FUN(42, 0.815);

Method FB1.METH:

METHOD METH : INT
VAR_INPUT
    i     : INT;
END_VAR
METH := FUN(CUBE(i), 3.1415);

Function Cube:

FUNCTION CUBE : INT
VAR_INPUT
    i     : INT;
END_VAR
CUBE := i*i*i;

Function FUN:

FUNCTION FUN : INT
VAR_INPUT
    a     : INT;
    f     : LREAL;
END_VAR
FUN := LREAL_TO_INT(f*10)*a;

 

Coupling Between Objects (CBO)

Title short form

CBO

Categories

Maintainability, reusability

Definition

Number of additional function blocks that are instantiated and used in a function block

Further information

CBO = Coupling Between Objects

A function block with a high level of coupling between objects is likely to be involved in many different tasks and therefore violates the principle of clear responsibility.

Standard upper limit for the associated rule SA0179

30

Sample:

FUNCTION_BLOCK FB_Base
VAR
    fb3  : FB3;  // +1 instantiated here
END_VAR
FUNCTION_BLOCK FB_Sub EXTENDS FB_Base   // +0 for EXTENDS
VAR
    fb1  : FB1;  // +1: instantiated here
    fb2  : FB2;  // +1: instantiated here
END_VAR
fb3();           // +0: instantiated in FB_Base, no increment for call

 

Complexity of reference (Elshof)

Complexity of reference = referenced data (number of variables) / number of data references

Title short form

Elshof

Categories

Efficiency, maintainability, reusability

Definition

Complexity of the data flow of a POU

The complexity of reference is calculated using the following formula:

<Number of variables used> / <Number of variable accesses>

Further information

Only variable accesses in the implementation part of the POU are taken into account.

Sample:

PROGRAM MAIN
VAR
    i, j  : INT;
    k     : INT := GVL.m;
    b, c  : BOOL;
    fb    : FB_Sample;
END_VAR
fb(paramA := b);   // +3 accesses (fb, paramA and b)
i := j;            // +2 accesses (i and j)
j := GVL.d;        // +2 accesses (j and GVL.d)

For the metric Complexity of reference (Elshof), MAIN:

Attention:

 

Lack of Cohesion Of Methods - LCOM

Title short form

LCOM

Categories

Maintainability, reusability

Definition

Cohesion = pairs of methods without common instance variables minus pairs of methods with common instance variables

The metric is calculated using the following formula:

MAX(0, <number of object pairs without cohesion> - <number of object pairs with cohesion> )

Further information

LCOM: Lack of Cohesion of Methods

The cohesion between function blocks, their actions, transitions and methods describes whether they access the same variables.

The lack of cohesion of methods describes how strongly the objects of a function block are connected to each other. The lower the lack of cohesion, the stronger the connection between the objects.

Function blocks with a high lack of cohesion are likely to be involved in many different tasks and therefore violate the principle of unambiguous responsibility.

Sample:

Function block FB:

FUNCTION_BLOCK FB
VAR_INPUT
    a    : BOOL;
END_VAR
VAR
    i,b  : BOOL;
END_VAR

Action FB.ACT:

i := FALSE;

Method FB.METH:

METHOD METH : BOOL
VAR_INPUT
    c    : BOOL;
END_VAR
METH := c;
i := TRUE;

Method FB.METH2:

METHOD METH2 : INT
VAR_INPUT
END_VAR
METH2 := SEL(b,3,4);

For the metric Lack of Cohesion Of Methods (LCOM) , the result for FB:

 

Number of SFC branches

Title short form

SFC branches

Categories

Testability, maintainability

Definition

Number of alternative and parallel branches of a POU of the implementation language SFC (Sequential Function Chart)

Sample:

Metrics - overview and description 2:

The above code snippet in SFC has 4 branches: 3 alternative and 1 parallel branch.

 

Number of SFC steps

If the function block is implemented in Sequential Function Chart (SFC), this code metric indicates the number of steps in the function block.

Title short form

SFC steps

Categories

Maintainability

Definition

Number of steps in a POU of the implementation language SFC (Sequential Function Chart)

Further information

Only the steps contained in the POU programmed in SFC are counted. Steps in the implementations of actions or transitions called in POUs are not counted.

Sample:

Metrics - overview and description 3:

The above code snippet in SFC has 10 steps.

 

 

 

Metrics that are available in TwinCAT versions < 3.1.4026.14:

Column abbreviation in Standard Metrics view

Description

Prather

Complexity of nesting (Prather)

n1 (Halstead)

Halstead – number of different used operators (n1)

N1 (Halstead)

Halstead – number of operators (N1)

n2 (Halstead)

Halstead – number of different used operands (n2)

N2 (Halstead)

Halstead – number of operands (N2)

HL (Halstead)

Halstead – length (HL)

HV (Halstead)

Halstead – volume (HV)

D (Halstead)

Halstead – difficulty (D)

Complexity of nesting (Prather)

Nesting weight = statements * nesting depth

Complexity of nesting = nesting weight / number statements

Nesting through IF/ELSEIF or CASE/ELSE statements, for example.

Halstead (n1, N1, n2, N2, HL, HV, D)

The following metrics are part of the "Halstead" range:

- Number of different used operators - Halstead (n1)

- Number of operators - Halstead (N1)

- Number of different used operands - Halstead (n2)

- Number of operands - Halstead (N2)

- Length - Halstead (HL)

- Volume - Halstead (HV)

- Difficulty - Halstead (D)

 

Background information:

 

For each program the following basic parameters are formed:

 

These parameters are used to calculate the Halstead length (HL) and Halstead volume (HV):

 

Various key figures are calculated from the basic parameters:

The key figures usually match the actual measured values very well. The disadvantage is that the method only applies to individual functions and only measures lexical/textual complexity.