Files
2026-10-02 07:19:13 +02:00

9.8 KiB

mitsubishi-label-converter-py

A Python CLI tool that converts Mitsubishi GX Works3 global label XML exports (Global.xml, PLCopen TC6 format, typically UTF-16 LE encoded) to Excel and back to XML with a lossless round-trip.

Each <globalVars> section group in the source file becomes its own worksheet: sections sharing the same name (e.g. a plain and a retain Global list) are merged into one sheet, with the per-variable retain flag in the Retain column. The reverse conversion splits the rows back into the original sections. Every <variable> becomes one row with:

Column Content
Name The variable's name attribute
Address The variable's address attribute (empty when unassigned)
Type The declared type (see below)
Comment The variable comment from variableComments addData (if present)
Line Original source line number from variableLineNumber addData
Retain TRUE when the variable belongs to a retain="true" globalVars section

Structured variables (derived types like ST_Din_STA) are expanded: each member with a device address gets its own row directly under the variable, using a dotted name (PLC_FEL_STA.Value -> D0.0). The addresses come from variableStructDeviceAssignment addData.

Member types — the Global XML export carries only member names and addresses; member types live in a separate SDT export. Pass it via --sdt to populate the Type column of member rows (BOOL, WORD, STRING[16], ARRAY[1..4] OF INT, nested struct names, ...). Without the flag, member Type cells stay empty.

A Meta worksheet records document metadata (fileHeader, contentHeader, PLC type) plus the sheet-to-section mapping (names and varListProperties timestamps) so the reverse conversion is lossless. Retain information lives in the Retain column, not in Meta.

Type rendering

  • Primitive types (<INT/>, <BOOL/>, <REAL/>, <WORD/>, ...) are written as-is.
  • <string length="12"/> is written as STRING[12].
  • Structured/derived types (<derived name="FB_Din"/>) are written using the derived name, e.g. FB_Din.

The reverse conversion recognizes primitives from the standard IEC set; everything else is emitted as a <derived> reference again.

Validation report

The report subcommand runs consistency checks on an XML export or an Excel workbook and prints the findings grouped by severity:

Severity Check Code
error duplicate variable names NAME_DUPLICATE
error identical device address used by several variables ADDR_DUPLICATE
warning bit address inside another variable's word range ADDR_BIT_IN_WORD
warning word ranges of two variables overlap (types sized in 16-bit registers; STRING[n] occupies (n+2)/2 registers) ADDR_OVERLAP
warning address in an unrecognized format ADDR_FORMAT
warning struct member not defined in the SDT struct MEMBER_UNKNOWN
info struct members without a device address MEMBER_UNASSIGNED
info structured variable without any device assignment STRUCT_UNASSIGNED
info derived type missing from the SDT file (aggregated per type) TYPE_UNKNOWN

Members of the same structured variable may legitimately share word registers, so only cross-variable collisions are reported. The command exits with code 1 when errors are found (0 for warnings/info only), so it can gate CI pipelines. Address parsing handles word-level (D450), bit-level (D6005.0), and hexadecimal bit suffixes (D201.A = bit 10).

Installation

# from the project root
pip install -e .[dev]

Usage

# XML -> Excel: output is written next to the source file (Global.xlsx)
mitsubishi-label-converter to-xlsx assets/Global.xml

# with member types from an SDT (structured data type) export
mitsubishi-label-converter to-xlsx assets/Global.xml --sdt assets/SDT.xml

# explicit output path
mitsubishi-label-converter to-xlsx assets/Global.xml -o labels.xlsx

# Excel -> XML: regenerate a GX Works3-compatible XML file
mitsubishi-label-converter to-xml assets/Global.xlsx -o output/Global.xml

# validate labels (XML or Excel input); exit code 1 on errors (CI-friendly)
mitsubishi-label-converter report assets/Global.xml --sdt assets/SDT.xml

# save the report to a file instead of stdout
mitsubishi-label-converter report assets/Global.xlsx -o output/validation.txt

# overwrite existing outputs
mitsubishi-label-converter to-xlsx assets/Global.xml --force

The round-trip is lossless: parsing the regenerated XML yields exactly the same sections, variables, members, comments, line numbers, retain flags, and metadata as the original file. The to-xml conversion regroups the rows of a merged sheet by their Retain flag, restoring the original section split (plain first, retain second). Member types from the SDT file are Excel-only decoration and are intentionally not written to the XML (the XML format stores member types in its dataTypes section, not in device assignments).

Development

pip install -e .[dev]
pytest

Building a self-executing exe

The CLI can be packaged as a standalone Windows executable with PyInstaller.

pip install -e .[dev]      # includes pyinstaller

# one-folder build (default): fast start-up, distribute the whole folder
.\build.ps1

# single-file build: one .exe, slower start-up (self-extracting)
.\build.ps1 -Onefile

# rebuild from scratch and smoke-test the exe against assets/
.\build.ps1 -Clean -Test

# explicit interpreter if several Pythons are installed
.\build.ps1 -Python C:\Path\To\python.exe

Results land in dist/:

  • one-folder (default): dist/mitsubishi-label-converter/mitsubishi-label-converter.exe plus supporting DLLs — distribute the whole folder; starts fast.
  • onefile: dist/mitsubishi-label-converter.exe — a single 8.8 MB file to copy anywhere; a little slower to start.

Notes:

  • The script prefers the project .venv, then python/py on PATH; the Microsoft Store python alias is detected and skipped automatically.
  • run.py at the project root is the packaging entry point (PyInstaller cannot execute cli.py directly because of its relative imports).
  • Antivirus software may flag unsigned PyInstaller exes; sign the exe or add an exclusion if this matters in your environment.
  • Build artifacts (build/, dist/, *.spec) are git-ignored.

TODO / Roadmap

Done:

  • Self-executing exe — build.ps1 wraps PyInstaller with one-folder/onefile modes, clean rebuild, built-in smoke test, and robust Python resolution; verified all three subcommands and the lossless round-trip through the packaged exe.

  • Expose struct members with addresses in Excel — dotted member rows inline under each structured variable (189 structured variables -> 1,220 member rows in the sample file).

  • Member types from SDT export — --sdt flag populates the Type column of member rows from a structured data type XML export; without it the workbook is unchanged.

  • Merged sheets + Retain column — sections sharing a name merge into one worksheet; the per-variable Retain column marks retain variables, and to-xml regroups rows by flag to restore the original sections.

  • Validation report — report subcommand checks duplicate names/addresses, address overlaps (bit-in-word, word ranges), struct member consistency, and unassigned structs; works on XML and Excel input; CI-friendly exit codes.

  • Excel -> XML round-trip conversion — to-xml subcommand; Meta sheet carries document metadata and sheet mapping; verified lossless against the real 290-variable file.

  • CLI subcommands — to-xlsx / to-xml with -o/--output and --force flags.

Planned:

  1. --all-members: expose SDT members without device addresses (requested) GX Works3 only exports struct members that carry a device address; a structured variable without any assignment (e.g. GP01_RUNTIME_PAR of type ST_Runtime_PAR) has no member rows at all — its internal members (StartsTot, RunTimeTot, ...) exist only in the SDT file. Plan:

    • expand_missing_members() in sdt_parser.py: append every SDT-defined member missing from the device assignment as an address-less row (Type populated, Retain inherited from the parent variable).
    • --all-members flag on to-xlsx (requires --sdt).
    • xml_writer must skip address-less <member> entries so to-xml stays lossless (the original XML has no such members).
    • Optional follow-up: recursive expansion for nested struct members (Parent.Sub.uHour, e.g. stError_Time -> M+FX5UCPU_Error_Time).
    • Verification: fabricated fixture (tests/test_all_members.py) with (a) a partially assigned struct, (b) a struct variable with no assignment at all, (c) both in the retain section.
  2. CLI conveniences

    • --list flag: print the parsed label tables to the terminal.
    • --sheet-per-type flag: split variables into one worksheet per type.
    • --filter <glob> flag: convert only variables matching a name pattern.
    • --round-trip flag: convert back and forth and verify losslessness automatically.
  3. Multiple input files — accept several XML files in one invocation, each producing its own workbook (e.g. when a project contains several resources/POUs).

  4. CSV output — --format csv to emit one CSV file per globalVars section, for tools that cannot read .xlsx.

  5. GUI drag & drop (long-term) — a small GUI (e.g. tkinter or a web UI) for drag-and-drop conversion for non-CLI users.