掌桥专利:专业的专利平台
掌桥专利
首页

用于确定性地报告软件系统中的原因和影响的方法

文献发布时间:2023-06-19 10:46:31


用于确定性地报告软件系统中的原因和影响的方法

相关申请

本申请要求2018年9月27日提交的序列号为62/737,691的美国临时申请的优先权,该申请被整体地并入本文中。

技术领域

本文中公开的实施例总体上涉及报告软件中的错误,包括但不限于在软件中的功能与过程之间传递因果(causal)ID,该软件可能与硬件(诸如,临床化学分析仪)交互或者控制硬件。

背景技术

由软件报告的错误的根本原因可能难以跟踪。当软件涉及硬件系统时可能尤其如此,其中由软件报告的错误可能是由现实世界中的硬件错误所引起的,但是其中有关硬件错误的有用诊断信息不一定在软件中被完全地向前传送到错误报告点。这种系统以及大多数大型软件环境(其中所报告的错误可能更经常是由与硬件无关的纯软件组件引起的)可以从更好的错误跟踪中受益。尽管这种错误跟踪方法可以对许多软件或硬件/软件系统具有效用,但是使用具体示例(在这种情况下为临床化学分析仪)来描述这些概念是最简单的。示例性临床分析仪是可从Siemens Healthcare Diagnostics获得的Dimension Vista临床分析仪。这种分析仪通常以每小时1500次测试的示例性最大平均吞吐量对患者流体样本执行医学测试并且在试管中接受血液、尿液和血浆。为了实现这一点,示例性分析仪包含多于400个计算机控制的硬件致动器和传感器,包括电动机(用于控制臂、探针、泵、定位器)、螺线管(用于激活阀、闩锁、锁),灯(用于指示器)、传感器(用于诸如光度计、浊度计、电化学、化学发光、压力、条形码、障碍物、编码器、开关等之类的应用)。

图1示出了分析仪的示例性机构图,以说明可以彼此有关并且共享可能导致冲突或线程锁定的物理空间的物理组件的相互作用(interplay)。例如,比色皿环可以与IMT探针、多个样本臂和多个试剂臂进行交互。样本臂可以与等分通道(aliquot lane)进行交互,等分通道与各种等分器(aliquotter)系统进行交互。试剂臂可以与试剂服务器进行交互,试剂服务器包括各种试剂子系统。

图2A示出了用于临床化学系统的用户界面的示例性现有技术屏幕截图,该屏幕截图显示了未能成功完成用户先前请求的执行,该请求为在患者样本04上运行NA测试(即,钠测试)。该测试请求的结果显示“错误”和“测量错误”,但是没有显示该错误的实际根本原因。在该特定系统中,针对这种一般错误后果(outcome),实际上存在数百万种可能的根本原因。虽然从那些数百万种可能性当中,引起了该特定错误的实际根本原因的细节经由事件数据库的一般显示、以及经由面向开发者的追溯日志而被记录并且显示在该系统的其他地方,但是在此处被显示给用户的负面(negative)后果与在其他地方记录的根本原因信息之间不存在任何连接(connection)。负面影响与具体根本原因细节之间的连接的这种缺乏是用户的软件设计和系统的常见缺陷。

图2B示出了用户界面的示例性现有技术屏幕截图,该屏幕截图显示了多个“不可用”条件,其中用红色三角形显示了针对ESDSH模块状态的某些能力,该“不可用”条件指示来自用户的需要这些能力的任何未来请求将失败,直到不可用性的根本原因被解决为止。同样,实际上存在关于能力可能下降的数百万种可能的原因,但是没有任何具体的根本原因信息可用于解释这些能力为什么下降。而且,在这种特定情况下,不同的能力由于不同的根本原因而下降。这是根本不明显的。用户可能会容易并错误地假设这些能力由于相同的根本原因而下降。同样,负面影响与具体根本原因之间的连接的这种缺乏是用户的软件设计和系统的常见缺陷。

图2C示出了多个错误的示例性现有技术屏幕截图,其中控制单独的机电硬件的五个不同的线程在启动时检测到了硬件“归位”错误。在这种特定情况下,实际上仅有一个硬件错误要对整个故障集合负责。也就是说,一个根本原因物理硬件故障使得其他四个几乎相同的硬件故障被报告,所有这些故障在不同的硬件机构子系统上。从该现有技术屏幕截图中不清楚哪个错误可能是根本原因,这是因为所有错误都发生在相同的时间戳和错误代码处。仅有一个机构具有物理问题,并且是根本原因。其他四个机构遇到了软件超时错误,从而等待第一个机构在物理上让开道路(move out of the way)。由于机构之间的扩展的依赖性,可能会使得这些问题更糟。这是系统重置期间的常见场景。

用以解决报告引起了软件系统中经历的任何负面后果的实际根本软件条件的主要问题的许多先前尝试首先开始于:尝试静态地预先确定软件系统中的所有可能的原因和影响(cause and effect)路径,并且因此预先确定所有可能的故障模式和非故障模式。

这些尝试中的假设是:如果可以针对软件系统确定所有可能的原因和影响路径,或者可以确定原因和影响的某个“足够的”子集,则可以在原因和影响的该静态列表之上构建某个系统,该系统将允许向用户报告任何负面后果的原因。

然而,对于复杂系统而言,确定该系统中所有可能的原因和影响、以及因此确定该系统中的负面后果的所有可能原因的尝试可能会引起难处理(intractable)的问题。也就是说,对于任何软件开发组织来说,针对甚至中等复杂性的软件来确定所有可能的原因和影响路径都是非常耗费资源的,并且因此实际上是不可能的。或者,经常发现的是,许多许多不同种类的原因可以各自促成许多许多不同种类的负面后果,这实际上使得该信息对于用户的故障诊断(troubleshooting)来说是无用的。

即使能够针对软件系统来确定所有可能的原因和影响以及它们之间的所有可能路径,但是用于映射这些原因的传统方法是不够的。考虑如下软件系统:针对该软件系统,构造该系统中的所有可能原因和影响的静态有向图,该静态有向图图示了每个可能的根本原因以及每个根本原因的每个可能的负面后果。通常,这种图将示出根本原因与可能后果之间的重叠路径,其中根本原因可能共享一个或多个后果,并且不同的后果可能共享一个或多个根本原因。在运行时对数据和执行状态的任意改变可以并且经常改变给定根本原因条件的观察到的最终结果。也就是说,对程序中的各种状态的正常运行时改变可以使得静态图的不同子集在任何给定时间处活跃(active)。然后,这可以引起相同根本原因条件的不同发生,从而在不同时间点处、以看似任意的方式导致不同的负面后果。这实际上使得在所有可能原因和影响的静态图中捕获的知识变得无用:在运行时,由于在任何给定时间处的原因和影响路径通常是可变的,因此无法利用该知识。对于尝试在原因和影响的静态列表之上构建系统的任何解决方案来说,这些问题通常表示硬“停止点”。用以解决该问题的尝试根本无法被构建在关于所有可能的原因和影响路径的静态知识之上,这是因为在运行软件时、在任何时间点处观察到的实际的原因和影响路径通常不是静态的。取而代之,它们通常是所有可能的原因和影响路径的该总列表的变化子集。也就是说,为了通知用户最终的(一个或多个)根本原因是什么,该软件将如何得知哪个(哪些)可能路径是有效的

根本原因条件的细节通常会被记录到单独的数据库或日志文件,但是对于用户而言,不存在从UI中显示的负面后果返回到该详细根本原因信息的直接连接。用户被要求浏览这些数据库或日志,并且有效地做出关于所列出的可能根本原因条件中的哪些引起了其特定负面后果的猜测。在其他应用或节点中发生的根本原因条件使该问题甚至更加困难,这是因为用户必须知道哪些其他应用或节点可能对当前应用产生影响,而这很少是容易知道的。

根本原因条件有时可能会在它们发生时被显示在弹出对话框窗口中,但是仍然通常不包含返回到可能的多个下游负面后果的直接连接。软件开发者也偶尔从事于尝试开发出自动启发式方法来报告负面后果的根本原因条件,但是根据启发式系统的性质,结果通常是不准确的,并且开发成本高昂。

基于上述开发者在软件中提供的信息,用户和企业尝试以下一般类的动作来进行故障诊断:搜索根本原因条件数据库(事件数据库);手动启发式方法(分析已知模式的症状、用户动作和软件输出);检查文档;查看追溯日志文件;以及询问其他人员,包括重复升级(escalation)。所有这些方法都可能具有如下问题:即导致对主要问题的高度不完整的覆盖,并且因此导致显著浪费的时间和金钱。第一,事件数据库中的事件通常不会通过软件以任何对于用户有意义的方式被连接回到负面后果,并且通常包含许多不相关的事件,这实际上隐藏了感兴趣的事件。任何根本原因事件与其负面后果之间的实际原因和影响关系对于用户而言可能非常模糊,并且因此不明显,从而导致故障诊断者忽略了实际根本原因事件。第二,启发式方法根据定义是不精确的,并且容易出错(例如,误诊断)。有效的启发式分析需要特定故障及其症状模式的先前经验(以及其记忆)以便进行诊断。这由于如下情况而受挫折:即许多不同类型的根本原因条件在用户级别下展现出相同的确切症状和负面后果,从而阻止它们被启发式地诊断。第三,文档无法充分地捕获任意事件的大量组合。第四,操作者通常无权随时访问追溯日志文件,这是因为它们意图供软件开发者内部使用,并且通常不容易由用户来解释。最后,询问其他人员通常会增加通信延迟,并且由于诊断限制而不一定能解决该问题;对于提供技术支持的制造商而言,这可能是昂贵的服务成本。

发明内容

可以通过使用维持因果事件数据库的系统来解决现有技术中的前述问题中的一个或多个,其中每个因果事件被指派有基本唯一的因果ID,并且可以通过引用该因果ID而链接到父代因果事件。知道因果数据的布尔操作可以用于传播因果ID,以帮助链接父代事件和子代事件。

一些实施例的一个方面包括一种用于跟踪软件系统中的因果事件的方法,该方法包括如下步骤:(由处理器)标识在该系统的操作期间发生的、满足多个预定义因果条件之一的多个因果事件;向每个因果事件指派基本唯一的因果ID,并且在因果数据库中创建针对每个因果事件的条目。处理器通过如下操作而继续:由处理器将每个因果ID与描述系统状态的系统状态值相关联;执行多个因果布尔操作,每个因果布尔操作将一个或多个输入因果ID以及相关联的系统状态值作为输入,并且输出布尔值以及所选的一个或多个输入因果ID。如果改变与输入因果ID相关联的系统状态值将改变输出布尔值,则处理器选择该输入因果ID以用于输出。该方法基于因果布尔操作的执行和因果数据库的内容来进一步向软件系统的用户显示如下界面:该界面针对软件系统中的用户传达事件与负面后果的因果关系。

在一些实施例中,因果布尔操作中的至少一个是AND(与)操作,如果输出为false(假),则AND操作针对输出选择与false输入状态相关联的至少一个因果ID,并且如果输出为true(真),则AND操作针对输出选择至少一个输入因果ID。如果输出为true,则AND操作可以输出所有输入因果ID。在一些实施例中,因果布尔操作中的至少一个是OR(或)操作,如果输出为true,则OR操作针对输出选择与true输入状态相关联的至少一个因果ID,并且如果输出为false,则OR操作针对输出选择至少一个输入因果ID。如果输出为false,则OR操作可以输出所有输入因果ID。在一些实施例中,因果布尔操作中的至少一个是NOT(非)操作,如果输出为false,则NOT操作针对输出选择与true输入状态相关联的至少一个因果ID,并且如果输出为true,则NOT操作针对输出选择至少一个输入因果ID。

在一些实施例中,图形界面允许用户点击负面后果以展开或折叠针对该负面后果的根本原因事件的显示。在一些实施例中,处理器通过维持多个对象来将因果ID与系统状态值相关联,该多个对象包括因果ID以及关于系统状态的一个或多个数据两者作为每个状态对象的变量。

在一些实施例中,该方法包括执行非布尔因果操作的步骤,该非布尔因果操作返回输出值,并且选择性地返回与由处理器确定对输出值具有贡献的输入值相关联的任何因果ID。

在一些实施例中,软件系统便于临床分析仪的操作。

一些实施例的另一个方面包括一种用于跟踪软件系统中的因果事件的方法,该方法包括如下步骤:(由处理器)标识在该系统的操作期间发生的、满足多个预定义因果条件之一的多个因果事件;以及向每个因果事件指派基本唯一的因果ID,并且在因果数据库中创建针对每个因果事件的条目。该过程进一步执行如下步骤:经由因果数据库将每个因果ID与系统状态值相关联,系统状态值描述了由被指派有因果ID的因果事件导致的系统状态;以及执行多个因果函数,每个因果函数将一个或多个输入系统状态值和相关联的因果ID作为输入。这些因果函数输出由输入系统状态值的因果函数定义的结果以及所选的一个或多个输入因果ID,所选的输入因果值是与如果被改变则将导致结果改变的系统状态值相关联的因果ID。该方法基于与导致负面后果的一个或多个状态相关联的一个或多个因果ID、以及因果数据库的内容来进一步向软件系统的用户显示如下界面:该界面针对软件系统中的用户传达事件与负面后果的关系。

在一些实施例中,该多个因果函数中的至少一个是AND操作,如果输出为false,则AND操作针对输出选择与false输入状态相关联的至少一个因果ID,并且如果输出为true,则AND操作针对输出选择至少一个输入因果ID。如果输出为true,则AND操作可以输出所有输入因果ID。在一些实施例中,该多个因果函数中的至少一个是OR操作,如果输出为true,则OR操作针对输出选择与true输入状态相关联的至少一个因果ID,并且如果输出为false,则OR操作针对输出选择至少一个输入因果ID。如果输出为false,则OR操作可以输出所有输入因果ID。在一些实施例中,因果布尔操作中的至少一个是NOT操作,如果输出为false,则NOT操作针对输出选择与true输入状态相关联的至少一个因果ID,并且如果输出为true,则NOT操作针对输出选择至少一个输入因果ID。

在一些实施例中,该界面允许用户点击负面后果以展开或折叠针对该负面后果的根本原因事件的显示。

在一些实施例中,处理器通过维持多个对象来将因果ID与系统状态值相关联,该多个对象包括因果ID以及关于系统状态的一个或多个数据两者作为因果数据库中的每个状态对象的变量。在一些实施例中,该多个因果函数包括非布尔因果操作,该非布尔因果操作返回输出值,并且选择性地返回与由处理器确定对输出值具有贡献的输入值相关联的任何因果ID。

一些实施例的另一个方面包括一种用于跟踪软件系统中的因果事件的方法,其中处理器执行如下步骤:维持因果事件数据库,其中存储有关于多个因果事件中的每一个的信息,每个因果事件具有所指派的基本唯一的因果ID,并且因果事件的至少子集还将另一个因果事件标识为该因果事件的父代原因;以及标识在执行软件序列期间发生的、满足至少一个预定义因果条件的第一因果事件。处理器执行如下进一步的步骤:确定第一因果事件是否是现有父代因果事件的结果;向该因果事件指派第一基本唯一的因果ID;以及存储关于第一因果事件的信息,包括第一基本唯一的因果ID、相关状态信息、以及父代因果事件的因果ID,如果已经确定了一个的话。然后,处理器将第一基本唯一的因果ID传递到软件序列的输出,使得遇到另外的因果事件的后续软件序列可以将第一因果事件作为父代因果事件链接到该另外的因果事件。

在一些实施例中,该方法基于因果数据库的内容向软件系统的用户显示传达因果事件的因果关系的界面。

在一些实施例中,处理器在后续软件序列中执行多个因果布尔操作,该多个因果布尔操作将均包括状态值和因果ID的一个或多个因果数据值作为输入,其中因果布尔操作均求值(evaluate)为输出布尔值、以及对该一个或多个因果数据值中的如下至少一个因果值的选择:如果相关联的状态值被改变,则该至少一个因果值将导致不同的输出布尔值。在一些实施例中,因果布尔操作选择与如果被改变则将导致不同的输出布尔值的每个输入状态值相关联的所有因果ID作为输出。在一些实施例中,当存在如果被改变则将导致不同的输出布尔值的多个输入状态值时,因果布尔操作仅选择一个因果ID作为输出。

在一些实施例中,向每个因果事件指派努力值(effort value),该努力值近似校正该因果事件所需的努力量。在一些实施例中,经由用户界面向用户显示因果事件,并且在界面中,所有父代因果事件被链接到每个子代因果事件。在一些实施例中,经由用户界面向用户显示父代因果事件的努力值,从而允许用户看到关于哪些父代因果事件解决起来应该花费最少的努力量的近似。

附图说明

当结合附图阅读时,从以下详细描述中最佳地理解本发明的前述及其他方面。出于说明本发明的目的,在附图中示出了目前优选的实施例,然而要理解的是,本发明不限于所公开的具体手段。在附图中所包括的是以下各图:

图1是用于与一些实施例一起使用的示例性系统的系统示图;

图2A-C是用以错误报告的示例性现有技术方法的用户界面;

图3是用于与一些实施例一起使用的图示了依赖性的示例性系统的符号系统示图;

图4A-4B是示例性因果性树(causality tree),该因果性树可以发生在用于与一些实施例一起使用的示例性系统中;

图5A-5C是示出了某些说明性实施例中的线程之间的交互的时序示图;

图6A-C是示出了某些说明性实施例中的线程之间的交互的时序示图;

图7A-7B是示出了某些说明性实施例中的线程之间的交互的时序示图;

图8A-8E是示例性运行时关系的依赖性示图,其图示了可以与一些实施例一起使用的示例性系统中的状态和错误传播;

图9是图示了一些说明性实施例中的可以基于某些根本原因条件向用户显示的负面后果的类型的关系示图;

图10A-10C是示例性运行时关系的依赖性示图,其图示了可以与一些实施例一起使用的示例性系统中的状态和错误传播;

图11是示例性运行时关系的依赖性示图,其图示了可以与一些实施例一起使用的示例性系统中的因果状态传播;

图12是图示了系统状态与正面(positive)/负面后果之间的差异的表;

图13A-13D是示例性运行时关系的依赖性示图,其图示了可以与一些实施例一起使用的示例性系统中的因果ID传播;

图14A-14N是示例性因果输出表,其图示了因果ID在某些实施例中通过软件向输出布尔值的指派以及其逻辑性质;

图15A-15F是示例性因果输出表,其图示了因果ID在某些实施例中通过软件向输出布尔值的指派;

图16是适合于实现一些实施例的系统的系统示图;

图17A-L是一些说明性实施例的流程图和操作;

图18A-H和图19A-C是通过示例性因果布尔操作的示例性因果ID传播的逻辑示图,该因果布尔操作被求值以确定是否应当向用户显示负面后果、以及该后果的根本原因;以及

图20是用于与某些实施例一起使用的示例性计算系统的系统示图。

具体实施方式

软件用户花费大量的时间对软件所传送的故障模式进行故障诊断,以便诊断和校正问题。针对用户经历的任何给定故障,大量的该时间被花费以尝试确定由软件检测到的详细根本原因条件。

也就是说,软件通常会告诉用户某个地方存在基本故障或问题——也就是说,他们在用户界面中经历了“负面后果”,其中请求被报告为已经失败,或者系统被报告为不可用于处理未来的请求——并且然后那些用户花费大量的时间尝试将该负面后果连接回到由软件检测到的使得该负面后果被显示的根本条件。在此,“故障模式”被定义为:与用户界面中的一种可能的负面后果相组合的由软件检测到的一种可能的根本原因条件,该负面后果是由该条件所导致的。

为了补救(remedy)任何给定的故障模式,用户经常需要做出从在用户界面级别处经历的具体负面后果返回到由软件检测到的具体源条件的因果连接。通常,用户只有在做出该连接并且补救了去往所涉及的源条件的输入之后,才可以继续按预期使用该软件。

软件的两个常见性质是:单个根本原因条件可能引起多个不同类型的不同负面后果,这些负面后果有时并行发生,并且单个负面后果可能由许多类型的根本原因条件引起,这些根本原因条件有时也并行发生。因此,系统中的可能故障模式的总数被认为是源条件及其可能的负面后果的所有可能组合。在甚至中等复杂性的软件中,可能故障模式的数量通常在数百万的范围内,这太大了以致于实际上无法进行枚举或记录。也就是说,针对例如故障诊断指南提供所有可能故障模式的详尽列表通常是难处理的问题。由于难处理的大量可能故障模式、以及下面描述的软件的其他性质,用户和开发者用来做出这种诊断连接的当前技术对于有效的用户体验而言通常是不够的。

因此,这是一个软件企业及其客户两者都花费大量资源的领域:即尝试回答哪个基本软件条件使得故障被明确地显示给用户。无法快速且有效地在负面后果的任何发生回到该发生的(一个或多个)特定根本原因条件之间做出连接会花费大量的时间和金钱。见证到如下情况是常见的:即多个软件操作者和工程师花费数小时或数天来尝试将单个屏上故障追溯回到日志中的简单硬件错误或用户动作。

出于许多原因,如下所描述,大多数软件当前仅提供相对有限的“成功”、“失败”或针对失败显示的类似指示符,其中关于实际失败条件的大部分详细信息(用户需要该信息来补救问题)以对于用户而言极难连接到最初显示的负面后果的方式被提供在其他地方。在典型临床分析仪中存在大量该问题的示例。例如,几乎所有UI显示可以以某种基本方式向用户传送负面后果(例如,失败的患者测试、不可用的模块、失败的维护活动、不可用的能力等),但是源条件通常是在不同的显示上(例如,在组合的通用错误日志或操作系统事件日志中)被捕获的。问题在于:软件通常无法将各种屏幕上示出的负面后果连接回到事件日志显示中所捕获的详细源条件信息。这导致了许多猜测以及从客户到制造商的问题升级,并且经常通过服务和R&D组织向上升级,这变得极其昂贵。下面描述了软件和用户无法容易地做出这些连接的原因。

该问题不仅限于实验室诊断产品,甚至也不限于一般的医疗保健产品或医疗保健软件。该问题影响了几乎所有可以向用户报告负面后果的作为大多数软件的软件系统。这包括操作系统和在其上运行的应用两者。

用户尝试诊断的许多源原因条件实际上是由软件在内部检测到的,但是由于几乎所有软件系统中的数据流的复杂性、以及软件工程师关于这些数据流的错误认识(misconception),当前的软件设计无法准确地或有效地向用户报告这些详细源原因条件,无论用户在何时以及在何处看到所显示的负面后果。这导致了针对软件客户和软件提供商两者的显著停机时间,这是由于针对有时被认为是次要的、容易校正的问题所需要的扩展的故障诊断分析、以及针对更复杂的问题通常所需要的甚至更多的分析。

在医疗保健和其他硬件仪器中、或者在一般的非仪器软件中,可以传送给用户的负面后果的示例包括:患者测试未开始或未完成;校准或QC测试未开始或未完成;试剂装载/卸载未开始或未完成;手动请求的维护活动(诸如,更换消耗品、灌注、清洁等)未开始或未完成;自动化维护活动未开始或未完成;消耗品意外地示出为空或以其他方式不可用;模块状态下降或不可用;模块能力下降或不可用;子系统停机或不可用;禁用(灰显)的用户控件;任何其他手动或自动请求的功能未开始或未完成;以及任何其他系统状态或能力报告为下降或不可用。

由软件检测到的但是通常不作为任何给定负面后果发生的直接原因被传送出的常见根本原因条件包括:任意硬件错误(诸如,致动器错误;报告了不正确的电动机或螺线管位置的致动器传感器反馈错误、电流汲取超出限制等;数据分析错误,其包括来自指示化学、机械或其他问题的一个或多个传感器的多个读数分析;通信错误;逻辑问题);取消其他请求和/或使系统不可用的操作者动作(操作者按下“暂停”或“停止”);来自操作者的运行维护序列的请求,该请求使得系统不可用于其他请求;来自操作者的启动或重置系统的请求,该请求导致系统在初始化时不可用于处理其他所请求的功能;来自操作者的请求或其他高优先级影响,这中止了低优先级请求和/或使系统在任何时间段内不可用;操作者将系统切换到不同的系统模式,诸如输入模式、审查模式等,这使得任意功能或控件变得不可用;或者操作者安装软件、或者重置数据库化数据或其他持久数据,这创建了尚不适合于处理未来请求的初始数据条件,该条件可以指示不执行任何校准的默认、消耗品为空的默认等;取消其他请求和/或使系统不可用的自动启动的活动(诸如,自动维护活动、自动预热或冷却时段、以及自动系统重置);级联错误或事件,其中上面列出的问题引起了下游的一个或多个进一步检测到的问题,该进一步检测到的问题然后引起了负面后果(诸如,跨函数调用、数据路径、线程、进程、计算机、和/或网络和网络节点而传播的错误或其他根本原因条件,这些错误或其他根本原因条件然后引起了那些后续实体中的其他错误或因果条件、以及该级联原理向零个或多个下游负面条件的递归或迭代应用)。

图3示出了五个硬件机构控制线程之间的扩展的依赖性的示例性图10。在图3中所示的示例中,机构A具有根本原因错误。由于机构A未完成,因此所有其他机构线程最终具有其自己的错误,从而阻止了其他机构完成其任务。在美国专利9,298,535中解释了对这些问题中的一些的一种解决方案,该美国专利被共同拥有并且通过引用并入本文中。

还可以建立因果性树。通过观察该树中的分支,可以快速地辨别出哪个错误是其他分支错误的根本原因。父代被认为是其子代的原因。没有所定义的父代的错误(树根)被认为是可能的根本原因错误。具有所定义的父代的错误(分支或叶)被认为是肯定的非根本原因错误。当被表述为向下分支图时,父代是其下面的所有子代的最终原因。

图4A示出了具有两个父代错误(机构A电动机错误和机构E归位失败)的示例性根本原因树14,其可以作为GUI的一部分被显示给用户。

图4B示出了示例性用户界面,其中用户界面上的按钮允许该树折叠、隐藏子代错误,从而允许用户快速查看可能的根本原因错误,创建经折叠的因果性树18,因果性树18是因果性树14的经修改的显示。折叠该树可以帮助由用户快速地评估根本原因。

为了构建因果性树,软件应当创建如下能力:即在构建时将错误链接在一起,并且然后在运行时将错误链接在一起。在Dimension Vista的示例中,错误由可抛出(throwable)的类对象来表示:

class ErrorClass

{

int iErrorCode;

string strErrorDescription;

};

为了创建链接能力,可以利用如下两个新字段来扩展错误类(error class):1)iSerialNumber,其唯一地标识所生成的每个错误实例;以及 2)iParentSerialNumber,其指向当前错误的父代(如果父代已知的话)。

class ErrorClass

{

int iErrorCode;

string strErrorDescription;

//针对每个对象实例的唯一ID

int iSerialNumber;

//父代实例的SN。如果没有父代,则等于0

int iParentSerialNumber;

};

使用该示例,可以使用以下代码以未链接的方式来显示错误。

void InitializeMechanism()

{

try

{

MoveMotor();

}

catch (ErrorClass originalError)

{

PostErrorToUserInterface(originalError);

ErrorClass initializationError(INITIALIZATION_ERROR);

PostErrorToUserInterface(initializationError);

}

}

可以使用以下示例性代码通过添加父代序列号来显示经链接的版本:

void InitializeMechanism()

{

try

{

MoveMotor();

}

catch (ErrorClass originalError)

{

PostErrorToUserInterface(originalError);

ErrorClass initializationError(INITIALIZATION_ERROR);

InitializationError.SetParentSerialNumber(originalError.GetSerialNumber());

PostErrorToUserInterface(initializationError);

}

}

可以存在用于使用父代ID来链接错误的多种解决方案。它可以取决于错误类型。用于链接错误的一般策略是在代码中找到错误创建点,确定错误是否可能是由父代错误引起的子代,并且如果是,则找出如何将父代的序列号传给子代。

单线程错误之间的链接可以通过使序列号与相关联的错误代码和事件一起在调用堆栈中向上和向下传递来实现。跨线程错误(超时、线程中断)往往有点棘手,但是可以在一些实施例中被处理。示例性跨线程错误包括超时错误、单向信号上的超时错误、以及线程中断错误。

图5A示出了使用互斥体(mutex)25来保护物理共享空间的线程A 22与线程B 24之间的示例性交互20。在该图中,线程A 22当在该空间中执行其工作时锁定互斥体25,并且然后释放互斥体。线程B 24等待线程A 22释放互斥体25,之后在该空间中进行其工作期间锁定互斥体,并且然后在其工作完成之后释放互斥体。

图5B示出了当互斥体25在线程A 22遇到了电动机错误32之后导致线程B 24中的超时错误34时的示例性交互30,这是因为线程A 22不能够及时释放互斥体25以使线程B 24成功地操作。

为了便于链接这些错误,可以扩展互斥体类以存储错误序列号,并且在超时时返回当前错误序列号。在美国专利9,298,535中进一步详细讨论了这个概念。这可以通过包括以下示例性代码来实现。

class Mutex

{

int iErrorSerialNumber;

//如果返回false,则超时,并且iSerialNumber

//等于父代错误序列号

bool AcquireMutex( [out] int&iSerialNumber);

};

这可以用于:一旦发生错误,就将错误序列号推入当前线程所获取的所有互斥体中,作为任何错误处理例程的一部分。然后,当另一个线程尝试获取该互斥体时,当AcquireMutex(获取互斥体)例程返回了超时时,它还可以返回保存到该互斥体中的活跃错误序列号作为父代错误。然后,可以将其传递给该第二个线程中的任何错误处理例程。

每个线程可以使用意图列表来保持跟踪所有获取的互斥体。意图列表可以用于表示当前线程所持有的所有对象,任何其他线程可能正在等待关于当前线程的信号。这可以被称为“意图到信号”列表或“意图”列表。意图列表可以保持跟踪其他线程可能正在等待的项目。每个列表可以是线程局部的(thread local),并且每个线程一个列表。意图列表中的所有项目应当实现如下能力:即保存错误序列号,以将错误传播到可能依赖于那些相同项目的其他线程。可以与意图列表一起使用的一些示例性命令(具有自描述名称):

a)在获取互斥体时:AddToIntentList(signalable_item),例如互斥体、信号、事件;

b)在释放互斥体时:RemoveFromIntentList(signalable_item)和SetSnOnIntentList(serial_number) (将特殊的“无错误”错误序列号(例如,空或零序列号)保存到当前处于列表中的每个项目)。

c)在遇到任何错误时:SetSnOnIntentList(error.GetSerialNumber()) (将错误序列号保存到意图列表中的所有项目、即由当前线程当前获取的所有互斥体中)。

d)在遇到互斥体超时错误时:使用保存在互斥体中的序列号作为超时错误的父代。

图5C图示了这些命令中的一些可以如何被用于在互斥体和电动机错误的情况下传播错误。在情况36中,当线程A 22遇到电动机错误32时,它将错误序列号放置到互斥体25中。当线程B 24遇到等待互斥体25的超时错误时,它可以然后取得该错误序列号,并且将该序列号与错误通知一起传递,从而允许在稍后分析期间将电动机错误32链接为超时错误34的根本原因错误。

还可以利用错误传播来增强可发信号的(signalable)对象。图6A示出了使用信号对象的两个线程之间的示例性相互作用40。在该示例中,信令对象45用于将信号从线程A42传递到线程B 44。这可以表现得像握手一样,从而让线程B知道线程A已经完成某些工作。线程B 44在继续进行所得工作之前等待由线程A 42稍后发送的信号。线程B 44可以具有超时阈值,使得可以限制线程B 44将等待线程A 42发信号通知的时间量。

图6B示出了在由线程A中的电动机错误50引起的线程B中的超时错误48的示例的情况下的相互作用46,其中由于电动机错误,没有任何信号从线程A被发送到线程B。链接信号超时与链接互斥体超时非常类似。可以扩展信号类以允许设置序列号,就像在互斥体类中一样。可以在每个工作单元期间的适当点处从意图列表添加和移除信号对象。

图6C示出了在已经被修改成允许将错误序列号从线程A 42传递到线程B 44的信号对象的示例的情况下的相互作用52。在该示例中,第一线程A 42将信号对象添加到其意图列表。当发生电动机错误时,该线程将错误序列号(105)传递给其意图列表中的每个对象(诸如,信号45),这包括将错误序列号传递给信号对象。然后,线程A 42在其已经完成了任何错误处理(这使该对象的信令延迟)时从其意图列表中移除该信号对象。当第二个线程遇到等待该信号对象的超时错误时,它可以找到该错误序列号并且在错误处理例程中传递该信息。在启动将发信号通知的工作单元时,示例性线程将执行AddToIntentList(signal)。在设置信号时,该线程将执行RemoveFromIntentList(signal)并且将信号对象中的序列号设置为0。在遇到任何错误时,该线程将执行SetSnOnIntentList(sn),这将错误序列号保存到意图列表中的所有项目(即,意图由当前线程用信号发送的所有互斥体和信号)中。在完成任何错误处理时,该线程将执行RemoveFromIntentList(signal)。在遇到信号超时错误时,每个线程将使用从信号对象返回的序列号作为超时错误的父代。

线程还可能遇到线程中断错误。线程中断通常用于响应于另一个错误或事件而中止线程操作。这些错误通常表现为抛出的异常或返回的错误标志,并且因此经常由被中断的线程利用错误处理来处置。因此,链接引起该中断从而中止该线程的任何父代错误或事件是合期望的。

图7A示出了追溯线程中断错误的传统问题的示例。在情况54中,在已经发生电动机错误50之后,线程A可以向线程B的线程对象56发送中断请求。线程B在此同时针对中断而定期地轮询其线程对象56。在遇到中断后,线程B中止,并且针对中断错误58执行错误处理。

图7B示出了在经修改的线程对象62的情况下的类似过程60,该经修改的线程对象62在线程A发送中断时接收错误序列号作为自变量(argument),从而允许线程B在遇到中断时从线程对象62接收到该错误序列号。因此,在处理中断错误58时,线程B可以因果性地将该中断链接到线程A。

为了将实现中断系统的线程类修改成允许在中断请求的情况下设置序列号,并且在该线程接收到中断时返回接收到的序列号,示例性线程类可以被定义为包括以下函数。

class Thread

{

int iInterruptErrorSerialNumber;

void Interrupt(int iSerialNumber);

//如果返回true,则中断,并且

//iSerialNumber等于父代错误序列号

bool CheckForInterrupt( [out] int&iSerialNumber);

};

应当注意的是,尽管前述内容可以允许错误序列号在线程之间传递,从而允许以用嵌套方式标识父代事件的方式来标识父代和子代事件并且将其显示给用户,但是该解决方案还可以包括竞赛(race)条件。即,在另一个线程有机会在共享同步对象中设置错误序列号之前,可能发生超时。在这种情况下,没有针对子代标识出父代,使得该子代将被认为是树的根。由于这些系统与电动机和其他硬件对接,因此超时限制可能比传统软件应用显著地更长。

此外,竞赛条件可能不是特别成问题,这是因为由软件提供给用户的行为合约是:当搜索根本原因(相对于搜索任何原因)时被标识为子代的任何错误被认为肯定不是根本原因,并且尽管已知父代错误是其子代的原因,但是父代错误也可能是或者可能不是其子代的最终根本原因(例如,父代可能是无法由软件标识的另一个根本原因的子代结果,特别是在硬件系统中,其中组件的物理交互引起其他组件中的错误,其中软件没有预期到该交互)。因此,虽然从子代到其树根的世系中的所有错误都被认为是该子代的原因,但是该世系的最终的根可能是或者可能不是最终根本原因。通常,知道父代世系中的至少一些仍然是有帮助的,这是因为这使得用户更接近并且更快地发现真正的根本原因。

错误可以被理解为预期状态与实际状态之间的不匹配。当错误导致状态改变成可能引起未来错误的事物时,软件可以基于该状态将该错误的序列号与针对任何未来错误的状态一起传送。即使非预期的状态通常不被认为是错误本身。传统上,超时错误可能是成问题的,这是因为它们不一定是菊花链;从属线程可能会在它们所依赖的线程之前超时。这可以通过包括如下代码来缓解:该代码在错误序列号被放置到共享对象中后立即在从属线程中触发错误。因此,可以在达到超时限制之前抛出子代错误。这可以使用增强的线程同步类来实现。此外,如果在系统中将原语操作修改成原子地存储传统上不被存储在那些原语中的数据,诸如事件或错误序列号,则可以更广泛地传播错误/事件序列号。尽管使用经修改的原语类可能向该系统增加性能开销,但是控制机械机构的系统关于该系统的计算能力可能不具有高性能要求,从而允许足够的计算时间来处理经增强的原语的额外负担。

尽管已经关于错误描述了这些基本概念,但是应当领会,这些原理可以应用于该系统中具有因果性的任何类型的事件。因此,由不一定或不通常被认为是错误的用户动作或其他事件所引起的错误可能被跟踪到不一定是错误的事件。

简化了针对任意软件系统的可能的原因和影响路径的示例性图是图8A中所示的原因和影响图70。该示图中最左侧的每个节点(圆圈)表示按照上面列出的示例由软件检测到的根本原因条件(源条件72)的可能类型(例如,硬件错误、操作者动作等)。实际的软件系统可能具有数十个、数百个、或更多个这些可能的条件。最右侧的每个节点(负面后果76)表示按照上面列出的示例向用户显示的负面后果的可能类型(未能处理不同的用户请求、不同的子系统不可用等)。实际的软件系统可能具有数十个、数百个、或更多个这些可能的负面后果。中心区域中的每个节点(中间数据和执行路径74)表示受左侧的源条件直接或间接影响的数据或执行路径——数据变量、数据对象、持久数据、网络数据、函数调用、条件语句、代码块等,并且可能直接或间接地引起右侧的负面后果。实际的软件系统可以并且经常具有数百个、数千个、或更多个这些中间路径。箭头表示节点之间的原因和结果路径,其中从箭头(的左端)指向的节点表示因果源,并且指向(右端)的节点表示因果后果。实际上,受因果后果影响的节点将指示对该节点的状态的改变:对该节点所表示的数据值的改变、或者对该节点所表示的函数、条件、代码块、操作等的执行或不执行的改变。

要注意的是,图8A中的示图是高度简化的。实际的软件系统通常还具有带有从右侧的节点指向左侧的节点的箭头的反馈循环,从而产生另外的级联事件条件和更多的可能路径。如该示图所展示,在任何给定的软件系统中,存在许多复杂的原因和影响路径,它们被分解成许多子路径和子段。

按照上面“故障模式”的定义,从源条件到负面后果的任何单个路径指示单个类型的故障模式,如图8B中的单个故障路径78所图示的那样。

将用户的负面后果自动连接回到根本因果事件的总体问题中存在多个技术子问题,这些子问题显著地促成了解决该总体问题的复杂性。

子问题#1:因果路径复杂性。根本原因(源)条件与负面后果之间的关系可以并且经常展现出多对一、一对多和共享路径的行为。对于多对一关系的示例而言,图8C中的多源故障路径80示出了与仅一种类型的负面后果相关联的源条件。在图8C中,源条件是通过拾取右侧的负面后果并且向左向后追溯到该后果的所有可能原因而找到的。

图8D是一对多关系的示例。右侧是与左侧的仅一种类型的源条件相关联的负面后果。负面后果是通过拾取左侧的源条件并且向右向前追溯到该源条件的所有可能后果而找到的。单源多错误故障路径82说明了:单个根本原因可能引起许多错误。

图8E是共享路径行为的示例,其中两个不同的源条件均可以导致两个不同的负面后果,但是在它们之间共享一个或多个因果路径。中心节点具有共享路径。多源多错误故障路径84说明了:多个根本原因可能引起许多错误,并且可以在中间节点中共享单个故障路径。

软件中的原因和影响的这些性质中的任何单个性质都将表现出对解决如下总体问题的显著阻碍:即,针对任何给定负面后果发生向用户准确地报告(一个或多个)实际源条件。所有这些一起产生了必须针对根本性问题来解决的极为复杂的行为,对于解决方案下的传统尝试而言,该行为已经到了难处理的程度。

子问题#2:太多的可能故障模式。图9是关于不同的根本原因条件86可以如何引起多个不同类型的负面后果88的图示。根本原因条件86中列出的每个根本条件表示条件的一般类,其中每个类可以包括数百个或数千个更详细的实际根本条件。负面后果88中列出的每个负面后果表示负面后果的一般类,其中每个类可以包括数百个或数千个更详细的实际负面后果。对于中等复杂性或更高复杂性的软件系统而言,这实际上为用户创建了根本原因条件和负面后果的数百万个可能的组合。这些数百万个可能的组合阻止了产生有效的文档以描述所有可能的故障模式,这阻止了文档路由成为真正全面的故障诊断的可行途径。大量的组合还阻止了所有可能的组合在软件中被明确地枚举或以其他方式被捕获以用于自动显示,这显著促成了解决软件中的该问题的复杂性。

子问题#3:实际的原因和影响可能取决于周围系统状态而变化。取决于该系统的其余部分的当前状态,检测到的相同的单个根本原因条件的不同发生可能会任意地引起0个、1个、2个或更多个负面后果。也就是说,基于当时存在的其他任意操作条件或状态,根本原因条件与负面后果的关系在任何给定时间处通常似乎是随机的。图10A-10C是如下示例,其中相同的确切源条件“D”发生了不同的三次,但是每次具有三个不同的总体后果。图10A示出了源条件D第一次发生的影响。在这种情况下,D的第一次发生恰好引起了类型“U”和“X”的两个负面后果。图10B示出了如下情况,其中由于中间状态或条件中的某些任意差异,源条件D的第二次发生根本未导致任何负面后果发生。图10C示出了第三种情况,其中源条件“D”的第三次发生又引起了第三个总体类型的后果发生,其是负面后果“Z”。要注意的是,Z的单个负面后果与D的第一次发生时不同,D的第一次发生引起了两个后果U和X发生。关于此的示例是:电动机开始退化并且间歇性地卡住,从而生成间歇性错误,该错误然后在没有用户干预的情况下自动恢复。如果在运行需要该电动机的两个患者测试“U”和“X”时发生电动机错误,则U和X将由于该电动机而失败。在这种情况下,我们想要报告该电动机错误,作为两次患者测试失败的原因。稍后,如果该系统处于空闲状态,并且该电动机在相对不重要的自动冲洗例程期间具有错误且其自动恢复,则不存在针对该错误的负面后果,并且不需要直接向用户报告该电动机错误(尽管通常仍将在内部记录该电动机错误)。再稍后,如果整个系统正在通过启动初始化过程——其中所定义的是任何错误将导致整个系统停止“Z”——并且发生了电动机错误,则整个系统将停止,并且我们想要报告该电动机错误作为整个系统停止的原因。存在如下示例:其中取决于该系统中的其他状态,相同的共享源条件——检查电动机是否具有问题的软件代码——可能引起不同的后果。因此,无法始终先验地预测给定源条件的后果。

通常,仅因为根本原因条件的发生并不意味着将始终因此而发生负面后果,也不意味着当它产生负面后果时将每次都发生相同的(一个或多个)负面后果。因此,始终向用户显示每个可能的根本原因条件发生、并且特别是没有恰好引起负面后果的那些条件可能会对用户产生很多误导性信息,并且因此会潜在地增加故障诊断的负担而不是减少该负担。

在理想情况下,对该问题的解决方案应当是:仅在适当的时候直接向用户报告根本原因条件,并且当它们完全没有影响、或者对用户当前正在诊断的负面后果没有影响时不报告它们。该问题源自于根本原因条件如何通过各种代码设计而传播、以及如下其他条件的数量:这些其他条件将也控制任何给定故障模式的中间状态记入考虑。这表示了显著复杂的因素,该因素必须被解决以提供对总体问题的可靠解决方案。

子问题#4:根本原因条件的影响是开放式的(open-ended)。根本原因条件与负面后果的关系相对于时间是开放式的。过去几秒钟、几分钟、几小时或几天内发生的根本原因条件可能会随着对该系统做出新的请求而继续生成新的负面后果。例如,考虑如下源条件,该源条件的单次发生的影响最初在某个中间节点处停止(例如,如图10B中所示),但是在该节点上设置了可能引起未来的延迟故障发生的某些状态(例如,图10C,但是针对条件D的单次发生)。例如,在发生了源条件之后的一个小时,用户做出需要检查该节点中的状态的请求,并且因为该状态被源条件设置为对于该请求来说是负面的一些内容,因此用户经历了负面后果(诸如,图10C中的负面后果Z)。在该示例中,在时间上进一步向前,即在发生了源条件之后的一整天,用户做出了另一种请求,并且经历了另一种类型的负面后果(例如,除了后果Z之外还导致了负面后果V)。并且最后,在一个月后,相同的事情在另一个用户请求下再次发生,该另一个用户请求失败了,这是因为该中间状态仍然尚未被解决(例如,还引起了负面后果W)。该问题源自于根本原因条件对任何给定数据或执行状态的影响可以如何无限期地持续存在:根本原因条件可以产生负面影响的时间量由该根本原因条件所引起的任何中间负面状态持续存在多长时间来确定,这通常是未知的、不可预测的和/或开放式的,其基于所讨论的软件的设计。这表示了显著复杂的因素,该因素必须被解决以提供对总体问题的可靠解决方案:负面后果的原因可以回溯到根本原因条件的发生,该根本原因条件在时间上比尝试对该问题进行故障诊断的用户可能预期的要早得多地发生。

子问题#5:双重性质的因果路径。故障模式还可能是由“正面路径条件”引起的,该正面路径条件诸如启动、初始化、自动维护活动、更高优先级的用户请求等,尽管它们可能在一个区域中实现某些期望的正面后果,但是也可能同时在其他区域中促成负面后果。图11是源条件与正面后果89和负面后果76两者(而不仅仅是负面后果)之间的示例性路径的图。如可以看出的,单个源条件可能导致正面和负面后果两者的组合。这可能难以追溯,这是因为如何分类或报告可能引起正面和负面后果两者的根本原因条件、以及是否或何时应当甚至将其报告通常是不清楚的。正面后果被定义为不表示需要被补救的任何问题的针对用户的场景:例如,用户界面向用户显示他们的请求如所预期的成功了、或者子系统状态如所预期的那样等等。可能同时是正面和负面两者的根本原因条件的双重性质显著促成了解决该问题的复杂性。

对于更具体的示例,考虑具有以下简单性质的软件模块。该模块具有可以对其请求的两个功能:它可以运行“患者测试”,或者它可以运行“诊断测试”。该模块具有“模块状态”变量,该变量具有两个可能状态:运行(Running)或停止(Stopped)。为了使该模块运行“患者测试”,该模块必须处于状态“运行”中。因此,如果用户请求运行“患者测试”,并且该模块处于状态“运行”中,则显示成功。如果该模块不处于状态“运行”中,则显示失败。

为了使该模块运行“诊断测试”,该模块必须处于状态“停止”中。因此,如果用户请求运行“诊断测试”,并且该模块处于状态“停止”中,则显示成功;如果该模块不处于状态“停止”中,则显示失败。

在此,“模块状态”变量表示可以运行“患者测试”还是“诊断测试”的双重性质因果条件。当该模块处于“运行”状态中时,这对于运行“患者测试”是好的,但是对于运行“诊断测试”是不好的。相反,当该模块处于“停止”状态中时,这对于运行“患者测试”是不好的,但是对于运行“诊断测试”是好的。

图12中所示的表展示了该变量的双重性质,其中其两个不同的可能值均可以导致正面后果和负面后果。模块状态变量的值在任何给定时间处是好还是不好完全取决于在任何给定时间处其下游所需要的内容的上下文。也就是说,如果状态变量的当前值也可以导致正面后果,则基于该状态变量的值的任何错误的根本原因通常不会被认为是错误本身。这通常会让尝试解决传送出负面后果的因果条件的总体问题的开发者感到困惑,这是因为相同的因果条件也可能任意地促成正面后果。现实生活示例可能并且常常比这更加复杂,其中关于给定变量可能具有多于两个值,并且多于一个变量促成任何给定的正面和负面后果场景。因此,双重性质(同时为正面/负面)的上下文相关的值的这种复杂因素已经表示了对于提供对总体问题的解决方案的显著阻碍。

子问题#6:跨任意组成和复杂性的操作和表达式的正确因果元数据的传播。一些实施例通过利用并且扩展美国专利No.9,298,535中描述的技术来解决上面描述的主要报告问题,该专利通过引用并入本文中。示例性技术涉及将唯一的“错误序列号”(在此被重新定义并且重新称为“因果ID”)附加到各种代码路径,以帮助建立多个错误实例之间的父代-子代因果连接,这主要是通过将因果ID传递通过多个线程共享的对象来进行的。实现这一点揭示了另一个子问题:即当多个因果路径输入在一个或多个操作中会聚以产生组合因果路径输出时,如何正确地计算并传播(一个或多个)感兴趣的正确因果ID,其中单个组合输出的状态任意地取决于多个输入的状态。

这些会聚点中有许多是任意组成和复杂性的表达式,诸如由布尔、比较、算术和其他操作组成的复合表达式。顶级表达式、其子表达式及其子操作通常被预期产生如下输出值,该输出值表示对输入值的一些组合操纵。然后,总体表达式需要将(一个或多个)适当的因果ID从(一个或多个)输入值附加到输出值。在这些情况下,可能存在并且经常存在与去往该表达式的不同的多个输入值相关联的多个不同的因果ID。因此,这些表达式和操作应当确定来自该多个输入当中的哪个(哪些)因果ID要被包括以便传播到输出值,并且同样重要的是,确定来自输入的哪个(哪些)因果ID要从输出值中排除,以便防止向用户传送不正确的根本原因。

因果路径流动经过的操作和表达式的示例是各种布尔操作和表达式、比较操作和表达式、枚举状态操作和表达式、算术操作和表达式等。这些个体操作中的大多数从潜在地不同因果源中取得两个或更多个输入,并且产生单个因果输出。将因果ID附加到数据和执行路径然后意味着:该表达式或操作不需要将任何因果ID传播到输出、需要将因果ID中的一些或全部传播到输出。软件中的许多表达式由这些操作中的两个或更多个组成,从而产生通常由三个或更多个输入组成的复合表达式。累加或“折叠”表达式通常可以由数十个、数百个或更多个输入组成。

多输入原因到单输出结果的这些会聚点有时必须有意地丢弃因果信息,以便避免针对输出结果报告错误的原因。要注意的是,显著地,这些包括/排除决策可能跨相同表达式的多个求值而不同,这是由于输入值和输出值可能在求值之间不同。在传统上不清楚(或甚至未考虑)的是:应当如何关于哪个因果元数据将传播通过任何给定操作来做出这些决策。

上面的子问题5使该问题复杂化,其中单个数据状态(或表达式)的实际值有时可能具有同时促成正面后果和负面后果两者的双重性质。尝试在任何给定复合表达式的输入和/或输出穿过该表达式时单独地跟踪这些输入和/或输出是正面后果产生和/或负面后果产生是难处理的问题:作为一般解决方案,从开发的角度来看,它可能极度复杂且昂贵。

为了说明该问题,考虑以下两个输入的示例,该示例向下汇集到基于两个输入变量的简单布尔表达式。考虑如下软件模块:该软件模块在某些系统不可用时抛出错误异常。确定该系统是否可用的函数在此处被任意地定义成:基于两个基本布尔变量仅返回简单布尔表达式的值。以下伪代码对此进行了说明。

//在确定该系统是否可用时被计入考虑的两个值

//

bool bValue1 = ...;

bool bValue2 = ...;

//////////////////////////////////////////////////// /////

//

//返回该系统是否可用

//

bool IsSystemAvailable()

{

return(bValue1 && bValue2);

}

//////////////////////////////////////////////////// /////

//

//如果该系统不可用,则抛出异常(exception)

//(例如,在运行患者测试之前的检查)

//

void ThrowErrorIfUnavailable()

{

bool bSystemAvailable = IsSystemAvailable();

if(!bSystemAvailable)

{

Exception AvailabilityError(

“System is unexpectedly unavailable.”);

throw AvailabilityError;

}

}

//////////////////////////////////////////////////// //////

//

//如果该系统可用,则抛出异常

//(例如,在运行诊断例程之前的检查)

//

void ThrowErrorIfAvailable()

{

bool bSystemAvailable = IsSystemAvailable();

if(bSystemAvailable)

{

Exception UnavailabilityError(

“System is unexpectedly available.”);

throw UnavailabilityError;

}

}

在此,在需要一般可用性的某个函数(诸如,运行患者测试)之前在名义上调用ThrowErrorIfUnavailable(),并且在需要不可用性的某个函数(诸如,运行诊断测试)之前在名义上调用ThrowErrorIfAvailable()。从用户的角度来看,此处的目标是报告该系统在请求患者测试时为什么意外地不可用的源原因,以及报告该系统在请求诊断测试时为什么意外地可用的源原因。

从源代码的角度来看,这两个原因都流动经过相同的IsSystemAvailable()函数,该函数在ThrowErrorIfAvailable()和ThrowErrorIfUnavailable()两者中被调用。要注意的是,这两个throw(抛出)函数在检查IsSystemAvailable()的返回值的其“if”状态中也是不同的:一个直接检查返回值,而另一个检查返回值的否定(negation)。此外,IsSystemAvailable()所返回的值——不管它在两个截然相反的上下文中的哪一个中被调用——最终是从bValue1和bValue2的完全相同的值、以及将它们组合的完全相同的布尔AND表达式中导出的。如果bValue1和bValue2可以经由不同的源故障模式条件(它们通常可能处于任何正常的软件应用中)彼此独立地被修改,则该软件应当能够传送这些(一个或多个)源原因中的哪些要对IsSystemAvailable()所返回的值负责,并且其应当排除这次不是因果因素的原因。IsSystemAvailable()有效地取得这两个输入因果变量,并且返回单个因果变量,该输出变量的两个可能值可以各自表示失败或成功,这取决于IsSystemAvailable()为什么被调用。

考虑更复杂的示例,其具有与多个类型的操作一起处理的5个输入,这是实际软件中的许多因果路径的更典型示例。

//在确定该系统是否可用时被计入考虑的五个值

//

bool bValue1 = ...;

bool bValue2 = ...;

enum StateEnum

{

Initializing,

Running,

Stopped,

Resetting,

Diagnostics

};

StateEnum enState_SubSysA = ...;

StateEnum enState_SubSysB = ...;

float fSensorReading = ...;

//////////////////////////////////////////////////// //////

//

//返回该系统是否可用

//

bool IsSystemAvailable()

{

return

(bValue1 && bValue2) ||

((((enState_SubSysA == StateEnum :: Running)&&

(enState_SubSysB!= StateEnum :: Diagnostics))||

((fSensorReading <= 5000.0)&&

(fSensorReading> 100.0)));

}

// ...包括与上面相同的“ThrowErrorIf ...()”函数

//

现在,计算可用性的IsSystemAvailable()中的表达式是复杂的嵌套表达式,该嵌套表达式包含以下六种类型的九个运算符:布尔AND;布尔OR;等于;不等于;小于或等于;以及大于。这些组合了五个单独的数据值输入:bValue1;bValue2;enState_SubSysA;enState_SubSysB;以及fSensorReading。数据由三种不同的数据类型组成:bool(布尔);StateEnum;以及浮点。此处在源代码中未直接表述的是编译器在对总体表达式中的个体操作进行求值时生成的临时值。这些表示了来自较低级表达式的中间输出以及去往较高级表达式的中间输入,并且因此可能还需要携带因果ID信息。

与上面的两输入版本一样,如果可以从不同的源故障模式条件(它们通常可能处于任何正常的软件应用中)以不同的方式设置这五个不同的数据输入,则该软件再次需要能够传送这些(一个或多个)源原因中的哪些要对IsSystemAvailable()所返回的值负责,并且需要排除不是在对该主要表达式进行任何特定求值时的因果因素的原因。

从任何已知的传统软件工程角度来看,软件可以如何自动地确定任意表达式的哪些输入因果性地促成了在对该表达式进行任何给定求值时的其输出的问题不是解决起来微不足道的问题。尽管并非软件中的所有因果表达式都直接地如5输入示例那样复杂,但是存在通常等效地复杂和/或甚至更加复杂的许多表达式,这是因为表达式通常被隐含地分解成不同的子表达式,这些子表达式在代码的不同函数、块或区域中被计算。即使没有在其软件中观察到如此复杂的表达式,但是通常也存在需要以相同或类似的方式来处理的具有等效复杂性的隐含表达式。

先前,不存在广泛已知的方法来处理以正确的方式跨任意源代码表达式来传播因果信息的该子问题,其中此处的正确性被定义为向用户提供适当的源原因,并且不向用户提供不适当的源原因。

子问题#7:混合软件架构。中等或更大范围的大多数软件系统由许多彼此通信的不同子系统组成,每个子系统具有不同的内部数据、事件以及错误处理表示和设计。这使得在任意根本原因条件跨这些不同子系统的输入/输出和架构边界传播时跟踪这些根本原因条件的因果影响变得更加困难。该因素还与子问题#2组合,从而给用户解决总体问题创造了另一个主要障碍:许多不同的子系统实现方式通常包含并且定义了许多不同的根本原因条件(例如,错误或事件类型),并且任何个体子系统通常不知道并且也无法知道与个体子系统交互的其他子系统中的所有可能的外部根本条件或根本事件,这些外部根本条件或根本事件可能导致该个体子系统产生负面后果。传统上,这已经使得个体子系统报告其负面后果的任何特定外部原因是极其困难的。

子问题的总结:所有这些因素一起构成了总体技术问题,该总体技术问题的性质非常复杂,以至于迄今为止它尚未用任何一般或有效的方式被解决。通常,开发者对该总体问题的看法是:软件系统中的原因和影响的行为是如此及其复杂以至于解决方案也必然是及其复杂的,并且因此开发者得出如下结论:该问题因其性质而难以解决。也就是说,他们得出如下结论:就开发资源和/或计算能力而言,该问题过于资源密集而无法解决,并且因此仅能做出有限的尝试来解决它,如果他们进行了任何尝试的话。因此,这些子问题是重要的复杂因素,这些因素已经共同地、根本地和传统地在经历负面后果的用户与该用户能够快速且有效地标识该后果的(一个或多个)根本原因条件之间产生了主要技术障碍。

已知的现有技术解决方案通常要求所涉及的所有软件子系统共享重要的基础架构和实现方式细节(诸如,低级别下的共享错误表示),这在现实世界软件几乎始终在其中操作的高度混合的软件和产品环境中通常是不可行的或不具成本效益的。这些解决方案也仅仅覆盖了该软件中的所有可能因果场景的一小部分。

理想的解决方案应当自动地将显示给用户的任意负面后果连接回到在该软件中检测到的引起了那些后果的任意因果事件。正确地将因果事件追溯到根本原因的系统应当在经历了负面后果时即时地、或者以尽可能接近即时的方式在现场(live)软件中产生并且显示回到(一个或多个)因果事件的这种连接。不应当需要在经历了负面后果之后运行任何“事后”工具或分析。关于根本原因的反馈和详细信息应当尽可能即时且简单地来获取。该系统应当在显示负面后果的地方、或者尽可能接近地向用户显示要对任何给定负面后果负责的(一个或多个)因果事件,以最小化或消除对于用户针对(一个或多个)原因来搜索用户界面的另外区域(例如,其他屏幕、应用或文件)的需要。这种理想解决方案应当将对软件架构、设计和实现方式的其他方面的影响最小化。它应当允许跨针对用户的负面后果与初始根本原因条件之间尽可能多的因果路径来连接因果事件和负面后果,以便最大化问题覆盖。它应当支持多对一、一对多、以及共同共享路径性质的因果事件条件与负面后果之间通常遇到的关系,并且尽可能地具有确定性,并且尽可能地避免启发式方法,以便既最大化故障诊断的准确性又最小化实现成本。实施例尝试实现这些目标中的一个或多个,但是不需要实现这些目标的任何给定组合。

要注意的是,大多数软件系统可以向用户显示负面后果。通常,总体软件系统中的某个地方应当已经存在至少一个条件操作,该软件系统在内部检测初始原因,并且然后以至少一种方式作用于该条件检测,以最终导致显示负面后果。也就是说,软件的一般性质是:显示给用户的所有负面后果最终将连接回到总体软件系统中的一个或多个条件。

可以驱动负面后果的显示的任何软件条件在此处被定义为“因果事件条件”。因果事件条件被满足并且其输出子句因此进行执行的实例被认为是“因果事件”。引起负面后果的因果事件条件可以是,例如:针对错误条件的任何种类的检查;(传感器数据分析、网络数据分析、用户提供的数据分析、逻辑检查、系统可用性检查、关于(一个或多个)状态的检查等);针对用户动作的任何种类的检查(针对用户按下“停止”按钮以中止其他动作的检查、针对用户安装新软件的检查、针对用户启动软件的检查、针对用户重置系统的检查、针对用户初始化或重新初始化持久的或存储器内的数据(例如,数据库或其部分)的检查等);任何其他种类的检查(例如基于经过的时间间隔或其他系统计数对开始参与任何上述用户动作的自动化版本的检查、针对所需资源的耗尽或到期的检查等)。

取决于所涉及的软件,所有这些条件可能共同地导致负面后果。实施例可以利用软件的这种一般性质——其中负面后果最终源自于软件内的条件——以向用户通用地显示任意因果事件,作为负面后果的原因。要注意的是,软件中的因果事件条件可能由软件外部的物理事件触发,该物理事件诸如操作者进行的物理按钮按压、与传感器的流体交互、有故障的硬件等。因此,负面后果的“真正”根本原因可能会扩展超出由软件检测到的因果事件条件,从而回到物理(非软件)领域中。因此,为了真正地补救问题,可能需要用户诊断软件代码中被触发的(一个或多个)条件的物理原因。然而,此处的描述仅涉及将在软件中检测到的根本条件(以因果事件的形式)表述为负面后果的原因,并且以尽可能最准确且有效的方式向用户报告这些确切的因果事件。

一旦因果事件条件被标识为负面后果的原因,对于包括以故障诊断指南等形式的针对用户的帮助的软件,这些指南通常以因果事件条件已经被标识的假设而开始(即使它们没有以该术语标识这些条件),并且以补救该特定条件的形式提供帮助。实施例可以以经历负面后果与找到因果事件条件之间的差距(gap)为目标,并且不以一旦确定了因果条件就如何补救该因果条件(这是高度特定于软件和系统的)为目标,并且包括故障诊断指南的软件系统通常已经以那些补救步骤为目标。

在驱动负面后果的显示时涉及多个条件的情况并不少见。也就是说,从软件根本原因条件到负面后果的因果性链可能涉及多于一个条件被求值并被满足。代码中的任何条件操作是否可以被认为是因果事件条件取决于在该条件被满足与在UI中显示负面后果之间是否存在因果路径。

可以促成负面后果的一组因果事件条件中的条件可以被分类成两个一般的组:“根本原因条件”和“中间条件”。可能全部或部分地发起负面后果的任何条件——其中在该条件之前没有任何其他条件处于因果性链中——被认为是根本原因条件,无论该条件是否被认为是负面条件。因果性链中的根本原因条件之后的任何条件都被认为是中间条件。

为了说明这些概念,考虑以下示例C++代码,该代码展示了一组简单的正面和负面后果行为。“//”注释是用于给以下描述中的行附加注解的标记:

class Sensor

{

public:

bool CheckSensor()

{

int l_nSensorReading = GetReading();

bool l_bSuccess = true;

if(l_SensorReading> SENSOR_READING_MAX) // [1]

{

//返回传感器超出限制

l_bSuccess = false;

}

if(l_SensorReading

{

//传感器仍被认为在限制内,但是将警告记录到磁盘

//

Log(“Sensor below expected minimum”);

}

Return l_bSuccess;

}

};

Void VerifySensor()

{

Sensor l_Sensor;

if(l_Sensor.CheckSensor()) // [3]

{

DisplaySuccess(“Sensor OK.”);

}

else

{

DisplayFailure(“Sensor failed.”); // [4]

}

}

Void VerifyAllSensors

{

Sensor l_Sensor1;

Sensor l_Sensor2;

if(l_Sensor1.CheckSensor()&& l_Sensor2.CheckSensor()) // [5]

{

DisplaySuccess(“All sensors OK.”);

}

else

{

DisplayFailure(“One or more sensors failed.”); // [6]

}

}

在此,函数Sensor::GetReading()被定义为从与Sensor类的实例相关联的一些硬件传感器获取读数。函数Log()将消息写入到磁盘。为了便于讨论,使用该函数被写入到磁盘的任何消息不应被认为是针对该特定软件的负面后果。函数DisplaySuccess()和DisplayFailure()被定义为用户界面函数。每当DisplaySuccess()被调用时,它会向用户显示正面后果。每当DisplayFailure()被调用时,它会向用户显示负面后果。可以在某时刻通过一些外部代码来在名义上调用函数VerifySensor()和VerifyAllSensors()。它们从传感器获取读数,并且基于这些读数将成功或失败直接显示给用户。

因此,导致DisplayFailure()被调用的任何条件被认为是因果事件条件。在检查代码时,可以在VerifySensor()中的行[4]处和VerifyAllSensors()中的行[6]处调用DisplayFailure()。

在检查VerifySensor()的情况时,仅在行[3]处的条件求值为false时调用DisplayFailure(),这仅在Sensor::CheckSensor()返回false时发生。Sensor::CheckSensor()仅在行[1]处的条件返回true时返回false。由于在行[1]处的条件之前该因果性链中没有更多的条件,因此行[1]处的条件是行[4]处的DisplayFailure()负面后果的根本原因条件。

类似地,在检查VerifyAllSensors()的情况时,仅在行[5]处的条件求值为false时调用DisplayFailure(),这仅在Sensor::CheckSensor()返回false时发生。同样,Sensor::CheckSensor()仅在行[1]处的条件返回true时返回false。行[1]处的条件也是行[6]处的DisplayFailure()负面后果的根本原因条件。

在该总体示例中,Sensor::CheckSensor()中的行[1]处的条件是两个不同故障模式的根本因果事件条件,该两个不同故障模式为:行[4]处的DisplayFailure()负面后果、以及行[6]处的DisplayFailure()负面后果。行[3]和[5]处的条件被认为是中间条件。它们各自在用以驱动一个或多个负面后果的路径中,但是它们无法在未首先满足一个或多个其他根本原因条件的情况下被满足。行[2]处的条件不被认为是因果事件条件,这是由于按照关于在该特定软件中什么被认为是负面后果的上述定义,在该条件被触发与任何所定义的负面后果之间不存在任何路径。然而,如果“将警告记录到磁盘”被改变为认为是负面后果,则行[2]处的条件将被认为是因果事件条件,并且是其处的根本原因条件。

回到一般的软件,因果性链中的多个条件可以按“串行”配置来布置,其中条件A的求值和满足引起了条件B的求值和潜在满足,并且依此类推,直到显示负面后果。在这种情况下,这些条件一起表现为“逻辑AND”,其中需要满足串行配置中的所有条件以使得负面后果被显示。类似地,多个条件可以以“并行”配置来布置,其中条件A的求值和满足或条件B的求值和满足都可以使得负面后果被显示。在这种情况下,这些条件表现为“逻辑OR”,其中满足并行配置中的任一个条件将使得负面后果被显示。

通常,因果性链由以串行和并行方式两者来布置的多个条件组成。上面的示例代码中就是这种情况。行[1]处的根本原因条件与行[3]和[5]处的每个后续中间条件是串行的。另外,行[5]处的条件并且特别是其否定版本(针对“if/else”语句的“else”子句而隐含的)包含两个根本原因条件的并行配置。实际代码通常也具有复杂得多的因果性链和条件组合。

还要注意到的另一个重要事情是:一些因果事件条件可能是“显式的”,其中开发者可在第一方代码中访问它们以及其直接输出子句以用于修改。其他因果事件条件可能是“隐式”的,其中基本条件以及其直接输出子句不可访问地被掩盖在开发者无法合理修改的代码中。这种代码的常见示例是封闭源、预编译的第三方代码,诸如专有操作系统、外部库或其他难以修改的软件。不管开发者对它们的相对可修改性或可访问性如何,第三方代码中的隐式根本原因条件仍然可以经由函数调用或数据递送(从包含根本原因条件的第三方代码到最终将驱动负面后果显示的第一方代码)来驱动第一方代码中的负面后果的显示。

第三方代码中的隐式条件的常见示例包括:进程启动(例如,由于操作系统检测到的外部条件而调用第一方软件中的main()进程启动函数)、以及用户输入事件(由操作系统检测到的键盘和鼠标输入条件,该操作系统在第一方软件中创建了通知)。这些示例可能潜在地驱动负面后果:直到初始化完成之前,进程的基本功能可能在启动时不可用,从而向用户临时产生“不可用”报告,并且用户输入可能导致取消先前请求和/或“不可用”状态。这两个条件都可能导致用户的无意中的沮丧,即使它们是由该用户发起的。尽管它们表示针对软件的某些区域的正面路径,但是它们也可能在软件的其他区域中产生负面后果。

因此,在一些实施例中,可能驱动负面后果的第一方代码中的这些回调和数据递送可以被认为是原始第三方条件的直接输出子句的扩展输出子句。此处涉及条件的“输出子句”的描述指代与第一方代码中的因果条件相关联的直接输出子句、和/或由第三方代码中的因果条件间接执行的第一方代码中的“扩展”输出子句,视情况而定。

要注意的是,根本原因条件与最终从其中显示的(一个或多个)负面后果之间可能存在数据流的许多层和许多边界。这些层可以包括:不同类型和不同值的多个数据实例、不同的函数调用、不同的参数、不同的类、对象、线程、进程、计算机等。

从软件用户的角度来看,在一些实施例中,只要UI向用户显示负面后果,UI就可以查找并且显示引起了该负面后果的特定根本原因条件实例,如果该软件能够跟踪它的话。也就是说,该软件在因果事件发生时跟踪这些因果事件,并且报告因果事件的特定实例作为负面后果的原因。

一些实施例合期望地避免和规避了尝试在任何软件系统中分析高度复杂、完整的原因和影响图的庞大且难解决的技术问题,并且避免了列出或以其他方式捕获软件系统的所有可能的故障模式。也就是说,至少一个实施例不尝试标识、分类或枚举每一个可能的根本原因条件是如何在任何给定软件系统中促成或不促成每一个可能的负面后果,这是因为实际上这样做仅仅是不可行的。取而代之,为了在现场软件情形下自动地确定负面后果的任何给定发生的实际根本原因,这种实施例利用并且依赖于该软件的代码本身的结构(其最终是定义了该软件的原因和影响图的事物)以自动地传递其生成的任何中间影响、以及因此其生成的任何最终负面后果的原因。

更具体地,实施例可以修改代码中的根本原因条件,诸如检测错误的条件、检测用户动作的条件、或已知可能引起负面后果的系统状态或输入的其他检测,以将其执行的每个实例(即,每个因果事件)记录到数据库,并且立即利用新的唯一的动态生成的因果ID值来标识这些实例。然后,对因果图中的每个因果节点(即,可能促成负面后果的每个数据变量、对象实例、函数调用、代码块等)进行修改,以附加并且传递可变的因果ID项。该附加的可变因果ID项的值有效地“指向”数据库中的(一个或多个)因果事件,这些事件已知是该节点的当前数据或执行状态的“原因”。取决于期望的系统行为和系统约束,因果ID项可以实现为等于空(nil)或非空因果ID值的单个因果ID变量,或者实现为可以可变地包含零个、一个、二个或更多个非空因果ID值的容器。当根本原因条件通过改变其状态(即,通过修改数据或执行某些操作)将影响传播到一个或多个下游节点中时,它将标识了由该根本原因条件生成的当前因果事件的因果ID值通过放置到受影响节点的所附加的因果ID项中而传递到该受影响节点,以用于与该节点的新状态一起存储。然后,该节点的所附加的因果ID项中的(一个或多个)因果ID的值描述了当前状态的原因以及在任何给定时间处对该节点的影响。也就是说,附加到因果节点的因果ID项用作关于该节点的当前状态的元数据。中间节点还在适当的时候将其可变因果ID项传播到其输出影响。也就是说,如果当前中间节点的值或状态引起了另一个下游节点中的新值或状态,那么当前节点的因果ID项也被传递到该下游节点并且存储在该下游节点中,从而潜在地覆盖该节点中的先前因果ID项的值。要注意的是,每个节点的因果ID项元数据是可变的,并且其值将通常随着每个因果节点的状态改变而改变。

如果节点的当前状态或影响然后导致了负面后果出现,则处理该负面后果的显示的代码使用附加到该负面后果的输入节点的因果ID项中的(一个或多个)因果ID的值,以从数据库中查找并且显示该负面后果的(一个或多个)根本因果事件,这些事件现在已知是该后果的原因。从用户的角度来看,这表示了如下解决方案:当用户查看负面后果时,该软件可以以来自数据库的因果事件的形式来显示现在已知引起了该后果的任意根本原因条件。

这种基本设计允许并且依赖于所讨论的软件的固有的任意结构,以便准确地、动态地且自动地传送出任何给定影响的实际原因,而开发者或软件从不需要知晓或理解该系统中的所有可能的原因、所有可能的后果或它们之间所有可能的连接的全体。

一些实施例自然地向上扩展以处理任意复杂性的软件,而无需每个实施例的实现方式本身变得显著更加复杂。尽管这些示例可能是简单的,但是它们应当准确地反映本发明的能力和精致(elegance)。

要注意的是,实代码的任何给定区域可以由一个或多个以下类型的因果代码来描述。也就是说,代码的任何给定部分可以在这些区域之间包含一些函数重叠,这是因为它可以包含因果事件条件、因果路径和/或用户界面显示中的一个或多个。

在一些实施例中,当因果事件发生时、即当因果事件条件的输出子句响应于预定条件而执行时,它确保了无论其采取什么其他动作,它也始终将因果事件条目写入到描述该事件实例的因果事件数据库。该条目应当包括导致了因果事件的系统状态信息。在将事件条目写入到数据库或作为其一部分之前,该子句动态地生成新的且唯一的因果ID值,以在数据库中标识该事件以及其条目。然后,将局部生成的因果ID值作为该条目的一部分写入到数据库中。在此时,因果ID值可以被认为是指向现在处于数据库中、或者不久将处于数据库中的特定因果事件的“指针”。只有用户界面级别的代码才将会“取消引用”该指针来查找和显示该事件,但是因果事件条件与UI显示之间的所有其他代码可以传递该指针并且根据需要来存储它,以便在UI中将其引向任何相关联的负面后果。因此,在因果事件条件的输出子句中刚生成的新因果ID值也将局部地保留在该子句中,以便根据需要进行进一步传输。

如果已知另一个因果事件导致了当前事件被生成,如通过另一个因果路径是否部分或全部地导致了当前因果事件条件所确定的那样,则输出子句还可以在适当的时候将当前事件的“父代因果ID”字段设置到其他事件,以反映这些事件之间的因果关系。这支持“级联错误”或“级联事件”场景,其中因果事件有时可能是由其他因果事件引起的。

要注意的是,针对第三方代码中存在的隐式因果事件条件的第一方代码中的输出子句也可能并且也应当通常生成因果事件,如果它们可能生成负面后果的话。局部的第一方输出子句的示例是由第三方代码调用的第一方代码中的函数回调、或从第三方代码到第一方代码中的数据递送。第一方代码被定义为开发者能够修改的代码,并且第三方代码被定义为开发者不能够修改的代码。在这些情况下,通常,由第三方条件执行的第一方代码的第一行应当将因果事件发布到数据库。这解决了如下一般问题:即,针对一些因果事件条件的实际条件式和主要子句存在于第一方代码之外。通常,因果事件条件对应于错误条件、操作者动作、以及已知潜在地引起针对用户的负面后果的系统状态或输入的其他检测。

因果事件条件与确定是否应当显示负面后果的用户界面代码之间的任何数据或执行路径被认为是因果路径。因果路径应当在必要时附加可变的因果ID项参数,以确保任何所显示的负面后果的(一个或多个)因果ID可以使其到达UI系统。

因此,每当因果事件条件的输出子句采取可能导致UI潜在地向用户显示负面后果的一个或多个动作时,该子句应当传递新的因果ID值作为该动作的一部分。该因果ID值描述了采取(一个或多个)动作的“原因”,并且因此描述了UI为什么显示负面后果的原因(如果其最终显示了负面后果的话)。也就是说,因果ID描述了数据库中(或不久将处于数据库中)的哪个因果事件是用户观察到负面后果的原因。

动作可以包括调用函数和触发其他执行,和/或修改任何种类的数据,包括状态变量、局部变量、对象和/或其成员变量、持久数据等。

在各种实施例中,因果ID项中的(一个或多个)因果ID的(一个或多个)当前值描述了将导致负面后果的数据的当前值的原因、和/或将导致负面后果的当前代码执行的原因。目标是确保:导致UI触发要显示的负面后果的任何动作和/或数据还包括指向要显示的该负面后果的(一个或多个)事件原因的正确因果ID项。

传递因果ID项值可以以任何数量的方式而发生:作为直接函数参数、作为添加到正在被传递的对象实例的另外数据成员、作为绑定到正在被传递的变量的值(例如,经由利用泛型(generic)或模板实现的包装类(wrapper class))、作为绑定到状态变量的值、作为返回值、作为现有变量旁边的单独变量等。当以这种方式被附加到动作和数据时,因果ID项成为关于那些动作和数据的元数据,使得动作或数据段的因果ID项表示当前正在采取特定动作的“原因”、或数据在该特定时刻处具有其特定值的“原因”,其中“原因”被认为是存储在数据库中的一个或多个因果事件。

另外,将多于一个输入因果路径组合成较少数量的输出因果路径的任何操作应当选择适当的输入因果ID项(如果有的话)以包括在输出中。这还意味着:当已知来自输入的某些因果ID项不是输出值或动作的原因时,可以有意地将它们从输出中排除。

如果用户界面由于数据分析(诸如,成功/失败值的检查)或直接动作请求(诸如,用以显示错误的函数调用)而确定必须向用户显示负面后果,并且存在附加到该数据或动作请求的因果ID项,并且该因果ID项包含一个或多个非空因果ID值,则UI在事件数据库中查找由那些(一个或多个)非空因果ID值指定的(一个或多个)因果事件条目,并且将那些一个或多个相关联的事件条目视为(一个或多个)候选事件,以用于向用户报告作为要显示的负面后果的(一个或多个)原因。

如果候选事件的父代因果ID是空因果ID,则UI会将该候选事件的信息与负面后果显示报告给用户,作为负面后果的原因。如果候选事件的父代因果ID不是空因果ID,则UI会查找与该父代因果ID相关联的父代因果事件,并且然后该父代因果事件成为新的候选事件。重复该过程——遍历每个事件的父代因果ID所隐含的父代-子代事件树——直到找到其父代因果ID等于空因果ID的候选事件,在此时,将上一个候选事件的信息与负面后果显示一起显示给用户,作为负面后果的原因。要注意的是,这支持多个事件潜在地被显示为单个负面后果的根本原因,这可能在现实中发生。

如果正在查找的因果ID的事件条目尚不处于数据库中,则该查找可以等待事件出现,和/或在超时的情况下失败并且返回诸如“信息尚不可用”之类的显示。按照定义,不是空因果ID的所有因果ID最终应当指向数据库中的事件条目,使得数据库中的该事件条目最终被预期是存在的。

将多于一个输入因果路径组合成较少数量的输出因果路径的任何操作必须选择适当的输入因果ID项(如果有的话)以包括在输出中。这还意味着:当已知来自输入的某些因果ID项不是输出值或动作的原因时,可以有意地将它们从输出中排除。一般且常见的示例是如下复合表达式:它从多于一个因果数据路径取得多个输入值,并且然后对这些输入值应用各种操作,诸如等式比较、布尔AND、OR和NOT、算术操作等,以计算单个输出值,然后该输出值将成为去往UI的因果路径的一部分。如果一个或多个输入具有附加到它们的因果ID项,则该表达式需要正确地选择那些因果ID项中的哪些要传播到输出值、以及哪些要从输出值中排除。这是通过修改或重载所涉及的操作来完成的,以便分析输入值,并且从具有已知导致给定输出值的值的输入中选择针对输出值的(一个或多个)因果ID项。

如果该输出然后用作去往另一个操作的输入(如通常在复合表达式中发生的),则按照大多数编程语言的指定行为,临时值以及该语言进行的其他自动处理负责照顾(take care of)将适当的因果ID项向上传播通过该表达式中的每个操作。一旦操作已经通用地被修改成使用本发明的实施例的概念来处理因果ID项,则从因果报告的角度来看,涉及该操作的大多数表达式自动地“负责照顾”它们自己。

如果在任何时候该软件都需要填入因果ID项值,例如作为动作或数据分配的一部分,并且没有任何因果ID或因果ID项可用(这是因为例如去往该因果动作或数据修改的输入或输出尚未针对因果事件报告而被修改),则取而代之应当指定空因果ID值。这指示了针对被附加的数据或动作的“没有已知原因”,并且始终被认为是要填入的安全值。空因果ID将导致没有任何因果事件信息被显示给用户,这要好于错误的因果事件信息被显示给用户。

一些实施例将针对任何单个负面后果发生而一次报告单个因果事件。对于用户来说,这通常证明是绰绰有余的。然而,在一些实施例中,针对任何一个后果一次报告多个原因也是可能的。对任一种报告模式的支持是通过如何实现附加到因果路径的因果ID项来完成的。为了简单起见,该描述中的大多数描述了将通用因果ID项附加到因果路径。如果将因果ID项实现为单个因果ID值,则这通常支持针对任何给定负面后果最多报告零个或一个根本因果事件。在这种情况下,空因果ID值指示因果ID项所附加到的节点的当前状态的“没有已知原因”,并且非空因果ID值指代要对该节点的值(以及因此由于该节点而导致的任何下游负面后果)负责的因果事件。如果将因果ID项实现为零个、一个、两个或更多个因果ID值的容器,则这通常支持针对任何给定负面后果报告零个、一个、两个或更多个因果事件。如果因果ID项容器为空(即,不包含任何因果ID值),则这指示因果ID项所附加到的节点的当前状态的“没有已知原因”。如果因果ID项容器不为空,则该容器中的每个因果ID值表示要对该节点的值(以及因此由于该节点而导致的任何下游负面后果)负责的(一个或多个)因果事件。

关于图13A-13D描述了用以最小化其对代码的影响的各种重要技术。图13A示出了实施例中的简单原因和影响。粗体圆形节点表示具有所附加的因果ID项变量的数据或执行路径的实例(父代/根节点B、C、D、子代/叶节点V、X和y、以及中间子代节点)。在该示例中,为了简化讨论,将每个节点定义为执行计算步骤的软件内的因果函数/函数对象,该因果函数/函数对象通常生成然后由下一个节点使用的值/结果。为了清楚起见,此处未示出每个函数的值/结果——仅示出了被传递到后续函数的每个函数的因果ID项。对于源条件(因果事件条件72)而言,粗体节点(B、C和D)表示根本原因源条件,该条件在被执行时将(除了任何函数值之外)生成数据库中的因果事件、以及用以标识该事件的相关联的唯一因果ID值,其中当已知该上游节点的值会引起下游节点的值或状态时,该因果ID值将被向下游传递到使用该上游节点的结果作为函数输入的任何后续函数(节点)的因果ID项。对于中间路径节点(使用上游函数/节点的后果的那些函数或采取那些值作为输入的函数),粗体节点示出了附加到该相应数据或执行路径的因果ID项变量。粗体节点也将包含任何函数值,但是此处未显示它们。在任何给定时间处的每个节点的可变因果ID项的值指示该节点的当前状态的(一个或多个)因果事件原因。通常,如果每个下游节点(V、X、Y)与负面后果相关联(例如,具有偏离标称系统状态的函数值),则可以使用该下游节点的因果ID项以在数据库中查找针对该负面后果的(一个或多个)因果事件,并且经由UI显示来呈现它们作为负面后果的(一个或多个)原因。

图13B是如下示例的原因和影响图:其中类型“D”的源条件第一次发生,生成了具有针对其任意生成的唯一因果ID“1”的因果事件实例,并且将该因果ID传递到其输出影响的(一些)因果ID项。沿粗体路径120的所有节点恰好共享共同的因果ID项“1”,这是因为它们的当前状态全都恰好由因果ID“1”所指代的事件条件引起,并且当设置它们的当前状态时,将该因果ID项设置在它们上,作为因果事件条件D的输出子句的结果。在准备显示所导致的负面后果“V”、并且看到附加到要对驱动该后果的显示负责的数据或动作的非空因果ID项“1”时,UI就知晓数据库中应当存在因果事件或者不久将在数据库中存在因果事件,可以查找并且显示该因果事件作为负面后果的原因。在这种情况下,它将显示与因果ID“1”相关联的因果事件实例,该因果事件实例是发布到数据库的类型D的事件实例。

图13C是如下示例的原因和影响图:其中类型“B”的不同的源因果事件条件进行执行,生成新的因果事件,并且使用动态生成的唯一因果ID“2”来标识它。这种源条件发生导致了在上述源条件D的第一种情况下发生的相同类型的负面后果“V”的另一个实例。在这种情况下,相同类型的负面后果的该第二次发生显示了与“2”相关联的因果事件,该因果事件是类型B的事件实例,而不是如以前那样与“1”相关联的因果事件实例(类型D事件)。该因果ID沿粗体路径122传播,该路径122将错误V与源条件B所生成的因果事件实例相链接。

图13D是如下示例的原因和影响图:其中(与图13B中)相同的源因果事件条件D再次执行,该条件是与在第一种情况下相同的条件。它生成了与原因、影响和负面后果V的第一种情况下相同的确切模式。要注意的是,由于这是源条件的单独出现,因此会在数据库中生成新的因果事件实例条目,其具有被任意生成为”3”的新的唯一因果ID值。该因果ID沿粗体路径124传播,该路径124将错误V与源条件D所生成的最新因果事件实例相链接。

在此,负面后果V的该第三次发生将显示与因果ID“3”相关联的因果事件,而不是来自上述第一种情况的因果ID“1”。如果存在触发了因果事件条件D的该发生或与该发生相关联的不同核心数据值,诸如引起错误的不同传感器读数、或与该发生相关联的不同日期和时间戳,则所显示的事件将向用户反映关于根本原因条件D的不同实例的该不同信息。

如果节点在因果事件条件对其产生影响很久之后仍保持在相同的状态下,则附加到该节点的因果ID项的值也将保持。该节点将具有的任何未来负面后果影响仍将报告适当的因果ID项作为该后果的原因。这负责照顾如下问题:即其中源条件的影响相对于时间是开放式的。

在软件系统中,实际上是因果性的并且可以产生负面后果的条件、数据路径和执行路径的数量相对于非因果性的并且不产生任何负面后果的条件、数据路径和执行路径的数量通常是少数。虽然从该图中似乎实现因果事件报告需要许多重大修改,但是在许多实施例中,这并不构成对软件或软件开发的主要影响。

在一些实施例中,因果事件数据库用于记录每个因果事件(即,因果事件条件被满足以及其输出子句进行执行的示例)。数据库中的每个因果事件条目表示因果事件条件的输出子句的一次执行,并且至少包含以下字段:

描述:描述事件类型的代码、字符串或类似字段。

因果ID:用以唯一地标识因果事件实例的因果ID值。因果ID应当始终等于唯一/伪唯一(经保证在数据库内是唯一的、或变得不唯一的机会足够低)的非空因果ID值。

父代因果ID项:父代因果ID项字段,如果已知(一个或多个)其他因果事件引起了当前因果事件,则该父代因果ID项字段包含那些(一个或多个)其他因果事件的因果ID项。如果没有任何(一个或多个)其他因果事件已知引起了当前事件,则将父代因果ID字段设置为空因果ID。

其他相关信息:在理想情况下,还应当添加其他字段和信息,以便在对事件实例进行故障诊断时为用户提供进一步的诊断信息。示例包括:去往事件条件的相关输入,诸如相关的传感器读数、逻辑检查输入、相关类型的用户或自动化动作等;周围的操作上下文,诸如在事件发生时的日期/时间戳、发起了相关偶条件(even condition)输入的用户、或在该事件的时间处登录的用户、或源硬件标识符、源网络标识符等。

每当因果事件发生时,与因果事件条件相关联的输出子句除了执行该输出子句所执行的任何其他特定于应用的动作之外,还执行以下特定于发明的动作。因果ID生成:输出子句动态地生成新的唯一因果ID值,以标识该因果事件实例并且将其与该因果条件的其他执行(即,由该条件产生的其他因果事件实例)、或由任何其他因果条件产生的任何其他因果事件实例区分开。向数据库的因果事件发布:输出子句将新的因果事件记录写入到因果事件数据库、或发起将新的因果事件记录写入到因果事件数据库。要添加的数据库事件条目的字段可以包括:用以描述基本因果事件条件的事件的类型;事件的因果ID,其被设置为所生成的因果ID;如果已知该事件是由(一个或多个)其他事件经由上游因果路径引起的,则在适当的时候将当前事件的父代因果ID项设置为与该因果路径相关联的因果ID项,否则将当前事件的父代因果ID项设置为空因果ID或空集;针对事件的其他相关信息。当输出子句修改数据或采取可能引起负面后果的其他动作时,它会传递所生成的因果ID值作为该数据或动作的一部分,这发起了沿着(一个或多个)相关因果路径传送该因果ID。

在因果事件条件的子句与最终显示负面后果的UI代码之间的任何软件路径——其中需要某些数据值和/或执行来引起负面后果——在此被定义为“因果路径”。因果路径通常可以划分成数据路径和执行路径。作为解决方案的一部分,因果ID项变量被绑定(或附加)到因果路径。附加到因果路径的因果ID项变量的值表示该路径的当前状态的“原因”,即数据段为什么具有其当前值的原因、或者执行路径为什么当前正在执行的原因。“原因”被认为是由附加到该路径的因果ID项的当前值所指向的(一个或多个)因果事件。要注意的是,数据路径和执行路径之间可能存在显著的交互和重叠;执行路径通常修改数据,并且数据路径通常触发执行路径。

因果数据路径被定义为任何数据段,诸如简单变量、对象实例、数据库条目、复杂数据结构等,其中该数据的一个或多个可能值可以最终产生负面后果。按照软件工程中的任何数据的规范,因果数据可以以许多不同的形式来表示。例如,数据可以作为如下各项而存在:简单布尔值、整数值、浮点值、枚举值、指向其他数据的指针、其他原生类型;在聚合中,诸如类、结构、数组、容器、更复杂的数据结构等等。针对事件子句与显示负面后果的UI代码之间的每个因果数据路径,引入了因果ID项变量以便与该数据段相关联(附加到该数据段)。如本文中所讨论的,将因果ID项中存储的因果ID值描述为唯一或伪唯一的。通常,因果ID值应当至少基本上是唯一的。也就是说,应当向因果ID指派:已知的唯一值(例如,当前没有被任何进程使用的唯一值、以及当前没有被存储在任何持久数据中以供任何进程使用的唯一值,如由保持对ID的跟踪以验证唯一性的进程所指派的)、或者具有足够低的概率与其他伪唯一ID冲突的伪唯一值(例如,随机生成的值,其具有足够的总值范围,使得针对任何给定的系统状态,多个因果事件无意中使用了相同因果ID的概率在统计上是忽略不计的)。通常,任何伪唯一因果ID的所需位大小将取决于因果事件数据库中在任何时间处指派给根本因果事件的因果ID的最大数量。例如,针对一些简单的实施例,随机32位值将是足够的,但是针对更复杂的实施例,64位(或128位)的值可能是优选的。针对基本唯一的因果ID值的足够的生成器是如下生成器:其被设计成使得在所有系统实例的组合寿命内,系统在任何给定时间处使用两个无意中相同的因果ID的几率小于1%,尽管将领会的是,该机会越低越好。

绑定到数据段的因果ID项的当前值有效地描述了该数据段的当前值的“原因”。“原因”被认为是因果ID项所指代的数据库中的(一个或多个)因果事件(或不久将处于数据库中的(一个或多个)因果事件)。因果ID项的当前值指向(一个或多个)因果事件,已知这些因果事件描述了该数据段为什么具有其当前所具有的值。在一些实施例中,因果ID项的值在任何一般意义上都不涉及数据段的非值方面。因果ID项的值不涉及数据段的一般类型、变量名称、存储类型或位置,也不涉及与目标数据段有关的任何其他一般性质或元数据。因果ID项的值仅描述针对数据的当前值的一个或多个源事件。因为因果ID项变量的值描述了与其相关联的数据段的值的原因,所以每当与该因果ID项相关联的数据段的值改变时,该因果ID项的值就可以改变。

在各种实施例中,与因果数据相关联的因果ID项可以以不同的方式被表示并且绑定到该因果数据,如方便用于软件开发那样。可以构成因果数据路径的数据的示例性形式可以包括:简单变量、对象、数据库条目和其他数据路径。针对简单因果数据变量(诸如,布尔标志或枚举状态或状态变量),可以将因果ID项变量与简单数据变量一起引入到“现场”。各种编程语言可以提供更多的方式来容易地或有效地将因果ID值绑定到任意变量。例如,在支持它的语言中,可以使用通用因果类将因果ID项绑定到因果数据,该通用因果类包括针对目标因果数据的模板变量以及相关联的因果ID项变量。针对对象实例数据,可以将对象的类修改成包括因果ID项字段。或者,如针对简单变量那样,也可以使用通用因果类将保存了对象引用或值的变量绑定到因果ID,或者可以将新的因果ID变量与该变量一起引入到“现场”。针对数据库条目数据,可以将该表修改成包括该条目的因果ID字段。要注意的是,这指代除了存储在因果事件数据库中的因果事件以外的数据库中的数据。在一些实施例中,必要时使用类似或其他技术来将因果ID参数绑定到其他类型的数据。

因果执行路径被定义为该系统可能采取的任何可能产生负面后果的操作。执行路径包括函数调用、运算符、代码块、以及软件系统所采取的其他动作和动作组。执行路径可能直接导致负面后果被显示,诸如UI中的如下函数,其工作实际上是显示负面后果。它们还可能通过触发其他因果路径和/或修改因果数据而间接导致负面后果。执行路径还可能将数据的值改变为可能最终产生负面后果的值。

针对无论是通过间接修改数据还是通过直接影响UI改变而可能产生负面后果的每个执行路径,通常引入因果ID项参数以附加到该路径。绑定到执行路径的因果ID项的当前值有效地描述了该路径当前正在执行的原因。因果ID项的当前值指向数据库中的(一个或多个)因果事件(或不久将处于数据库中的(一个或多个)因果事件),已知这些因果事件描述了为什么该路径的当前执行被发起。由于因果ID项变量的值描述了当前执行路径正在运行的原因,因此每当调用该执行路径的原因改变时,该因果ID项的值就可以改变。

针对输出或修改因果数据、或调用其他因果执行路径的函数调用,可以将因果ID项参数添加到该函数的参数列表。替代地,还可以引入线程局部的后进先出(LIFO)数据堆栈,其中在进行函数调用或一系列调用之前,可以将因果ID项推到该堆栈的前面,并且其中可以在(一个或多个)函数调用内读取最前面的因果ID项,并且其中在从函数调用返回之后从该堆栈中弹出该因果ID项。尽管从实现方式的角度通常不推荐,但是由于它本质上隐藏了该函数接受因果ID项的事实,因此,如果非常深的调用堆栈中的许多函数需要被修改并且它们大部分充当传递(pass-through),则它可能是有用的。在一些实施例中,如果函数可以接受多于一个因果输入,则在修改因果数据或执行其他因果路径时,它可能需要从多个输入因果ID项当中进行选择。在一些实施例中,因果函数被适配成标识要对输出状态负责的因果输入,并且将这些输入的一个或多个因果ID项与输出一起包括。下面描述了正确地选择要负责的(一个或多个)因果输入。

软件函数运算符通常具有明确定义的行为,这些行为与数据修改紧密相关联。因此,它们倾向于跨越执行和数据路径边界。在一些实施例中,用于组成数据表达式以及对数据表达式进行求值的运算符通常不具有附加到它们的新因果ID项参数。取而代之,它们在因果领域中的工作通常是:根据每个运算符的基本行为的具体内容、以及在运算符的该当前执行时提供的特定输入值,来选择性地将适当的(一个或多个)因果ID项从其(一个或多个)输入值传播到其(一个或多个)输出值。

如果编程语言支持运算符重载,则运算符可以被重载,以便除了执行被请求的基础核心操作之外还提供对因果输入和输出值的处理,并且选择适当的(一个或多个)因果ID项以用于自动传播到输出值。如果该语言不支持运算符重载,则可以定义新函数,这些新函数表现为运算符,并且还可以执行(一个或多个)输入因果ID项向其输出值的适当且自动的传播。如果运算符可以接受多于一个因果输入,则在输出因果数据或执行其他因果路径时,它可能需要从多个输入因果ID项当中进行选择。

在一些实施例中,代码块通常使用传递到周围函数调用中的因果ID项,或者在必要时可以使用由另一个执行路径或数据路径返回的因果ID项来描述该块内的动作的原因。在一些实施例中,可以根据需要以类似或适当的方式来修改如可能存在于不同编程语言或定制源代码中的其他执行路径,以传递描述了该路径为什么被执行的适当因果ID项。

当因果路径需要选择输入因果ID项值以附加到其输出时,诸如在该路径下游的因果数据的指派期间、或者在触发另一个因果执行路径的执行时、或者为了在处理具有多个因果输入的操作的结果时选择将哪个(哪些)因果ID项指派给输出数据或动作,有必要针对该路径的输出提供(一个或多个)正确的因果ID项,以便正确地描述由当前路径引起的任何另外路径的当前状态的原因。通常,由于因果ID项的目的是描述因果路径的当前状态的原因(针对数据路径,该原因由数据的当前值来表示),因此关于要跨操作来传播哪个(哪些)因果ID项的选择取决于该操作每次执行时的实际输入和输出值,并且可能因实施例而变化。

以下因果ID传播规则的应用足以跨任意复杂性和组成的表达式来正确地传播适当的源因果事件信息,以便准确地通知用户需要补救哪些源条件以改变在对这些表达式的未来求值时的后果。因此,因果ID项传播规则的应用允许该系统准确地通知用户需要补救哪些源条件以改变依赖于这些表达式的任何负面后果的未来后果。

每次指派或触发因果路径(例如,向因果数据段指派一值或执行因果执行路径)时,也会指派(或覆盖)附加到该因果路径的因果ID项。相对于其他任意操作所需的因果处理,指派被认为是特殊情况的操作,其规则可以取代用于处理其他操作的规则(这反映了软件中的正常指派和指派运算符也往往是特殊情况,这是因为它们表现得不同于加法运算符或布尔AND运算符等)。按照软件规范,指派的源——被复制到输出变量的输入值——通常可以是文字值、命名的变量值、或编译器生成的未命名的临时变量值。指派的输入变量的值(特别是当它是临时变量时)可以是任意组成和复杂性的表达式的结果。

通常,在指派因果数据或触发因果执行路径时指派的因果ID项直接源自于正在被指派或正在触发该执行的输入因果表达式。然而,存在输入表达式的因果ID项被忽略时的情况,并且取而代之,在复制该指派的基础数据时指派空因果ID项。在此处列出了针对指派与一些实施例一起使用的规则,并且这些规则描述了何时使用附加到输入表达式的因果ID项以及何时使用空因果ID项。要注意的是,虽然这些规则描述了对“数据段”(因果数据路径)的指派,但是它们也在选择将哪个因果ID项附加到因果执行路径时适用,其中因果ID项可以被或者可以不被直接指定为不具有所附加数据的因果ID项类型参数。考虑到处理效率或其他合期望的特质(trait),可以在一些实施例中使用其他规则。

具有负面后果的指派:如果因果数据段被指派给已知始终会最终引起负面后果的值,则附加到该数据段的因果ID项也应当同时被指派给表示导致新数据值被指派的(一个或多个)因果事件的因果ID项。如果指派的输入值是文字值,则因果ID项应当通常源自于:(a)因果事件条件的输出子句中生成的因果事件的因果ID,如果该指派是作为输出子句的一部分而发生的话;或者(b)要对该指派负责的另一个因果表达式或数据段的因果项。如果该指派是来自另一个因果数据段,则通常将源数据的因果ID项径直地(straight over)指派给该指派的目标。

不具有负面后果的指派:如果数据段被指派给已知最终不会引起任何负面后果的值,则与该数据段相关联的因果ID项应当同时被指派给空因果ID项值。这通常发生在事件条件子句之外的代码中,其中数据值可能被改变为不会引起负面后果的状态,例如由于某种形式的重置、恢复、初始化、新请求等。当事件条件子句在发起其自己的(一个或多个)负面后果的过程中清除(或重置)其他负面后果条件时,这也可以发生在事件条件子句中。这也可能意味着:可以有意地忽略附加到该指派的输入值的非空因果ID项,而替代地倾向于指派空因果ID项。如果最终已知“具有可能负面后果的指派”值(在下一段中描述)在当前上下文中不会引起负面后果,则最终忽略与该“有时的”值相关联的因果ID项,而倾向于空因果ID项。

具有可能负面后果的指派:如果数据段被指派给有时引起负面后果但并非始终引起负面后果的值,并且在当前执行上下文中不知道将发生哪种后果,那么,在一些实施例中,与该数据段相关联的因果ID项应当被指派给最终导致该数据值被指派的(一个或多个)因果事件的因果ID项,这类似于负面后果指派。如果该数据值不表示负面后果在稍后的执行中变得已知,则受影响的表达式将忽略在此指派的因果ID项,并且取而代之,传播或指派空因果ID项,这是因为现在已知该数据值不会产生负面后果。

具有负面后果但没有源因果ID项的指派:如果数据段被指派给已知引起负面后果的值,但是表示引起了该值的(一个或多个)因果事件的因果ID项无法被传送到该数据指派,则与该数据段相关联的因果ID项应当被指派给空因果ID项。这防止了UI向用户显示不正确的因果信息。不显示任何因果信息被认为比显示不正确的因果信息更好。

通常,因果变量——在此被定义为具有所附加的可变因果ID项的可变数据段——被指派某个复合表达式的输出,其中所指派的值由对一个、两个或更多个输入值进行操作的一个、两个或更多个操作组成。如果表达式的一个或多个部分本身就是因果值(因果变量或因果文字),无论它们是临时变量还是命名的变量,则从该表达式指派的因果ID项都应当反映出哪个实际子表达式和/或表达式中的哪个输入数据值促成了被输出的最终值。这允许针对负面后果的正确原因进行选择,并且允许负面后果的正确原因从源条件到UI显示流动通过该系统。附加到输出因果变量的因果ID项的值应当反映出哪个(哪些)因果事件导致该输出变量的当前值,并且因此反映出哪个(哪些)事件引起了感兴趣的实际负面后果。

实际上,包含两个或更多个因果输入的复合表达式或操作意味着两个或更多个不同的因果事件条件可能促成任何单个负面后果(由于可能原因的复杂性迅速增长,因此这使得追溯根本原因的常规努力变得困难)。另外,在一些情况下,可能是不同因果输入中的仅子集促成了表达式的结果,这意味着与没有促成实际结果值的输入相关联的任何因果ID项不应当被附加到该结果值。这对于防止向用户报告负面后果的错误根本原因是重要的。为了以因果方式来处理任意表达式,应当修改表达式中的每个个体操作以适当地处理其因果输入和输出。每个操作需要从其(一个或多个)输入中选择(一个或多个)正确的因果ID项以传播到其(一个或多个)输出,并且相应地从其(一个或多个)输入中选择要从其(一个或多个)输出中排除哪个(哪些)因果ID项。这一旦完成,则关于编译器如何处理复杂的嵌套表达式(例如,具有中间临时值)的典型性质就通常允许“自动地”计算总体表达式的因果输出,而无需开发者的特殊干预。

对于以下描述,让“因果值”被定义为(1)“基础值”(具有任意类型的应用特定的数据段,其也被设置为一个值)加上(2)描述了要对该基础值为什么被设置成现在的样子最终负责的(一个或多个)因果事件的因果ID项值的聚合。此处的基础值本质上是感兴趣的操作对其进行操作的正常数据类型和值。也就是说,它是针对未修改的操作的正常操作数。与该基础值聚合的因果ID项的值描述了使得该特定基础值被设置的一个或多个因果事件。

为了计算去往任意操作的哪个(哪些)输入因果ID项应当被包括以用于传播到该操作的(一个或多个)输出值、以及哪个(哪些)因果ID项应当针对传播而被排除,首先按照以下规则针对该操作来定义“因果表”,该因果表声明了:针对哪些输入-输出值组合,哪个(哪些)输入因果ID项要传播到该操作的输出。然后,修改操作实现方式,以展现由该因果表指定的因果ID项传播行为。因为附加到数据路径的因果ID项的目的是描述该路径的当前值或状态的“原因”((一个或多个)因果事件),所以该因果表是基于对该操作的所有可能输入值、当存在多个可能同时的输入时的其组合、以及其对应输出值进行分析和分类来确定的。具体地,针对操作的因果表首先从因果角度对该操作的输入值的所有可能组合、以及其对应输出值进行分类。因果表然后定义针对输入值的每个列出的分类要传播哪个(哪些)输入因果ID项。针对输入值的每个可能分类,选择要传播到输出值的(一个或多个)因果ID项是按照以下规则通过分析哪个(哪些)输入值引起了观察到的(一个或多个)输出值来确定的。

确定针对操作的因果表之后,然后在来自其常规操作对应物的代码中修改该操作,以在基本操作之上实现由该表所定义的另外的因果行为。经修改的操作的总体目标是将原始基本(非因果)操作应用于其所有输入因果值的基础值(好像它们不是因果输入一样),并且然后除了使其成为因果值之外输出与该基本操作相同的值,其中(一个或多个)正确的输入因果ID项被附加到该值。基于针对该操作开发的因果表,确定要附加到输出的(一个或多个)因果ID项。

实施例可以在其编程环境中以最方便的任何形式来修改该操作。例如,为了修改内置运算符(诸如,C++的“&&”、“||”、“*”运算符等),开发者可以提供接受因果值作为输入并且产生因果值作为输出的运算符重载(当可用时),其中因果值是运算符将通常对其操作的基础项类型加上因果ID项变量的聚合。如果运算符重载不可用,则开发者可以提供等效的函数,诸如CausalAnd()、CausalOr()、CausalMultiply(),等等。如果所讨论的操作已经在自定义函数中被定义,使得自定义函数现在需要处理因果输入和输出,则要么可以直接修改该函数,要么可以提供函数重载或替代方案来处理何时提供因果输入以及预期因果输出。

对于许多实施例,对传统操作进行以下修改以添加因果功能。将操作修改成接受作为“因果值”的一个或多个输入。针对不是因果值的任何输入,将该操作修改成暂时将输入提升为因果值(通常经由中间局部变量或临时变量),其具有等于原始输入值的基础值、以及等于空因果ID项的因果ID项。当该操作的一个或多个输入是因果值时,将该操作修改成输出因果值。然后,输出因果值是基础值和因果ID项值的聚合,基础值等于将基本的非因果操作应用于输入的基础值,因果ID项值描述了要对该基础输出值为什么被设置成现在的样子最终负责的(一个或多个)因果事件。利用反事实方法来确定哪些观察到的输入值被认为是观察到的输出值的原因:如果改变输入的任意子集的所有观察值将改变输出的观察值,则输入值的该子集一起被认为是观察到的输出的一个原因。如果改变输入子集的所有观察值将不会改变输出的观察值,则输入值的该子集不会一起被认为是观察到的输出的原因。通常,对于每个可能的输出值,分析观察到的输入值的每个可能子集,以确定输入值的哪个(哪些)子集要对观察到的输出负责(即,哪些输入子集被认为是该特定输出的原因,以及输入的哪个(哪些)子集不对该特定输出负责,以及哪些输入子集不被认为是该输出的原因)。在已经标识了要对观察到的输出负责的所有输入子集之后,与该子集中的输入相关联的因果ID项会被传播到输出。在到输出的该传播发生之前,要负责的因果ID项被折叠,其中可能地用以减少重复并且简化该输出。另外,可以收集关于输入因果ID项的该最终子集的另外的语义,并且将其包括作为总体输出因果ID项的一部分。然后,这些另外的语义可以用于以后为用户提供更详细的故障诊断逻辑和方法。

因果ID项,除了包含描述了要对相关联因果路径的当前状态负责的(一个或多个)因果事件的因果ID值之外,还可以描述该项中包含的(一个或多个)因果ID的另外的因果语义。因果ID项可以由以下形式之一来表示:单个因果ID“x1”;具有“方括号语义”的对等因果ID项列表,即“[x1...xn]”;具有“尖括号语义”的对等因果ID项列表,即“ ”;或者采用括号的对等因果ID项的混合(即复合)列表,其中方括号或尖括号集合中的一个或多个个体因果ID项本身可以是嵌套的对等因果ID项,并且其中这种嵌套可以是任意深的。也就是说,因果ID项可以是因果ID、或者是因果ID的嵌套方括号或尖括号集合的另一个因果ID项、或(一个或多个)更多的因果ID项,例如“[x1 x3 x5 <[x4 x2] x4>]”。任何列表中的因果ID项可以被复制任何次数,诸如上一个示例中的出现两次的因果ID项x4。以上因果ID项形式的含义将在接下来的段中描述。

要注意的是,因果ID括号语义的实际软件表示不需要在代码中使用括号。括号在此处用于表示某些语义。在一些实施例中,可以通过包含对等因果ID项、与对等因果ID项相关联的枚举值的容器的类型等来指示语义。

此处的每个因果ID项x1、x2...xn表示附加到操作的基础值输入项X1、X2...XN之一的因果ID项。也就是说,x1表示附加到主输入项X1的因果ID项,x2表示附加到主输入项X2的因果ID项,依此类推。因此,例如,如果操作仅具有2个输入,则它只关心与主输入项X1和X2相关联的因果ID项x1和x2。

让具有大写字母的方括号主输入项符号“[Aj...Ak]”表示针对某个因果操作的执行而观察到的变量主输入项的子集,其中同时改变该子集[Aj...Ak]中的所有项的值将改变因果操作的观察到的输出。然后,输入项“[Aj...Ak]”一起共同被认为是因果操作的观察到的输出的一个单一原因。

因此,让具有小写字母的方括号因果ID项符号“[aj...ak]”表示与主输入项[Aj...Ak]相关联的因果ID项集合。由于声明输入项子集[Aj...Ak]一起共同是特定观察到的输出的原因,因此与主输入项[Aj...Ak]相关联的因果ID项子集[aj...ak]表示一起共同引起了因果操作的特定观察到的输出的因果事件。

由于因果ID项[aj...ak]所表示的所有因果事件需要一起引起观察到的输出,因此可以构造以下等效布尔表达式来表示哪些因果事件(即主输入项)必须改变以便使观察到的输出改变:OutputWillChange = Change(aj) AND...AND Change(ak)。

Change(x)表示因果ID项x所表示的输入项是否由于改变了其相关联的(一个或多个)基础因果事件条件的输入而被改变,并且如果其被改变,则返回true。因此,仅当与关联于因果ID项[aj...ak]的因果事件相关联的所有条件都改变时,OutputWillChange才将为true。

让具有大写字母的尖括号符号“”表示针对某个因果操作的执行而观察到的变量输入项的子集,其中改变该子集中的任何单个输入Bi并且仅改变该输入就将改变因果操作的观察到的输出,并且对于中的所有输入项都是如此。中的每个输入项然后被认为是因果操作的观察到的输出的独立原因。

因此,让具有小写字母的尖括号主输入项符号“”表示与主输入项相关联的因果ID项集合。由于子集中的每个主输入项被声明是特定观察到的输出的独立原因,那么与主输入项相关联的因果ID项子集表示彼此独立地引起了特定观察到的输出的因果事件。

由于中的每个因果ID项所表示的每个因果事件被认为是观察到的输出的独立原因,因此可以构造以下等效布尔表达式来表示哪些因果事件(即主输入项)必须改变以便使观察到的输出改变:OutputWillChange =Change(bj) OR...OR Change(bk)。与方括号符号一样,Change(x)表示因果ID x所表示的输入项是否由于改变了其相关联的(一个或多个)基础因果事件条件的输入而被改变,并且如果其被改变,则返回true。因此,如果与关联于的因果事件相关联的条件中的任何一个或多个改变,则OutputWillChange将为true。

简而言之,方括号符号[...]指示方括号内直接指代的所有因果事件必须被补救以便使输出改变,尖括号符号<...>指示方括号内直接指代的仅一个(或多个)因果事件必须被补救以便使输出改变。例如,由“[pqr]”标注的因果ID项指示:仅当与p、q和r所指代的因果事件相关联的所有条件都被逆转(即,被改变为不发生)时,与该因果ID项相关联的基础值才将改变。相比之下,由“”标注的因果ID项指示:如果与p、q或r所指代的事件相关联的任何一个或多个条件被逆转(即,被改变为不发生),则与该因果ID项相关联的基础值将改变。

括号符号内的输入项和因果ID项可以被嵌套。例如,符号“”指示:要么与因果ID p本身相关联的事件必须改变、要么与q本身相关联的事件必须改变、要么与[rst]相关联的事件全部一起必须改变以便使输出改变。该嵌套可以是任意深的。

然后,还会嵌套等效的布尔表达式。例如,可以被表述为:OutputWillChange =Change(p) OR Change(q) OR (Change(r) AND Change(s) ANDChange(t))。涉及哪些因果事件需要改变以便经由因果ID项和所包含的因果ID来改变输出的这些布尔表达式还可以被转换回到因果ID项符号(即,括号内的符号)。例如,表达式:OutputWillChange = Change(r) AND (Change(s) OR Change(t))等效于括号符号:[r]。

因果ID项的这些布尔等效性可以用于通过如下方式来确定其他等效因果ID列表:首先将因果ID项形式转换成等效的布尔表达式,然后使用布尔代数法则将表达式变换成期望形式,并且然后转换回到因果ID项形式。例如,因果ID项:<[pq][pr]>等效于OutputWillChange=(Change(p) AND Change(q)) OR (Change(p) AND Change(q))。

根据分布性布尔定律,它等于:OutputWillChange=(Change(p) OR (Change(q)AND Change(q))。然后,可以将其转换回到:[p]。这表明了<[pq][pr]>在语义上等于[p]。

另一个甚至更简单的示例使用交换律:

→ OutputWillChange = Change(p) OR Change(q)

→ OutputWillChange = Change(q) OR Change(p)

以下因果ID项等效性也可以从布尔代数定律中导出。由于描述了哪些因果事件必须被补救以便改变负面后果的因果ID项符号可以用布尔代数来表示,所以它遵循与常规布尔代数相同的性质,并且因此可以利用恒等式和布尔代数定律来实现因果ID列表上的各种变换。该软件可以选择执行这些变换,以简化去往用户的输出和/或增加性能。它们还用于简化下面描述的因果事件报告方法的进一步方面:

要注意的是,仅包含一个项的一组括号(诸如,[aj] or )等效于不具有括号的项:aj。

以下过程用于针对任意操作构建因果表。这将使用布尔AND操作作为示例。

步骤1:列出去往该操作的所有输入项,然后列出针对这些输入项的输入值、以及来自该操作的其对应输出的所有可能组合。针对每个输入项使用一列,加上针对输出使用一列,以及针对可能输入值以及(一个或多个)那些输入项的对应输出的每个组合使用一行:

括号内的符号{X,x}表示因果值,其中因果ID项“x”与基础值“X”相关联。在此,去往因果操作布尔AND的一般输入项被指定为{P,p}和{Q,q},其中P和Q是基础布尔输入项(例如,可以表示true或false的变量),并且p和q分别是当前与那些基础输入值相关联的可变因果ID项。“输出值”列中的“

步骤2:针对输入项(例如,P、Q、PQ等)的每个可能子集(组合)添加一列,该列描述了改变所列出的输入项、并且是仅那些输入项的(一个或多个)值是否将使得该行上描述的操作的输出改变。针对具有M个输入项的操作,从所有可能的1输入组合开始,接着是所有可能的2输入组合,然后是3输入组合等等,直到所有的M输入组合,应当仅存在其中一个。在该示例中,操作布尔AND包含2个输入项,因此其表被扩展成如下这样:

在此,方括号符号[X1...Xn]用于将输入项分组在所有它们的不同的可能子集(组合)中,其目的是跟踪当被一起分组在括号的单个集合中的所有输入项的值、并且是仅那些输入项同时被改变时会发生什么。在这种情况下,存在两个可能的1输入子集[P]和[Q],并且存在一个可能的2输入子集[PQ]。这是所有可能的输入子集的完整集合,其相应地在该表中被列出。

这些列用于跟踪针对以下问题的答案:如果括号中列出的所有项的值改变并且仅括号中列出的那些项改变,则该行所指定的操作的输出值将会改变吗。“Y”(“是”)表示:如果所指定的(一个或多个)输入项的(一个或多个)值改变并且仅(一个或多个)那些项改变,则输出值将从该行中的当前输出值而改变。“N”(“否”)表示:如果所指定的(一个或多个)输入项的(一个或多个)值改变并且仅(一个或多个)那些项改变,则输出值将不会从该行中的输出值改变。这表示了对反事实方法的新颖使用,用以解决确定哪些输入值(以及随后的输入项)在对该操作的任何给定求值时引起了观察到的输出的问题。

例如,让我们考虑这四行中的每一行,这四行共同地指定了去往AND操作的所有可能值输入,从第一行开始(其中包括列标题):

该行指定了在输入“false AND false”和对应输出“false”的情况下的AND操作“PAND Q”。[P]列得到了“N”(“否”),这是因为将P的值从false改变为true不会将操作的输出从false改变为true。也就是说,该行的原始操作(由左侧的三个列指定)是“false ANDfalse”,其产生了输出“false”。通过将P从false改变为true而将其改变为“true ANDfalse”仍产生输出“false”,这不会改变后果。因此,[P]列将得到了“N”。

与[P]列类似,[Q]列也得到了“N”,这是因为将Q的值从false改变为true不会将该行上的AND操作的输出从false改变为true。最后,[PQ]列得到了“Y”(“是”),这是因为一起同时将P从false改变为true并且将Q从false改变为true会将该行上的操作的输出从false改变为true。也就是说,该行的原始操作(仍然)是“false AND false”,这产生了输出“false”。通过改变P和Q两者而将其改变为“true AND true”将产生输出“true”,这改变了后果。因此,[PQ]列得到了“Y”。

此处是第二行(其中包括列标题):

该行指定了在输入“false AND true”和对应输出“false”的情况下的AND操作“PAND Q”。在这种情况下,[P]列得到了“Y”,这是因为将该操作从“false AND true”改变为“true AND true”(通过改变P)会将后果从false改变为true。[Q]列得到了“N”,这是因为将该操作从“false AND true”改变为“false AND false”(通过改变Q)不会改变false的后果。[PQ]列也得到了“N”,这是因为将该操作从“false AND true”改变为“true AND false”(通过改变P和Q两者)不会改变false的后果。

此处是第三行(其中包括列标题):

该行指定了在输入“true AND false”和对应输出“false”的情况下的AND操作“PAND Q”。[P]列得到了“N”,这是因为将该操作从“true AND false”改变为“false ANDfalse”(通过改变P)不会改变false的后果。[Q]列得到了“Y”,这是因为将该操作从“trueAND false”改变为“true AND true”(通过改变Q)会将后果从false改变为true。

[PQ]列得到了“N”,这是因为将该操作从“true AND false”改变为“false ANDtrue”(通过改变P和Q两者)不会改变false的后果。

此处是第四行(其中包括列标题):

该行指定了在输入“true AND true”和对应输出“true”的情况下的AND操作“PAND Q”。[P]列得到了“Y”,这是因为将该操作从“true AND true”改变为“false AND true”(通过改变P)会将后果从true改变为false。[Q]列也得到了“Y”,这是因为将该操作从“trueAND true”改变为“true AND false”(通过改变Q)会将后果从true改变为false。[PQ]列也得到了“Y”,这是因为将该操作从“true AND true”改变为“false AND false”(通过改变P和Q两者)会将后果从true改变为false。

步骤3:基于在步骤2中添加和计算的列中的信息,添加提供了要传播的因果ID的初始“未检选(unculled)”列表的列:

为了填充该列,应用以下规则。针对每一行,如果一个或多个单元格被设置为“Y”,则通过以下内容来构建该未检选列表。针对被设置为“Y”的每个单元格,将与该单元格的(一个或多个)关联方括号项“[...]”相关联的(一个或多个)因果ID项本身置于方括号内,并且将其添加到针对该行的列表。将所有括号因果ID的列表本身包含在尖括号“<...>”的外部集合中。尖括号用于表示如果关联项中的任一个的值改变将会发生什么(将其与方括号进行比较,方括号表示如果所有关联项改变将会发生什么)。

例如,如果针对标题为“[A]”、“[B]”、“[AC]”、“[CD]”和“[DB]”的行的单元格全部被设置为“Y”,则将未检选列表设置为“<[a] [b] [ac] [cd] [db]>”。类似地,如果单元格“[QS]”和“[PQRS]”被设置为“Y”,则将未检选列表设置为“<[qs] [pqrs]>”。

如果一行中的所有单元格被设置为“N”,则将未检选列表设置为“(子句ID)”。

步骤4:添加一列用以存储要传播到输出的(一个或多个)因果ID的“已检选”列表,该列表是通过在每个未检选列表上应用因果ID项转换而导出的:

为了填充该列,在一些实施例中,将每一行的未检选列表复制到其对应的已检选列表单元格中,并且将以下规则依次应用于该已检选单元格中的每个列表。如果该列表等于“ ”,则没有任何改变。这就是已检选“列表”。这指示了:没有任何输入项对输出值具有影响,并且任何输出因果ID项应当源自于该操作外部的子句。也就是说,应当由开发者经由周围的源代码子句手动地(在适当的时候)指派针对该行中的输出值的因果ID项(这通常等效于来自常数或文字的指派)。在一些实施例中,来自输入的任何因果ID项都不被传播到输出,至少不通过修改的操作来自动传播(开发者可以确定传播输入因果ID项之一在该子句的上下文中是正确的做法,但是这是特定于应用的,并且在此处的方法范围之外)。

在一些实施例中,如果该列表包含方括号内的项(即,该列表不等于),则移除任何方括号“[...]”内的因果ID项的如下子列表:针对该子列表,其因果ID项的子集也在该列表中。这本质上是吸收和结合等效性的应用,从而简化该列表,该列表通过移除不重要的因果ID项还产生了更准确的结果。

例如,如果未检选列表是<[p] [s] [pq] [qr] [rs]>,则[pq]和[rs]两者都应当被移除,从而将该列表转换成<[p] [s] [qr]>。[pq]被移除,这是因为[p]在该列表中,并且[p]是[pq]的子集;[rs]被移除,这是因为[s]在该列表中,并且[s]是[rs]的子集。如果未检选列表是<[qs][pqrs]>,则[pqrs]应当被移除,从而将该列表转换成<[qs]>。[pqrs]被移除,这是因为[qs]在该列表中,并且[qs]是[pqrs]的子集。

步骤5:为了通过仅突出显示必要信息来改进人类和计算机理解,添加对已检选列表的符号进行简化的列。该列还反映了针对每个操作要输出的最终因果ID项及其语义性质。因此,“

为了填充该列,将每一行的已检选列表复制到其对应的简化列表单元格,并且将以下规则依次应用于该已检选单元格中的每个列表。针对每个“[...]”括号内的因果ID项列表,如果在“[...]”括号中仅有一个因果ID项,则从该因果ID项移除周围的“[...]”括号。例如,<[p] [s] [qr]>被转换成;<[p] [q] [r] [s]>被转换成;<[pq][prs] [qs]>不需要转换;并且<[q]>被转换成

针对“<...>”,如果在“<...>”中仅有一个因果ID项或一个“[...]”括号,则移除周围的“<...>”括号。例如,<[qr]>被转换成[qr];

被转换成p;<[pq] [prs] [qs]>不需要转换;不需要转换。

在此时,该方法已完成,并且针对输入值的每个可能集合的要传播的因果ID项的最终列表也已完成。此处是布尔AND的最终表,其仅具有最终的期望信息:

“{P,p}”和“{Q,q}”是因果值表达式、或已经暂时被提升为因果值并且可以通过布尔AND对其进行操作的非因果值表达式。“{underlying_value,causal_id_term}”是表示因果值的聚合,其中underlying_value是可以通过原始布尔AND对其进行操作的某个值,并且causal_id_term是当前与该基础值相关联的因果ID项。要注意的是,underlying_value的相同值可以在不同的时间处与不同的因果ID相关联。causal_id_term值的当前值描述了underlying_value为什么具有其当前值。关于为什么underlying_value在不同的时间处可能具有相同的值,在那些不同的时间处可能存在不同的原因。因此,即使underlying_value的相关联的输入值在不同的时间处是相同的,causal_id_term的值也可能在运算符的应用之间不同。

出于该表的目的,causal_id_term可以具有以下值之一:

“p”,表示附加到因果输入值P的因果ID项。针对该表右侧的输出值,这指示了:输出underlying_value的当前值仅取决于与p相关联的(一个或多个)因果事件,使得如果输入P的underlying_value已经不同,则所讨论的输出underlying_value的值也将不同(这取决于该操作的具体内容)。

“q”,表示附加到因果输入值Q的因果ID项。针对该表右侧的输出值,这指示了:输出underlying_value的当前值仅取决于与q相关联的(一个或多个)因果事件,使得如果输入Q的underlying_value已经不同,则所讨论的输出underlying_value的值也将不同(这取决于该操作的具体内容)。

”,表示分别附加到因果输入值P和Q的因果ID项p和q。针对该表右侧的输出值,此处对尖括号“< >”的使用指示了:输出underlying_value的当前值取决于与p和q相关联的因果事件两者,使得如果输入P或Q中的任一个已经被设置为不同的值,则所讨论的输出underlying_value的值也将不同(这取决于该操作的具体内容)。这意味着,为了改变该特定输出的值,可能将仅需要补救与p或q相关联的因果条件之一。一些实施例选择的是:仅选择p或q中的一个,并且将其返回作为与输出相关联的因果ID(见下文)。

“[pq]”,表示分别附加到因果输入值P和Q的因果ID项p和q。针对该表右侧的输出值,此处对方括号“[ ]”的使用指示了:输出underlying_value的当前值取决于与p和q相关联的因果事件两者,使得当且仅当输入P或Q中的两者都已经被设置为不同的值时,则所讨论的输出underlying_value的值也将不同(这取决于该操作的具体内容)。这意味着,为了改变该特定输出的值,可能将需要补救与p和q相关联的因果条件两者。一些实施例选择的是:仅选择p或q中的一个,并且将其返回作为与输出相关联的因果ID(见下文)。

要注意的是,p和q分别表示与P和Q相关联的因果ID项。它们均可以等于空因果ID值、引用因果事件的非空因果ID值、一个或多个非空因果ID值的列表、或另一个嵌套的因果ID项。它们在任何给定时间处被具体设置成什么取决于软件应用的具体内容。在一些实施例中,聚合因果值的underlying_value部分在一个或多个点处被设置为软件中的文字true或false值,并且在这些点处,空或非空因果ID项与true或false一起被有意地指定和设置。例如,如果因果布尔值在因果事件条件子句之外被设置为文字“true”或“false”,则因果ID项通常也将在该点处被设置为空因果ID项。文字的唯一位置指派通常还会设置将处于因果事件条件子句中的非空因果ID项,其中因果事件刚刚被发布并且其因果ID将用于描述布尔在该时间处为什么被设置为true或false,或者作为下游因果路径的一部分。这全部是根据上面描述的规则:该规则关于何时、如何、以及在哪里生成因果事件及其因果ID、以及它们的基本目的是什么。对于上表中的操作输出值,causal_id_term描述了(一个或多个)输入因果ID项中的哪些被传播到输出值。这也隐含了有意地不传播哪个(哪些)输入因果ID项。

与[pq]之间的主要差异在于:指示如果输入值P或Q中的任一个改变则输出值将改变(这意味着,为了改变该特定输出,与p或q相关联的因果条件中的仅一个将在名义上需要被补救),并且[pq]指示当且仅当输入值P和Q两者都改变时输出值才将改变(这意味着,为了改变该特定输出,与p或q相关联的因果条件两者都将在名义上需要被补救)。然而不管该语义差异,和[pq]共享一共同点:即这两者都指示了P和Q两者的值是输出值的直接因果原因,而不是仅仅“p”(这指示只有P的值对输出值具有影响)、或者仅仅“q”(这指示只有Q的值对输出值具有影响)。在一些实施例中,因果性质是还是[pq]的指示也可以被传播以提供关于原因的(一个或多个)最小集合的进一步信息,该最小集合需要被补救以在未来尝试时改变负面后果。

要注意的是,括号内的因果ID的数量不限于1个或2个。括号的单个集合中的因果ID可以与去往该操作的输入项一样多。例如,如果操作接受了四个不同的输入A、B、C和D,则输出项中的因果ID可以包括许多不同的组合,包括、[abcd]、[cd]等等。

在一些实施例中,当遇到、[pq]、或指定了传播多个因果ID项的其他后果时,软件将仅挑选p或q之一来返回,作为要与输出值相关联的因果ID项。当因果ID项被实现为单个因果ID值而不是因果ID值或其他因果ID项的容器时,这可能特别经常发生。出于性能原因,可以这样做。由一些实施例执行的返回p和q两者(并且因此将多个因果ID的列表与单个因果值相关联)的替代动作可能潜在地引入了性能问题,特别是对于实时应用而言,这是因为项的任意地且可变地长的列表以及任意地且可变地深的列表可能需要利用动态存储。在一些实施例中,针对两个原因,按照单个因果数据值传播一个因果ID值是足够的。首先,通常情况下,通常只有一个因果条件促成了实际经历的任何给定故障模式。其次,如果存在针对负面后果的多于一个因果事件,则仅向用户显示这些事件之一是足够的。用户可以补救该一个故障源,再次尝试该动作,并且如果由于仍在发生的另一个因果事件而继续发生负面后果,则现在将显示该另一个因果事件(因为先前的因果事件已经被补救,并且因此不再会促成负面后果),并且用户然后可以补救该下一个条件,再次尝试该动作等等,直到引起负面后果的所有条件都被校正。

在选择返回可以针对操作的输出而指定的多个因果ID项的仅子集(诸如,当因果表指定返回[pq]时仅返回因果ID项p或q之一)的实施例中,用于确定从指定的输出因果ID项中选择因果ID项的哪个子集以帮助确保使最有用的因果信息到达用户的一组规则可以是:如果因果表所指定的输出包括空和非空因果ID项两者,则优先选择返回非空因果ID项而不是空因果ID项;如果因果ID项中的两个或更多个是非空的,则始终优先选择该操作的“最左侧”或“最右侧”输入。在这些情况下,非空因果ID项被返回,它将使至少一个相关源事件被显示给用户。选择去往该操作的最左侧或最右侧输入中的相同的一个还允许软件开发者在以较低级别选择哪些因果事件可以被显示时进行一些控制,该控制基于他们如何根据输入的次序来布置表达式,经由例如哪些表达式在运算符的左侧或右侧。

在一些实施例中,软件可以经由如下方式来按每个因果ID项传播多个因果ID值:将因果ID项实现为因果ID列表以及可能地因果ID项的嵌套树结构,而不是将单个因果ID值与总体因果ID项相关联。例如,如果从先前操作输出的pq型因果ID列表用作去往另一个新操作的输入因果ID项,则该因果ID项中的因果ID列表被视为单个单元,例如NewInput:p=OldOutput:pq。如果在新操作中针对输出进行选择,则这可能具有嵌套影响,从而导致所有具有贡献的因果事件被适当地捕获。这具有如下优点:即,能够在UI处同时显示导致负面后果的所有因果事件,而不是一次只显示一个事件。当多个事件是原因时,一次显示一个根本原因事件需要用户多次重复如下序列:即查看针对负面后果的单个根本原因事件、补救该单个事件、重新尝试主要动作、再次经历负面后果、查看下一个根本原因事件、补救该下一个根本原因事件、重新尝试主要动作等等,直到所有的根本原因事件都被补救为止。相比之下,同时显示所有根本原因事件允许用户在重新尝试主要动作之前尝试一次性补救所有根本原因事件,这可以节省时间。

如果将因果ID项实现为因果ID项的嵌套树,则软件还可以传播结果(即,“需要所有这些事件都已经发生”)与[p...q]结果(即,“需要这些事件中的任一个已经发生”)之间的语义差异,作为每个嵌套因果ID项(树节点)的一部分,以向用户提供关于负面后果中涉及的因果条件的性质的更多信息。这对用户是有用的,使得可以作为UI显示的一部分使他们意识到:他们是否将需要补救所有因果事件或仅其中的子集。按照[p...q]与OutPutWillChange = Change(P) AND...AND Change(Q)、以及与OutPutWillChange = Change(P) OR...OR Change(Q)的上述次级布尔等效性,软件可以利用被包括在每个因果ID项中的这些“括号”语义来计算为了使后果改变而需要补救的事件的最小数量。在此处对该技术的描述中,术语“节点”将指代因果ID项树中的因果ID项。

为了计算为了校正针对用户的负面后果而需要补救的事件的最小数量,系统从根节点(根本因果ID项)对因果ID项的内部子项树执行递归的降序分析,并且跟踪、计数和构建每个当前节点下的子代节点和事件(即,该节点中的因果ID)的子集,该子集必须被补救以便校正当前节点,这考虑到了给定节点处的所有子代节点是否需要被补救以使父代项被认为是已补救的(针对[]节点),或者该节点中的任何一个或多个子项是否需要被补救以使父代项被认为是已补救的(针对<>节点)。它递归地这样做,直到树中的所有节点都被计及,并且所有根项和子项都具有所有可能路径的列表,可以通过补救包含在它们和它们的子树内的因果ID的不同子集来补救它们。

例如,如果负面后果的因果ID项是“]>”,则在没有该另外方面的情况下,本发明将把以下因果事件全部报告为负面后果的根本原因(假设事件之间没有父代-子代关系,这将从所显示的列表中移除子代事件):a、b、c、d、e和f。然而在该另外方面的情况下,本发明然后还可以报告:为了校正总体负面后果,用户仅需要补救该总根本原因事件列表的以下子集之一中的条件:a、b、c、dea或def。这些子集中的每一个都可以被认为是“解决方案子集”。也就是说,仅补救单个子集中的事件条件就足以更改用户的负面后果。例如,补救事件a并且单独地补救事件a就足以校正负面后果,或者针对单独地补救事件b或c也是如此:单独地补救事件a、b或c中的任一个都将更改负面后果。针对在所有事件dea或所有def中的所收集的事件条件也是如此,其中补救任一个子集中的所有事件并且单独地补救该子集就足以更改负面后果。进一步的分析还可以允许从所显示的解决方案列表中移除子集dea,这是由于通过下面示出的因果ID布尔等效性的吸收性原理,事件a其本身的存在足以排除dea作为有用的解决方案。

在此时,可以对每个解决方案子集中的事件数量进行计数,并且可以显示具有最少事件数量的子集(或与最少事件数量关联的子集)作为对于负面后果的“可能最容易的”解决方案。一种变型涉及以从最少事件数量到最多事件数量的经排序的次序向用户显示所有解决方案子集,以便呈现从要补救的最少事件到要补救的最多事件的不同的可能补救场景。

对此的进一步增强是:如果补救每个单一类型的因果事件条件所需要的现实世界努力是已知的或能够估计的,例如通过外部的、特定于应用的分析(例如,如通常可以在用于解决根本原因事件条件的故障诊断指南或服务手册中捕获的那样),则这些努力值可以作为关于每个事件类型的数学权重被计入考虑到分析结果中。然后,代替于将事件的数量相加,系统可以将解决方案子集中的每个事件的权重相加,这导致了计算针对每个可能的解决方案所需的实际或所估计的现实世界努力,然后这可以用于从最少现实世界努力到最多现实世界努力对负面后果的推荐解决方案进行排序。

例如,如果预先确定上述个体事件类型需要以下时间量来修复:a = 60分钟、b =45分钟、c = 120分钟、d = 10分钟、e = 10分钟、f = 5分钟,则软件可以计算补救每个解决方案子集所涉及的实际努力:a = 60分钟、b = 45分钟、c = 120分钟、dea = 80分钟、def =25分钟。在这种情况下,即使存在总计具有较少事件要补救的子集(也就是说,子集a、b和c均具有1个事件),但是所发现的是,将可能需要最少的现实世界努力量的解决方案子集是def,其需要25分钟。解决方案子集a、b和c将各自分别需要60、45和120分钟的努力。因此,该软件可以向用户推荐首先尝试补救def以便修复负面后果,这是因为这将可能需要所有可能解决方案的最少努力量。

以下示例性操作详细描述了一些特定的常见操作类型,并且给出了每组因果ID规则的因果解释和直观理解。

表5

此处再次是相同的表,这次该表按照上述规则针对因果传播而更新。同样,使用与针对上述通用因果表的情况相同的基本约定。现在,将该表中的所有输入和输出值视为因果布尔值。因果布尔值是标准布尔值(“true”或“false”)加上一些因果ID项值的聚合,这些因果ID项值表示了为什么标准布尔值具有其特定的当前值。以下内容详细描述了该表中的每个操作,并且给出了每个因果ID项输出的基本原理。它们均包括来自以下因果表的相关摘录(excerpt)。

表6

表7

在所有情况下,改变输入项P的值将改变输出值,因此,针对所有情况,始终简单地将输入因果ID项“p”传播到输出。

表8

在第一行值中,其中输入P和Q两者等于false,按照正常的AND操作,输出被设置为false。此处,为了使输出为false,P或Q中任一个必须为false,并且在该情况下,两者均为false。也就是说,当且仅当两个输入都切换为true时,输出才将切换为true。输入因果ID项“[pq]”被传播到输出,以表示输出值取决于两个输入的特定值,并且当且仅当这些输入值中的两者都不同时,输出值也将不同。

在第二行值中,其中输入P等于false并且输入Q等于true,按照正常的AND操作,输出被设置为false。在此,如果Q被设置为false,则将不存在对输出的任何改变。如果P被设置为true,则输出将改变为true。输入因果ID项“p”被传播到输出,以表示输出值仅取决于P的值。这还反映出在输出值中没有将Q的值计入考虑。

在第三行值中,其中输入P等于true并且输入Q等于false,按照正常的AND操作,输出被设置为false。在此,如果P被设置为false,则将不存在对输出的任何改变。如果Q被设置为true,则输出将改变为true。输入因果ID项“q”被传播到输出,以表示输出值仅取决于Q的值。这还反映出在输出值中没有将P的值计入考虑。

在第四行值中,输入P和Q两者都等于true,按照正常的AND操作,输出被设置为true。在此,为了使输出为true,需要两个输入也都为true。也就是说,如果两个输入中的任一个将值切换为false,则输出值也将改变为false。输入因果ID项“ ”被传播到输出,以表示输出值取决于两个输入的特定值,并且如果这些输入值中的任一个是不同的,则输出值也将不同。

表9

在第一行值中,其中输入P和Q两者都等于false,按照正常的OR操作,输出被设置为false。在此,为了使输出为false,需要两个输入也都为false。也就是说,如果两个输入中的任一个将值切换为true,则输出值也将改变为true。输入因果ID项“”被传播到输出,以表示输出值取决于两个输入的特定值,并且如果这些输入值中的任一个是不同的,则输出值也将不同。

在第二行值中,其中输入P等于false并且输入Q等于true,按照正常的OR操作,输出被设置为true。在此,如果P被设置为true,则将不存在对输出的任何改变。如果Q被设置为false,则输出也将改变为false。输入因果ID项“q”被传播到输出,以表示输出值仅取决于Q的值。这还反映出在输出值中没有将P的值计入考虑。

在第三行值中,其中输入P等于true并且输入Q等于false,按照正常的OR操作,输出被设置为true。在此,如果Q被设置为true,则将不存在对输出的任何改变。如果P被设置为false,则输出也将改变为false。输入因果ID项“p”被传播到输出,以表示输出值仅取决于P的值。这还反映出在输出值中没有将Q的值计入考虑。

在第四行值中,其中输入P和Q两者都等于true,按照正常的OR操作,输出也被设置为true。在此,为了使输出为true,P或Q中任一个必须为true,并且在该情况下,两者均为true。也就是说,当且仅当两个输入都切换为false时,输出才将切换为false。输入因果ID项“[pq]”被传播到输出,以表示输出值取决于两个输入的特定值,并且当且仅当这些输入值中的两者都不同时,输出值也将不同。

因果AND和OR的幂等性分别在图14A和14B中示出。每个表中的经标记的列应当彼此相等,以便表明它们在幂等操作期间的因果传播等效性。

因果AND和OR的交换性分别在图14C和14D中示出。每个表中的经标记的列应当彼此相等,以便表明因果传播等效性。

因果AND和OR的结合性分别在图14E和14F中示出。每个表中的经标记的列应当彼此相等,以便表明因果传播等效性。

因果AND和OR、以及OR和AND的吸收性在图14G中示出。每个表中的经标记的列应当彼此相等,以便表明因果传播等效性。

因果AND和OR、以及OR和AND的分布性分别在图14H和14I中示出。每个表中的经标记的列应当彼此相等,以便表明因果传播等效性。

因果AND和OR的泛界在图14J中示出。

因果AND和OR的互补在图14K中示出。

德摩根定律可以用作对上述规则的进一步测试。德摩根定律陈述了布尔运算符AND和OR的以下等效性:

第一个等效性陈述了AND操作可以表述为OR和NOT操作的复合表达式。类似地,第二个等效性陈述了OR操作可以表述为AND和NOT操作的复合表达式。这些等效性可以用于帮助测试上面针对核心布尔操作确定的因果表的一致性和实用性,并且因此帮助总体上测试因果规则的一致性和实用性。这是通过如下方式来完成的:即,通过将所生成的因果表应用于每个等效性的右侧的复合表达式,通过将它们应用于每个子表达式并且构建直到最终输出值的结果,并且然后验证由等效性的右侧输出的因果ID项与针对等效性的左侧上的核心操作所声明的因果ID项是否匹配。

图14L中的表经由德摩根等效性分解了对AND的计算。左边的AND列128示出了本发明中此处针对AND所呈现的公理(axiomatic)(核心)因果表,并且右边的AND列130示出了在应用本发明中此处针对OR和NOT所呈现的因果规则之后使用德摩根等效性而计算的最终因果值。针对每组输入返回的因果ID项(包括[pq]和组合的语义类型)完全匹配。

图14M中的表经由德摩根等效性分解了对OR的计算。左边的OR列132示出了本发明中此处针对OR所呈现的公理(核心)因果表,并且右边的OR列134示出了在应用本发明中此处针对AND和NOT所呈现的因果规则之后使用德摩根等效性而计算的最终值。针对每组输入返回的因果ID项)包括[pq]和组合的语义类型(完全匹配。

在公理布尔等效性操作期间,这些因果传播等效性指示了因果ID项规则彼此一致,并且因果传播如说明性实施例中所预期的那样工作。如图14A-B中所看到的,在许多实施例中,与布尔表达式一起使用的因果输出与其德摩根等效性一致。在图14N中总结了与一些实施例一起使用的前述布尔操作及其所得的因果输出值。

图15A-D中的因果输出表是其中包括所有因果行为的16个布尔操作的完整集合,如从上述规则中导出的,或者如从与核心AND、OR和NOT操作的等效性中导出的。

在图15A-D中,操作“始终为False”、“关于P的NOT”、“关于Q的NOT”、“AND”、“关于P的恒等式”、“关于Q的恒等式”、“OR”、“AND”、“OR”和“NOT”实现了核心因果规则。两个“恒等式”操作(操作130和132)等效于来自变量输入的简单指派或其表达式,其中布尔值在输入与输出之间不改变,并且它们简单地选择输入因果ID项作为输出因果ID项。“始终为True”(“重言式”操作136)和“始终为False”(“矛盾式”操作138)操作本质上等效于来自文字或其他常数值(诸如,源代码中显式的“true”或“false”)的指派或其表达式。在这种情况下,输出因果ID项被指定为“”,其指示通常应当从周围的源代码块或子句来手动地获取并设置因果ID项。这些操作本质上表示数据的“入口点”、以及其进入布尔变量和其他数据集和操作中的原因。因此,该因果ID项是根据上述关于在指派因果值时应何时、何处以及如何设置因果ID项的规则而设置的。如果文字在因果事件条件输出子句中,则因果ID项值可以被或者可以不被设置为该事件的因果ID(这取决于应用的具体内容)。如果文字在因果事件条件输出子句之外,则因果ID项可以被或者可以不被设置为空因果ID项(这再次取决于应用的具体内容)。

针对其他操作的因果ID项规则类似地表现。例如,针对XOR(异或)146的因果ID项规则表现成:使得针对所有值后果,所返回的因果ID被指定为。这指示了:针对所有XOR输入情况,改变任一个输入都会导致输出改变。

如上所描述,用于构建因果表的技术可以适用于具有任意数量的输入的操作,即适用于具有多于两个输入的操作。此处是使用该技术针对一些自定义的3输入布尔操作来构建因果表的示例,这在图15E中示出。

在此处,CustomOperation()函数接受三个输入因果布尔项{P,p}、{Q,q}和{R,r},并且根据该表、针对输入值的每个可能组合来输出单个因果布尔true或false值。作为示例,输出值在此处是任意地选择的。

要注意的是,该自定义操作还恰好等效于由标准2输入布尔操作组成的任意复合表达式

对于构建因果表的步骤2,存在输入项的7个可能的组合要检查:P、Q、R、PQ、PR、QR和PQR、以及因此在该表中间的7个对应列。

要注意针对一些输出所返回的更复杂的因果ID项。

在第一行中,输出因果ID项“”指示为了校正该特定后果,用户可以校正与因果ID r其自己相关联的事件条件(即,改变输入R),或者校正与因果ID p和q一起相关联的事件条件(即,一起改变输入P和Q)。要注意的是,一起改变所有输入R、P和Q将不会改变后果。如果这是有效的解决方案,则它将会被导出并且反映为“[pqr]”。

类似地,在第5、6和7行中,输出因果ID项分别为。即使这三个不同的行(输出)包含相同的因果ID,但是针对每个行,语义是不同的。在第5行中,指示用户必须改变Q其自己,或者一起改变P和R,以便改变后果。在第6行中,指示用户必须改变P其自己,或者一起改变Q和R,以便改变后果。在第7行中,指示用户可以改变P、Q或R中的任一个,以便改变后果。

这也表明了在嵌套或复合操作发生时可能发生的因果语义报告的嵌套。也就是说,如果该自定义操作的因果表是经由上述等效性

要注意的是,在第7行中,第10列中的因果输出“”直接从第9列中的子表达式的因果输出“<r>”中导出。这两个因果输出表示是等效的。这是因为<r>意味着“为了改变后果,校正r或中的任一个,其中意味着校正p或q中的任一个”,并且意味着“校正p、q或r中的任一个”。这两个语句导致了相同的语义,并且因此对于用户是相同的指令,如果用户希望校正总体后果的话。在第2行第9列中,对该子表达式的“[r[pq]]”的引用具有类似的影响,这是因为它等效于[pqr]:这意味着为了改变该子表达式的后果,必须一起改变所有的r、p和q。然而,在该情况下,该特定子后果没有使其到达最终的输出:它被总体表达式中的一些其他因果影响所掩盖和消除了。

表10

符号“

表10表示针对一般算术操作的基本“回退”因果表,该表假定对任一个输入的任何改变将始终改变输出。要注意的是,用于算术和按位操作的可能输入的数量通常太大,以致于无法在因果表中实际地枚举。如果期望的话,通常仍可以生成针对这些操作的更具体的因果表。

首先,仅需要针对已知引起负面后果的输出值来确定因果ID项传播。针对算术操作,通常只有单个值或一小组输出值可能引起负面后果,而绝大多数输出值并不表示负面后果。只有与已知可能引起负面后果的那些输出值相关联的因果表行才需要标识其传播的因果ID项:其他非因果输出值可以忽略。

其次,可能引起负面值(negative value)的算术操作的输出值通常被限制成具有作为“强制元素”的性质的值。强制元素值是用于特定操作的任何输入值,已知该输入值将输出值强制为单个特定值、而不管(一个或多个)其他输入的(一个或多个)值如何。让满足将输出值强制为单个特定值而不管其他输入的值如何的性质的任何输入值被称为“强制元素”(或“强制输入值”)。用于不同操作的强制元素的示例是:

用于乘法的“0”(零):将任何值乘以0始终得到输出0。也就是说,任何输入0都始终将输出强制为0,而不管其他输入的值如何。

(M) * (0)= 0

(0) * (M)= 0

其中M等于任何数字。

用于求幂的底数输入的“0”;以及用于求幂的指数输入的“0”: 0的任何指数值次幂都会得到输出0,并且除0之外的任何底数值的指数值0次幂都会得到输出1。要注意的是,求幂操作具有两个元素,这两个元素将强制输出值,并且强制的输出值彼此不同。如果所有候选强制元素相对于彼此具有可预测的优先级(它们在此处这样做,是因为底数输入0优先于指数输入0),则它们被认为是强制元素。在确定因果关系和适当的因果ID项选择时,可以将它们的优先级计入考虑。

(0) ^ (M)= 0

(N) * (0)= 1

其中M等于任何数字,并且N等于除0之外的任何数字。

满足用于操作的“吸收元素”的数学概念的值被认为是强制元素。吸收元素是如下输入值:其将输出值强制成与吸收元素相同。上面的所有示例都被认为是吸收元素,除了求幂的指数输入为0的情况,这是因为它将输出强制为1而不是0。

IEEE 754标准特殊值,诸如“sNaN”(“信令非数(signaling not a number)”)、“qNaN”(“安静非数(quite not a number)”)、“+INF”(正无穷大)和“-INF”(“负无穷大”)值。这些值充当强制元素。例如,经由浮点加法将任何数字加到qNaN值将得到输出qNaN值。

因果表仅需要传播针对引起负面后果的输出值的因果ID项,使得如果这些强制元素中的任一个引起了操作的负面后果,则因果表通常可以大大简化。示例将是IEEE 754浮点乘法。除了上述NaN和INF强制元素之外,值0也是强制元素。如果所讨论的软件从未使用乘法中的INF或0值来表示与NaN值分离的负面后果,并且仅曾使用NaN值来表示负面后果,则可以从因果表中排除INF和0值的分离列表,并且这些被排除的值可以隐含地包括在非强制元素值行中:

在该表中,M和N是除NaN之外的任何输入值。(为了描述简单起见,它忽略了qNaN与sNaN之间的差异)。

类似地,如果操作包含强制元素,但是没有任何一个强制元素曾用于产生与非强制元素分离的负面后果,则该操作可以被视为不具有强制元素。

在一些实施例中,避免不必要的强制元素处理的实现增加了可靠性、可维护性和计算性能。在一些实施例中,已经被添加有因果事件报告的软件不需要实现本节中的任一个表。此处的算术和按位操作通常可以划分成以下类别,这些类别在每个类别内通常共享共同的因果表。

表11

符号“

乘法:表12示出了整数乘法(其中唯一的强制元素是零)和IEEE 754浮点乘法(其中强制元素是0和NaN)的因果关系(为了简单起见,忽略了qNaN与sNaN之间的差异,并且忽略了+INF和INF强制元素)。当已知NaN或0可能引起负面后果时,将使用该表。在任何给定软件系统中使用乘法表的该特定因果变体的需要可能是非常罕见的,然而将其包括在此处,以便示出这是可能的。

表12

“M”和“N”表示未在另一行上明确列出的所有其他可能输入值的集合。

任意IEEE 754浮点操作:表13是用于任意IEEE 754浮点操作的通用基本因果表,其具有强制元素qNaN、sNaN和INF(为了简单起见,忽略了+INF与-INF之间的差异)。该表将用于其中输出值sNaN、qNaN和Inf可能引起负面后果的系统。

表13:

IEEE 754浮点加法:表14是用于IEEE 754浮点加法的因果表,其具有强制元素NaN(为了简单起见,忽略了sNaN与qNaN之间的差异),并且忽略了该标准中的其他强制元素。将其与用于整数加法的因果表进行对比,整数加法不具有强制元素并且因此是简单的,从而每次发送p或q。该表将用于其中输出值NaN可能引起负面后果的系统。

表14

除法:关于表15解释了整数除法,表15是用于整数除法的因果表,其中分子中的0和分母中的0两者都是强制元素,但是具有不同的影响。经由表16解释了IEEE 754浮点除法,表16是用于如下除法的因果表:该除法被扩展成包括根据IEEE 754标准的NaN输入(忽略了sNaN与qNaN之间的差异)。表15将用于其中未定义行为(“undef/inf/err”)或0可能引起负面后果的系统。

表15

表16

求幂:表17是用于被扩展成处理NaN值的求幂的因果表(忽略了qNaN与sNaN之间的差异)。这将用于其中输出值NaN和0可能引起负面后果的软件系统。

表17

要注意的是,大多数软件系统的设计不需要实现算术运算符的因果版本来实现向用户的良好因果报告。因此,对任何算术因果表的使用通常是罕见的,并且因此对大多数软件系统没有影响。如果需要因果算术操作,则用于任何给定操作的具体表将取决于哪些输出值或输出值类可能在所讨论的软件系统中引起负面后果。

表18

在实现实施例时,第一步骤通常是确定因果表,诸如上面的那些因果表。在已经构建了因果表之后,应当将操作修改成输出针对指定输入值的指定输入因果ID项。大多数实施例不需要修改系统中的每个可能的操作,也不需要广泛地修改操作以覆盖每个输入值组合。通常,只有操作中的可能引起负面后果的那些输出值需要具有针对它们所传播的因果ID。一些实施例仅修改常见的布尔操作以及比较操作。在支持它的软件语言和开发环境中,对运算符重载和泛型的使用可以大大简化这些实现方式。

以下内容是用于布尔AND的示例实现方式。在此处,C++类Causal是模板类,它包装了开发者所指定的任意数据类型,并且向其附加了因果ID项。在这种情况下,将因果ID项实现为单个因果ID值,该值可以是空因果ID值,或者是一些非空因果ID值。它还包含运算符重载,以进行因果报告并且自动向下转型(downcast)成基础类型值。它还包含函数GetFirstCause(),该函数返回两个指定输入的因果ID参数中为非空ID的任何一个,如果两者都是非空的,则优先选择第一个参数,如果两者都是空的,则返回空ID。根据一些实施例,这支持针对和[pq]情况下的输出挑选单个因果ID。在适当的时候,可以容易地修改该示例性代码以用于与返回p和q两者的实施例一起使用。

//逻辑AND运算符,其中两个操作数都是因果类型

template< typename t_ItemType1, typename t_ItemType2 >

decltype(auto)

operator&& (

const Causal & a_rCausalOperand1,

const Causal & a_rCausalOperand2)

{

//

if (t_ItemType1(a_rCausalOperand1))

{

// true &&

if (t_ItemType2(a_rCausalOperand2))

{

// true && true

return Causal(

t_ItemType1(a_rCausalOperand1) &&

t_ItemType2(a_rCausalOperand2),

GetFirstCause(a_rCausalOperand1, a_rCausalOperand2));

}

else

{

// true && false

return Causal(

t_ItemType1(a_rCausalOperand1) &&

t_ItemType2(a_rCausalOperand2),

a_rCausalOperand2.GetCausalId());

}

}

else

{

// false &&

if (t_ItemType2(a_rCausalOperand2))

{

// false && true

return Causal(

t_ItemType1(a_rCausalOperand1) &&

t_ItemType2(a_rCausalOperand2),

a_rCausalOperand1.GetCausalId());

}

else

{

// false && false

return Causal(

t_ItemType1(a_rCausalOperand1) &&

t_ItemType2(a_rCausalOperand2),

GetFirstCause(a_rCausalOperand1, a_rCausalOperand2));

}

}

}

此处是用于实现因果乘法的示例性代码,其中任何值可能引起负面后果。

//乘法运算符,其中两个操作数都是因果类型

template< typename t_ItemType1, typename t_ItemType2 >

decltype(auto)

operator* (

const Causal & a_rCausalOperand1,

const Causal & a_rCausalOperand2)

{

return Causal(

t_ItemType1(a_rCausalOperand1) *

t_ItemType2(a_rCausalOperand2),

GetFirstCause(a_rCausalOperand1, a_rCausalOperand2));

}

遍及本文讨论的其他因果函数可以使用类似的方法来实现。要注意的是,由于该因果乘法运算符实现方式不跟踪任何特定输出值(诸如,0(零)或NaN)的影响,因此就因果ID传播而言,其实现方式和等式运算符实现方式两者是相同的。这两个函数中的唯一差异是*运算符与==运算符针对输出的基础值的应用。

当发现所有候选事件都具有空父代因果ID项时,则在UI中显示这些事件作为负面后果的(一个或多个)原因。在理想情况下,事件显示应当与负面后果显示紧密相关联,以使得用户不必进行任何搜索或进行最少的搜索来找出负面后果的原因。如果与导致UI显示负面后果的执行或数据路径相关联的因果ID项被设置为空因果ID项值,则这指示针对该负面后果的“没有已知原因”。在这种情况下,UI不会显示针对负面后果的任何另外的因果事件信息。或者,它显示等效于“没有已知原因”的指示符等等。该行为与针对何时设置或不设置空因果ID项的规则一起确保了不会向用户显示负面后果的不正确的根本原因事件。

要注意的是,基于多线程和其他形式的并行处理,当对任何因果ID的查找发生时,该因果ID的因果事件可能不在因果事件数据库中。如果是这种情况,则事件显示功能在理想情况下应当等待,直到事件到达数据库并且查找成功为止(通过周期性地重试该查找或利用数据库实现方式中可用的某些等待功能),和/或在未能找到与因果ID相关联的因果事件时,在某个点处声明超时、或声明逻辑问题或类似物。这是因为根据此处解决方案的定义,任何非空因果ID都应当始终指代数据库中的事件、或不久将处于数据库中的事件。在某个合理的时间段之后无法针对其找到因果事件的任何因果ID将指示因果事件报告系统之外的逻辑、通信或其他类似问题。

与条件相关联的输出子句可以是单个语句或语句块,包括对其他函数或任意深度的执行代码的子调用。输出子句还包括对软件系统的其他任意部分的调用和输出。此处使用的术语“输出子句”通常涵盖了条件被满足的下游影响。要注意的是,代码中的单个条件语句可以包含多个因果事件条件,这是因为一些软件语言中的条件语句可以包含多于一个输出子句。例如,“if/else”语句及其单个显式条件表达式包含两个输出子句。“if”部分包含一个显式条件表达式和子句,并且“else”部分及其子句隐含了第二个条件表达式,该第二个条件表达式等于“if”部分中包含的显式表达式的否定。作为另一个示例,“switch”语句可以包含任意数量的输出子句:“switch”语句的每个“case”子句还指定另外的条件。在这些情况下,任何输出子句与其显式或隐式条件的组合都被认为是单独的因果事件条件,只要该子句的执行可能导致负面后果。类似地,代码中的单个条件语句的多个子句中只有一些子句可能是因果事件条件。如果不知道子句促成了负面后果,则它不会形成因果事件条件。同样地,如果已知与条件语句相关联的任何子句都没有引起负面后果,则该条件语句及其子句不是因果事件条件。

在一些实施例中,UI中所显示的负面后果与因果条件和子句有多“远”并不重要,子句是否始终使得UI显示负面后果也不重要。如果条件及其相关联的输出子句可能甚至仅有时促成了针对用户生成负面后果,则其被认为是因果事件条件。成为因果事件条件所需要的是条件和子句是已知负面后果的一部分,使得执行该子句对于引起负面后果来说是必要的。即使该条件仅仅是部分必要的,也是如此(即,可能存在也需要执行来引起负面后果的其他条件和子句——这些也将是其他因果事件条件)。

在所有这些情况下,在一些实施例中,其中外部系统调用第一方软件(诸如,调用局部的、开发者定义的函数回调)作为外部条件的结果的第一局部代码可以被认为是隐式事件条件的输出子句的一部分。即使条件部分实际存在于外部第三方代码中,也是如此。因此,受隐式因果条件影响的第一局部第一方代码(子句)名义上应当表现为因果事件条件,这意味着它应当发布因果事件,并且在适当的时候发起具有局部下游影响的因果ID的传输。这允许因果事件数据库仍然针对外部条件生成和跟踪因果事件。

要注意的是,可能存在与条件相关联的多于一个输出子句,诸如“if/else”语句的单独的“if”和“else”子句、以及“switch”语句的多个“case”子句。在这种情况下,短语“输出子句”的使用通常指代在讨论上下文中执行的任何一个子句。当已知输出子句可能引起负面后果时,则该子句及其条件被认为是因果事件条件。

除了空因果ID值,事件数据库中的事件条目与因果ID值之间存在1对1的对应关系。因果ID值确切地表示一个因果事件,并且因果事件确切地由一个因果ID值表示。要注意的是,“因果事件”指代因果事件条件的执行的单个实例。每当因果事件条件执行时(无论其之前是否被执行),它都生成新因果事件,其具有新的唯一因果ID以标识该新事件实例。非空因果ID值可能存在,并且在其相关联事件被输入到数据库中之前被传递作为相关联的因果ID。在这种情况下,相关联事件应当始终最终使其到达数据库。也就是说,非空因果ID指代如下事件条目:该事件条目要么已经在事件数据库中,要么将在未来某时刻处于数据库中。实际上,在一般化的实施例中,除了空因果ID值之外,不应当存在不指代已发生、并且已经在事件数据库中或者最终将处于事件数据库中的事件的因果ID值。因果ID可以被认为是指向因果事件的一般通用指针。

条目表还应当包含用以记录每个事件的因果ID的字段。这是针对每个事件生成的唯一ID值。它用于唯一地标识该因果事件实例。要注意的是,数据库通常不会自动生成该因果ID值——它通常应当是从因果事件条件的输出子句中生成和获得的,并且通常伴随着关于被添加到数据库表的事件条目的所有其他信息。条目表还应当包含用以记录每个事件的父代因果ID项的字段。如果已知(一个或多个)其他事件(全部或部分地)引起了当前事件,则(一个或多个)那些其他事件被认为是当前事件的(一个或多个)父代因果事件,并且当前事件被认为是(一个或多个)那些父代的(一个或多个)子代因果事件。在这种情况下,理想情况下,应当在可能的时候将(一个或多个)父代的因果ID项记录在用于子代事件的数据库条目中。如果已知没有任何(一个或多个)其他事件引起了当前事件、或者如果无法将父代因果ID项传送给软件中的子代事件,则针对数据库中的子代事件的父代因果ID项应当被设置为空因果ID项。

数据库应当可从包含因果事件条件子句的任何软件子系统直接或间接地写入。数据库应当可从可以向用户显示负面后果的任何软件UI中读取。要注意的是,在一些实施例中,实际上可以存在位于多于一个软件或硬件模块中的多于一个物理数据库,只要可以显示负面后果的任何(一个或多个)相关联IU能够访问和读取为了找到并报告感兴趣的(一个或多个)因果事件所需要的任何和所有数据库。也就是说,向用户显示负面后果的任何软件UI通常都应当能够针对接收到的任何因果ID来查找因果事件。

要注意的是,一些故障模式可能是由于两个或更多个因果事件条件的组合被满足而引起的。类似地,因果事件条件的不同组合可能引起不同的负面后果。通常,产生负面后果的因果事件条件的这些可能组合中的每一个也可以被计数为单个故障模式。要注意的是,不同的故障模式可能然后包含因果事件条件的重叠子集,这在尝试枚举所有可能的故障模式时会导致甚至更多的分析困难。

在一些实施例中,如果常规操作不需要是因果性的,则实现方式不会将常规操作修改为因果性的。当将操作修改为因果性的时候,实现方式不需要将该操作中的所有输出值连接起来(wire up)以用于因果处理,如果这些输出值不能够表示负面后果的话。例如,如果零输出不是乘法的结果时的因果因素,但是NaN值是,则实现者可以考虑仅将NaN值连接起来。如果在具体软件系统中根本没有针对操作的任何输出值可以引起故障(即,对于因果报告而言根本不被需要),但是基础值仍然可以通过比较来表示负面后果(例如,“整数值3、5和6可能引起故障”),则一些实施例可以修改该操作以始终输出“”原因,并且提供做同样事情的因果比较运算符以处理报告故障模式。

在该示例中,为了将简单C++布尔成功标志转换成还包括因果ID项的因果类型,可以将布尔变量的类型从bool(布尔)转换成Causal。然后,可以将Causal变量的基础布尔值设置为false,以指示失败,并且将Causal变量的因果ID项设置为等于刚刚发布的因果事件的因果ID。Causal类可以包括自动类型转换运算符,从而通过在可能的情况下将变量的表达式自动转换成基础数据类型以及从基础数据类型自动转换,使包装的类表现得尽可能像表达式中的基础值类型一样。可以针对内置运算符提供运算符重载,以使它们在被需要时是因果性的。也就是说,将它们修改成接受因果输入,并且返回具有正确因果ID项的因果输出。

考虑用于在临床分析仪中实现流体测试的例程。GetFluidLevelStatus()函数可以返回Causal成功结果。该结果将包含使得成功返回值等于“false”或“true”的任何因果事件的因果ID项,如通过这些事件如何在结果变量的表达式(即,导致结果变量指派的因果路径)指派中馈送因果运算符来自动确定的那样。还要注意的是,该结果可以直接在“if”条件中使用,就像它在纯布尔值中那样。这是因为在Causal类中提供了对基础项类型的隐式转型转换运算符,从而允许在适当或可能的情况下将结果用作简单布尔。

由于该实现方式,可以容易地将因果事件报告添加到许多类型的数据变量。通过将它们从例如“bool”转换成“Causal”类型、或将“StateEnum”转换成“Causal”类型,来简单地消除大量工作和代码复杂性。因果ID关联、因果ID存储、从包装的数据类型的向上转型和向包装的数据类型的向下转型、以及用以跨任意组成和复杂性的表达式来正确地传播因果ID的运算符重载通常全部都是通过对Causal<>类中的目标变量进行包装而被自动负责的。另外,在将因果ID项实现为单个因果ID值时,可以容易地使行为成为实时的,使得它可以在实时代码中安全地被使用。

示例性实施例的目的是将两个可能的异常链接回到源因果原因。在该示例中,Exception类将在被捕获时作为中间因果事件而发布,并且允许设置父代因果ID项。在此处,变量bValue1和bValue2是因果路径的最早部分。如果未使用任何泛型或运算符重载,并且未创建任何特定于操作的效用函数来处理常见的因果操作,则将代码修改成在逐个表达式的基础上手动添加因果报告可以通过如下方式来完成:即根据遍及全文讨论的适当因果表从每个相关因果输入中得到因果ID项。因此,当调用ThrowErrorIfUnavailable()或ThrowErrorIfAvailable()时,它将报告bValue1和/或bValue2的适当因果ID项。

IsSystemAvailable()的因果实现方式中的简单表达式返回(bValue1 &&bValue2)然后可以使用复杂的双嵌套if-else结构,以按照针对布尔AND操作构建的因果表来确定将哪个输入因果ID传播到输出值。这可能不是合期望的实现方式,这是因为可能需要将这些嵌套的if-else结构添加到软件中的每个因果表达式。这可能会增加代码的复杂性,降低可靠性和可维护性,并且因此开发成本过高。另外,必须添加四个显式变量,以将“所附加的”因果ID与bValue1、bValue2、以及从对IsSystemAvailable()的两次调用所返回的值一起存储。这也是不理想的,这是因为它引入了复杂性,其中开发者需要知道哪些因果ID变量与哪些目标变量相关联。

在一些实施例中使用的以下代码使用Causal类来包装因果值。Causal类是通用的“包装器”类,其具有针对常见因果操作的运算符重载。该类将因果ID项值绑定到目标数据值,其中因果ID项被实现为单个CausalId值,该类提供运算符重载以自动处理布尔表达式和其他内置运算符,并且提供自动类型转换运算符以自动使得Causal<>值在不需要因果行为的表达式(诸如,ThrowErrorIfUnavailable()和ThrowErrorIfUnavailable()中的“if”条件)中表现得像其基础包装类型一样。

//在确定该系统是否可用时被计入考虑的两个值

//

Causal bValue1 = ...;

Causal bValue2 = ...;

//返回该系统是否可用

CausalIsSystemAvailable()

{

return(bValue1 && bValue2);

}

//如果该系统不可用,则抛出异常

//(例如,在运行患者测试之前的检查)

Void ThrowErrorIfUnavailable()

{

CausalbSystemAvailable = IsSystemAvailable();

if(!bSystemAvailable)

{

Exception AvailabilityError(

“System is unexpectedly unavailable.”);

AvailabilityError.SetParentCausalId(

bSystemAvailable.GetCausalId());

throw AvailabilityError;

}

}

//如果该系统可用,则抛出异常

//(例如,在运行诊断例程之前的检查)

void ThrowErrorIfAvailable()

{

CausalbSystemAvailable = IsSystemAvailable();

if(bSystemAvailable)

{

Exception UnavailabilityError(

“System is unexpectedly available.”);

UnavailabilityError.SetParentCausalId(

bSystemAvailable.GetCausalId());

throw UnavailabilityError;

}

}

因此,与将显式因果代码添加到现有的非因果函数相比,利用因果包装器类来自动处理因果行为可能更易于实现和维护。特别地,包含感兴趣的核心因果表达式的函数IsSystemAvailable()几乎不需要进行任何修改。C++布尔AND运算符“&&”的Causal类的运算符重载会自动负责从其两个输入中选择正确的因果ID,并且将其指派给然后从该函数返回的临时变量。而且,自动类型转换使得在ThrowErrorIfUnavailable()和ThrowErrorIfAvailable()的“if”条件中检查现在的Causal bSystemAvailable变量的值变得简单。提取基础的true/false值不需要任何改变。

//在确定该系统是否可用时被计入考虑的五个值

Causal bValue1 = ...;

Causal bValue2 = ...;

enum StateEnum

{

Initializing,

Running,

Stopped,

Resetting,

Diagnostics

};

CausalenState_SubSysA = ...;

CausalenState_SubSysB = ...;

CausalfSensorReading = ...;

//////////////////////////////////////////////////// //////////////

//

//返回该系统是否可用

//

CausalIsSystemAvailable()

{

return

(bValue1 && bValue2) ||

((((enState_SubSysA == StateEnum :: Running)&&

(enState_SubSysB!= StateEnum :: Diagnostics))||

((fSensorReading <= 5000.0)&&(fSensorReading> 100.0)));

}

// ...包括与上面相同的经修改的“ThrowErrorIf ...()”函数

//

在该实施例中,Causal类解决了增加许多嵌套的因果输入值检查以使所涉及的操作具有因果性的在实践上难解决的问题,并且取而代之地使其变得可行和简单。要注意的是,具有其复杂表达式的函数IsSystemAvailable()几乎不需要任何修改。特别地,它的表达式部分现在不需要任何修改。在该实施例中,应用于常规函数的唯一改变是通过将其输入和其返回值包装在Causal类中来修改其输入和其返回值的类型。

class Sensor

{

public:

Causal CheckSensor()

{

int l_nSensorReading = GetReading();

Causal l_bSuccess = true;

if( l_SensorReading > SENSOR_READING_MAX ) // [1]

{

// 返回传感器超出限制

CausalEvent l_CausalEvent(

"Sensor reading of " + l_SensorReading

+ " is above maximum limit of " + l_nSensorReading

+ "." );

PostCausalEventToDatabase( l_CausalEvent );

l_bSuccess = Causal(

false,

l_CausalEvent.GetCausalId() );

}

if( l_SensorReading < SENSOR_READING_MIN_WARNING ) // [2]

{

// 传感器仍被认为在限制内,但是将警告记录到磁盘

//

//

Log( “Sensor below expected minimum” );

}

return l_bSuccess;

}

};

void VerifySensor()

{

Sensor l_Sensor;

Causal l_bSensorOk = l_Sensor.CheckSensor();

if( l_bSensorOk ) // [3]

{

DisplaySuccess( “Sensor OK.” );

}

else

{

DisplayFailure(

“Sensor failed.”, l_bSensorOk.GetCausalId() ); // [4]

}

}

void VerifyAllSensors

{

Sensor l_Sensor1;

Sensor l_Sensor2;

Causal l_bSensorsOk =

l_Sensor1.CheckSensor() && l_Sensor2.CheckSensor();

if( l_bSensorsOk ) // [5]

{

DisplaySuccess( “All sensors OK.” );

}

else

{

DisplayFailure(

“One or more sensors failed.”,

l_bSensorsOk.GetCausalId() ); // [6]

}

}

添加了类CausalEvent和函数PostCausalEventToDatabase()作为通用库实用程序,以用于创建和发布因果事件。每当构造实例时,CausalEvent类会自动为其自身生成唯一的CausalId(随机化的128位UUID)。可以利用CausalEvent::GetCausalId()来检索该因果ID。DisplayFailure()已经被修改成接受因果ID,并且从数据库中查找和显示相关联的因果事件。

刚刚在[1]上面将“true”指派给l_bSuccess隐含了空因果ID项也被指派。它还利用自动类型转换,以使l_bSuccess表现得尽可能像普通bool一样,直到需要相关联的因果ID项为止。这表明了:在一些实施例中,将因果事件报告添加到各种代码通常是多么容易。

//返回该系统是否可用(两输入示例)

CausalIsSystemAvailable()

{

return CausalAnd(bValue1,bValue2);

}

//返回该系统是否可用(五输入示例)

CausalIsSystemAvailable()

{

return

CausalOr(

CausalAnd(bValue1, bValue2),

CausalOr(

CausalAnd(

CausalEqual(

enState_SubSysA,

StateEnum::Running),

CausalNotEqual(

enState_SubSysB,

StateEnum::Diagnostics)),

CausalAnd(

CausalLessThanEqual(fSensorReading, 5000.0),

CausalGreaterThan(fSensorReading, 100.0))));

}

虽然没有像在使用运算符重载时那么简单和低影响,但是这可以比手动编写if-else结构和中间计算来确定输出因果ID项更优选。该示例表明了:即使运算符重载不可用,因果事件报告技术也仍然可行。要注意的是,在任何给定软件应用中,通常只有少数表达式将期望因果报告,使得上述那样的构造应当是较少见的。对于可能频繁报告负面后果的软件系统而言,考虑到跨软件的所有用户而节省的集体故障诊断时间,编写和维护上述表达式的努力可能是可接受的。

当因果ID项被实现为单个因果ID值时,本发明针对每个负面后果发生一次报告最多一个单个因果事件。这在此处被称为“单因果报告”。如果期望的话,在一些实施例的情况下可以表示和报告多个原因。这在此处被称为“多因果报告”。为了表示后果的多个原因,代替于将单个因果ID一次附加到因果路径,因果ID项可以将多于一个因果ID作为所收集的集合或作为指向ID集合的指针来附加。在任何给定时间处附加到路径的当前因果ID集合表示该路径的当前数据或执行状态的当前收集的原因集合。通常,该因果ID集合将使用传统的“集合”容器语义来实现,其中表示该路径的当前原因集合的因果ID被包含在具有以下性质的集合类型容器中:不允许重复的ID(重复的ID被安静地折叠成该ID的仅一个副本);并且空的集合等效于空因果ID。

当因果ID项被实现为多个因果ID或因果ID项值的容器时,因果表将按以下方式来使用。“p”值表示附加到输入操作数“P”的因果ID项,并且“q”值表示附加到输入操作数“Q”的因果ID项。然后,“p”和“q”输出情况两者只是将从该输入操作数指定的因果ID项(p或q)传播到输出值。“”和“[pq]”输出情况两者合并“p”和“q”输入集合因果ID项,并且将所得的合并项附加到输出值。

当UI将要显示负面后果的原因时,如果与负面后果相关联的因果ID项被实现为因果ID值的集合或树并且是非空的,则UI查找针对因果ID项中的每个因果ID的事件,找到(一个或多个)每个事件的(一个或多个)根本事件(递归父代查找),将任何重复的根本事件折叠到非重复的根本事件的最终列表中,并且然后将每个根本事件显示为负面后果的原因。取而代之,如果与负面后果相关联的因果ID项为空,则不显示任何额外内容,就像空因果ID的情况下那样。在一些实施例中,一个实现方式选项是在所得的附加因果ID和/或因果ID集合上维持与[pq]输出情况之间的语义差异,以便向用户提供关于如下的更多信息:他们是否将需要补救某些子集的所有显示的原因(通过[pq]关系传播的那些)、和/或某些子集的仅一些显示的原因(通过关系传播的那些)。

一些实施例以树的形式存储每个因果ID集合,其中将输入合并的和[pq]输出情况被组装成树,该树在字面上反映了引起该后果所必需的条件的层次构成,并且其中重复的ID没有被折叠。可能影响决定实现哪个实施例的潜在性能影响包括:当附加了多个因果ID时,如果针对因果ID集合容器的存储器管理是基于堆的(如在许多软件库中常见的那样)情况下的通常的实时和存储器碎片化(fragmentation)考虑;以及由于添加到针对因果报告而修改的操作的另外的列表处理而导致的一般定时考虑。

由于与传播多个因果ID相关联的潜在性能影响,一些实施例简单地将单个因果ID附加到因果路径。单个因果ID实现方式的另外优点是:以实时方式来进行实现是非常容易的。在大多数情况下,附加单个因果ID证明对于用户而言是绰绰有余的。如早先所述,大多数负面后果仅由单个条件引起,因此报告单个因果ID足以报告大多数问题的原因。在其中负面后果由多于一个因果事件引起的情况下,仅显示其中一个原因通常仍是足够的。这是因为用户可以补救这一原因,并且然后再次尝试该操作。如果由于一个或多个其他原因导致该操作仍然失败,则将在重新尝试该操作时显示那些其他原因之一,并且然后用户可以补救该原因,并且再次尝试。重复该过程,直到所有原因都已经被补救为止。

还要注意的是,有可能使用实施例来报告正面后果的原因,而不仅仅是负面后果的原因。然而,为了使报告正面后果的原因对最终用户实际上有用,该实现方式应当在名义上进行多因果报告(多事件报告)而不是单因果报告(单事件报告)。这是因为在上面的一般原因和影响观察中,任何给定的正面后果通常都是很多很多输入原因的结果。然后,在利用本发明使用多因果报告来完成正面后果报告的情况下,可能将存在针对每个正面后果所报告的大量因果输入事件。这需要开发特殊的分类和因果事件树遍历过滤器,以突出显示用户最可能感兴趣的因果事件。为了使这些过滤器有用,还需要针对每个类型的正面后果来定义“正面后果中用户感兴趣的事件”,和/或使得可在运行时、在每次后果发生的基础上配置“正面后果中用户感兴趣的事件”。

这突出显示了一些实施例对于报告负面后果的原因比报告正面后果的原因可能更有用。造成这种情况的部分原因是:正面后果的原因和影响的性质比负面后果的原因和影响的性质复杂得多,并且在正常使用的情况下不那么感兴趣。

至少存在两种值得注意的情况,在这两种情况下,可能期望一个或多个中间条件来发布因果事件。第一,当实际的根本原因条件不能够由软件本身标识时,则可能期望发布中间因果事件,以便针对用户提供至少某种因果反馈而不是根本没有反馈,即使它不是最优的。这种反馈可以作为回到实际根本原因事件的部分追溯,并且往往会缩小用户要调查的可能根本原因条件的列表,即使它没有指向引起该问题的具体根本条件。第二,即使当上游根本原因条件也正在发布事件时,有时也可能期望中间条件有意地发布因果事件条件,以便向用户提供关于总体故障模式的上下文、状态和中间影响的更多信息。在这些情况下,中间事件应当在理想情况下将其父代因果ID项设置为从根本原因条件传入的因果ID项(如果可用的话),以便潜在地抑制在负面后果上显示中间事件,而倾向于显示根本原因事件,或潜在地显示在其发生时的原因和影响的层次结构。

这是有用的,因为因果路径的状态有时可以同时表示或引起正面和负面后果两者。不正确地将因果ID直接用作正面或负面状态值将破坏对该子问题的解决方案,并且因此将破坏对提供准确因果事件报告的总体问题的解决方案。

空因果ID项(和空因果ID)在涉及显示根本原因时具有特定含义,其中空因果ID项值可以有效地附加到失败状态值,指示针对该失败状态的“没有已知原因”。如果因果ID项变量不正确地用于直接指示成功或失败值,例如通过使非空ID项表示失败并且空ID项表示成功,(而不是因果ID描述了单独但被附加的成功或失败值的原因),则“没有该失败的已知原因”的因果情况不能够被正确地表示。这是因为在这种情况下不存在一种方式将“没有已知原因”的原因附加到该失败状态,因为语义上重载的因果ID在此处不可以同时是空(表示“没有该状态的已知原因”)和非空(表示某个负面后果产生状态)两者。

类似的问题也在假设实现方式中适用,其中空因果ID项代替地用于表示失败状态。该特定实现方式简单地破坏了该解决方案表示失败原因的基本能力,这是由于还需要非空ID项来表示失败的源因果事件,并且因果ID项不可以同时是空(失败状态)和非空(失败原因)。因此,将因果ID项本身视为负面状态将导致频繁地报告错误的根本原因,或频繁地未能报告任何根本原因,这两者在一些实施例中是要避免的。代替地,应当将代码编写成返回某个其他成功/失败状态值,诸如至少是“成功/失败”bool、或错误代码枚举,并且然后,在理想情况下,如果已知它可能引起负面后果,则使该值变成因果性的(通过将单独的因果ID值附加到它)。

代替于返回因果ID项来指示失败是否发生,示例性MoveMotor()函数返回bool来指示成功或失败,并且经由Causal<>包装器将因果ID项附加到它,以指示任何一个特定值被返回的原因,无论它是true还是false。因此,附加到结果的因果ID项是关于该结果的元数据,这是大多数实施例的正确实现方式。

这是通过如下方式完成的:将软件中的因果事件条件(显式和隐式两者)标识为用户的任意负面后果的任意源;生成数据库事件条目和唯一因果ID值,该因果ID值用于在因果事件条件每次发生时指向该事件条目;将(一个或多个)那些因果ID值作为元数据附加到作为该因果条件的结果而发生的任意现场数据值路径和现场执行路径,并且将所输入的(一个或多个)因果ID值从影响了某个其他路径上的因果改变的任何受影响路径传播到该其他路径,其中要传播的(一个或多个)因果ID由所涉及的操作的反事实预分析来确定。然后,这允许每个因果路径在任何给定时间处“指向”数据库中记录的(一个或多个)因果事件,作为该路径的当前值或状态的因果原因,并且因此,如果路径然后充当现场负面后果结果的直接原因,则允许负面后果指向数据库中要对该后果负责的源原因事件。

实施例可以动态地报告根本原因条件发生的具体实例,作为用户所经历的负面后果的原因,而不仅仅是简单的“成功/失败”状态代码和/或不仅仅是静态错误代码。这意味着,当针对不同的负面后果报告相同类型的根本原因条件(例如,错误条件或操作者动作条件)的不同实例时,也可以针对负面后果动态地报告可被可选地收集的特定于每个根本原因实例的可变信息(诸如,根本原因发生的日期/时间戳、特定于该根本原因发生的传感器数据、在该根本原因发生时登录的操作者等)。

一些实施例在用户经历负面后果时在现场地、主动地运行的软件中针对负面后果产生即时结果,而不需要用户运行任何后果前的准备或后果后的分析。实施例可以为用户产生高度准确且具有确定性的结果,并且避免可能导致不准确的结果或开发成本过高的启发式方法。当实现实施例的软件可以报告负面后果的具体因果事件时,它会这样做,否则,它不提供任何额外信息,而不是提供错误信息。

从启发式解决方案导出的结果已知经常是不准确的,这可能会增加用户的故障诊断负担,并且因此增加较长故障诊断时间的基本问题而不是减少它。实施例通常避免启发式方法,这是因为启发式解决方案开发起来可能很昂贵以致于不可行。在不同问题领域中产生启发式解决方案的许多尝试简单地无法实现其目的,并且从未投入使用。也就是说,它们从未被运送给客户,或者甚至部分地用于内部,从而导致失败的项目和业务资源的显著浪费。

通常,当使用实施例时,用户不需要搜索独立的事件报告列表来猜测根本原因,也不需要挖掘表示了不熟悉和任意的内部软件设计的不熟悉的追溯文件,也不需要几乎与在其他情况下一样频繁地将对问题的解决升级到其他方以便确定任意负面后果的(一个或多个)根本原因条件。就在负面后果显示处来名义上报告根本原因条件。

在开发资源允许的情况下,也可以用逐段(piece-by-piece)的方式在预先存在的软件中实现实施例。这可以通过转换个体因果路径和子路径以进行因果事件报告来完成。未转换的路径有效地退回到先前的行为,即针对感兴趣的负面后果不向用户报告任何额外的负面信息。即使不是所有路径都已完成转换,被转换的每个因果路径也有助于减少故障诊断时间。

由于子路径中的因果报告实现方式经由使用共同的因果ID类型而被设计为可互操作的,因此出现了正面的“临界质量(critical mass)”意外结果(side effect),其中随着更多的个体根本因果条件至负面后果的路径被转换,转换每个新路径往往变得更容易,这是由于从根到后果的完整路径通常共享代码中的中间子路径。

一些实施例利用标准的、随机化的128位UUID来实现其基本唯一的因果ID(例如,对于每个ID,ID冲突具有小于十亿分之一的发生概率),并且向可以从受影响的UI访问的共同数据库、或者向可以从受影响的UI访问的逻辑上共同的数据库集合报告其因果事件,这些实施例可以在它们之间传播原因和影响信息,而不管它们的个体子系统架构或内部因果事件(例如,错误或操作者动作)表示。这意味着软件通常可以被实现为跨多个不同的软件线程、软件进程、软件应用、计算机、网络节点等准确地跟踪原因和影响。

实施例通常支持:通过针对每次发生而生成新的、基本唯一的因果ID值来跟踪因果事件条件的个体发生;针对每次发生将由具有特定于发生的数据的该新的唯一因果ID标识的新因果事件实例写入到数据库;附加“当前”因果ID项变量,以跟踪该因果事件和其他因果事件对软件系统中的实际当前数据值和实际当前执行的影响,直到它们在用户界面中的任意负面后果;以及经由附加到中间路径的因果ID项变量来传播最初在因果事件条件发生中生成的唯一因果ID值,直到负面后果的显示。

各种实施例利用常规软件代码中已经存在的因果路径,以通过向那些路径添加简单的元数据段(因果ID变量)从而在原因和影响发生时自动跟踪原因和影响,并且不依赖于对所有可能的故障模式(因果事件条件和负面后果的所有可能组合,其范围通常可以达到数百万)的任何外部分析以便确定原因和影响。

向原因和影响链中的因果路径的每个段或节点添加因果ID项有效地向系统添加了现场状态驱动的“自跟踪”,并且当每个节点的输出影响或不影响一个或多个其他节点的状态时,因果ID项在受影响的因果状态改变时从一个节点到下一个节点的传播、以及因果ID项在未受影响的状态改变时的不传播自然地负责自动报告多种可能故障模式的(一个或多个)原因。这还自动地负责照顾通常共享重叠中间路径和后果的许多故障模式。

将因果ID实现为随机生成的、基本唯一的、128位UUID、并且将因果事件输出到共同数据库有助于通过使用事件(例如,错误)表示分母(denominator)(UUID)来跟踪因果错误,该事件表示分母跨不同的软件架构更有可能是可实现且低影响的。大多数软件语言和系统能够处理UUID,并且许多软件系统已经包括某种形式的事件数据库,不同的软件子系统需要向该数据库报告它们的感兴趣事件,而不管它们的内部事件表示(例如,错误表示)如何。

图16是可以用于实现本文中公开的因果事件报告的各种实施例的示例性系统170的系统示图。各种硬件系统172可以由处理器174控制。在一些实施例中,系统170是用于执行患者样本测试的临床分析仪。在其他实施例中,硬件系统172可以是来自汽车、制造、计算机外围设备等的任何硬件,这是由于本文中讨论的概念可以在临床化学领域之外使用,因为临床分析仪是可以受益于这些概念的仅一个实施例。硬件系统172可以包括用于执行和管理患者测试的各种电动机、传感器、测试系统等。处理器174可以是任何合适的计算系统的一部分,该计算系统具有一个或多个处理器和相关联的计算硬件、以及控制硬件系统172并与之交互所需的其他电路的微控制器。在处理器174上执行的是多个因果线程176。这些因果线程包括因果执行路径,诸如本文中所公开的因果函数。各种因果线程176与因果数据路径(诸如,简单变量、对象、持久数据等)178交互。因果数据178由经由UI 182与硬件系统172、计算系统或用户交互的线程176来使用,以执行软件系统的期望的、特定于应用的功能。如所讨论的,这些因果线程可能会遇到因果根本条件,生成因果事件,并且在特定状态改变时将所选的因果ID附加并且指派到因果数据。

如所讨论的,因果线程176还利用因果数据库180,以便以因果链接的方式来跟踪系统事件。因果线程176与用户界面182交互以寻求用户输入并且显示错误或其他信息。关于错误,UI 182可以响应于因果数据178和线程176报告的错误,并且访问因果数据库182以向用户报告负面后果的根本原因事件,确定父代-子代关系,并且显示因果树。这允许用户基于其因果性质容易地补救负面系统状态。

实施例利用在计算机(并且通过扩展,处理器)上执行的软件实现方式。该软件可以在任何合适的计算机上并且以任何合适的编程语言来实现。尽管描述了某些实施例以用于临床分析仪硬件和系统,但是这仅仅是可以从这些实施例的概念中受益的系统的示例。一些实施例适用于任何计算机或计算机和硬件系统。图20图示了示例性计算环境700,在该示例性计算环境700内,可以用于实现本文中描述的实施例。计算环境700包括计算机系统710,计算机系统710是可以在其上实现本发明的实施例的计算系统的一个示例。计算机和计算环境(诸如,计算机系统710和计算环境700)对本领域技术人员来说是已知的,并且因此在本文中简要描述。

图17A是高级流程图,其示出了由处理器执行具有因果功能的软件模块时所涉及的步骤。方法200开始于步骤202,其中处理器遇到应用相关序列。采用因果功能的软件应用可以包括特定于该应用中实现的实际函数的各种不同功能。因此,遇到的每个函数及其被处理的方式均是应用相关的。步骤202下面的步骤是在该应用内可能遇到的函数序列的示例性类型。在步骤202处遇到的应用的每个相关序列可以包括输入参数,该输入参数可以包括因果数据和非因果数据的任何组合。软件可以包括以下应用相关序列中的任一个,每个序列被表述为图17A中的步骤。要注意的是,此处解释了所有这些类型的序列,但是给定的应用可以在步骤210、220、290、300、350和370中使用这些应用相关序列的任何子集。如果遇到了这些类型的应用相关序列中的任一个,则处理器将执行对应的过程。

在步骤210处,处理器可能遇到不可能引起负面后果的序列。这些是在设计上不可能导致错误或负面后果的序列。例如,许多算术运算符或函数可以执行而不曾引起负面后果。在许多实例中,即使在执行这种函数时的错误也可能不会导致整个系统的负面后果。实现系统的软件工程师可以将任何给定函数视为与步骤210一致地运行。关于图17B进一步解释了步骤210。

在步骤220处,处理器可能遇到因果表达式,由此处理器将根据图17C对该因果表达式进行求值。在步骤290处,一些函数可能导致因果指派,在图17H中对此进行了解释。在步骤300处,处理器还可能遇到导致因果事件条件的应用相关序列,如图17I中所解释的那样。如果处理器遇到的应用相关序列是需要显示因果后果的序列,诸如可能导致需要向用户显示的后果的函数,则处理进行到步骤350,步骤350在图17L中示出。通常,应用相关序列不包括因果功能,诸如常规函数,在这种情况下,处理进行到步骤370,其中执行常规应用相关的指令。这些步骤中的每一个都是使用自变量来执行的,这些自变量可以是因果数据或非因果数据的任何组合,如每个应用相关序列中使用的函数所定义的那样。

一旦执行了给定的应用相关序列,在步骤204处,处理器就检查该应用代码,以确定执行是否完成或者是否存在要执行的另外的应用相关序列。如果执行完成,则在步骤206处,处理器完成应用相关序列的处理,并且返回因果数据和非因果数据的任何组合。

应当注意的是,图17A中的几乎任何序列都可以调用任何其他序列。在一些实施例中,大多数序列都接受具有所附加的因果ID项的因果数据参数以及非因果数据参数两者。用于一个序列的任何输入参数可能或可能不会形成用于其他序列的自变量。因果性质可以利用以下性质:当一个序列调用另一个序列时,该性质可以将或者可以不将因果数据自变量转换成非因果数据自变量,并且反之亦然。序列调用可以修改或者可以不修改与那些自变量一起发送的因果ID项。序列通常对作为自变量传递给下一个序列的(一个或多个)因果ID项进行以下操作中的一个或多个:不管它;在将因果数据转换成非因果数据时将它删除;或者在将非因果数据转换成因果数据时将它设置为空因果ID项。因果事件条件(过程300)是用新因果ID值来填充因果ID项的唯一地方。除此之外,因果ID项要么被传播,要么被初始地设置为空,要么被覆盖为空,要么被丢弃。

图17B示出了用于不可能引起负面后果的序列的过程210。在步骤212处,处理器通过传递因果或非因果数据参数以发起不可能引起负面后果的序列,从而开始执行。作为不可能引起这种后果的函数的指定是通过草拟应用的功能来完成的。例如,数据聚合或基本UI显示函数通常不可能导致负面后果。通常,控制外部硬件的软件函数可能导致负面后果,而纯计算指令中的许多可能无法导致负面后果。然后,可以在步骤214处递归地进行执行,这导致发起新的应用相关序列200。应当注意的是,如果在步骤212处已经发起了不可能引起负面后果的序列,则在步骤214处发起的任何应用相关序列的结果也不可能导致负面后果。因此,由递归调用返回到步骤200的任何所得因果ID将被移除或替换为空因果ID,这是因为,根据定义,过程210所调用的任何子函数都不可能导致负面后果。对过程200的调用可以包括如下自变量:该自变量是被转换成具有空因果ID的因果数据的非因果数据、或被转换成非因果数据从而移除了因果ID项的因果数据、或非因果数据。

替代地,处理器可能遇到不可能引起负面后果的应用相关指令,该指令不会导致对过程200的递归调用。这可以是不可能引起负面后果的任何所定义的函数,并且可以使用作为因果和非因果数据的自变量。一旦对指令或递归调用进行求值,处理器将返回结果,该结果包括不可能引起负面后果的值。这些值可以包括:被转换成具有空因果ID的因果数据的非因果数据、被转换成非因果数据的因果数据、或非因果数据。

在图17C中解释了过程220,即对因果表达式的求值。在步骤222处,处理器开始对因果表达式进行求值,该因果表达式可以包括因果数据和非因果数据的任何组合的参数。这可以以四个可能的方式中的任一个来进行。如果因果表达式是布尔AND,则求值进行到步骤230,其中处理器对布尔AND进行求值。如果因果表达式是布尔OR,则求值进行到步骤250,其中处理器对布尔OR进行求值。如果因果表达式是布尔NOT,则求值进行到步骤270,其中处理器对布尔NOT进行求值。如果因果表达式是任何其他操作,则求值进行到步骤280,其中处理器对该操作(标记为Operation_X)进行求值。这些过程中的每一个都可以将因果数据、或通过将因果ID项设置为空而被转换成因果数据的非因果数据作为自变量。关于图17D-G解释了这些过程。在步骤224处,一旦已经对因果表达式进行了求值,则处理器返回因果数据结果。

图17D示出了执行因果AND的示例性过程230的细节。在步骤232处,处理器发起对布尔AND的执行,传递包括变量P和Q(或用于多路AND的任何其他变量)的因果形式的参数。在步骤234处,处理器对因果值P的非因果值进行求值,以确定P的非因果部分(标记为P)是否为true。在步骤236和242处,处理器确定因果值Q的非因果部分(标记为Q)是否为true。如果两者都不为true,则在步骤238处,处理器返回false以及新因果ID项,该新因果ID项包含P和Q两者的因果ID项或其任何子集。如果P为false并且Q为true,则在步骤240处,处理器返回false、以及P的因果ID项。如果P为true并且Q为false,则处理器返回false以及Q的因果ID项。如果P和Q两者都为true,则处理器返回true以及新因果ID项,该新因果ID项包含P和Q两者的因果项或其任何子集。这与解释因果AND函数如何工作的表是一致的,遍及全文对该表进行了解释。

如图17E中所示,用于执行因果OR的示例性过程250表现得也与遍及全文所解释的表一致。在步骤252处,处理器发起因果布尔OR操作,将因果值作为参数。在步骤254处,处理器求值P是否为true。在步骤256和262处,处理器求值Q是否为true。与这些表一致,如果P和Q都不为true,则在步骤258处,该过程将返回false以及新因果ID项,该新因果ID项包含P和Q两者的因果ID项或其任何子集。如果P为false并且Q为true,则在步骤260处,该过程将返回true以及Q的因果ID项。如果P为true并且Q为false,则在步骤264处,该过程将返回true以及P的因果ID项。如果P和Q两者都为true,则在步骤268处,该过程将返回true以及新因果ID项,该新因果ID项包含P和Q两者的因果ID项或其任何子集。

如图17F中所示,用于执行因果NOT的示例性过程270表现得也与遍及全文所解释的表一致。在步骤272处,处理器发起因果布尔NOT操作,将因果值P作为参数。在步骤274处,NOT操作仅仅返回值P的逻辑NOT以及P的因果ID项。

图17G示出了用于执行通用因果函数(标记为Operation_X)的综合(catchall)过程280。这可以包括比较、算术等。软件开发者应当根据本文中所述的方法来提供因果表以创建因果功能。在步骤282处,处理器发起Operation_X,接收所定义数量的因果值{A_1,a_1}...{A_n,a_n}。在步骤284处,Operation_X根据其定义而执行,并且返回具有由该操作的定义所确定的非因果成分的因果值、以及与软件开发者针对Operation_X创建的因果表一致的因果ID项。

图17H示出了用于因果指派的简单情况的过程290,其中函数复制、设置或初始化因果数据值。处理器将简单地将因果ID项中的两个基础值一起复制。当从然后转型为因果值的文字值指派因果数据值时,如果没有针对该文字值的已知原因,则可以指定空因果ID项,或者如果开发者已知执行的因果事件条件子句是该文字值被指派的原因,则因果ID项可以源自于与该子句相关联的因果数据。当从然后转型为因果值的非因果数据指派因果数据值时,如果没有针对该非因果数据值的已知原因,则可以指定空因果ID项,或者如果开发者已知执行的因果事件条件子句是该文字值被指派的原因,则因果ID项可以源自于与该子句相关联的因果数据。在步骤292处,处理器发起因果指派,将因果数据或被转换成因果数据的非因果数据指派给新因果值的存储器中的位置。在步骤294处,处理器将因果参数、基础值和相关联的因果ID项复制到指派目标数据位置,从而覆盖任何先前的值。在步骤296处,该过程返回。

图17I示出了用于对因果事件条件进行求值的过程300。通常存在两种因果事件条件:不具有所发送的父代事件的非父代因果事件(源于不是因果表达式的核心条件表达式);以及具有父代的因果事件,其中当被作为因果表达式的表达式触发时,可以发送父代ID。在步骤302处,处理器开始执行可以将因果数据或非因果数据作为参数的因果事件条件。取决于情况,因果事件条件可以是非父代因果事件条件(过程310,图17J),或者可能是父代因果事件条件(过程330,图17K)。过程310将通过移除因果ID项而被转换成非因果数据的因果数据、或非因果数据作为自变量。传递给被指定为非父代的条件的任何因果ID项应当忽略任何因果ID项,因为根据定义,该事件不可能由另一个因果事件触发,或者触发该事件的输入不应当具有非空因果ID。过程330将因果数据、或被转换成具有空因果ID的因果数据的非因果数据作为自变量。根据定义,父代因果事件应当具有至少一个具有非空因果ID的输入参数。

在图17J中解释了用于处理非父代因果事件条件的过程310。在步骤312处,处理器开始对非父代因果事件条件进行求值,将非因果数据作为参数。在步骤314处,对非因果表达式进行求值,并且将任何结果发送到步骤316。在步骤316处,处理器将表达式结果值与已知可能产生负面后果的条件(诸如,错误条件)进行比较。如果不满足因果条件,则在步骤324处,处理器应当返回,并且可以可选地包括具有空因果ID项的成功代码。

如果满足了因果条件(诸如,错误条件),则处理器在步骤318处开始用于创建新因果ID的过程。在步骤318处,处理器生成适当唯一的因果ID项,并且收集与该条件相关的任何诊断信息。然后,处理器生成新因果事件条目并且将其发布到因果数据库,其中:事件的因果ID被设置为刚生成的唯一因果ID;事件的父代因果ID项被设置为空因果ID项;事件的类型被设置为标识该因果条件的类型;以及事件的诊断数据被设置为刚收集的诊断数据。然后,处理器将新生成的因果ID发送到下一个步骤(320),作为针对所传递的自变量的因果ID项。

在步骤320处,处理器执行已知可能引起负面后果的动作。这可以通过调用过程200以执行对于实行可能引起负面后果的动作所需要的序列来完成。所传递的自变量包括其因果ID项被设置为新生成的因果ID项的因果数据、或非因果数据。在步骤322处,该过程返回。所返回的值可以可选地包括因果数据(诸如,故障代码),其中因果ID项可以被设置为刚生成的因果ID等。

在图17K中示出了用于处理具有因果父代的事件条件的过程330。在步骤332处,处理器开始对具有作为参数而传递的因果数据的因果事件条件进行求值(与步骤312进行比较,在步骤312中,作为参数而传递非因果数据)。步骤334与步骤220基本相同,在步骤334中,处理器对因果表达式进行求值,将因果数据作为自变量,并且将结果发送到步骤336。在步骤336处,将因果表达式的基础值与已知可能产生负面后果的条件进行比较,类似于步骤316。如果否,则处理器在步骤346处返回,并且可以可选地包括其因果ID项被设置为空的因果成功代码。如果是,则在步骤338处,处理器将因果事件发布到因果数据库,除了该事件会将步骤334的结果中的因果ID项指定为该事件的父代因果ID项之外,与步骤318类似。也就是说,处理器将生成适当唯一的因果ID,收集关于该条件的诊断信息,并且使用新生成的因果ID在因果数据库中创建新因果事件条目,将父代因果ID项设置为在步骤334处返回的因果ID,基于因果条件来设置因果事件类型,以及将该事件中的任何诊断数据包括在数据库中。刚生成的因果ID将被发送到下一个步骤。

在步骤340处,处理器执行已知可能引起负面后果的动作。这可以通过调用过程200以执行对于实行可能引起负面后果的动作所需要的序列来完成。所传递的自变量包括其因果ID项被设置为新生成的因果ID项的因果数据、或非因果数据。在步骤342处,处理器返回。所返回的值可以可选地包括因果数据(诸如,故障代码),其中因果ID项可以被设置为刚生成的因果ID等。

在图17L中图示了过程350,其中处理器执行针对因果后果的检查。在步骤352处,处理器发起用以检查要显示的因果后果的过程,将因果数据、或被转换成具有空因果ID项的非因果数据作为参数。

在步骤354,处理器确定因果数据的值是否表示要(立即)显示给用户的应用相关的负面后果。某些负面后果可能比其他负面后果更重要,从而有必要提醒用户。如果负面后果的显示被批准,则执行进行到步骤356,并且如果否,则执行进行到步骤364。在步骤356处,处理器确定与因果数据相关联的因果ID项是否是空因果ID项。如果是,则在步骤362处,处理器将向该应用的用户显示应用相关的负面后果,而不显示与该后果相关联或一起显示的因果事件(原因)。

如果因果ID项不是空ID,则在步骤358处,处理器基于因果数据的因果项中的因果ID在因果事件数据库中查找因果事件。针对具有非空父代因果ID的所有事件,将查找这些父代事件。该过程将继续,针对所有新查找的事件来查找所有父代事件,直到已经找到所有相关的非子代事件为止。这些非子代事件中的每一个都是潜在的根本因果事件。在步骤360处,处理器向用户显示应用相关的负面后果、以及在步骤358中找到的作为负面后果的原因的根本原因事件。在一些实施例中,因果事件的格式化(format)和解释可以基于包含在源因果项中的语义、应用相关的细节、事件类型、或包含在与每个因果ID相关联的因果事件数据库中的具体信息。GUI也可以被配置成允许子代事件的切换(toggling)以向用户提供额外的上下文。

返回步骤364,处理器还可以确定因果数据的值是否表示要立即显示给用户的应用相关的正面后果。在一些实施例中,可以向用户显示某些正面事件,诸如任务的成功完成。如果因果数据表示这种后果,则执行进行到步骤368,如果否,则执行进行到步骤366。在步骤368处,该界面向用户显示应用相关的正面后果。通常,该显示将不包括与该后果相关联的任何因果事件,并且忽略与因果数据相关联的因果ID项。这是因为正面事件通常不具有用户所认为的根本原因,或者根本原因不重要,并且因此可能被用户针对重要信息而混淆。在步骤366处,当不存在要显示的正面后果时,该界面在处理器的控制下将不向用户显示任何正面或负面后果。通常,将不显示任何因果事件,并且将忽略因果ID项。在步骤369处,该过程将返回。

图18A-H和19A-C图示了不同的数据值和因果ID可以如何流动通过相同和不同的表达式的示例,这些表达式由使用遍及全文讨论的因果表的基本布尔操作组成,并且该方法产生了期望的结果。在这些示例中,开发者不需要担心“true”或“false”在任何给定时间处是否表示错误或成功,除非当涉及显示最终结果的时候。如果某些读数高,则示例性函数ReadingIsHigh()返回true,如果读数低,则返回false。在一些情况下,高读数是好的而低读数是不好的;并且在其他情况下,低读数是好的而高读数是不好的,这取决于处理读数的表达式。在该示例中,假设在调用ReadingIsHigh()时发布了因果事件,该事件标识了传感器和读数,并且该事件的因果ID与该调用的true/false结果一起被返回。例如,传感器A返回{true,a}或{false,a},其中“a”是通过对传感器A的ReadingIsHigh()的调用所发布的因果事件的因果ID;传感器B返回{true,b}或{false,b},其中“b”是通过对传感器b的ReadingIsHigh()的调用所发布的因果事件的因果ID;等等。“a”所指代的因果事件会将传感器A命名为源,并且理想情况下包括传感器读数、日期/时间等。同样地,“b”所指代的因果事件会将传感器B命名为源,并且理想情况下包括传感器读数、日期/时间等。ReadingIsHigh()具有因果值输出,该因果值输出包括传感器高/低值(true或false)以及与读取该传感器的事件相关联的因果ID。这些示例示出了随着因果值通过表达式而传播,每个因果函数的因果输出,该因果函数包括因果OR、因果AND以及因果NOT。

图18A示出了两个简单的情况,其中读取传感器A确定是否应当显示负面后果。如果函数的结果为false,则显示负面后果,而如果函数结果为true,则不显示负面后果。图18B示出了相反的情况,其中当函数的输出为true时,显示负面后果。图18C示出了与图18A和B中所示的规则类似的规则,但是具有添加到该逻辑的因果NOT函数,这翻转了后果。

图18D示出了稍微更复杂的示例,其中使用了两个传感器,并且其结果与因果AND相组合,如果函数求值为false,则其结果引起了错误。呈现了传感器读取函数输出的四个可能逻辑组合中的每一个。因为传感器A和B的读取函数的负面求值可能是负面后果的原因,所以负面后果与源事件一起被显示,源事件指向哪些传感器未能具有高读数。图18E示出了类似的情况,其中如果函数求值为true,则显示错误。所显示的负面后果包括两个读取函数均求值为true时的两个传感器的标识。任何其他组合都不会导致负面后果的显示。在这些示例中,基于AND函数来显示合理的原因,该AND函数表现得与遍及全文讨论的逻辑表相符合。

图18F和18G呈现了其中使用了因果OR的两个不同示例。图18F示出了如下方案的结果:其中如果OR求值为False,则显示负面后果;图18G示出了如下方案的结果:其中如果OR求值为true,则显示负面后果。在这些示例中,基于OR函数来显示合理的原因,该OR函数表现得与遍及全文讨论的逻辑表相符合。

图18H示出了一示例,其中使用复合逻辑表达式来确定是否使用NOT和AND的因果版本的组合来显示负面后果。如所预期的那样,根据这些操作基于因果ID的传播标识了合理的根本原因。

图19A-C示出了一示例,其中使用复合逻辑表达式来确定是否针对三个传感器使用NOT、OR和AND的因果版本的组合来显示负面后果(仅呈现ReadingIsHigh()函数的输出的可能组合的子集)。如所预期的那样,根据这些操作基于因果ID的传播标识了合理的根本原因。

如图20中所示,计算机系统710可以包括通信机构,诸如用于在计算机系统710内传送信息的系统总线721或其他通信机构。计算机系统710进一步包括与系统总线721耦合的用于处理信息的一个或多个处理器720。处理器720可以包括一个或多个中央处理单元(CPU)、图形处理单元(GPU)或本领域中已知的任何其他处理器。

计算机系统710还包括耦合到总线721的用于存储要由处理器720执行的指令和信息的系统存储器730。系统存储器730可以包括以易失性和/或非易失性存储器的形式存在的计算机可读存储介质,诸如只读存储器(ROM)731和/或随机存取存储器(RAM)732。系统存储器RAM 732可以包括(一个或多个)其他动态存储设备(例如,动态RAM、静态RAM和同步DRAM)。系统存储器ROM 731可以包括(一个或多个)其他静态存储设备(例如,可编程ROM、可擦除PROM和电可擦除PROM)。另外,系统存储器730可以用于在由处理器720执行指令期间存储临时变量或其他中间信息。包含有助于在计算机系统710内的元件之间传送信息(诸如,在启动期间)的基本例程的基本输入/输出系统(BIOS)733可以存储在ROM 731中。RAM 732可以包含对处理器720来说可立即访问和/或目前由处理器420操作的数据和/或程序模块。系统存储器730可以另外包括例如操作系统734、应用程序735、其他程序模块736和程序数据737。应用程序735可以包括例如一个或多个可执行应用,诸如因果ID数据库的实现方式以及遍及全文讨论的各种因果线程。

计算机系统710还包括耦合到系统总线721以控制用于存储信息和指令的一个或多个存储设备的磁盘控制器740,该存储设备诸如硬盘741和可移除介质驱动器742(例如,光盘驱动器、固态驱动器等)。可以使用适当设备接口(例如,小型计算机系统接口(SCSI)、集成设备电子器件、通用串行总线(USB)或火线(FireWire))将存储设备添加到计算机系统710。

计算机系统710还可以包括耦合到总线721以控制用于向计算机用户显示信息的显示器766的显示控制器765,显示器766诸如液晶显示器(LCD),该计算机用户的任务是对固定机器人系统的控制器计算系统进行编程或维护。计算机系统包括输入接口760和一个或多个输入设备,诸如键盘762和指点设备761,用于与计算机用户交互并且向处理器720提供信息。指点设备761可以是例如鼠标或指点杆,用于向处理器720传送方向信息和命令选择,并用于控制显示器766上的光标移动。显示器766可以提供触摸屏接口,该触摸屏接口允许输入补充或替换由指点设备761对方向信息和命令选择的传送。

计算机系统710可以响应于处理器720执行包含在存储器(诸如,系统存储器730)中的一个或多个指令的一个或多个序列而执行本发明的实施例的部分或全部处理步骤。可以将这种指令从另一计算机可读介质(诸如,硬盘741或可移除介质驱动器742)读取到系统存储器730中。硬盘741可以包含本发明的实施例所使用的一个或多个数据储存器和数据文件。可以对数据储存器内容和数据文件进行加密以改进安全性。处理器720还可以被用在多处理布置中以执行包含在系统存储器730中的一个或多个指令序列。在替代实施例中,可以代替软件指令或与软件指令组合而使用硬连线电路。因此,实施例不限于硬件电路和软件的任何具体组合。

如上所陈述,计算机系统710可以包括:至少一个计算机可读介质或存储器,用于保持根据本发明实施例而编程的指令且用于包含本文描述的数据结构、表、记录或其他数据。如本文使用的术语“计算机可读介质”指代参与将指令提供给处理器720以用于执行的任何介质。计算机可读介质可以采取许多形式,包括但不限于非易失性介质、易失性介质和传输介质。非易失性介质的非限制性示例包括光盘、固态驱动器、磁盘和磁光盘,诸如硬盘741或可移除介质驱动器742。易失性介质的非限制性示例包括动态存储器,诸如系统存储器730。传输介质的非易失性示例包括同轴线缆、铜线和光纤,包括组成总线721的导线。传输介质还可以采取声波或光波(诸如,在无线电波和红外数据通信期间生成的那些声波或光波)的形式。

当在联网环境中使用时,计算机系统710可以包括调制解调器772,用于通过网络771与云服务610(见图6)建立通信。调制解调器772可以经由用户网络接口770或经由另一适当机构连接到总线721。网络771可以是本领域中公知的任何网络或系统,包括互联网、内联网、局域网(LAN)、广域网(WAN)、城域网(WAN)、直接连接或连接序列、蜂窝电话网络、或者能够便于在计算机系统710与其他计算机之间进行通信的任何其他网络或介质。网络771可以是有线的、无线的或其组合。可以使用以太网、通用串行总线(USB)、RJ-11或本领域中公知的任何其他有线连接来实现有线连接。可以使用Wi-Fi、WiMAX、和蓝牙、红外、蜂窝网络、卫星或本领域中公知的任何其他无线连接方法来实现无线连接。另外,若干网络可以单独地或彼此通信地工作以便于网络771中的通信。

本公开的实施例可以利用硬件和软件的任何组合来实现。另外,本公开的实施例可以被包括在具有例如计算机可读非暂时性介质的制品(例如,一个或多个计算机程序产品)中。介质中体现有例如用于提供和便于本公开实施例的机制的计算机可读程序代码。该制品可以作为计算机系统的部分被包括或者被分离地出售。

虽然在本文中已经公开了各种方面和实施例,但是对于本领域技术人员而言,其他方面和实施例将是显而易见的。本文中公开的各种方面和实施例用于说明的目的并且不意图是限制性的,其中真实的范围和精神由所附权利要求指示。除非另行陈述,如根据以下讨论显而易见的那样,将领会的是,诸如“应用”、“生成”、“标识”、“确定”、“处理”、“计算”、“选择”等等之类的术语可以指代计算机系统或者类似的电子计算设备的动作和过程,该计算机系统或者类似的电子计算设备将表示为计算机系统的寄存器和存储器内的物理(例如,电子)量的数据操纵和变换成类似地表示为计算机系统存储器或寄存器或者其他这种信息存储装置、传输或显示设备内的物理量的其他数据。

本文中所描述的方法的实施例可以使用计算机软件来实现。如果以符合公认标准的编程语言来编写,则设计成实现所述方法的指令序列可以被编译用于在各种硬件平台上执行以及用于与各种操作系统对接。另外,没有参照任何特定编程语言来描述本发明的实施例。将领会的是,各种编程语言可以用于实现本发明的实施例。如本文中所使用的可执行应用包括如下代码或机器可读指令:该代码或机器可读指令用于调节处理器以例如响应于用户命令或输入来实现预定功能,诸如操作系统、上下文数据获取系统或其他信息处理系统的那些功能。

可执行应用是代码段或机器可读指令、子例程、或代码的其他不同区段、或者用于执行一个或多个特定过程的可执行应用的部分。这些过程可以包括接收输入数据和/或参数、在所接收的输入数据上执行操作和/或响应于所接收的输入参数而执行功能,以及提供所得到的输出数据和/或参数。

如本文中所使用的“图形用户界面”(GUI)包括一个或多个显示图像,该显示图像由显示处理器生成并且使得能够与处理器或其他设备进行用户交互以及进行相关联的数据获取和处理功能。GUI还包括可执行过程或可执行应用。可执行过程或可执行应用调节显示处理器以生成表示GUI显示图像的信号。这些信号被供应到显示设备,该显示设备显示图像以供用户查看。处理器在可执行过程或可执行应用的控制下响应于从输入设备接收到的信号而操纵GUI显示图像。以此方式,用户可以通过使用输入设备来与显示图像交互,从而使得能够与处理器或其他设备进行用户交互。

本文中的功能和过程步骤可以自动地或者完全或部分地响应于用户命令而被执行。自动执行的活动(包括步骤)在没有用户对活动的直接发起的情况下、响应于一个或多个可执行指令或设备操作而被执行。

各图的系统和过程不是排他性的。根据本发明的原理可以得到其他系统、过程和菜单以实现相同目标。尽管已经参考特定实施例描述了本发明,但是要理解的是,本文中所示和描述的实施例和变型仅仅用于说明目的。在不偏离本发明范围的情况下,可以由本领域技术人员实现对当前设计的修改。如本文中所描述,可以通过使用硬件组件、软件组件和/或其组合来实现各种系统、子系统、代理、管理器和过程。不应根据35 U.S.C. 112(f)的规定来解释任何权利要求要素,除非使用短语“means for(用于......的部件)”明确记载了该要素。

相关技术
  • 用于确定性地报告软件系统中的原因和影响的方法
  • 城轨车辆部件故障模式和故障原因综合影响度的确定方法
技术分类

06120112678332