industrialfield-device-tool

How to perform a catalog update correctly in FDT 1.x?


I have heard rumours that performing a catalog update correctly in FDT 1.x is quite complex. There seem to be more than the obvious steps, which are in pseudo code:

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);
}

I could not find any hints in the FDT specification that would require a more complex catalog update procedure, so are the rumours true? What makes a correct catalog update procedure so complex?


Solution

  • Basically the idea for the catalog update is correct. Unfortunately the rumours are also true: doing a catalog update involves some more thoughts, as there are:

    Frame application interface considerations

    During the catalog update, the DTM is not part of a project yet. Therefore the frame application could be implemented without project specific interfaces such as IFdtTopology or IFdtBulkData. However, many DTMs will query for those interfaces immediately and throw an exception if the frame application does not support those interfaces.

    Also, during the catalog update, the frame application could expect that the DTM works without user interface, because this is a batch operation which should not require user interaction. This means the frame application could be implemented without the IFdtActiveX and IFdtDialog interfaces. Unfortunately there are also DTMs that use those interfaces during catalog update time.

    .NET considerations

    Doing a catalog update on a system with many DTMs installed could require a lot of memory. Therefore some frame applications do the catalog update in an external process. While this is a good idea, you need to consider the FDT .NET specifications and best practice documents.

    The base line here is: the external process must be a .NET 2.0 process, independent of the actual implementation technology of your frame application. If you have a C++ implementation, you'll need a very small .NET 2.0 object being loaded before any DTM is started.

    Memory considerations

    Since FDT 1.x is a conglomerate of COM and .NET, there will be pinned objects. This makes it likely that your application suffers from small object heap fragmentation. In addition FDT passes XMLs as strings which makes it more likely that your application suffers from large object heap fragmentation. The overall combination is very dangerous.

    One solution might be to start a limited number of DTMs in the same process and then restart the process, e.g. like

    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();
    }
    

    In the update process you'll need to create the COM object and follow the state machine etc.

    FDT 1.2.1 scanning information

    In FDT 1.2.1, additional information was introduced to better recognize device during a hardware scan. Although there is no fully FDT 1.2.1 compliant DTM at the time of writing, many FDT 1.2.0 DTMs implement the additional interface IDtmInformation2 to support device detection.

    For you as the frame application developer, this means that you have to extend the GetDtmInformation() method in the update process:

    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);
            }
        }
    }
    

    Schema path updates

    FDT 1.2.0 had the problem that the user needed to install XDR schema definitions manually, which was very uncomfortable. FDT 1.2.1 solves this problem in the way that the DTM can now bring XDR schemas with it. The definition is in the XML from GetInformation() at the XML elements <FDT>, <DtmInfo>, <DtmSchemaPaths>. The DTM will publish a directory name there. In theory, this is an easy task: to install the XDR schemas, we need to update the GetDtmInformation() a little bit:

    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);
            }
        }
    }
    

    Unfortunately there is a logical bug in the sequence now. Since the DTM was already started, it has already asked the frame application for the schema path (using IFdtContainer::GetXmlSchemaPath()) and it has already set up the schema cache to validate XMLs. The DTM cannot be notified about updates in the schema path.

    Therefore you need to restart the DTM in order to be sure that it gets the latest version of XDR schemas. In code, this means you have to update the whole code to:

    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 schema version information issue

    In the chapter before, I have used a simple CopyFiles() operation to update the XDR schema files. This method is not so simple as it seems, because this method needs to perform a version number check.

    The version is given in the XDR schema like this:

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

    The attribute @default defines the version number of the schema. @schemaVersion itself is not used anywhere else.

    Version numbers that are used at the time of writing:

    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
    

    Version 1.21 highly suggests that it correlates to FDT version 1.2.1, which brings up the question on how to interpret the version number. There are three possible ways of interpreting it:

    a) as a simple float number as defined in the datatype of XDR (dt:type="number") b) as a version number in format major.minor c) as a version number in format major.minorbuild where minor and build are simply concatenated

    Ok, I'll leave that puzzle up to the reader. I have suggested a document clarifying this version number issue.

    Anyway, this is our CopyFiles() method:

    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 schema update impact on other DTMs

    In the last chapter we return a flag from CopyFiles() in order to determine whether or not the DTM needs to be restarted in GetDtmInformation(). However, this update may not only affect the current DTM, it may also affect other DTMs of the same protocol which have been added to the catalog before.

    While you can simply restart the whole catalog update from scratch, this would imply a huge performance impact. The better way seems to do it selectively.

    To apply a selective approach, you need to maintain a list of protocols that were updated (in T GetDtmInformation()):

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

    And of course, don't forget to re-update the catalog for affected DTMs:

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

    Getting protocol groups right

    Next, you need to know the concept of protocol groups. A protocol group shares XDR schema files across different protocols, where each protocol is identified by a protocol ID. A good example is the CIP protocol family, which consists of the single protocols DeviceNet, CompoNet and Ethernet/IP.

    These protocols share a common set of XDR schema files, so you'll find the same file three times on your hard disk. This duplication also has some impact on the catalog update since you need to update all copies even if the DTM comes for a single protocol only.

    The reason is in the way a schema cache is constructed: when adding XDR schemas to the schema cache, the first file will win. Other files with the same name will not be added any more. Therefore it is important to ensure that the first file added to the cache is the one with the highest version number. This can only be achieved by updating all copies to the latest version.

    This results in an update of the CopyFiles() method:

    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;
         }  
    }
    

    Device DTMs and schema paths

    For whatever reason, it is defined that only communication DTMs and device DTMs need to bring XDR schemas with them. The rationale behind that probably was that you cannot use a device DTM without a communication or gateway DTM.

    Unfortunately, when querying the Windows Registry for DTMs, you cannot predict the order in which you get DTMs. This may lead to the case that you get a device DTM first. Starting this DTM and getting information from it may result in errors or invalid XML if there is no XDR schema for the protocol of the DTM yet.

    So you need to continue the catalog update, hopefully find a communication DTM or gateway DTM of the same protocol which brings the XDR schemas. Then you start the device DTM again and it will deliver useful information.

    This does not need an update to any code. It should already work if you followed all the steps described before. The only thing to consider here is good error handling (but I'll not do that in pseudo code here).

    Conclusion

    Hopefully I could cover all the topics which are important to know in conjunction with the FDT 1.x catalog update. As you can see, this is not only a rumour.