0

我听说在 FDT 1.x 中正确执行目录更新非常复杂。似乎有比明显的步骤更多的伪代码:

foreach (progid in Registry having component category "FDT DTM")
{
    dtm = CoCreateInstance(progid);
    StartDTMAccordingStateMachine(dtm);
    info = dtm.GetInformation("FDT");
    catalog.Add(info);
    ShutdownDTMAccordingStateMachine(dtm);
    Release(dtm);
}

我在 FDT 规范中找不到任何需要更复杂目录更新程序的提示,那么谣言是否属实?是什么让正确的目录更新过程如此复杂?

4

1 回答 1

1

目录更新的想法基本上是正确的。不幸的是,谣言也是真的:进行目录更新涉及更多的想法,因为有:

框架应用程序接口注意事项

在目录更新期间,DTM 还不是项目的一部分。因此,框架应用程序可以在没有项目特定接口(例如 IFdtTopology 或 IFdtBulkData)的情况下实现。但是,如果框架应用程序不支持这些接口,许多 DTM 会立即查询这些接口并抛出异常。

此外,在目录更新期间,框架应用程序可以预期 DTM 在没有用户界面的情况下工作,因为这是一个不需要用户交互的批处理操作。这意味着框架应用程序可以在没有 IFdtActiveX 和 IFdtDialog 接口的情况下实现。不幸的是,还有一些 DTM 在目录更新期间使用这些接口。

.NET 注意事项

在安装了许多 DTM 的系统上执行目录更新可能需要大量内存。因此,一些框架应用程序在外部进程中进行目录更新。虽然这是一个好主意,但您需要考虑 FDT .NET 规范和最佳实践文档。

这里的基线是:外部进程必须是 .NET 2.0 进程,与您的框架应用程序的实际实现技术无关。如果您有 C++ 实现,则需要在启动任何 DTM 之前加载一个非常小的 .NET 2.0 对象。

内存注意事项

由于 FDT 1.x 是 COM 和 .NET 的综合体,因此会有固定对象。这使得您的应用程序很可能遭受小对象堆碎片的影响。此外,FDT 将 XML 作为字符串传递,这使得您的应用程序更有可能遭受大对象堆碎片的影响。整体组合非常危险。

一种解决方案可能是在同一进程中启动有限数量的 DTM,然后重新启动该进程,例如

updateprocess = StartProcess();
dtmCount = 0;
foreach (progid in Registry having component category "FDT DTM")
{
    dtmCount++;
    if (dtmCount % 10 == 0)
    {
        // restart process to avoid out of memory situation
        updateProcess.SignalShutdown();
        updateProcess.WaitForExit();
        updateProcess = StartProcess();
    }
    updateProcess.StartDTM(progid);
    info = updateProcess.GetDtmInformation();
    catalog.Add(info);
    updateProcess.ShutdownDTM();
}

在更新过程中,您需要创建 COM 对象并遵循状态机等。

FDT 1.2.1 扫描信息

在 FDT 1.2.1 中,引入了附加信息以在硬件扫描期间更好地识别设备。尽管在撰写本文时还没有完全符合 FDT 1.2.1 的 DTM,但许多 FDT 1.2.0 DTM 实现了附加接口 IDtmInformation2 以支持设备检测。

对于作为框架应用程序开发人员的您来说,这意味着您必须在更新过程中扩展 GetDtmInformation() 方法:

T GetDtmInformation()
{
    var result = new T(); // a type defined by you
    result.info = dtm.GetInformation();
    foreach (deviceType in result.info)
    {
        foreach (protocol in deviceType)
        {
            deviceInfo = dtm.GetDeviceIdentificationInformation(deviceType, protocol);
            result.deviceinfo.Add(deviceInfo);
        }
    }
}

架构路径更新

FDT 1.2.0 存在用户需要手动安装 XDR 模式定义的问题,这很不舒服。FDT 1.2.1 以 DTM 现在可以带来 XDR 模式的方式解决了这个问题。该定义位于 XML 元素 <FDT>、<DtmInfo>、<DtmSchemaPaths> 处的 GetInformation() 中。DTM 将在那里发布目录名称。理论上,这是一项简单的任务:要安装 XDR 模式,我们需要稍微更新一下 GetDtmInformation():

T GetDtmInformation()
{
    var result = new T(); // a type defined by you
    result.info = dtm.GetInformation();

    schemaPaths = result.info.SelectNodes("/FDT/DtmInfo/DtmSchemaPaths/DtmSchemaPath");
    foreach (dtmSchemaPath in schemaPaths)
    {
        CopyFiles(from dtmSchemaPath to frameSchemaPath);
    }

    // *) read on, more code needed here

    foreach (deviceType in result.info)
    {
        foreach (protocol in deviceType)
        {
            deviceInfo = dtm.GetDeviceIdentificationInformation(deviceType, protocol);
            result.deviceinfo.Add(deviceInfo);
        }
    }
}

不幸的是,现在序列中有一个逻辑错误。由于 DTM 已经启动,它已经向框架应用程序询问模式路径(使用 IFdtContainer::GetXmlSchemaPath()),并且它已经设置了模式缓存以验证 XML。无法通知 DTM 有关架构路径中的更新。

因此,您需要重新启动 DTM 以确保它获得最新版本的 XDR 架构。在代码中,这意味着您必须将整个代码更新为:

T GetDtmInformation()
{
    var result = new T; // a type defined by you
    result.info = dtm.GetInformation();

    schemaPaths = result.info.SelectNodes("/FDT/DtmInfo/DtmSchemaPaths/DtmSchemaPath");
    schemasUpdated = false;
    foreach (dtmSchemaPath in schemaPaths)
    {
        schemasUpdated |= CopyFiles(from dtmSchemaPath to frameSchemaPath);
    }

    if (schemasUpdated)
    {
        // restart the DTM to make sure it uses latest versions of the schemas
        dtm = CoCreateInstance(progid);
        StartDTMAccordingStateMachine(dtm);
        info = dtm.GetInformation("FDT");
    }

    foreach (deviceType in result.info)
    {
        foreach (protocol in deviceType)
        {
            deviceInfo = dtm.GetDeviceIdentificationInformation(deviceType, protocol);
            result.deviceinfo.Add(deviceInfo);
        }
    }
}

XDR 架构版本信息问题

在之前的章节中,我使用了一个简单的CopyFiles()操作来更新 XDR 模式文件。这个方法并不像看起来那么简单,因为这个方法需要进行版本号检查。

版本在 XDR 模式中给出,如下所示:

<AttributeType name="schemaVersion" dt:type="number" default="1.0"/>

@default 属性定义了模式的版本号。@schemaVersion 本身不在其他任何地方使用。

在撰写本文时使用的版本号:

1.0  // e.g. FDTCIPCommunicationSchema CIP version 1.1-02
1.1  // e.g. FDTCIPChannelParameterSchema CIP version 1.1-02
1.00 // e.g. DTMIOLinkDeviceSchema IO Link version 1.0-1
1.21 // e.g. FDTIOLinkChannelParameterSchema IO Link version 1.0-1
1.22 // e.g. FDTHART_ExtendedCommunicationSchema

版本 1.21 高度表明它与 FDT 版本 1.2.1 相关,这带来了如何解释版本号的问题。有三种可能的解释方式:

a) 作为 XDR 数据类型中定义的简单浮点数 (dt:type="number") b) 作为major.minor 格式的版本号 c) 作为major.minorbuild 格式的版本号,其中minor 和build 是简单连接

好的,我将把这个谜题留给读者。我建议了一份文件来澄清这个版本号问题。

无论如何,这是我们的 CopyFiles() 方法:

bool CopyFiles(sourceDir, destinationDir)
{
    filesCopied = false;
    foreach(filename in sourceDir)
    {
         existingVersion = ExtractVersion(destinationDir + filename);
         newVersion = ExtractVersion(sourceDir + filename);
         if (newVersion > existingVersion)
         {
            File.Copy(sourceDir + filename, destinationDir+filenam);
            filesCopied = true;
         }
    }
    return filesCopied;
}

XDR 架构更新对其他 DTM 的影响

在上一章中,我们返回一个标志,CopyFiles()以确定 DTM 是否需要重新启动GetDtmInformation()。但是,本次更新可能不仅会影响当前的 DTM,还可能会影响之前添加到目录中的其他相同协议的 DTM。

虽然您可以简单地从头开始重新启动整个目录更新,但这将意味着巨大的性能影响。更好的方法似乎是有选择地做到这一点。

要应用选择性方法,您需要维护已更新的协议列表(在 中T GetDtmInformation()):

    foreach (dtmSchemaPath in schemaPaths)
    {
        schemasUpdated = CopyFiles(from dtmSchemaPath to frameSchemaPath);
        if (schemasUpdated)
        {
             listOfChangedProtocols.Add(ExtractProtocolId(destinationDir));
        }
    }

当然,不要忘记为受影响的 DTM 重新更新目录:

affectedDtms = catalog.GetDtmsForProtocols(listOfChangedProtocols);
// TODO: perform catalog update again
// NOTE: remember that this might apply recursively

让协议组正确

接下来,您需要了解协议组的概念。协议组跨不同协议共享 XDR 架构文件,其中每个协议由协议 ID 标识。一个很好的例子是 CIP 协议系列,它由单一协议 DeviceNet、CompoNet 和 Ethernet/IP 组成。

这些协议共享一组通用的 XDR 架构文件,因此您会在硬盘上找到 3 次相同的文件。这种重复也会对目录更新产生一些影响,因为即使 DTM 仅针对单个协议,您也需要更新所有副本。

原因在于架构缓存的构建方式:将 XDR 架构添加到架构缓存时,第一个文件将胜出。其他同名文件将不再添加。因此,重要的是要确保添加到缓存的第一个文件是具有最高版本号的文件。这只能通过将所有副本更新到最新版本来实现。

这导致CopyFiles()方法的更新:

List<protocolID> CopyFiles(sourceDir, destinationDir)
{
    protocolsChanged = new List<protocolID>();
    foreach(filename in sourceDir)
    {
        foreach (subdirectory in destinationDir)
        {
            files = GetFiles(subdirectory, pattern = filename);
            if (files.Count == 1)
            {
                  UpdateXDRConsideringVersionNumber(sourceDir, subdirectory);
                  protocolsChanged.Add(ExtractProtocolId(subdirectory));
            }
        }

    }
    return protocolsChanged;
}

void UpdateXDRConsideringVersionNumber(sourceDir, destinationDir)
{
     existingVersion = ExtractVersion(destinationDir + filename);
     newVersion = ExtractVersion(sourceDir + filename);
     if (newVersion > existingVersion)
     {
        File.Copy(sourceDir + filename, destinationDir+filenam);
        filesCopied = true;
     }  
}

设备 DTM 和架构路径

无论出于何种原因,它都被定义为只有通信 DTM 和设备 DTM 需要携带 XDR 模式。其背后的基本原理可能是您不能在没有通信或网关 DTM 的情况下使用设备 DTM。

不幸的是,在查询 DTM 的 Windows 注册表时,您无法预测获得 DTM 的顺序。这可能会导致您首先获得设备 DTM。如果 DTM 的协议还没有 XDR 模式,则启动此 DTM 并从中获取信息可能会导致错误或无效的 XML。

因此,您需要继续更新目录,希望找到具有相同协议的通信 DTM 或网关 DTM,从而带来 XDR 模式。然后再次启动设备 DTM,它将提供有用的信息。

这不需要更新任何代码。如果您按照前面描述的所有步骤操作,它应该已经可以工作了。这里唯一要考虑的是良好的错误处理(但我不会在此处的伪代码中这样做)。

结论

希望我能涵盖与 FDT 1.x 目录更新相关的所有重要主题。正如你所看到的,这不仅仅是一个谣言。

于 2014-10-21T21:11:34.697 回答