Skip to main content

指标

提供的指标的详细描述 CODESYS Static Analysis

提示

代码大小, 尺寸可变, 堆栈大小, 和 通话次数 仅报告项目中集成的库中的 POU 的指标。

指标:代码大小(字节数)

类别: 信息丰富、效率

功能块为应用程序代码贡献的字节数

该数量还取决于代码生成器。例如,ARM 处理器的代码生成器通常比 x86 处理器的代码生成器生成更多字节。

指标:可变大小(字节数)

类别:信息丰富、高效

对象使用的静态内存的大小

对于功能块,这是用于功能块实例的大小(可能包括内存间隙,具体取决于内存对齐)。对于程序、函数和全局变量列表,这是所有静态变量的大小之和。

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

该函数有 3 个类型的静态变量 INT (f, g, 和 h),每个需要2个字节的内存。因此, FUN1 具有 6 个字节的可变大小。

指标:堆栈大小(字节数)

类别:信息丰富、高效、可靠

调用函数或功能块所需的字节数

输入变量和输出变量与内存对齐。这可能会在这些变量和局部变量之间产生间隙。这个差距是被计算在内的。

不适合寄存器的被调用函数的返回值将被压入堆栈。这些值中的最大值决定了额外分配的内存,这也很重要。在所考虑的 POU 内调用的函数或函数块有自己的堆栈帧。因此,此类调用的内存不算在内。

根据所使用的代码生成器,计算的中间结果也使用堆栈。这些结果不计算在内。

132. 例子
//Declaration
FUNCTION FUN1 : INT
VAR_INPUT
    a,b : INT;
END_VAR
VAR
    c,d,e : INT;
END_VAR
VAR_STAT
    f,g,h : INT;
END_VAR

//Implementation
c := b;
d := a;
e := a+b;

假设:对于计算,假设 CODESYS Control Win 它使用 x86 代码生成器。

上面的示例的调用者大小为 8 个字节:两个为 4 个字节 INT inputs 和 4 个字节的返回值。该设备的堆栈对齐为 4 字节,因此存在 2 字节的间隙。调用者大小为 8 个字节:三个局部变量,每个变量 2 个字节,加上用于堆栈对齐的 2 个字节间隙。结果,总堆栈大小为 FUN1 是16字节。

VAR_STAT 不存储在堆栈中,因此不会增加 POU 的堆栈大小。



指标:调用次数(Calls)

类别:信息丰富

下 POU 的调用次数 节目单位

133. 例子
//Declaration PLC_PRG
PROGRAM PLC_PRG
VAR
    myFB : FB1;
END_VAR

//Implementation
myFB(b := FALSE);
//Declaration FB1
FUNCTION_BLOCK FB1
VAR_INPUT
    b : BOOL;
END_VAR
VAR
    i : INT;
END_VAR

//Implementation
METH(i);
IF b THEN
    METH(i*i);
END_IF
//Declaration FB1.METH
METHOD METH : BOOL
VAR_INPUT
    i : INT;
END_VAR

//Implementation
METH := i >= 42;

如果 PLC_PRG 在一个任务中被调用,那么这个调用也被统计在内。

FB1 仅有一个调用(在 PLC_PRG)。

METH 有两个电话,都在 FB1



指标:任务(Tasks)的调用次数

类别:可维护性、可靠性

任务数量(任务)其中指定的 POU 节目单位 叫做

对于功能块,计算调用功能块本身或功能块继承树中的任何功能块的任务数量。

对于方法和操作,会显示调用(父)功能块的任务数。

134. 例子
FUNCTION_BLOCK FB
//...
FUNCTION_BLOCK FB2 EXTENDS FB
//...
FUNCTION_BLOCK FB3 EXTENDS FB2
//...

每个功能块都有自己的调用 PROGRAM。每个 PROGRAM 有它自己的任务。

在任务中调用 指标返回 1 为 FB3 和 2 为 FB2 因为来自的电话 FB3FB2 被计算在内。该指标结果为 3 FB 因为在这种情况下,来自的电话 FB3, FB2, 和 FB 被计算在内。



指标:使用的全局变量的数量(Globals)

类别:可维护性、可重用性

下 POU 中使用的全局变量数量 节目单位

135. 例子
//GVL
VAR_GLOBAL
    gvla : INT;
    gvlb : INT;
    gvlc : INT;
END_VAR
/PRG declaration
PROGRAM PRG
VAR
    x : INT := GVL.gvlc;
    y : INT;
END_VAR

//PRG implementation
x := GVL.gvla;
y := GVL.gvla*GVL.gvlb;

PRG 程序使用 3 个变量 GVL: gvla, gvlb, 和 gvlc



指标:直接地址访问 (IO) 数量

类别:可重用性、可维护性

直接地址访问次数(IO)在对象的实现中。

136. 例子
//Declaration
PROGRAM PRG
VAR
    xVar : BOOL:= %IX0.0; // +1 direct address access
    byVar : BYTE;
END_VAR

//Implementation
xVar := %IX0.0; // +1 direct address access
%QX0.0 := xVar; // +1
%MX0.1 := xVar; // +1
%MB1 := byVar; // +1

该示例有 5 个直接地址访问。



指标:局部变量(Locals)的数量

类别:信息丰富、高效

中声明的变量数量 VAR POU 的区域。继承的变量不计算在内。

137. 例子
//Declaration
FUNCTION_BLOCK FB
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
    i,j,k,l : INT;
    m,n,o : BOOL;
END_VAR

在功能块中,声明了7个局部变量。



指标:输入变量的数量(输入)

类别:可维护性、可重用性

对应的默认上限 SA0166 规则:10

声明的变量数量 VAR_INPUT 程序单元的。继承的输入变量不计算在内。

138. 例子
FUNCTION_BLOCK FB
VAR_INPUT
    i : INT;
    r : REAL;
END_VAR

在功能块中,声明了 2 个输入变量: ir

METHOD METH : BOOL
VAR_INPUT
    j : INT;
    l : LREAL;
END_VAR

该方法有 2 个输入: jl



指标:输出变量(输出)的数量

类别:可维护性、可重用性

对应的默认上限 SA0166 规则:10

中的变量数量 VAR_OUTPUT 节目单元的

对于功能块,这是自定义输出变量的数量(VAR_OUTPUT)。对于方法和函数,这是自定义输出变量的数量加一(如果它们有返回值)。返回值也被计算在内。继承的输出变量不计算在内。

大量输出变量表明违反了唯一责任原则。

139. 例子
FUNCTION_BLOCK FB
VAR_OUTPUT
    i : INT;    // +1 output
    r : REAL;   // +1 output
END_VAR

该功能块有 2 个输出变量: ir

METHOD METH : BOOL
VAR_INPUT
    j : INT;
    l : LREAL;
END_VAR

该方法有 3 个输出: METH, j, 和 l

METHOD METH1                    // +0 outputs (no return type)
VAR_OUTPUT
    ar : ARRAY[0..10] OF INT;   // +1 output
    l : LREAL;                  // +1 output
END_VAR

METH1 方法有 2 个输出: ari



指标:NOS – 语句数量

类别:信息丰富

功能块、函数或方法的实现中的语句数

声明中的语句、空语句或编译指示不计算在内。

140. 例子
//Declaration:
FUNCTION POU : BOOL
VAR_INPUT
END_VAR
VAR
    c : INT := 100; // statements in the declaration are not counted
END_VAR
VAR_OUTPUT
    test : INT;
    i : INT;
END_VAR

//Implementation:
IF TRUE THEN //if statement: +1
    test := 0; // +1
END_IF

WHILE test = 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
test := 2; //+1

该示例有 6 个语句。



指标:评论的百分比

类别:可维护性

源代码中注释的百分比

该数字根据以下公式计算:

百分比 = 100 * <注释中的字符数> / <源代码中的字符数与注释中的字符数之和>

源代码中连续的多个空格会被算作一个空格,这样可以防止源代码缩进的权重过高。对于空对象(没有源代码和注释),返回 0 的百分比。

141. 例子

声明部分:

FUNCTION_BLOCK FB //comments in the declaration are counted, as well
VAR_TEMP
    hugo : INT;
END_VAR

执行:

hugo := hugo + 1;
//Declaration: 40 letters non comment; 50 letters comment
//Implementation: 13 letters non comment; 152 letters comment
// 100 * 202 / 255 -> 79% comments

百分比 100 * 202 / 255 的计算返回 79%。



指标:复杂性 (McCabe)

类别:可测试性

建议上限:10

McCabe 认为,圈复杂度是源代码可读性和可测试性的衡量标准。它是通过计算 POU 控制流中二进制分支的数量来计算的。然而,圈复杂度会损害高分支,因为高分支会增加高测试覆盖率所需的测试用例数量。

142. 例子: IF 陈述
// 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

该代码片段的圈复杂度为 4。



143. 例子: CASE 陈述
// 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

该代码片段的圈复杂度为 4。



144. 示例:循环语句
// 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

该代码片段的圈复杂度为 4。



145. 示例:其他陈述

以下语句也会增加圈复杂度:

//Declaration
FUNCTION FUN : STRING
VAR_INPUT
    condition_return : BOOL;
    condition_jmp : BOOL;
END_VAR
VAR
END_VAR

//Implementation
// every POU has an initial cyclomatic complexity of 1, since it has at least 1 branch
JMP(condition_jmp) 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';

该代码片段的圈复杂度为 3。



指标:认知复杂性

类别:可维护性

对应SA0178规则的默认上限:20

认知复杂性是 Sonarsource™ 于 2016 年引入的源代码可读性和可理解性的衡量标准。然而,它会惩罚控制流和复杂布尔表达式的大量嵌套。仅针对结构化文本实现计算认知复杂度。

以下示例显示了如何计算认知复杂性。

提示

显示当前编辑者的认知复杂性 命令可用于另外显示结构化文本的增量。

146. 示例:控制流程

操纵控制流的语句将认知复杂度增加 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

该代码片段的认知复杂度为 4。



147. 示例:控制流的嵌套

嵌套控制流时,每层嵌套增量加1。

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

该代码片段的认知复杂度为 8。



148. 示例:布尔表达式

由于布尔表达式在理解源代码方面发挥着重要作用,因此在计算认知复杂性时也会考虑它们。

理解与同一布尔运算符关联的布尔表达式并不像理解包含交替布尔运算符的布尔表达式那么困难。因此,表达式中任何相同布尔运算符链都会增加认知复杂性。

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

不带运算符的简单表达式的增量为 0。

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

表达式带有一个 AND 链接增量为 1。

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

表达式还有一个 AND。但由于是同一个运营商,因此相同运营商形成的链的数量不会改变。

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

该表达式有一个链 AND 运营商和连锁 OR 运营商。这导致增量为 2。

b := b1 AND b2 OR b3 AND b4 AND b5;    //+3

代码片段的增量为 3。

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

一元运算符 NOT 不考虑认知复杂性。



149. 示例:其他带有增量的语句

结构化文本具有更改控制流的附加语句和表达式。

以下陈述会因认知复杂性的增加而受到惩罚:

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

EXITRETURN 陈述不会增加认知复杂性。



指标:DIT——继承树深度

类别:可维护性

到达不扩展任何其他功能块的功能块之前的继承数

150. 例子
FUNCTION_BLOCK MyBaseFB
// ...
FUNCTION_BLOCK AChildFB EXTENDS MyBaseFB
// ...
FUNCTION_BLOCK AGrandChildFB EXTENDS AChildFB
// ...

MyBaseFB DIT 为 0,因为它本身是一个功能块,不扩展任何其他功能块。

为了 AChildFB,DIT 为 1,因为需要一步才能到达 MyBaseFB

AGrandChildFB DIT 为 2:需要一个步骤 AChildFB 另一个到 MyBaseFB



指标:NOC – 儿童数量

类别:可重用性、可维护性

扩展给定基本功能块的功能块数量。间接扩展基本功能块的功能块不计算在内。

151. 例子
FUNCTION_BLOCK MyBaseFB
// ...
FUNCTION_BLOCK AChildFB EXTENDS MyBaseFB
// ...
FUNCTION_BLOCK AGrandChildFB EXTENDS AChildFB
// ...

MyBaseFB 只有一 (1) 个子对象: AChildFB,它又具有一个子对象, AGrandChildFBAGrandChildFB 没有子对象。



指标:RFC – 类响应

类别:可维护性、可重用性

被调用并因此生成下面指定的 POU 的响应的不同 POU、方法或操作的数量 节目单位

152. 例子
//Declaration FB1
FUNCTION_BLOCK FB1
VAR
    d,x,y : INT;
END_VAR

//Implementation
x := METH(d+10);
y := FUN(42, 0.815);
//Declaration FB1.METH
METHOD METH : INT
VAR_INPUT
    i : INT;
END_VAR

//Implementation
METH := FUN(CUBE(i), 3.1415);
//Declaration CUBE
FUNCTION CUBE : INT
VAR_INPUT
    i : INT;
END_VAR

//Implementation
CUBE := i*i*i;
//Declaration Function FUN
FUNCTION FUN : INT
VAR_INPUT
    a : INT;
    lr : LREAL;
END_VAR

//Implementation
FUN := LREAL_TO_INT(lr*10)*a;
  • 从...开始 FUNCUBE,这些函数的 RFC 为 0,因为它们都不调用其他函数、功能块或方法来进行计算。

  • FB1.METH 用途 FUNCUBE,导致 RFC 为 2。

  • 功能块 FB1 本身调用 METHFUN,这将其 RFC 增加了 2。

    对于 FB1,还必须考虑其 METH 方法。 METH 使用 FUN 和 CUBE。 FUN 已添加到 RFC 中。因此,只有在METH中使用CUBE才能将FB1的RFC增加到3



指标:CBO – 对象之间的耦合

类别:可维护性、可重用性

对应SA0179规则的默认上限:30

在功能块中实例化和使用的其他功能块的数量

对象之间耦合度高的功能块很可能涉及许多不同的任务,因此违反了唯一责任原则。

153. 例子
// Declaration
FUNCTION_BLOCK FB_Child EXTENDS FB_Base objects // +0 for EXTENDS
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
    i_fb1 : FB1; // +1 instantiated here
    i_fb2 : FB2; // +1 instantiated here
END_VAR

//Implementation
i_fb3(); // +0 instantiated in FB_Base, no increment for call
  • 功能块的扩展不会增加对象之间的耦合。

  • i_fb3 在执行中被实例化 FB_Base 并传递给 FB_Child (EXTENDS)。来电 FB_Child 不会增加对象之间的耦合。

  • 国会预算办公室 (CBO) FB_Child 是 2。



指标:参考复杂性 (Elshof)

类别:效率、可维护性、可重用性

POU 数据流的复杂性

引用复杂度根据以下公式计算:

<使用的变量数量> / <变量访问数量>

仅考虑 POU 实现部分中的变量访问。

154. 例子
//Declaration
PROGRAM PRG
VAR
    i, j : INT;
    k : INT := GVL.m;
    b, c : BOOL;
    myFB : FB;
END_VAR

//Implementation
myFB(paramA := b);      // +3 accesses (myFB, paramA and b)
i := j;                 // +2 accesses (i and j)
j := GVL.d;             // +2 accesses (j and GVL.d)

引用代码片段结果中的复杂性:

6 使用的变量数量 /7 变量访问次数 = 0.85

警告:

  • ck 未使用,因此不计为“已使用的变量”。

  • 分配 k : INT := GVL.m 不被计算在内,因为它是程序声明的一部分。



指标:方法缺乏凝聚力 – LCOM

方法缺乏凝聚力 – LCOM

类别:可维护性、可重用性

功能块、它们的动作、转换和方法之间的内聚性描述了它们是否访问相同的变量。

方法内聚性的缺乏描述了功能块的对象相互连接的强度。内聚力缺乏程度越低,对象之间的联系就越强。

高度缺乏内聚性的功能块可能涉及许多不同的任务,因此违反了唯一责任原则。

该指标根据以下公式计算:

MAX(0, <无内聚的对象对数量> - <有内聚的对象对数量>)

155. 例子
//Declaration
FUNCTION_BLOCK FB
VAR_INPUT
    a : BOOL;
END_VAR
VAR_OUTPUT
END_VAR
VAR
    i,b : BOOL;
END_VAR

//Implementation
i := 42;
//FB.ACT
i:= 0;
//FB.METH Declaration
METHOD METH : BOOL
VAR_INPUT
	c : BOOL;
END_VAR

//Implementation
METH := c;
i := 1;
//FB.SecondMETH Declaration
METHOD SecondMETH : INT
VAR_INPUT
END_VAR

//Implementation
SecondMETH := SEL(c,3,4);

无连接的对象对(4 对):

  • FB, FB.ACT

  • FB , FB.METH

  • FB.ACT , FB.SecondMETH

  • FB.METH , FB.SecondMETH

带连接的对象对(2 对):

  • FB , FB.SecondMETH (两者都使用 c

  • FB.ACT , FB.METH (两者都使用 i

4. 下表显示了哪些变量连接了 FB 的哪些对象:

FB

FB.ACT

FB.METH

FB.SecondMETH

FB.SecondMETH

c

0

0

.

FB.METH

0

i

.

.

FB.ACT

0

.

.

.

FB

-

.

.

.





指标:证监会分支机构数量

类别:可测试性、可维护性

SFC(顺序功能图)实现语言的 POU 的替代分支和并行分支的数量

156. 例子
_san_img_metric_sfc_branch_count.png

上述SFC中的代码片段有4个分支:3个替代分支和1个并行分支



指标:SFC 步骤数

类别:可维护性

SFC 中 POU 的步数(顺序功能图)

仅对 SFC 中编程的 POU 中包含的步进行计数。在 POU 中调用的操作或转换的实现中的步骤不计算在内。

157. 例子
_san_img_metric_sfc_steps_count.png

SFC 中的代码片段有 10 个步骤。