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.
2. Two Direct Download Links for .AMLX Files
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:
- https://www.automationml.org/wp-content/uploads/publications/amlbook/AutomationML_Example_Models_V1.1.zip
- https://www.automationml.org/wp-content/uploads/2021/06/BPR-008E_BPR_Container_V1.0.0.zip (contains documentation and potential example structures for containers)
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).
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;
// }