Skip to content

JaskRendix/amplab2mml

Repository files navigation

Ampla to B2MML Transformer

This project converts an Ampla Project XML export into an ISA‑95 B2MML Equipment model.
It replaces the original XSLT‑only approach with a modern Python implementation featuring:

  • a command‑line interface
  • a FastAPI service
  • multiple output formats
  • a fully tested, deterministic multi‑pass transformation pipeline

Purpose

Ampla Project XML files contain:

  • equipment hierarchy
  • equipment classes
  • class inheritance
  • class and instance properties

This tool parses the XML and produces:

  • B2MML Equipment XML
  • JSON model
  • Excel workbook (.xlsx) with Equipment and Classes sheets
  • CSV exports
  • HTML report

The B2MML XML output follows the ISA‑95 B2MML v0400 schema, matching the structure of the original Ampla XSLT.

Additional capabilities:

  • Validation warnings for structural issues (unknown class IDs, malformed items, missing config)
  • Diff mode to compare two Ampla configurations
  • Statistics summary (equipment counts, class counts, property totals, hierarchy depth)

The transformer is deterministic, regression‑tested, and suitable for automation, CI, and configuration comparison.


Architecture

The transformation pipeline is implemented in a multi‑pass class:

from app.transformers.ampla import AmplaTransformer

The pipeline performs:

  1. Class ID lookup construction
  2. Class hierarchy extraction
  3. Inheritance chain computation
  4. Equipment tree parsing
  5. Full‑name resolution
  6. Property merging (class inheritance + instance overrides)
  7. Sorting and normalization

This reproduces the behaviour of the original XSLT while adding validation, configurability, and structured warnings.


Data Model Overview

The transformer produces a clean, normalized internal representation of the Ampla project.
This model is independent of any output format (XML, JSON, Excel) and is used by all builders.

UML Model

classDiagram
    class Equipment {
        +String id
        +String name
        +String level
        +List class_ids
        +List children
        +Dict overrides
        +List properties
        +String full_name
    }

    class EquipmentClass {
        +String name
        +String parent
        +List properties
        +List inheritance_chain
    }

    class ClassProperty {
        +String name
        +String value
        +String datatype
    }

    class EquipmentProperty {
        +String name
        +String value
        +String datatype
    }

    Equipment "1" *-- "many" Equipment : children
    Equipment "1" *-- "many" EquipmentProperty : properties
    EquipmentClass "1" *-- "many" ClassProperty : properties
    Equipment ..> EquipmentClass : resolves attributes from
Loading

Transformation Pipeline (Sequence)

sequenceDiagram
    participant XML as Raw XML
    participant AT as AmplaTransformer
    participant CTX as Context (Shared State)
    participant Model as Internal Model

    AT->>XML: Pass 1: Build ID Lookup Table
    AT->>XML: Pass 2: Extract Classes & Compute Inheritance
    AT->>XML: Pass 3: Recursive Equipment Tree Parsing
    AT->>CTX: Resolve Class IDs & Log Warnings
    AT->>Model: Pass 4: Merge Properties & Apply Overrides
    Note right of Model: Final deterministic B2MML-ready model
Loading

Design Principles

  • Deterministic output — same XML → same model every time
  • Multi-pass resolution — ensures class inheritance and overrides are fully resolved
  • Separation of concerns — parsing, transformation, and output builders are isolated
  • Extensible — new output formats can be added without touching the transformer
  • Safe — warnings collected instead of throwing on malformed XML

---

## **Configuration (mapping.toml)**

Level mapping is now externalized in:

config/mapping.toml


Example:

```toml
[level_map]
"Citect.Ampla.Isa95.EnterpriseFolder" = "Enterprise"
"Citect.Ampla.Isa95.SiteFolder" = "Site"
"Citect.Ampla.Isa95.AreaFolder" = "Area"
"Citect.Ampla.General.Server.ApplicationsFolder" = "Other"

If the file is missing or invalid, the transformer logs a warning and falls back to defaults.


Programmatic usage

from lxml import etree
from app.transformers.ampla import AmplaTransformer

# Parse XML
root = etree.parse("input.xml").getroot()

# Create transformer (loads mapping.toml automatically)
transformer = AmplaTransformer(config_path="config/mapping.toml")

# Run transformation
model = transformer.transform(root)

print(model["equipment"])
print(model["classes"])
print(model["warnings"])   # non-fatal issues detected during parsing

The returned model is a dictionary:

{
    "equipment": [...],
    "classes": [...],
    "warnings": [...]
}

Command-line usage

Convert Ampla XML to B2MML XML

b2mml convert input.xml output.xml

Convert to JSON

b2mml json input.xml output.json

Write to stdout:

b2mml json input.xml

Export to Excel

b2mml excel input.xml output.xlsx

Show model statistics

b2mml stats input.xml

As JSON:

b2mml stats --format json input.xml

Export HTML report

b2mml html input.xml report.html

Diff two Ampla XML files

b2mml diff baseline.xml updated.xml

As JSON:

b2mml diff --format json baseline.xml updated.xml

Exit codes:

  • 0 → no differences
  • 1 → differences found

Validate an Ampla XML model

Run structural validation on the parsed model:

b2mml validate input.xml

JSON output:

b2mml validate --format json input.xml

Exit codes:

  • 0 → model is valid (no warnings)
  • 1 → warnings detected (unknown classes, missing parents, circular inheritance, etc.)

API usage

Start the FastAPI server:

uvicorn app.api:app --reload

Endpoints

Method Path Description
GET /health Health check
GET /info API and pipeline version info
POST /convert/json JSON model
POST /convert/xml B2MML XML
POST /convert/excel Excel workbook
POST /convert/csv/equipment Equipment CSV
POST /convert/csv/classes Classes CSV
POST /convert/html HTML report
POST /stats Model statistics
POST /diff/json JSON diff
POST /diff/text Text diff
POST /validate Validate model and return warnings

Examples:

curl -X POST -F "file=@input.xml" http://localhost:8000/convert/json
curl -X POST -F "file=@input.xml" http://localhost:8000/stats
curl -X POST -F "file_a=@baseline.xml" -F "file_b=@updated.xml" http://localhost:8000/diff/text

Interactive docs:
http://localhost:8000/docs


Docker Compose (local development)

Start the API:

make up

Service available at:

http://localhost:8000

Stop:

make down

Project structure

  • app/parsers — XML parsing
  • app/transformers — Ampla → internal model
  • app/builders — B2MML XML serialization
  • app/validators.py — model validation + warnings
  • app/diff.py — structural diff engine
  • app/stats.py — statistics computation
  • app/excel_export.py — Excel export
  • app/csv_export.py — CSV export
  • app/html_report.py — HTML report generator
  • app/cli.py — CLI entrypoints
  • app/api.py — FastAPI service
  • app/schemas.py — Pydantic response models
  • tests/ — full test suite + regression fixtures

License

BSD 3‑Clause License.
Includes derivative work from the original
Ampla_to_Equipment_B2MML.xslt by Oleg Tkachenko (2005),
also under BSD 3‑Clause.

About

Modern Ampla‑to‑B2MML (v0400) transformer with CLI, API, and full test suite.

Topics

Resources

License

Stars

0 stars

Watchers

1 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages