论文翻译:A PROCEDURE FOR DESIGNING ABSTRACT INTERFACES FOR DEVICE INTERFACE MODULES

个人翻译学习记录,如有错漏,务必指出
更推荐英文原版
英文原版可从该网址下载 https://dl.acm.org

A Procedure For Designing abstact interfaces for device interface Modules

by Kathryn Heninger Britton, R. Alan Parker, David L. Paranas

Code 7590, Naval Research Laboratory, Washington, D.D. 20375

摘要

这篇论文描述了抽象接口原则并展示了这个原则如何应用于设备接口模块的设计中。这个原则的目的是通过促进软件的对可替换硬件接口的适应性而降低嵌入式实时软件的维护成本。这个原则已被应用到Naval Research Laboratory (NRL)对A-7飞机的飞行软件的重新设计。本论文探讨基于抽象接口原则的设计方法,并展示在重新设计A-7中碰到的有趣问题的解决方案。A-7设备接口的规格文档已给出;这个文档为本文讨论的设计方法提供了一个完整的例子。

关键词:软件设计技术,模块规格,抽象接口,软件维护成本削减,信息隐藏模块,实时软件,嵌入式软件,设备接口模块,虚拟设备。

Ⅰ. 介绍

背景

在NRL,我们正在重新设计A-7飞机的飞行软件,以此来评估新软件工程技术对嵌入式软件设计的适用性。(一个嵌入式软件系统是巨型硬件或软件系统的一个独立组件。更多完整的描述见Parans关于抽象接口的论文。[5] )我们打算为包括好的结构软件和有益的文档提供完善的例子,以此来让其他设计者应用我们发现有用的技术。更多信息见Heninger关于项目的论文。 [1]

多数嵌入式实时软件的复杂度都关乎控制特定目标的硬件设备。很多设计者通过在软件设备接口模块中隔离设备特征以降低复杂度,透过这一做法,可以让程序在不需要了解设备细节的情况下编写。尽管这些设备接口模块一般来说确实让软件的其他部分更简单了,但是他们的接口通常是临时设计的(their interfaces are usually hte result of an ad hoc design process,)并且没法完全地封装设备细节。结果就是,设备改动导致整个软件的改动,而不是仅仅在设备接口模块。我们基于设计A-7设备接口模块的接口的抽象接口原则 [5] 开发了一个系统化的procedure。我们相信最终的接口可以成功的封装设备依赖,因此替换或修改一个设备仅仅要求设备接口模块的改动,而不是软件的其余部分。这篇论文解释和说明了这个procedure。

内容

尽管本文描述的潜在的(underlying)原则不是新的,但设计procedure是新的,而且和当下的实践是有巨大的偏差的。结果就是,我们希望这篇论文更能引起实践软件工程师的兴趣,而非关注时下热点研究人员。

第二部分讨论设备接口模块和设计者试图使用这些模块达成的目标。尽管这个材料(material)已经不新了,但这能够引出这篇文章的其余部分。

第三部分定义了本文剩余部分使用的条款(terms)。尽管这些定义不是新的,他们还并未广为人知。对procedure的展示依赖于对这些条款(terms)的精确使用。

第四部分描述和说明了系统化的procedure。说明展示了在开发A-7设备接口模块之一的一个抽象接口时的几个阶段。

即便是系统化的procedure也没有办法让软件设计变得更加简单。在A-7设计中,我们经常需要在灵活性和运行时效率两者间做出艰难的权衡。回想起来(In retrospect),我们发现了几个反复出现的问题。这些问题没有逼得我们改变基础procedure,但是他们确实让我们在设备接口设计上增加了额外的指导原则。第五部分描述了部分困难,和最终得出的指导原则。

Ⅱ. 目标(Objectives)

嵌入式实时软件系统经常有复杂且限制性的外部接口。因为嵌入式软件通常是一个大得多的系统的一个小组成部分,所以接口很少为了软件开发者的便利而修改。A-7航空电子软件就是一个典型:有21个设备连接到计算机上,包括传感器,显示器和计算机控制的装备。那些随意的接口特征,比如值编码格式和timing怪癖,都有可能在开发过程中或者在初始部署之后发生改动。缺陷(inadquacies)可能会在设备规格中出现;supplier可能会deliver一个被认为是足够的设备,尽管它实际上并不满足规格;一个设备可能被上位替代品代替;又或者新的连接要添加到设备之间。

嵌入式软件总是有着普遍而不受人待见的特性——一个在设备接口上的改动居然辐射到了整个软件,而这正是因为程序构建于随意的接口细节之上。如果一个接口改动,那么依赖这个接口的程序都会变得无效。因为这些依赖关系很少被明确的记录,接口改动经常有着令人惊讶的后果。

为了避免这些问题,通常会将软件划分为两组组件:1)包含了设备依赖代码的设备接口模块;2)设备无关的剩下的部分,包括用户程序,这么称呼是因为他们使用设备接口模块。设备接口模块提供虚拟设备,软件中部分实现的类设备能力。比如说,A-7系统中的一个虚拟高度表。虚拟高度表返回一个类型为range的值,而不是从实际传感器上读取的bitstring。原始数据被在高度设备接口模块中读取,缩放,修正,再筛选。这个软件接口在图1中展示。

设备接口模块的设计有如下的目标:

  • 限制改动:设计设备接口模块是信息隐藏 [6] 的一种特殊情况;硬件接口细节应该隐藏在这些模块里,这样当设备被修改或替换时才能将改动约束在这些模块中。限制改动中的问题由以下三种错误造成。

    1. 设备接口模块允许用户程序发掘/窥探到特定设备的特殊特征,这样当设备替换时用户程序就必须改动了。

    2. 虚拟设备缺少必要的功能,因此用户程序必须直接访问实际的设备;同样当设备替换时用户程序也必须改动了。

    3. 并不依赖设备的程序却被放入了设备接口模块。结果就是,需求修改时设备接口模块可能会改动,即使设备根本没有改动。并且,这样的模块在设备改动时更难修改。

    总结,设备接口模块理想情况下应

    1. 是设备改动时,唯一需要修改的模块

    2. 是设备不改动时,没有必要改动的模块

    3. 是相对较小的、直接的(straightforward)以便改动

  • 简化软件剩余部分:嵌入式软件通常难以理解因为它的正确性依赖于随意的接口细节。如果这些细节被限制在设备接口模块内,用户程序就会更简单,更轻松得正确的编写,也更容易理解,相比于直接使用硬件接口的话。

  • 强制规范使用资源:当所有程序访问设备时都遵守某个原则时,软件reliability会增强,比如定期检查不良事件(undersired event)[7] 和设备共享中的标准协议 [3] 。如果这些原则在设备接口模块内构建,他们就会系统性地强制执行(systematically enforced);开发者写用户程序的时候就不需要考虑这些东西了。

  • 代码共享:当很多程序直接访问设备时,他们大多包含了用于最终同一方式使用设备的相似的子程序。如果有了设备接口模块,这些代码就只需要写一次,包括保存,debug,测试,还可能是计算机存储。

  • 设备高效使用 相互独立地编写的程序通常导致设备不必要地重复动作。中心化设备访问代码更容易避免这些无意义操作。

    为了达成这些目标并避免先前提到的问题,设备接口模块和用户程序之间的接口应该是抽象接口,正如下一节中定义的那样。

三. 定义

  • 接口:两个程序之间包含了一组假设,这些假设都是对其他程序所作出的,目的是为了保证自己的程序运作正常。为了方便,我们采用“程序A对程序B所作的假设”这个表述,来表示,在B的特性必须满足的情况,A才能正常工作。这些假设不仅仅局限于传统上在接口文档中写明的调用顺序和参数格式;这些假设还包括了额外的信息,比如说信息交换的意义和限制(the meaning and limits of informatino exchanged),事件顺序的限制,以及在意外事件发生时的期望行为。在程序和设备之间的接口同样存在类似的定义。
  • 抽象:一个对一组对象的抽象是一个可以平等地应用于其中任何一个的描述。。每一个对象都是抽象的实例。对于一个非平凡抽象,在抽象和它所描述的对象们之间存在一个一对多的关系。Differential equations are an example of a mathematical abstraction representing systems as diverse as electrical circuits and collections of springs and weights.

对目标的合理抽象相比于实际系统更容易学习理解,因为它隐藏了对目标无关的细节。一条道路图是对道路网络的抽象;图表展示了方向,相对长度,以及道路路口,但是它并不显示一条道路是由沥青做的或者它是怎么建造的。相比于探索实际道路,学习一个道路图来找到一条好的路线是简单得多的。

任何从抽象中获取的结论,都可以应用于同一抽象表述的任何系统。广为人知的图论结论可以应用于道路图来决定最短路径;相同的方法同样可以应用来解决其他由有向图表示的很多问题。如果结论是从不合适的抽象,比如说,错误地忽视了相关细节的抽象,中得出的,那么他们最终可能会导致错误。举个例子,一个道路图不足以找到最快的路线,因为他们没有展示其他的更多影响行驶事件的因素,比如道路限速。

  • 抽象接口:一个抽象接口是一个意味着多个接口的抽象;它由它所表述的所有接口中的假设组成。某种类型的设备的抽象接口仅仅会暴露实际设备的部分细节:这样的抽象只会描述出这一类型设备的普遍的方面,而隐藏那些可以把实际设备从这一类设备中辨别出来的方面。

  • 设备接口模块:一个设备接口模块是一组用于将在抽象接口和实际硬件接口之间转换的程序。仅当抽象接口中的所有假设对于实际设备而言都成立时,该模块的实现才有可能。

  • 秘密:设备接口模块的秘密是用户程序不允许做出的,关于实际设备的假设。秘密就是相比其他相同功能的设备而言,当前使用的设备必须成立的假设信息。想要软件正确地工作,秘密必须在某处合适得考虑到;它们被封装在一个设备接口模块。

  • 意外(Undersired)事件假设:程序A和B之间的接口包括A对B做出的假设和B对A做出的假设。系统可以设计成只有两个之一去依赖另一个满足它自己规格的程序。一个程序可以设计成不依赖于正确使用它的用户程序;它可以检查不恰当的使用并在这些时间发送意外事件。然而,错误检查和报告都需要额外的指令。在A-7软件的开发版本中,设备接口模块会假设意外事件可以出现;它们会包含检查用户程序犯错的代码。在生产版本中,没有空间给这些错误检查。设备接口模块会假设不当使用不会出现;错误检测代码会忽略掉来让系统更小更快。如果在生产系统的操作中出现了问题,错误检查会被插入以定位原因。软件可以像这样子编写,这样组装程序时,错误检查代码可以方便的引入或忽略。这只应用于编程错误;软件需求写明的错误检查永远不会忽略。

  • 访问函数:一个访问函数是一个模块内的函数,可能会被其他模块调用。有很多种不同的访问函数;一些返回信息给调用者;另一些改变了所属模块的状态。

  • 事件:事件用于通知用户程序本模块内部的状态发生了改变。他们类似于硬件中断,因为他们的出现不可预测,且于用户程序的控制流不同步。在A-7系统,模块会使用一个mechanism比如 eventcount [8] 来发送一个事件给等待该事件的用户程序。

Ⅳ. 设计方法

这一部分会讲述用于抽象接口设计的procedure。这个procedure是基于获取接口的两个部分冗余的描述(partially reduntant descriptions)。

  • 描述1:表征(characterizing)虚拟设备的假设列表

对于一个应用程序领域,比如说航空电子,很多设备最终都属于标准类型;所有该类型的设备都有着很多相同的特征。比如说,正如航空周和空间技术(Aciation Week and Space Technology)里的广告展示的一样,飞行员看到的计算机面板都没啥区别。对每一个硬件设备,都要列一个表,这个表记录着所有当该设备被其他同类型设备取代时不太可能改动的特征。做到这一点必须要仔细地考虑可用的甚至开发中的设备。这个普遍特征表是用户程序可以对虚拟设备做出的假设。这些假设表征了设备的能力、模式、信息要求、行为和设备的正确使用。一个经典的假设可能是:

”设备提供了可以决定气压高度的信息“

很明确的一点是,只有满足了这个假设的设备可以取代当前的气压高度传感器。注意,这个假设并没有提及信息从何处获取,而这可能各不相同。

很多假设可能看起来相当无害,但是它们必须被记录下来。在A-7设计的复盘中,一些看起来人畜无害的假设最终没有成功

  • 描述2:体现假设的编程结构(Programming Constructs)

第二个描述指定用户可以使用的访问函数和事件。用户程序可以调用访问函数来访问数据或者虚拟设备提供的设施。比如说,一个接口可能提供一个访问函数“GET_BAROALT",这个函数会返回一个气压高度值。对于每个访问函数,我们指定返回值,限制,和它能对所属的虚拟设备造成的影响。用户程序还可以使用事件来监听虚拟设备状态的改变。比如说,用户程序可能需要在某个虚拟传感器不再可控后收到通知。

为什么是两个描述?

这两个描述是部分冗余的,举例来说,编程结构的定义(specifications)意味着假设。比如说,对访问函数"GET_BAROALT"的定义意味着这样的假设——设备是从某个能够决定气压高度的地方提供的信息。访问函数的定义提供了额外的信息,即设备接口模块和用户程序之间交换数据的形式。比如说,设备接口模块不直接提供气压高度,而是提供两个或三个可以从中计算出来的量。这样的设计改动需要函数定义的改动,但不会改动假设列表。

接口的两种描述有着不同的目的:1)假设列表明确地表述了假设,而这个假设是隐含在函数定义中的,这使得无效的假设更容易被察觉,以及2)编程结构可以在用户程序中直接使用。这两种描述必须一致。假设应该在编程结构定义中被清晰明了的体现,而编程结构定义不应该暗含任何没有在假设列表中讲述的功能。假设列表应该被开发者、用户、硬件工程师以及其他任何有必需知识检查有效性和一般性的人去审查。比如说,A-7的假设有被系统工程师和熟悉A-7,A-6和F-18飞机的硬件工程师审查。以散文形式写就的假设对于非开发者来说更容易审查。编程结构定义应该交由熟悉相似程序的开发者来审查。这些审查者会评估抽象接口对用户程序的支持好与坏,以及抽象接口模块是否实现高效。用户审查 A-7 设备接口模块的procedures和准则在设备接口文档 [4] 中描述。

设计Procedure

得到一个抽象接口的正确且一致的双重描述是一个交互式的过程。尽管我们尝试着先列出假设,还是有很多必要的假设相当的微妙,而且仅在我们设计编程结构时才比较明显。对假设列表的审查可以暴露编程结构上的错误。接口是数轮审查的结果,包括NRL内部的,和NWC的A-7维护团队的。初稿由NRL A-7 团队 非正式地审查了数次,随后才提交给NWC进行非正式审查。在NRL团队内部深度审查后,我们再在NWC进行一个正式审查,最终得到当前的版本。

说明(Illustration)

作为该procedure的一个例子,这一部分简单讲一下一下对飞行数据计算器(ADC)的抽象接口开发。ADC是一个用于测量气压高度,真实空速,和代表空速的马赫数的传感器。

图2至4为从成功的ADC抽象接口中摘录的部分。每一张图包括了一个假设列表和用于展示相关编程结构的表。这些表不是完整的定义;其他的表定义了系统生成参数并制定了值得范围和求解(resolution)。在访问函数表中,"I" 表示该输入参数由用户程序提供;"O" 表示该输出参数又ADC模块返回。


Figure 2:ADC抽象接口的早期草案摘录

假设列表

  1. ADC提供对气压高度,马赫数,和真实空速的测量(measure)

  2. 上述的测量能力基于一组通用的传感器(a common set of sensors)。因此ADC传感器中任何一个的不准确,都可能影响输出

  3. ADC会在任何其拥有的传感器无法正确工作时发出提示

  4. 测量基于这样的假设——海平面压力为29.92英寸汞柱

访问函数表

函数名 参数类型 参数信息
G_ADC_ALTITUDE p1: distance; O 高度假设29.92英尺为海平面压力
G_ADC_MACH_INDEX p1: mach; O 马赫
G_ADC_TRUE_AIRSPEED p1: speed; O 真实空速
G_ADC_FAIL_INDICATOR p1: logical; O 当ADC失败时为true

尽管图2展示的早期草案对我们而言显得简单而合理,NRL和NWC审查者在其中找到了如下错误。

  • 当前的ADC硬件和多数可替换的设备内含内置的测试能力,但是却无法通过当前接口访问的。

  • 描述没有指明ADCfailed的时候访问函数返回的值。

  • 描述没有指明测量值的可能范围。范围是依赖于设备的,但是它们仍会影响用户程序。

  • 虚拟ADC在fail时没有发送信号。用户程序必须轮询有效性函数来检测可靠性的变动。

  • 描述没有清晰说明模块是否对原始传感器值执行设备依赖(device-dependent)的矫正。


Figure 3:正式NWC审查时使用的ADC抽象接口草案摘录

假设列表

  1. ADC提供对气压高度,真实空速,和代表了飞机飞行速度的马赫数。任何已知的测量错误都会在模块内进行补偿(be compensated for)高度测量基于这样的假设——海平面的气压是29.92英尺汞柱。

  2. 所有的这些测量均基于一组通用的传感器(a common set of sensors);因此ADC传感器中任何一个的不准确,都会影响所有测量。

  3. 用户程序会在ADC硬件fails的时候通过事件的形式被通知到。如果气压高度,真实空速,和马赫数的访问函数在ADCfail的时候被调用,会返回最近一个有效测量(旧值)

  4. ADC可以通过软件的指令来进行自检。自检结果会返回给软件。

  5. 马赫数和真实空速的最小可测量值是0。气压高度最小值会在系统生成时间之后被固定下来,所有测量值的最小值和resolution也是如此。

访问函数表

函数名字 参数类型 参数信息
G_ADC_BARO_ALTITUDE p1: distance; O 正确的高度,基于这样的假设——海平面压力 = 29.92英尺汞柱
G_ADC_MACH_INDEX p1: mach; O 正确的马赫
G_ADC_RELIABILITY p1: logical; O 如果ADC可靠,返回true
G_ADC_TRUE_AIRSPEED p1: speed; O 正确的真实空速
TEST_ADC p1: logical; O 如果ADC通过自检,返回true

事件表

事件 什么时候触发
@T(ADC unreliable) 当“ADC reliable”从true变为false时

在我们纠正了这些错误后,图3展示的接口又交由NWC正式的审查。指出了下列问题。

  • 设备依赖的纠正对于海平面压力是必要。假设一个不可改动的值为海平面压力是不太好的,因为这会迫使一个设备依赖的纠正落到用户程序的职责里。以后的硬件可能可以自动做到这一纠正。

  • 尽管当前的硬件有所有三个值的可靠性提示器,替代的设备不一定有。在替换设备中,测量可能通过单独的传感器实现。

  • 我们不能假设最小马赫和最小真实空速的值为0;一些设备可能没法测量如此之低的值。

图4说明了产品的最终版本。


Figure 4:ADC抽象接口的最终版本摘录

假设列表

  1. ADC提供对气压高度,真实空速,和代表飞机飞行速度的马赫数(mach index)。任何已知的测量错误会在模块内补偿(are compensated for)

  2. 用户程序会在一个或更多输出不可用时被以事件的形式通知到。一个用户程序也可以查询某单独输出的可靠性。如果气压高度,真实空速,和马赫数的访问函数在值不可靠时被调用,会返回最近的有效测量值(旧值)

  3. ADC可以遵循用户程序的指令进行自检。该次检查的结果会返回给用户程序。

  4. ADC所有的测量值的最小值,最大值,和resolution都会在系统生成时间后固定下来。

  5. ADC会基于用户程序提供的代表海平面压力(SLP)的值来计算它的输出。如果没有提供该值,那么SLP会默认为29.92。

访问函数表

函数名字 参数类型 参数信息
G_ADC_ALTITUDE p1: distance; O
p2: logical; O
正确的高度,基于SLP=29.92或者用户提供的SLP
如果高度有效,返回true
G_ADC_MACH_INDEX p1: mach; O
p2: logical; O
正确的马赫
如果马赫有效,返回true
G_ADC_TRUE_AIRSPEED p1: speed; O
p2: logical; O
正确的真实空速
如果真实空速有效,返回true
S_ADC_SLP p1: pressure; I 海平面压力
TEST_ADC p1: logic; O 如果ADC通过自检,返回true

事件表

事件 什么时候触发
@T(高度无效) 当“高度有效”从true转为false
@T(空速无效) 当“空速有效”从true转为false
@T(马赫无效) 当“马赫有效”从true转为false

对ADC抽象接口的开发展示了procedure是如何支撑我们的设计工作的。即便是图2中的版本也是一个合理的设计;其包含的错误都是经典的嵌入式软件错误。我们procedure的一个结果就是,错误的假设被明确地以便于审查的形式写了下来,而不是模棱两可。而那些有可能在实际编码前难以注意到的不明智的假设,也能在它们能被较为简单地纠正时发现。ADC接口当前的版本不一定就完美,但肯定比它不走这个precedure的版本要好得多。让接口拥有两个部分冗余的描述是非常重要的。我们检查过我们的记录,发现了有些错误是在假设列表中发现的,而另一些是在编程结构定义中发现。很少有同样的错误同时出现在两种描述里

Ⅴ. 设计困难

早前提到的设计考虑是设计的准则也是用于评估结果的标准。尽管这些设计考虑带来了很大帮助,但是 并不总是能很容易的应用它们。在以下三个设计目标之间出现了冲突:小巧的设备接口模块,设备依赖的用户程序,和效率。如果说用户程序在了解了并非在所有可替换设备中均成立的假设之后,可以工作得更高效的话,又如何?如果说封装了并不总是有效的假设最终会导致设备接口更慢或更大的话,又如何?可接受的假设必须基于对未来改动的可能性的预估。这一部分展示了困难的权衡和我们是如何解决的,同时还要尝试着最小化软件整个使用周期的期望成本。

困难1:可用设备之间的主要变化

当可替换设备之间存在主要不同(major difference)时,想要决定设备接口模块中应包含多少能力是非常困难的。比如说,新的内部测量集(Interial Measurement Set,IMS)生成当前的位置数据;其他的IMS设备生成速度数据;然而当前的 A-7 IMS仅生成速度增量。为了能够使用当前的IMS硬件模拟一个可以生成当前位置的IMS,需要往IMS设备接口模块里塞进很多导航网格软件;最终让模块变得很大。必须在这两者做出选择——a)在设备接口模块里面约束主要改动,b)保持设备接口模块小巧,这样次要但更有可能的改动就会更容易。在理解了剩余的不同可以局限在一小组用户程序后,我们的妥协限制虚拟IMS能展现的设备的范围。尽管我们的虚拟IMS确实没法提供当前的位置,但是现在能提供速度而不是速度增量了;速度增量只用于计算速度,而速度是被广泛使用的。由此差生的虚拟IMS比硬件IMS更容易使用,虽然我们希望IMS模块能更小。

困难2:特征独立变动的设备

一些设备具有数个可以独立变动的特征集。比如说,投影地图展示集(Projected Map Display Set,PMDS)包含了一组幻灯片(filmstrips)以及一个用于将幻灯片放置于显示器上的硬件驱动。相同的驱动能配合内含不同格式的地图的幻灯片,而同样的幻灯片也能被不同的驱动使用。鉴于信息隐藏的原则,两个独立的特征集应该隐藏在不同的模块,以便独立改动。然而对于用户程序而言,没有必要知道到这是分隔开的。我们选择将两个特征集都隐藏在同一个PMSD设备接口模块里。模块随后会被划分了为两个子模块,改动会相互隔离。因为这个分隔是PMDS设备模块的秘密,所以这是对用户程序隐藏的,也不会显现在接口定义中。

困难3:大概率改动的虚拟设备特征

一些可以改动的设备特征必需暴露给用户程序,这样它们就能更好的开发设备的效率。这有些例子,包括测量值的resolutions,交换的位置的数量(position on switches),以及设备限制比如最大可显示值。尽管我们希望用户程序与所有的设备改动隔离开来,但如果这样的特征改动了,它们也必须要有不同的表现。比如说,虚拟PMDS改为提供三种尺寸的地图,而不是两种,这种情况下,控制着PMDS的用户程序也必须有不同的表现。我们通过象征性常量(symbolic constants)标志这一类特征。用户程序和设备程序都按照象征性常量来编写而不是一个实际的值。在系统生成时间,代码可以通过基于参数值的条件性的宏扩张来生成。

我们为输入和输出数据的范围和resolution定义了系统生成参数,因为1)范围和resolution在不同的设备中是极有可能不同的;2)这些信息是对用户程序精确高效地实现算术是必需的。基于系统生成参数编程的用户程序在参数值改动时不需要重写。

一开始我们假设在系统生成时间时所有的参数都已知;比如,我们明确假设,无论何时一个新的可替换设备加入(is introduced),会生成一个新版本的程序,再和这个设备一起部署。这个假设在设计审查时遭到了质疑。在海军政策中,舰队中不能有多个版本的软件,尽管装备改动不能同时生效。仔细点说就是,我们不能要求在新设备损坏然后由旧设备暂时替代时生成一个新的系统。结果就是,一些参数必须能在运行时改变。#In theroy this is true for any of the parameters; in fact, changeover problems are movre likely for some devices than others. #运行时可变量的消耗在不同的设备间也有所不同,取决于参数能否用控制代码生成以便避免运行时检查。如果改动是不太可能发生的,我们是不乐意放弃在系统生成时绑定值的效率益处的;如果较早的绑定值不会导致显著savings,我们是不乐意放弃灵活性的。我们独立考虑每一个情况,使用以下的指导原则。

  1. 实现可变的消耗较低的参数将被视作运行时变量,无论变动的可能性大小。模块接口中会有这些变量的get,set访问函数。困难4是这些参数引发的额外困难。

  2. 改动的可能性较低且实现可变成本较高的参数,会在系统生成时间绑定好。

  3. 对于那些改动可能性又高,实现可变成本也高的参数,有以下两种可行的方案

a. 将他们视作运行时变量,但允许在系统生成时由给出的值提早绑定。这个选项允许我们推迟最终的决定直到我们拥有更多的信息。

b. 我们可以找到一个适用于多个设备的保守值,这样我们就能在系统生成时绑定值了。

困难4:运行时改动的,设备依赖的特征

在某些情况下,用户程序必须处理设备依赖的数据。比如说,当一个IMS被另一个同类型的替代时,软件必须做出调整,因为制造业的差异(manufacturing variations)。IMS软件是参数化的,这样就能做出调整来适应特定的硬件。有这么一个需求,就是我们得能够在不重新组装(reassembly)的情况下改变这些参数。参数在运行时从计算机面板输入。为了接收这些数据,IMS提供了访问函数,这些访问函数由用户程序调用来读入面板数据。不幸的是,一个运行时参数的存在比如漂移率会暴露IMS模块的秘密,即实际设备的偏移是不准的。一个额外的缺陷是,一个替换设备可能会要求不同的校准数据,这就导致接口的改动。我们限制了这些访问函数的使用,这样软件就会在有限范围内使用它们,而且也容易辨别。受限制的假设和访问函数被称为重配置接口,并且被加入到普通接口中。

困难5:虚拟设备间的互联

理想情况下,虚拟设备之间应该相互独立,这样关联的抽象接口也能独立地设计。然而A-7系统为了硬件方便,引入了设备相互依赖性。有些相互依赖性是基于硬件设计师对软件的假设。比如说,Doppler和Ship Inertial Navigation Set 可能会共享同一条数据路径,因为某人假设了这个软件不会同时需要这两个设备。我们能够隐藏相互依赖的性质(nature)但是不能隐藏相互依赖的存在;如果我们隐藏了相互联系,之后用户程序的改动可能会导致对设备的错误访问。相互依赖的存在会因假设列表中对虚拟设备的使用限制而被暴露。比如说,有个假设是两个虚拟设备不能同时使用。如果硬件相互依赖之后移除了,那么对一个或两者均有的额外用处就有可能存在和可取(desirable)了。这样的新增一定为要求抽象接口的两边均发生改动:用户程序改动以发掘新的能力以及设备接口模块改动以移除限制。既然这没法避免,那么就算暴露了限制也没有损失。

一个相似的困难也可能在一个单独的设备接口模块中出现,如果当前的硬件不允许虚拟设备的两个能力同时被使用的话。同样,我们选择向用户程序暴露限制,哪怕对于未来的设备这可能不会继续成立。

互联问题还会出现在当一个设备(提供者)提供另一个设备(接收者)需要使用的信息时。有两种情况需要考虑:

  1. 计算机能够检测到提供者的失败。如果是这样,当提供者失败时,虚拟接收者发送一个失败信号,即使实际接收者没有检测到失败。接收者的设备接口模块可以模拟对失败的检测,以此来隐藏互联关系。

  2. 计算机不能检测到提供者的失败。提供者无法检测的失败必须也考虑到接收者的无法检测的失败。写依赖于虚拟接收者的程序的开发者必须意识到无法检测的失败是有可能的,但他们没必要知道互联关系。

困难6:硬件接口的不统一

一个硬件接口可能通过并不相似的方式提供相似的功能。比如说,HUD上的标识有着三种状态:开启,关闭和闪烁。对于某些标识,HUD提供了一个闪烁指令,而其他的标识就必须要软件去切换标识的开关状态来模拟闪烁状态。无论何时硬件接口提供某些方式来执行动作,我们都要在虚拟设备中一致地提供该功能。虚拟HUD有着所有标识的闪烁指令。然而,当硬件接口不能提供一个方法来执行某项动作时,我们被迫暴露了接口的不一致。比如说,某些HUD标识只有两种状态:关闭和闪烁。

因为没有方法去模拟开启状态,所以虚拟设备也就没有办法提供开启指令。如果硬件约束在未来被移除了,不一致性也能从虚拟设备中移除。不可避免的,抽象接口的两边都要改动:用户程序改动以发掘新的能力,设备接口模块改动以实现它。

困难7:开关的命名法(nomenclatures)

很多开关仅仅是修改了计算机阅读的比特而已。我们可以匿名地命名开关(例如,用整数来命名)和使用非助记符(non-mnemonic)名字命名设置。然而,开关命名法的改动很大概率会伴随着需求的改动。使用非助记符工作的期望成本是更高的,也就是说更多的错误。正因我们试图最小化软件在整个生命周期中的期望成本,我们选择了在开关名字和开关设置的助记符值中暴露命名法。这些名字可能暗示了比假设陈述更多的信息;程序员不应对这些开关做出超出接口文档中明确描述的假设以外的假设。

困难8:有着硬件副作用的开关

当一个开关也会影响到其他设备的时候,它的意义就不仅仅是一个软件决定(software decision);#major hardware changes would be required to use it for any other prupose#。我们认为这样的开关是它所影响的设备的一部分,即使它不是物理上处于该设备内。就用户程序而言,这样的开关不存在;开关的效果看起来就是虚拟设备操作模式的变化。比如说,位于主控功能面板的“地形跟随”开关影响了前视雷达(Forward Looking Radar, FLR)的状态。不和其他主控功能开关出现在同一个接口描述中,它隐藏在FLR设备接口模块里。用户程序不能阅读开关(read the switch),但它们可以调用暴露了FLR操作模式的访问函数。

困难9:报告设备状态的变化

用户程序通常需要对设备状态的变化迅速反应。比如说,巨顶当前导航模式的用户程序需要知道什么时候IMS传感器的可靠性状态发生了变化。设备接口模块可以1)提供一个访问函数来报告设备的当前状态,也可以2)发送状态-变化事件。mechanism的选择取决于用户程序是根据当前状态做出决定的还是说在等待状态的改变。如果我们基于用户程序的需求进行设计,那么其需求的改变就会导致设备接口模块的改动,而这违反了我们“除非设备改动,否则不应改动设备接口模块”的设计目标。我们决定每种情况都考虑两种mechanism,但是最终只实现真正需要的那种。因此需求改动可能会导致设备接口的改动,但是那些改动将会预先实现。通过提前辨别可能新增的宫嗯,我们期望降低之后重新编程的成本。

困难10:要求从软件获取信息的设备

一些硬件设备会要求那些不在对应设备接口模块中计算的信息。比如说,当前IMS设备需要知道飞机是否在70°高度以上,尽管高度并不是在IMS模块中计算的。这时必须从两种方法中选择一个把信息给到:1)设备接口模块可以提供一个访问函数来让用户程序调用以提供信息;2)设备接口模块可以调用其他程序来获取信息。我们的选择基于,信息的获取对于这一类替换设备来说是否比较普遍。如果是,设备接口模块会提供会提供访问函数来接收信息。这一方案会导致对剩余的软件有了新的需求,并且当信息需要改变时,提供信息的用户程序也必须改变。如果对信息的需要对硬件设备来说是比较独有的,那么设备接口模块就会调用其他程序来获取数据。只要需要的信息在系统其余部分仍然可用,那么就算不再需要这些信息了,除了设备接口模块意外,也没有程序需要改动。对于上面的IMS示例我们选择了后一种方案,因为并不是所有的IMS设备都需要知道飞机是否飞到70°高度以上。

困难11:不与硬件设备对应的虚拟设备

一开始,我们假设每个虚拟设备都有对应的硬件设备。我们发现基于实际设备构造虚拟设备的模型并不总是能收获清晰的接口。一些相关联的能力分散在数个硬件设备;一些不相关的能力又因为物理上的方便而出现在同一个设备上;又有一些是因为历史发展的原因。比如说,A-7上武器相关的能力分散在数个设备中。一些武器数据来自控制武器释放的设备,一些存储在表里,还有一些由飞行员通过开关给出。此外,武器释放设备扮演了两个角色:既是输入数据的源,又是在计算机控制下释放武器的设备。我们最终的设计是两个虚拟设备,一个用于武器释放和一个用于武器数据。虚拟设备比实际设备更容易理解得多。不要被硬件单元的物理位置过分地影响是很重要的。

Ⅵ. 总结

我们已经基于抽象接口准则将一个系统化的procedure应用于一个重大的系统。我们发现抽象接口使得系统更容易编程,并且希望看到它们能让未来的改动更加简单。尽管我们抽象接口设计的成功与否必须等到A-7的实现和后续维护都完成之后才能判断,我们迄今为止的经历能让我们自信地向其他设计者推荐这个procedure。

这篇论文是更加完善的报告 [4] 的介绍。这篇报告包含有,所有A-7设备接口模块的完整规格,文件组织和符号的描述,额外的设计困难的讨论,对procedures的讨论和主要涉及审查时使用的问卷。该文件可从作者处获得。

posted @ 2022-04-08 22:20  keeptht  阅读(138)  评论(0)    收藏  举报