跳转至

何时使用 IDL 任务?IDL 作为异构计算环境中数据分析的关键

原文链接: https://www.nv5geospatialsoftware.com/Learn/Blogs/Blog-Details/when-might-i-use-an-idl-task-idl-as-a-key-to-data-analysis-in-a-heterogeneous-computing-environment

16080 评价这篇文章:

3.4

何时使用 IDL 任务?IDL 作为异构计算环境中数据分析的关键

Jim Pendleton 2017 年 1 月 26 日,星期四

IDL 8.6 中,我们引入了一个新功能,它标准化了从任何能通过标准输入、标准输出和标准错误在单个操作系统进程间通信的环境中调用 IDL 的方式。

在我们的定制解决方案组中,我们期待广泛部署这一新功能,以帮助我们的客户将更多分析能力集成到大型、异构的处理环境中。

虽然程序员在早期的 IDL 版本中,可以通过结合使用 IDL Runtime 可执行文件、IDL 函数 COMMAND_LINE_ARGS 以及其他技术,以一种临时的方式实现从操作系统级脚本调用 IDL 例程的目标,但新的 IDL 任务架构增加了一层通用性和标准化。

过去,您可能需要编写单独的 IDL Runtime 应用程序,在此类环境中对数据执行原子化处理过程。您的架构会打包参数,并通过 system()、fork() 或其他语言中与 IDL 的 SPAWN 等效的函数,使用标准输入命令行调用 idlrt.exe,并传递包含要执行的“任务”的 IDL SAVE 文件路径。

使用 IDL 任务架构,您可以使用标准 IDL 为您的功能编写过程化包装器,并结合一个简单的 JSON 文件来定义任务的参数、数据类型、传递方向等。

将编译好的 IDL 任务代码与 JSON 文件一起放在您的 IDL 会话搜索路径中,即可将任务暴露给 IDL 任务引擎。这本质上是一个封装了 IDL Runtime 解释器的无状态应用程序。它在调用您的 IDL 例程之前,执行验证输入输出参数并打包它们的基本工作。

您的作业分发系统,例如地理空间服务框架 (GSF),将使用一个 JSON 对象调用 IDL 任务引擎,该 JSON 对象表示要执行的任务名称以及任务的参数,这些内容被写入任务脚本的标准输入。

任务引擎为每个任务启动一个独立的 IDL 解释器进程,允许多个任务并行执行,最多可达可用处理许可证的数量。

IDL 任务的输入和输出参数必须使用可以在 JSON 中表示的数据类型。这一限制排除了不允许跨越进程边界的参数,例如对象或指针的引用,无论是在 IDL 中还是在其他语言中定义的。

示例 - 从多幅图像生成汇总报告

假设您通过 IDL 和 ENVI 之外的某种机制生成了一个图像文件目录。也许您拥有一支配备传感器的无人机机队,或者一两颗卫星。这些图像文件包含的数据,经过各种处理算法提炼后,可以生成一份单一的情报报告。

您希望将此工作流集成到一个更大的、由多个步骤组成的服务处理序列中,其中只有一步涉及报告的生成。

对于 IDL 部分,假设您已经在 IDL 中有一个对象类,它接收图像目录的路径作为输入,执行分类和时间序列分析,并输出一个包含结果摘要的 PDF 报告。因为在 IDL 中就这么简单。

我们称这个类为 IntelReportGenerator。我们将首先在 IDL 任务引擎的上下文之外查看这个类。为了简单起见,我将描述的类将只有两个方法:一个 ::Init 方法和一个 ::GenerateReport 方法。

这个类超级高效,只有少数几个成员变量。



Pro IntelReportGenerator__Define

!null = {IntelReportGenerator,  $

    ImageDirectory : '', $ ; path to the images to be read, input

    OutputReport   : '', $ ; path to the report file to be written, input

    Author         : '', $ ; a name to be applied to the report, input

    Debug          : !false $ ; a flag to toggle more detailed debugging information

}

End

重要提示:我强烈建议为每个类添加一个调试标志。在操作环境中可能不启用调试,但知道无需修改和重新部署代码就能开启调试总是好的。

该类的 ::Init 方法主要用于使用关键字参数填充成员变量。



Function IntelReportGenerator::Init, $

    Image_Directory = Image_Directory, $

    Author = Author, $

    Output_Report = Output_Report, $

    Debug = Debug, $

    Status = Status, $

    Error = Error

On_Error, 2

Status = !false ; Assume failure

Error = !null ; Clear any error string on input

Catch, ErrorNumber ; Handle any unexpected error conditions

If (ErrorNumber ne 0) then Begin

    Catch, /Cancel

    If (self.Debug) then Begin

        ; Return a complete traceback if debugging is enabled

        Help, /Last_Message, Output = Error

    EndIf Else Begin

        ; Return a summary error instead of a traceback

        Error = !error_state.msg

    EndElse

    Return, Status

EndIf

self.Debug = Keywod_Set(Debug)

self.Author = Author ne !null ? Author : 'UNKNOWN'

If (~File_Test(Image_Directory, /Dir)) then Message, 'Image directory does not exist.', /Traceback

self.ImageDirectory = Image_Directory

; ... More here.  you get the idea.

Status = !true

Return, 1

End

接下来,考虑 ::GenerateReport 方法。这只是一个简单的编程问题。我们遍历输入图像目录中的文件,施展魔法,生成一个输出文件。我喜欢简洁设计的优雅,你呢?



Pro IntelReportGenerator::GenerateReport, $

    Status = Status, $

    Error = Error

On_Error, 2

Status = !false

Error = !null

Catch, ErrorNumber

If (ErrorNumber ne 0) then Begin

    Catch, /Cancel

    If (self.Debug) then Begin

        Help, /Last_Message, Output = Error

    EndIf Else Begin

        Error = !error_state.msg

    EndElse

    Return

EndIf

Files = File_Search(self.ImageDirectory)

ForEach File, Files Do Begin

  ;... Magic analysis here.  Batteries not included.

EndFor

; Magic report-writing here.  Nope, still no batteries.

Status = !true

End

如果您写过任何 IDL 代码,特别是那些施展魔法的部分,那么到目前为止,所有这些对您来说都应该很熟悉。

为了将此功能放入 IDL 任务工作流,我们需要为我们的类编写一个过程化包装器,该包装器将使用适当的关键字实例化一个对象,然后执行生成报告的方法。我们将这个新例程命名为 IntelReportTask。



Pro IntelReportTask, $

    Image_Directory = Image_Directory, $

    Author = Author, $

    Output_Report = Output_Report, $

    Debug = Debug, $

    Status = Status, $ ; an output from this procedure, 0 = failure, 1 = success

    Error = Error ; An error string if Status is 0, or null on return otherwise

On_Error, 2

Error = !null ; Clear any error string

Status = !false ; assume failure

; ALWAYS include a CATCH handler to manage unexpected

; exception conditions.

Catch, ErrorNumber

If (ErrorNumber ne 0) then Begin

    Catch, /Cancel

    If (self.Debug) then Begin

        ; Return a complete traceback if debugging is enabled

        Help, /Last_Message, Output = Error

    EndIf Else Begin

        ; Return only a summary error without traceback if debugging is off

        Error = !error_state.msg

    EndIf

    Return

EndIf

; Attempt to create the report-generation object, passing through the keywords.

o = IntelReportGenerator( $

    Image_Directory = Image_Directory, $

    Author = Author, $

    Output_Report = Output_Report)

    Status = Status, $

    Error = Error, $

    Debug = Debug)

If (Obj_Valid(o)) then Begin

    ; Call the method to generate the report

    o.GenerateReport, Status = Status, Error = Error

EndIf

End

IDL 任务例程定义要求通过关键字传递所有参数。除了这个限制,它是一个标准的 IDL 过程。不需要任何魔法。

新的功能部分是要求一个 JSON 任务定义文件。在这个文件中,我们定义任务的名称(对应于 IDL 过程名)以及与每个关键字关联的类型定义。

参数类型定义允许 IDL 任务引擎本身在您的过程被调用之前执行参数类型检查和验证,从而减轻您编写代码来确保(例如)应该是字符串的目录路径没有被浮点数填充的负担。对于某些计算机科学流派的学究来说,IDL 在编译时薄弱的数据类型验证是一个缺点而不是优势。将纯 IDL 代码包装在任务中,并通过任务引擎强制执行更严格的参数类型,是缓解这种观点的一种方式,或许是通往更高认知层次的一个垫脚石。

当然,这也意味着您的 IDL 任务比在 IDL 内部本身更不通用。一个可以在从字节值到双精度数字的任何数据类型上运行的单个 IDL 例程,如果您想暴露多个功能,可能需要两个或更多不同的 IDL 任务例程作为包装器。另一种选择是编写具有多个关键字以接受不同数据类型的任务,然后将输入传递给一个通用的处理算法。

自定义 IDL 任务 的一般 JSON 语法在这里有描述。

与 IDL 任务关联的 JSON 如下。



{

  "name": "IntelReportTask",

  "description": "Generates a report from a directory of images.",

  "base_class": "IDLTaskFromProcedure",

  "routine": "intelreporttask",

  "schema": "idltask_1.0",

  "parameters": [

    {

      "name": "IMAGE_DIRECTORY",

      "description": "URI to the directory containing image files",

      "type": "STRING",

      "direction": "input",

      "required": true

    },

    {

      "name": "AUTHOR",

      "description": "Label to apply as the author to the output report",

      "type": "STRING",

      "direction": "input",

      "required": false,

      "default": "UNKNOWN"

    },

    {

      "name": "OUTPUT_REPORT",

      "description": "URI to the output report file",

      "type": "STRING",

      "direction": "input",

      "required": true

    },

    {

      "name": "DEBUG",

      "description": "Flag to enable verbose debugging information during errors or processing",

      "type": "BOOLEAN",

      "direction": "input",

      "required": false,

      "default": false

    },

    {

      "name": "STATUS",

      "description": "Status of the report generation request at completion, or error.",

      "type": "BOOLEAN",

      "direction": "output",

      "required": false

    },

    {

      "name": "ERROR",

      "description": "Any error text generated during processing",

      "type": "STRINGARRAY",

      "dimensions": "[*]",

      "direction": "output",

      "required": false

    }

  ]

}

在这里,我们标识了可选和必需的关键字、它们的输入/输出方向、数据类型等。

在 IDL 文档中,我们展示了一些在 IDL 本身内部,在 IDLTask 对象的上下文中调用过程的示例。实际上,除了调试之外,它的用处有限。如果您是一位至少半合格的 IDL 专家(如果您读到这里,我假设您是),您会认识到,在 IDL 的上下文中,IDLTask 类和您编写的任务包装器只是为您可以直接调用目标“工作”例程的调用增加了一些开销。

IDL 任务的真正价值体现在您将功能集成到 IDL 之外的异构工作流中时。

在这种环境中,您的框架将启动一个命令行级别的脚本来执行您的任务。

在 Windows 上,该脚本的默认位置在安装目录 "C:\Program Files\Harris\idl86\bin\bin.x86_64\idltaskengine.bat"。

在 Linux 上,默认路径是 /usr/local/harris/idl/bin/idltaskengine。

idltaskengine 脚本的输入是 JSON 格式的文本,表示任务名称以及参数。JSON 可以通过文件重定向(<)或管道(|)传递给脚本的标准输入,例如,

<安装路径>\idltaskengine.bat < *<文件路径>*my_intel_report_request.json

或者

echo '{"taskName":"IntelReportTask","inputParameters":{"IMAGE_DIRECTORY":""}, etc.}' | <安装路径>/idltaskengine

构造要传递给任务引擎脚本的适当 JSON 对象是您的框架的责任。

对于我们当前的示例,JSON 可以这样构造:



{

    "taskName": "IntelReportTask",

    "inputParameters": {

        "IMAGE_DIRECTORY": "/path-to-data/",

        "AUTHOR": "MELENDY",

        "OUTPUT_REPORT": "/path-to-report/myreport.pdf"

    }

}

任何定义为输出方向的参数都将以 JSON 格式写入标准输出。在我们的示例中,如果遇到已处理的错误,输出可能会以这种一般格式返回:



{

    "outputParameters": [{

        "STATUS": false

    }, {

        "ERROR": [

            "% SWAP_ENDIAN: Unable to swap object reference data type",

            "% Execution halted at: SWAP_ENDIAN        99 C:\\Program Files\\Harris\\IDL86\\lib\\swap_endian.pro",

            "%                      $MAIN$"

        ]

    }]

}

如果发生一个真正糟糕的错误,一个无法填充 JSON 的错误,还应该查询对 IDL 任务引擎脚本调用返回的标准错误输出。请参阅在线帮助主题的“退出状态”部分,位于页面底部。

您的外围框架应该设计为先验证从 IDL 任务引擎脚本返回的标准错误状态,然后再检查并解析标准输出上返回的任何 JSON。

更多示例

可以在此处找到更多 IDL 任务示例。

地理空间框架 (GSF)

地理空间服务框架 产品 (GSF) 只是一个分布式处理架构的示例实现,IDL 任务可以“插入”其中。尽管其营销名称如此,但它不仅限于处理地理空间数据。

别忘了拉伸!使用 ENVI 的拉伸工具观察人眼无法看见的事物。 IDL 中正浮点数的 60 进制编码