我有一个功能,通过简单检查信号是否在给定的容差范围内来监控受控信号。该函数称为is_within_limits
。我有一个名为is_within_expanded_limits
的第二个函数执行相同操作,但事先将扩展因子(扩展公差带)应用于配置的限制。通过目标值和该值的最大偏差或下阈值和上阈值,在monitor.config
中配置限制。枚举monitor.config.monitoring_mode
指定如何指定有效范围。
首先看一下代码。
规范
subtype Float_Signed1000 is Float range -1_000.0 .. 1_000.0;
subtype Float_Signed10000 is Float range -10_000.0 .. 10_000.0;
type Monitor_Config_T is record
monitoring_mode : Monitoring_Mode_T := mean_based;
mean : Float_Signed1000 := 0.0;
maximum_deviation : Float range Float'Small .. 1_000.0 := 100.0e-3;
lower_threshold : Float_Signed1000 := -100.0e-3;
upper_threshold : Float_Signed1000 := 100.0e-3;
settling_tolerance_expansion : Float range (1.0 + Float'Small) .. 2.0 := 1.2;
startup_time : Time_Span := Milliseconds (5);
settling_time : Time_Span := Milliseconds (2);
violation_time : Time_Span := Milliseconds (5);
retry_time : Time_Span := Milliseconds (100);
end record;
type Monitor_T is record
config : Monitor_Config_T;
timer : Time_Span := Milliseconds (0);
current_state : Monitor_State_T := reset;
next_state : Monitor_State_T := reset;
end record;
function is_within_expanded_limits (monitor : in Monitor_T;
signal_value : in Float_Signed1000)
return Boolean
with Pre => (if monitor.config.monitoring_mode = mean_based then
monitor.config.maximum_deviation > 0.0)
and then (if monitor.config.monitoring_mode = threshold_based then
monitor.config.lower_threshold <
monitor.config.upper_threshold)
and then monitor.config.settling_tolerance_expansion > 1.0;
实施
function is_within_expanded_limits (monitor : in Monitor_T;
signal_value : in Float_Signed1000)
return Boolean is
within_expanded_limits : Boolean := False;
expanded_lower_threshold : Float Float_Signed10000;
expanded_upper_threshold : Float Float_Signed10000;
begin
case monitor.config.monitoring_mode is
when mean_based =>
if abs (monitor.config.mean - signal_value) <=
(monitor.config.maximum_deviation *
monitor.config.settling_tolerance_expansion) then
within_expanded_limits := True;
end if;
when threshold_based =>
-- Added due to recommendation by Jacob Sparre Andersen
-- Assertion is proved successfully
-- pragma Assert (monitor.config.lower_threshold < monitor.config.upper_threshold);
-- Added due to recommendation by Martin Becker
-- Adding this assumption still causes the assertion to fail
-- pragma Assume (monitor.config.lower_threshold = 10.0);
-- Adding this assumption still causes the assertion to fail
-- pragma Assume (monitor.config.upper_threshold = 11.0);
-- Replacing the assumption with this one results in the assertion being proved
-- pragma Assume (monitor.config.upper_threshold = 20.0);
-- Calculate expanded thresholds
if monitor.config.lower_threshold >= 0.0 then
expanded_lower_threshold := monitor.config.lower_threshold /
monitor.config.settling_tolerance_expansion;
else
expanded_lower_threshold := monitor.config.lower_threshold *
monitor.config.settling_tolerance_expansion;
end if;
if monitor.config.upper_threshold >= 0.0 then
expanded_upper_threshold := monitor.config.upper_threshold *
monitor.config.settling_tolerance_expansion;
else
expanded_upper_threshold := monitor.config.upper_threshold /
monitor.config.settling_tolerance_expansion;
end if;
-- @TODO why does this assertion fail?
pragma Assert (expanded_lower_threshold < expanded_upper_threshold);
-- Check limits with expanded thresholds
if signal_value >= expanded_lower_threshold and signal_value <=
expanded_upper_threshold then
within_expanded_limits := True;
end if;
end case;
return within_expanded_limits;
end is_within_expanded_limits;
我的问题是断言pragma Assert (expanded_lower_threshold < expanded_upper_threshold)
被标记为可能会失败而我不明白为什么。我添加了断言来检查我的代码没有做任何奇怪的事情,比如反转低阈值和高阈值的关系,但主要是尝试断言。前置条件monitor.config.lower_threshold < monitor.config.upper_threshold
与计算expanded_lower_threshold
和expanded_upper_threshold
的代码一起应该保证断言始终成立。问题在哪里,我该如何解决?
答案 0 :(得分:1)
我的推测是你的推荐者只是暂停了。我试过你的例子,这就是发生在我身上的事。
通常,用户界面无法区分证明失败的不同原因。如果你遇到超时并且没有注意到,那么 它使您的代码看起来有问题,尽管它可能不是。由于您必须使用GNATprove(目前没有其他工具),请进入您的构建文件夹并查找名为“gnatprove”的子文件夹。在那里,你会找到一个名为your_package_name.spark的文件。这是一种JSON格式,可以为您提供投诉人真正报告的信息。我相信你会发现超时或限制。我认为你的代码可能没有缺陷,因为我会在一分钟内证实。
但首先,您的代码没有编译。你必须使用合适的类型
在函数is_within_expanded_limits
中定义参数signal_value的范围。由于范围相同,您可以使用Float_Signed1000
。或者,添加一个前置条件(但类型更好,因为那时不在SPARK_Mode中的调用者也会得到一些类型/范围检查)。
然后,考虑将比较更改为“&lt; =”,因为舍入错误可能会反驳严格的排序。我不确定在这种情况下这是否有问题,但要注意SPARK模拟符合IEEE754标准的计算,无论你有一个数字1.2(无法精确表示),或1.25(可以),它都会有所不同。因此,我对您的代码做了一点改动。
现在回到我的说法,尽管证据失败,你的代码仍然是正确的。将其添加到案例的顶部:
pragma Assume (monitor.config.lower_threshold = 10.0);
警告:除了弄清证明失败的原因外,不要使用假设编译指示。如果使用不当,事情会发生严重错误。
现在选择最慢的模式进行验证,你的断言检出(证明者有较少的“cominations”来检查)。您可以根据需要更改assume语句中的值,但要注意该值与类型范围和前置条件一致,否则所有内容都会被错误地证明(例如,尝试Float'Last
,然后放置{{1}假设后立即)。
如果某些事情没有得到证实,那就去看看为什么如上所述。在你知道证明者真正得出结论之前,改变你的代码毫无意义。
您可以试用SPARK引理库,它可以启用这些顽固的证据:http://www.spark-2014.org/entries/detail/gnatprove-tips-and-tricks-using-the-lemma-library