TIA Direct Export

How Export and Compare read TIA projects directly, with CLI options and conflict handling.

VS Code uses the direct exporter for Export, Compare, diff previews, Take TIA Version, and post-import refresh. A comparison exports the entire selected PLC into a fresh temporary directory and compares its sources and .s7res files with the workspace and the last synchronization baseline. It does not need TIA object fingerprints or a compile just to compare.

plccheck tia export software "Demo" --plc PLC_1 --engine direct --live --out exported
plccheck tia diff --project "Demo" --plc PLC_1 --root exported --engine direct exported/PLC_1

--live saves an open project and copies its persistence files while holding TIA exclusive access. VS Code uses this for explicit exports and manual comparisons even when its binding contains an .ap21 path, so unsaved TIA edits are included. CLI exports of an explicit project file without --live retain their offline behavior. Direct comparison uses a live snapshot for project paths/names; an explicit .zap21 compares the archive.

The CLI's existing fingerprint comparison remains available as tia diff --engine openness and remains the CLI default. Direct comparison requires --project and --plc. --snapshot-out <empty-directory> retains the comparison export for a caller that owns its lifetime; otherwise it is removed when the command finishes.

VS Code exports use --allow-partial: successfully rendered items remain usable when another PLC object or project-library definition is unsupported or cannot be rendered. Export warnings identify those objects and prevent stale-file pruning. Comparison snapshots record unavailable PLC identities separately; their existing local entries are unknown, never reported as deleted, and previews/replacements refuse those objects. Other selected objects remain comparable and fetchable. Invalid persistence, filesystem errors, and colliding sanitized object paths still fail the operation. Ordinary CLI exports remain strict by default; partial exports cannot use --delete-stale.

Baselines and changes

Export metadata includes:

  • sourceHash: SHA-256 of the exact exported source bytes, retained for existing tooling and old baselines.
  • sourceExtension: the synchronized source format, so format-only changes are attributed to the changed side. Older sidecars without this baseline require reconciliation when formats differ.
  • contentHash: SHA-256 after removing a UTF-8 BOM, normalizing line endings, and ignoring final newlines. Source-line whitespace is preserved. XML export provenance (DocumentInfo) is excluded.
  • resourceHash: SHA-256 of the accompanying resource file, or an empty string when no resource file existed.

Local and remote content are compared independently with that baseline. One-sided changes appear on the corresponding side; different changes on both sides are conflicts. Matching current content is unchanged even if both sides differ from the baseline. Older metadata can still use its exact sourceHash; ambiguous resource changes without a resource baseline remain unknown. Comparison does not overwrite sources or manufacture a new baseline from edited local content.

Folder comparisons include new TIA objects; file comparisons stay within the selected identities. Object identity includes the PLC, block/type kind, and TIA group path. A format change retains the existing local path in the result. Deleted local sources with surviving metadata remain unknown and are never interpreted as a request to delete from TIA. Missing TIA objects remain distinguishable from genuinely new local sources through their existing metadata.

Failed or incomplete exports never publish a comparison or trigger stale-file pruning. Tag tables under PLC tags participate alongside blocks and data types. Nested PLC roots and project-library definitions remain excluded. Ambiguous source identities and invalid metadata fail the comparison.

VS Code keeps the accepted sidebar comparison's snapshot for diff previews. Failed or superseded comparisons retain it, and import-only checks do not replace it. Mutations, backend changes, and disposal invalidate it; unlinking or rebinding removes obsolete cached snapshots. Binding checks prevent reusing it for a different project. Take TIA Version and post-import refresh use fresh exports.

Fetches use metadata identity when available, so a locally moved or renamed source can still preview its original TIA object. Take TIA Version restores the snapshot's original filesystem location, removes the obsolete local representation, and refuses to overwrite a different file already occupying the restored location.

Automatic comparison

Automatic comparison is enabled for every linked project, without a setting. The extension watches System/PEData*.plf and PEData.idx outside or inside the workspace and debounces persistence changes for 750 ms. A five-second metadata check recovers missed events and unavailable directories; it does not export unchanged projects. Name-based bindings resolve their storage path from the status bar's discovered TIA instances. Archives are not watched.

The first available storage snapshot of an open project triggers an initial comparison. Instance discovery polls at a 20-second minimum interval and also refreshes on focus and operations; opening a previously closed project re-arms comparison without requiring a save. Subsequent storage changes run a quiet whole-root comparison through the existing operation queue. Changes during a comparison coalesce into one follow-up. Unlinking, rebinding, and disposal cancel pending work and prevent publishing outdated results.

Background comparisons pass tia diff --engine direct --saved-only. The bridge attaches only to an already-open project, takes exclusive access and copies persisted storage without calling Save, so a refresh neither launches TIA, opens closed projects, saves unfinished edits nor triggers a save feedback loop. Filesystem events are advisory: non-source persistence writes may trigger a comparison, but only content differences appear in SCM. Automatic comparison does not modify local sources or baselines, compile, import, or install Openness trust. A user-started command can establish trust when needed.

Failed background comparisons retain the previous sidebar and cached preview. Transient failures retry after two and four seconds, then wait for another storage change. The first failure is logged to the Siemens TIA Portal output channel without a popup.

Import validation

Import still uses Openness. The bridge holds TIA exclusive access from validation through import and save. It exports the current content of the affected existing objects and checks the content/resource baselines again. A displayed comparison cannot authorize overwriting an intervening TIA edit. Missing baselines and conflicts require explicit overwrite; project-library guards still apply.

Group names retain their original TIA identity in metadata; only exported filesystem names are sanitized. Imports resolve that identity against the actual TIA group tree, including names containing /, instead of creating sanitized duplicate groups. Missing or ambiguous original groups require a fresh export before importing.

Document import results are checked before refreshing metadata. TIA can return Failure or PartialSuccess without throwing; these are failures, not successful synchronization. An external-source fallback is refused when it would lose a document's resource texts.

Verification

Automated coverage includes the source/resource change matrix, converged edits, missing and legacy baselines, additions, local deletions, format changes, folder/file scope, nested roots, invalid metadata, failed snapshots, cache cleanup, rebinding, queue reentrancy, and trust preflight handling. Run:

npm run typecheck
npx eslint ./client/src --ext .ts,.tsx --max-warnings=0
npm run test:tia
npm run test:webview:render
go -C go_lsp test ./...
dotnet build tia_openness_bridge/TiaOpennessBridge.csproj -c Release
powershell -NoProfile -File scripts/test-tia-bridge-helpers.ps1
node scripts/build-go-lsp.js --target windows-amd64

Watcher unit tests exercise actual file events, burst debouncing, missed events, storage replacement/deletion, unavailable paths, rebinding, cancellation, queueing, and bounded retries.