Task 027: .AMLX File Format

Task 027: .AMLX File Format

1. List of Properties Intrinsic to the .AMLX File Format

The .AMLX file format is a container based on the Open Packaging Conventions (OPC) standard, essentially a ZIP archive with a specific structure for AutomationML data exchange in industrial automation (as defined in IEC 62714 series). The properties intrinsic to the format, which define its structure and can be extracted from any valid .AMLX file, are as follows:

  • File Extension: .amlx (standard extension for AutomationML containers)
  • MIME Type: application/automationml-amlx+zip
  • Container Standard: Open Packaging Conventions (OPC)
  • File Signature: PK\x03\x04 (ZIP archive header)
  • Content Types File: Presence and contents of [Content_Types].xml, which lists MIME types for parts (e.g., application/automationml-aml+xml for .aml files)
  • Package Relationships File: Presence and contents of _rels/.rels, which defines relationships between parts
  • Root Relationship Type: http://schemas.automationml.org/container/relationship/RootDocument (must point to the root .aml file)
  • Root Document Path: Path to the primary .aml file (CAEX-based XML serving as the entry point)
  • Library Document Path: Path to the library .aml file (containing RoleClassLibs, InterfaceClassLibs, and SystemUnitClassLibs, referenced from the root)
  • Embedded Files List: List of all files within the container (e.g., .aml, .dae for COLLADA, .xml for PLCopen, .fmu for FMU)
  • Container Type: Local linked (relative paths only) or Free linked (allows URI references)
  • AutomationML Version: Version attribute from the root .aml file's CAEX schema

These properties ensure the format's integrity for data exchange in engineering tools.

After extensive searching across web sources, GitHub repositories, and AutomationML official sites, I was unable to locate direct download links for files specifically with the .amlx extension. AutomationML containers are often distributed as .zip files with equivalent internal structure (which can be renamed to .amlx if compliant with OPC). Here are two direct download links to example AutomationML ZIP archives that follow similar container principles and contain .aml files:

3. Ghost Blog Embedded HTML/JavaScript for Drag-and-Drop .AMLX File Dump

The following is a self-contained HTML snippet with JavaScript that can be embedded in a Ghost blog post. It allows users to drag and drop a .AMLX file, parses it as a ZIP using JSZip, extracts the properties from the list above, and dumps them to the screen. Note: This requires the JSZip library (included via CDN for simplicity).

Drag and drop a .AMLX file here

4. Python Class for .AMLX File Handling

The following Python class uses zipfile and xml.etree.ElementTree to open, decode, read, write, and print the properties from a .AMLX file.

import zipfile
import xml.etree.ElementTree as ET
from io import BytesIO

class AmlxHandler:
    def __init__(self, filename=None):
        self.filename = filename
        self.properties = {}
        if filename:
            self.read(filename)

    def read(self, filename):
        self.filename = filename
        with zipfile.ZipFile(filename, 'r') as zf:
            # File Extension
            self.properties['File Extension'] = '.amlx'

            # MIME Type
            self.properties['MIME Type'] = 'application/automationml-amlx+zip'

            # Container Standard
            self.properties['Container Standard'] = 'Open Packaging Conventions (OPC)'

            # File Signature
            with open(filename, 'rb') as f:
                sig = f.read(4)
            self.properties['File Signature'] = 'PK\\x03\\x04 (Valid ZIP)' if sig == b'PK\x03\x04' else 'Invalid'

            # Content Types File
            if '[Content_Types].xml' in zf.namelist():
                content_types = ET.parse(BytesIO(zf.read('[Content_Types].xml'))).getroot()
                self.properties['Content Types File'] = ET.tostring(content_types, encoding='unicode')
            else:
                self.properties['Content Types File'] = 'Not found'

            # Package Relationships File
            if '_rels/.rels' in zf.namelist():
                rels = ET.parse(BytesIO(zf.read('_rels/.rels'))).getroot()
                self.properties['Package Relationships File'] = ET.tostring(rels, encoding='unicode')
            else:
                self.properties['Package Relationships File'] = 'Not found'

            # Root Relationship Type and Path
            self.properties['Root Relationship Type'] = 'http://schemas.automationml.org/container/relationship/RootDocument'
            root_rel = next((rel for rel in rels.findall('.//{http://schemas.openxmlformats.org/package/2006/relationships}Relationship') if rel.attrib['Type'] == self.properties['Root Relationship Type']), None)
            self.properties['Root Document Path'] = root_rel.attrib['Target'] if root_rel else 'Not found'

            # Library Document Path (simplified: assume 'Library.aml' or parse root for reference)
            if self.properties['Root Document Path'] in zf.namelist():
                root_xml = ET.parse(BytesIO(zf.read(self.properties['Root Document Path']))).getroot()
                # Assume library is referenced; for simplicity, check for common name
                self.properties['Library Document Path'] = 'Library.aml' if 'Library.aml' in zf.namelist() else 'Not found'
            else:
                self.properties['Library Document Path'] = 'Not found'

            # Embedded Files List
            self.properties['Embedded Files List'] = ', '.join(zf.namelist())

            # Container Type
            self.properties['Container Type'] = 'Free linked' if any('http://' in name for name in zf.namelist()) else 'Local linked'

            # AutomationML Version
            self.properties['AutomationML Version'] = root_xml.attrib.get('Version', 'Not found')

    def print_properties(self):
        for key, value in self.properties.items():
            print(f"{key}: {value}")

    def write(self, new_filename):
        # Simplified write: create a basic .amlx with minimal structure
        with zipfile.ZipFile(new_filename, 'w') as zf:
            # Add [Content_Types].xml
            content_types = '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="aml" ContentType="application/automationml-aml+xml"/></Types>'
            zf.writestr('[Content_Types].xml', content_types)

            # Add _rels/.rels
            rels = '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.automationml.org/container/relationship/RootDocument" Target="root.aml"/></Relationships>'
            zf.writestr('_rels/.rels', rels)

            # Add root.aml
            root_aml = '<CAEXFile xmlns="http://www.dke.de/CAEX" SchemaVersion="3.0" FileName="root.aml" Version="2.0"></CAEXFile>'
            zf.writestr('root.aml', root_aml)

            # Add library.aml
            library_aml = '<CAEXFile xmlns="http://www.dke.de/CAEX" SchemaVersion="3.0" FileName="library.aml"></CAEXFile>'
            zf.writestr('library.aml', library_aml)
        self.read(new_filename)  # Reload properties from new file

# Example usage:
# handler = AmlxHandler('example.amlx')
# handler.print_properties()
# handler.write('new.amlx')

5. Java Class for .AMLX File Handling

The following Java class uses java.util.zip and javax.xml.parsers to open, decode, read, write, and print the properties from a .AMLX file.

import java.io.*;
import java.util.zip.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.InputSource;

public class AmlxHandler {
    private String filename;
    private java.util.Map<String, String> properties = new java.util.HashMap<>();

    public AmlxHandler(String filename) {
        this.filename = filename;
        if (filename != null) {
            read(filename);
        }
    }

    public void read(String filename) {
        this.filename = filename;
        try (ZipFile zf = new ZipFile(filename)) {
            properties.put("File Extension", ".amlx");
            properties.put("MIME Type", "application/automationml-amlx+zip");
            properties.put("Container Standard", "Open Packaging Conventions (OPC)");

            // File Signature
            try (FileInputStream fis = new FileInputStream(filename)) {
                byte[] sig = new byte[4];
                fis.read(sig);
                properties.put("File Signature", (sig[0] == 80 && sig[1] == 75 && sig[2] == 3 && sig[3] == 4) ? "PK\\x03\\x04 (Valid ZIP)" : "Invalid");
            }

            // Content Types File
            ZipEntry contentEntry = zf.getEntry("[Content_Types].xml");
            properties.put("Content Types File", contentEntry != null ? new String(zf.getInputStream(contentEntry).readAllBytes()) : "Not found");

            // Package Relationships File
            ZipEntry relsEntry = zf.getEntry("_rels/.rels");
            String relsStr = relsEntry != null ? new String(zf.getInputStream(relsEntry).readAllBytes()) : "Not found";
            properties.put("Package Relationships File", relsStr);

            // Root Relationship Type and Path
            properties.put("Root Relationship Type", "http://schemas.automationml.org/container/relationship/RootDocument");
            Document relDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(relsStr)));
            NodeList rels = relDoc.getElementsByTagName("Relationship");
            String rootPath = "Not found";
            for (int i = 0; i < rels.getLength(); i++) {
                Element rel = (Element) rels.item(i);
                if (rel.getAttribute("Type").equals(properties.get("Root Relationship Type"))) {
                    rootPath = rel.getAttribute("Target");
                    break;
                }
            }
            properties.put("Root Document Path", rootPath);

            // Library Document Path
            ZipEntry rootEntry = zf.getEntry(rootPath);
            String rootStr = rootEntry != null ? new String(zf.getInputStream(rootEntry).readAllBytes()) : null;
            properties.put("Library Document Path", (rootStr != null && rootStr.contains("Library.aml")) ? "Library.aml" : "Not found");

            // Embedded Files List
            StringBuilder files = new StringBuilder();
            zf.entries().asIterator().forEachRemaining(e -> files.append(e.getName()).append(", "));
            properties.put("Embedded Files List", files.toString().trim());

            // Container Type
            properties.put("Container Type", (rootStr != null && rootStr.contains("http://")) ? "Free linked" : "Local linked");

            // AutomationML Version
            if (rootStr != null) {
                Document rootDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(rootStr)));
                properties.put("AutomationML Version", rootDoc.getDocumentElement().getAttribute("Version"));
            } else {
                properties.put("AutomationML Version", "Not found");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void printProperties() {
        properties.forEach((key, value) -> System.out.println(key + ": " + value));
    }

    public void write(String newFilename) {
        try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(newFilename))) {
            // Add [Content_Types].xml
            zos.putNextEntry(new ZipEntry("[Content_Types].xml"));
            zos.write("<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\"><Default Extension=\"aml\" ContentType=\"application/automationml-aml+xml\"/></Types>".getBytes());
            zos.closeEntry();

            // Add _rels/.rels
            zos.putNextEntry(new ZipEntry("_rels/.rels"));
            zos.write("<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"http://schemas.automationml.org/container/relationship/RootDocument\" Target=\"root.aml\"/></Relationships>".getBytes());
            zos.closeEntry();

            // Add root.aml
            zos.putNextEntry(new ZipEntry("root.aml"));
            zos.write("<CAEXFile xmlns=\"http://www.dke.de/CAEX\" SchemaVersion=\"3.0\" FileName=\"root.aml\" Version=\"2.0\"></CAEXFile>".getBytes());
            zos.closeEntry();

            // Add library.aml
            zos.putNextEntry(new ZipEntry("library.aml"));
            zos.write("<CAEXFile xmlns=\"http://www.dke.de/CAEX\" SchemaVersion=\"3.0\" FileName=\"library.aml\"></CAEXFile>".getBytes());
            zos.closeEntry();
        } catch (IOException e) {
            e.printStackTrace();
        }
        read(newFilename);  // Reload properties
    }

    // Example usage:
    // public static void main(String[] args) {
    //     AmlxHandler handler = new AmlxHandler("example.amlx");
    //     handler.printProperties();
    //     handler.write("new.amlx");
    // }
}

6. JavaScript Class for .AMLX File Handling

The following JavaScript class uses JSZip to open, decode, read, write, and print the properties from a .AMLX file (intended for Node.js with 'jszip' and 'fs' modules installed).

const JSZip = require('jszip');
const fs = require('fs');

class AmlxHandler {
  constructor(filename = null) {
    this.filename = filename;
    this.properties = {};
    if (filename) {
      this.read(filename);
    }
  }

  async read(filename) {
    this.filename = filename;
    const data = fs.readFileSync(filename);
    const zip = await JSZip.loadAsync(data);

    this.properties['File Extension'] = '.amlx';
    this.properties['MIME Type'] = 'application/automationml-amlx+zip';
    this.properties['Container Standard'] = 'Open Packaging Conventions (OPC)';

    // File Signature
    const sig = data.slice(0, 4);
    this.properties['File Signature'] = (sig[0] === 80 && sig[1] === 75 && sig[2] === 3 && sig[3] === 4) ? 'PK\\x03\\x04 (Valid ZIP)' : 'Invalid';

    // Content Types File
    this.properties['Content Types File'] = await zip.file('[Content_Types].xml')?.async('string') || 'Not found';

    // Package Relationships File
    const rels = await zip.file('_rels/.rels')?.async('string') || 'Not found';
    this.properties['Package Relationships File'] = rels;

    // Root Relationship Type and Path
    this.properties['Root Relationship Type'] = 'http://schemas.automationml.org/container/relationship/RootDocument';
    const relXml = new DOMParser().parseFromString(rels, 'text/xml');
    const rootRel = Array.from(relXml.getElementsByTagName('Relationship')).find(r => r.getAttribute('Type') === this.properties['Root Relationship Type']);
    this.properties['Root Document Path'] = rootRel ? rootRel.getAttribute('Target') : 'Not found';

    // Library Document Path
    const rootContent = await zip.file(this.properties['Root Document Path'])?.async('string');
    this.properties['Library Document Path'] = rootContent?.includes('Library.aml') ? 'Library.aml' : 'Not found';

    // Embedded Files List
    this.properties['Embedded Files List'] = Object.keys(zip.files).join(', ');

    // Container Type
    this.properties['Container Type'] = rootContent?.includes('http://') ? 'Free linked' : 'Local linked';

    // AutomationML Version
    const rootXml = new DOMParser().parseFromString(rootContent, 'text/xml');
    this.properties['AutomationML Version'] = rootXml.documentElement.getAttribute('Version') || 'Not found';
  }

  printProperties() {
    for (const [key, value] of Object.entries(this.properties)) {
      console.log(`${key}: ${value}`);
    }
  }

  async write(newFilename) {
    const zip = new JSZip();
    // Add [Content_Types].xml
    zip.file('[Content_Types].xml', '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="aml" ContentType="application/automationml-aml+xml"/></Types>');

    // Add _rels/.rels
    zip.file('_rels/.rels', '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.automationml.org/container/relationship/RootDocument" Target="root.aml"/></Relationships>');

    // Add root.aml
    zip.file('root.aml', '<CAEXFile xmlns="http://www.dke.de/CAEX" SchemaVersion="3.0" FileName="root.aml" Version="2.0"></CAEXFile>');

    // Add library.aml
    zip.file('library.aml', '<CAEXFile xmlns="http://www.dke.de/CAEX" SchemaVersion="3.0" FileName="library.aml"></CAEXFile>');

    const content = await zip.generateAsync({type: 'nodebuffer'});
    fs.writeFileSync(newFilename, content);
    await this.read(newFilename);  // Reload properties
  }
}

// Example usage:
// const handler = new AmlxHandler('example.amlx');
// handler.printProperties();
// await handler.write('new.amlx');

7. C Class for .AMLX File Handling

The following is a C++ class (as "C class" likely implies C++ for object-oriented features) using zip library (assume libzip is linked) to open, decode, read, write, and print the properties from a .AMLX file. Note: Parsing XML is simplified using basic string operations for brevity; in practice, use a library like tinyxml2.

#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <zip.h>
#include <cstring>

class AmlxHandler {
private:
    std::string filename;
    std::map<std::string, std::string> properties;

public:
    AmlxHandler(const std::string& fn = "") : filename(fn) {
        if (!fn.empty()) {
            read(fn);
        }
    }

    void read(const std::string& fn) {
        filename = fn;
        std::ifstream file(fn, std::ios::binary);
        char sig[4];
        file.read(sig, 4);
        properties["File Extension"] = ".amlx";
        properties["MIME Type"] = "application/automationml-amlx+zip";
        properties["Container Standard"] = "Open Packaging Conventions (OPC)";
        properties["File Signature"] = (sig[0] == 'P' && sig[1] == 'K' && sig[2] == 3 && sig[3] == 4) ? "PK\\x03\\x04 (Valid ZIP)" : "Invalid";

        // Use libzip to open ZIP
        int err = 0;
        zip_t* z = zip_open(fn.c_str(), 0, &err);
        if (z) {
            // Content Types File
            zip_file_t* ct = zip_fopen(z, "[Content_Types].xml", 0);
            std::string contentTypes;
            if (ct) {
                char buf[1024];
                zip_int64_t len;
                while ((len = zip_fread(ct, buf, sizeof(buf))) > 0) {
                    contentTypes.append(buf, len);
                }
                zip_fclose(ct);
            }
            properties["Content Types File"] = !contentTypes.empty() ? contentTypes : "Not found";

            // Package Relationships File
            zip_file_t* rel = zip_fopen(z, "_rels/.rels", 0);
            std::string rels;
            if (rel) {
                char buf[1024];
                zip_int64_t len;
                while ((len = zip_fread(rel, buf, sizeof(buf))) > 0) {
                    rels.append(buf, len);
                }
                zip_fclose(rel);
            }
            properties["Package Relationships File"] = !rels.empty() ? rels : "Not found";

            // Root Relationship Type and Path
            properties["Root Relationship Type"] = "http://schemas.automationml.org/container/relationship/RootDocument";
            size_t pos = rels.find(properties["Root Relationship Type"]);
            std::string rootPath = "Not found";
            if (pos != std::string::npos) {
                size_t targetPos = rels.find("Target=\"", pos);
                if (targetPos != std::string::npos) {
                    targetPos += 8;
                    size_t end = rels.find("\"", targetPos);
                    rootPath = rels.substr(targetPos, end - targetPos);
                }
            }
            properties["Root Document Path"] = rootPath;

            // Library Document Path (simplified)
            zip_file_t* rootF = zip_fopen(z, rootPath.c_str(), 0);
            std::string rootContent;
            if (rootF) {
                char buf[1024];
                zip_int64_t len;
                while ((len = zip_fread(rootF, buf, sizeof(buf))) > 0) {
                    rootContent.append(buf, len);
                }
                zip_fclose(rootF);
            }
            properties["Library Document Path"] = (rootContent.find("Library.aml") != std::string::npos) ? "Library.aml" : "Not found";

            // Embedded Files List
            std::string filesList;
            zip_int64_t num = zip_get_num_entries(z, 0);
            for (zip_int64_t i = 0; i < num; ++i) {
                filesList += zip_get_name(z, i, 0) + std::string(", ");
            }
            properties["Embedded Files List"] = filesList;

            // Container Type
            properties["Container Type"] = (rootContent.find("http://") != std::string::npos) ? "Free linked" : "Local linked";

            // AutomationML Version
            size_t verPos = rootContent.find("Version=\"");
            std::string version = "Not found";
            if (verPos != std::string::npos) {
                verPos += 9;
                size_t end = rootContent.find("\"", verPos);
                version = rootContent.substr(verPos, end - verPos);
            }
            properties["AutomationML Version"] = version;

            zip_close(z);
        }
    }

    void printProperties() {
        for (const auto& p : properties) {
            std::cout << p.first << ": " << p.second << std::endl;
        }
    }

    void write(const std::string& newFn) {
        zip_t* z = zip_open(newFn.c_str(), ZIP_CREATE | ZIP_TRUNCATE, nullptr);
        if (z) {
            // Add [Content_Types].xml
            std::string ct = "<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\"><Default Extension=\"aml\" ContentType=\"application/automationml-aml+xml\"/></Types>";
            zip_source_t* src = zip_source_buffer(z, ct.c_str(), ct.size(), 0);
            zip_file_add(z, "[Content_Types].xml", src, ZIP_FL_OVERWRITE);

            // Add _rels/.rels
            std::string rels = "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"http://schemas.automationml.org/container/relationship/RootDocument\" Target=\"root.aml\"/></Relationships>";
            src = zip_source_buffer(z, rels.c_str(), rels.size(), 0);
            zip_file_add(z, "_rels/.rels", src, ZIP_FL_OVERWRITE);

            // Add root.aml
            std::string root = "<CAEXFile xmlns=\"http://www.dke.de/CAEX\" SchemaVersion=\"3.0\" FileName=\"root.aml\" Version=\"2.0\"></CAEXFile>";
            src = zip_source_buffer(z, root.c_str(), root.size(), 0);
            zip_file_add(z, "root.aml", src, ZIP_FL_OVERWRITE);

            // Add library.aml
            std::string lib = "<CAEXFile xmlns=\"http://www.dke.de/CAEX\" SchemaVersion=\"3.0\" FileName=\"library.aml\"></CAEXFile>";
            src = zip_source_buffer(z, lib.c_str(), lib.size(), 0);
            zip_file_add(z, "library.aml", src, ZIP_FL_OVERWRITE);

            zip_close(z);
        }
        read(newFn);  // Reload properties
    }
};

// Example usage:
// int main() {
//     AmlxHandler handler("example.amlx");
//     handler.printProperties();
//     handler.write("new.amlx");
//     return 0;
// }