目录更新的想法基本上是正确的。不幸的是,谣言也是真的:进行目录更新涉及更多的想法,因为有:
框架应用程序接口注意事项
在目录更新期间,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 目录更新相关的所有重要主题。正如你所看到的,这不仅仅是一个谣言。