指标
提供的指标的详细描述 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 内调用的函数或函数块有自己的堆栈帧。因此,此类调用的内存不算在内。
根据所使用的代码生成器,计算的中间结果也使用堆栈。这些结果不计算在内。
//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 的调用次数 节目单位
//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 节目单位 叫做
对于功能块,计算调用功能块本身或功能块继承树中的任何功能块的任务数量。
对于方法和操作,会显示调用(父)功能块的任务数。
FUNCTION_BLOCK FB //...
FUNCTION_BLOCK FB2 EXTENDS FB //...
FUNCTION_BLOCK FB3 EXTENDS FB2 //...
每个功能块都有自己的调用 PROGRAM
。每个 PROGRAM
有它自己的任务。
这 在任务中调用 指标返回 1 为 FB3
和 2 为 FB2
因为来自的电话 FB3
和 FB2
被计算在内。该指标结果为 3 FB
因为在这种情况下,来自的电话 FB3
, FB2
, 和 FB
被计算在内。
指标:使用的全局变量的数量(Globals)
类别:可维护性、可重用性
下 POU 中使用的全局变量数量 节目单位
//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)在对象的实现中。
//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 的区域。继承的变量不计算在内。
//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
程序单元的。继承的输入变量不计算在内。
FUNCTION_BLOCK FB VAR_INPUT i : INT; r : REAL; END_VAR
在功能块中,声明了 2 个输入变量: i
和 r
。
METHOD METH : BOOL VAR_INPUT j : INT; l : LREAL; END_VAR
该方法有 2 个输入: j
和 l
指标:输出变量(输出)的数量
类别:可维护性、可重用性
对应的默认上限 SA0166 规则:10
中的变量数量 VAR_OUTPUT
节目单元的
对于功能块,这是自定义输出变量的数量(VAR_OUTPUT
)。对于方法和函数,这是自定义输出变量的数量加一(如果它们有返回值)。返回值也被计算在内。继承的输出变量不计算在内。
大量输出变量表明违反了唯一责任原则。
FUNCTION_BLOCK FB VAR_OUTPUT i : INT; // +1 output r : REAL; // +1 output END_VAR
该功能块有 2 个输出变量: i
和 r
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 个输出: ar
和 i
指标:NOS – 语句数量
类别:信息丰富
功能块、函数或方法的实现中的语句数
声明中的语句、空语句或编译指示不计算在内。
//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 的百分比。
声明部分:
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 控制流中二进制分支的数量来计算的。然而,圈复杂度会损害高分支,因为高分支会增加高测试覆盖率所需的测试用例数量。
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。
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。
// 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。
以下语句也会增加圈复杂度:
//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 年引入的源代码可读性和可理解性的衡量标准。然而,它会惩罚控制流和复杂布尔表达式的大量嵌套。仅针对结构化文本实现计算认知复杂度。
以下示例显示了如何计算认知复杂性。
提示
这 显示当前编辑者的认知复杂性 命令可用于另外显示结构化文本的增量。
操纵控制流的语句将认知复杂度增加 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。
嵌套控制流时,每层嵌套增量加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。
由于布尔表达式在理解源代码方面发挥着重要作用,因此在计算认知复杂性时也会考虑它们。
理解与同一布尔运算符关联的布尔表达式并不像理解包含交替布尔运算符的布尔表达式那么困难。因此,表达式中任何相同布尔运算符链都会增加认知复杂性。
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
不考虑认知复杂性。
结构化文本具有更改控制流的附加语句和表达式。
以下陈述会因认知复杂性的增加而受到惩罚:
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
和 RETURN
陈述不会增加认知复杂性。
指标:DIT——继承树深度
类别:可维护性
到达不扩展任何其他功能块的功能块之前的继承数
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 – 儿童数量
类别:可重用性、可维护性
扩展给定基本功能块的功能块数量。间接扩展基本功能块的功能块不计算在内。
FUNCTION_BLOCK MyBaseFB // ... FUNCTION_BLOCK AChildFB EXTENDS MyBaseFB // ... FUNCTION_BLOCK AGrandChildFB EXTENDS AChildFB // ...
MyBaseFB
只有一 (1) 个子对象: AChildFB
,它又具有一个子对象, AGrandChildFB
。 AGrandChildFB
没有子对象。
指标:RFC – 类响应
类别:可维护性、可重用性
被调用并因此生成下面指定的 POU 的响应的不同 POU、方法或操作的数量 节目单位
//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;
从...开始
FUN
和CUBE
,这些函数的 RFC 为 0,因为它们都不调用其他函数、功能块或方法来进行计算。FB1.METH
用途FUN
和CUBE
,导致 RFC 为 2。功能块
FB1
本身调用METH
和FUN
,这将其 RFC 增加了 2。对于 FB1,还必须考虑其 METH 方法。 METH 使用 FUN 和 CUBE。 FUN 已添加到 RFC 中。因此,只有在METH中使用CUBE才能将FB1的RFC增加到3
指标:CBO – 对象之间的耦合
类别:可维护性、可重用性
对应SA0179规则的默认上限:30
在功能块中实例化和使用的其他功能块的数量
对象之间耦合度高的功能块很可能涉及许多不同的任务,因此违反了唯一责任原则。
// 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 实现部分中的变量访问。
//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
警告:
c
和k
未使用,因此不计为“已使用的变量”。分配
k : INT := GVL.m
不被计算在内,因为它是程序声明的一部分。
指标:方法缺乏凝聚力 – LCOM
方法缺乏凝聚力 – LCOM
类别:可维护性、可重用性
功能块、它们的动作、转换和方法之间的内聚性描述了它们是否访问相同的变量。
方法内聚性的缺乏描述了功能块的对象相互连接的强度。内聚力缺乏程度越低,对象之间的联系就越强。
高度缺乏内聚性的功能块可能涉及许多不同的任务,因此违反了唯一责任原则。
该指标根据以下公式计算:
MAX(0, <无内聚的对象对数量> - <有内聚的对象对数量>)
//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
)
FB |
|
|
| |
---|---|---|---|---|
|
| 0 | 0 | . |
| 0 |
| . | . |
| 0 | . | . | . |
| - | . | . | . |
指标:证监会分支机构数量
类别:可测试性、可维护性
SFC(顺序功能图)实现语言的 POU 的替代分支和并行分支的数量
![]() |
上述SFC中的代码片段有4个分支:3个替代分支和1个并行分支
指标:SFC 步骤数
类别:可维护性
SFC 中 POU 的步数(顺序功能图)
仅对 SFC 中编程的 POU 中包含的步进行计数。在 POU 中调用的操作或转换的实现中的步骤不计算在内。
![]() |
SFC 中的代码片段有 10 个步骤。