TeamcenterKnowledge

Skills

TC Dispatcher Cicd Pipeline

Skill tc-dispatcher-cicd-pipeline. Set up a Teamcenter Dispatcher translator end to end and use it as a CI/CD-style pipeline engine — a workflow-fired job runner with stages, gates, pass/fail routing and fan-out/fan-in. Full setup guide (translator registration, handler binding, site preferences, the Windows-service classpath trap) plus the router/config pattern for running many "pipeline stages" off one script. Use when standing up a new Dispatcher-triggered workflow integration, or explaining the pattern to someone else.

Teamcenter Dispatcher is marketed as CAD translation. Structurally it is a queued, distributed job runner: something stages a work order, a worker process picks it up, runs an arbitrary executable against a working directory, and reports a terminal state Teamcenter can read back. Nothing about that is CAD-specific. Read a workflow Do task as a pipeline stage, a Review or OR-task as a gate, and a translator's exit code as a pass/fail signal, and the whole thing behaves like a CI/CD pipeline with Teamcenter as the orchestrator — proven live on TC 2606 (vm2606), 2026-08.

Two companion skills go deep on specific failure modes this one only summarizes: tc-dispatcher-requests (creating/reading requests, the preference system, direct-vs-indirect translators) and tc-workflow-authoring (handler structs, task-graph semantics, performAction3). This skill is the setup path and the pipeline pattern — read those two when something in here breaks.

The mental model

CI/CD concept Teamcenter/Dispatcher equivalent
Pipeline / workflow An EPMTaskTemplate process
Stage A Do task, its Start action firing a translation request
Job runner Dispatcher (Module + Scheduler + DispatcherClient)
Job script The translator executable (translator.xml + Service.properties)
"Wait for the job" EPM-hold on the Do task's Complete action
Job success signals the pipeline The job performs the task's Complete action itself when it finishes
Fan-out (parallel stages) Multiple Do tasks with dependency_task_templates pointing at the same predecessor
Fan-in / gate A Review or EPMOrTaskTemplate, which starts only once every fan-out predecessor reaches Complete — proven live, see below
Pass/fail branch Success (solid) vs Fail (dashed) paths out of a task, drawn in Workflow Designer
Pipeline parameterization A JSON config file the router script reads, selecting behavior per invocation
Correlating which stage fired The marker dataset's name, read out of the staged TranslationTask XML

Part 1 — Setting up one translator/handler, start to finish

Do these in order. Skipping the order is exactly what costs the most time — several of these steps look independently optional and are not.

1. Register the translator

C:\apps\PLM\DSP\Module\conf\translator.xml:

<MyStage provider="SIEMENS" service="mystage" isactive="true" OutputNeeded="false">
  <TransExecutable dir="C:\apps\PLM\DSP\Module/Translators/MyStage" name="MyStage.bat" />
</MyStage>

OutputNeeded="false" + no .Load binding = a direct translator (it does its own I/O, nothing for basic.DatabaseOperation to load back). Use this for pipeline-stage scripts; reserve indirect translators for the classic CAD-file-in/CAD-file-out case (see tc-dispatcher-requests).

C:\apps\PLM\DSP\DispatcherClient\conf\Service.properties:

Translator.SIEMENS.mystage.Prepare=com.teamcenter.ets.translator.ugs.basic.TaskPrep
Translator.SIEMENS.mystage.Duplicate=false

Provider name casing must match translator.xml's provider= attribute byte for byte, everywhere else this string appears — the handler argument, the preference name, all of it. SIEMENS (all-caps) here means SIEMENS everywhere, not Siemens. A mismatch fails with an error that names the WRONG value (echoes back what was supplied, not what's registered), which reads exactly like a missing class/jar instead of a four-character typo. Full incident writeup in tc-dispatcher-requests.

2. Regenerate the Windows services — do not just restart

The Dispatcher Windows services snapshot their classpath into the registry at install time. Restart-Service reloads the process against that stale snapshot; it does not re-read Service.properties/translator.xml. After step 1, every time, on both services:

$vmCred = Import-Clixml "$env:USERPROFILE\.xcelerator\credentials\vm-admin.cred.xml"
$session = New-PSSession -ComputerName <host> -Credential $vmCred
Invoke-Command -Session $session -ScriptBlock {
  Stop-Service -DisplayName "Teamcenter DispatcherClient*" -Force
  Stop-Service -DisplayName "Teamcenter Dispatcher Module*" -Force
  cmd /c "C:\apps\PLM\DSP\DispatcherClient\bin\runDispatcherClientWinService.bat -r"
  cmd /c "C:\apps\PLM\DSP\Module\bin\moduleWinService.bat -r"
  cmd /c "C:\apps\PLM\DSP\DispatcherClient\bin\runDispatcherClientWinService.bat -i"
  cmd /c "C:\apps\PLM\DSP\Module\bin\moduleWinService.bat -i"
  Start-Service -DisplayName "Teamcenter DispatcherClient*"
  Start-Service -DisplayName "Teamcenter Dispatcher Module*"
}

-i/-c install, -r removes; install does not auto-start. Both scripts need the TC environment sourced first if you're calling them directly rather than through cmd /c against the whole batch file, which already does it internally. Matches a real Siemens KB for a different symptom (supportalorchestrationservice, missing jar) — same mechanism, "regenerate the service" is the documented fix there too.

3. Create the extract preference — and know the object-type ceiling first

TSTK-CreateTranslationRequest (the workflow handler, step 4) can only target a small, fixed set of OOTB dataset typesMISC, Text, and a handful of others (confirm the current list before relying on a count; get it from Siemens product documentation/support rather than assuming). It cannot target a custom or BMIDE-defined dataset type (Mdw0MDModel, or any site-authored type) — this is a real, confirmed product limitation, not a config gap you can preference your way around. Decide the source dataset type against that list before writing anything else.

Then, the preference: <Provider><Service>_ets_ds_types, literal case, no separator — e.g. SIEMENSmystage_ets_ds_types. SOA cannot write site-scope preferencessetPreferences2/setPreferencesDefinition report 200 OK and write nothing (or only the calling user's own scope). Use the CLI, and get the environment right or the exe dies at launch with STATUS_DLL_NOT_FOUND before it parses a single argument:

call C:\apps\PLM\tc_root\tc_menu\tc_Vanilla_Env.bat
cd /d C:\apps\PLM\tc_root\bin
preferences_manager.exe -u=infodba -p=infodba -g=dba -mode=import -scope=SITE ^
  -action=override -context=Teamcenter -file=C:\path\to\pref_import.xml

tem_init.bat is NOT this script — it sets three env vars and no PATH.

XML shape (get it right by exporting a working sibling preference first, not by hand-authoring from a guess):

<preferences version="10.0">
  <category name="MyPipeline">
    <preference name="SIEMENSmystage_ets_ds_types" type="String" array="true"
                disabled="false" protectionScope="Site" envEnabled="false">
      <preference_description>Dataset type(s) the mystage translator extracts from.</preference_description>
      <context name="Teamcenter"><value>MISC</value></context>
    </preference>
  </category>
</preferences>

Write it with [System.IO.File]::WriteAllText + a no-BOM UTF8EncodingSet-Content -Encoding UTF8 on Windows PowerShell 5.1 adds a BOM this importer rejects silently.

Verify with -mode=export, never trust the import's own stdout. A "the import operation has succeeded" message is not proof — the admin guide documents that preferences "not yet declared at the site level" are silently skipped, logged only to the run's own report file (C:\Temp\preferences_manager_<timestamp>.log), which is easy to never read. Read that report file. Then call Administration-2011-05-PreferenceManagement/refreshPreferences and confirm with getPreferences as a different user than the importer.

4. Bind the handler to a Do task

{"input":[{
  "clientID":"h1","handlerName":"TSTK-CreateTranslationRequest",
  "taskTemplate":"<EPMDoTaskTemplate uid>","businessRule":"","handlerType":"Action",
  "handlerToUpdate":"","action":4,
  "ruleQuorum":0,"changeExecutionOrder":0,
  "additionalData":{"-ProviderName":["SIEMENS"],"-ServiceName":["mystage"],
                     "-DatasetTypeName":["MISC"],"-Priority":["1"]}
}]}

(Workflow-2019-06-Workflow/createOrUpdateHandler; action is an integer — Assign 1, Start 2, Complete 4.)

  • Placement decides the pipeline semantics. Bind to Start if the job is fire-and-forget (the pipeline stage's own task completes immediately; the job runs in the background). Bind to Complete if you want the handler to fire only once the task is otherwise done.
  • The target must be an item revision, carrying a dataset of the declared type. One request per matching dataset. It refuses site-custom dataset types at the object level regardless of what the preference says (step 3).
  • Mandatory args: -ProviderName, -ServiceName, -DatasetTypeName. Optional: -Priority. (getSupportedHandlerArguments confirms this split live if it's ever in doubt on a given tier.)

5. Wire the "wait for the job" pattern, if the pipeline needs to block

Do task ("stage")
  ├─ Start action    : TSTK-CreateTranslationRequest
  └─ Complete action : EPM-hold          (Do tasks carry this by default)

EPM-hold checks task_result; while it isn't Completed, the task stays open. The translator itself performs the task's Complete action when it finishes — that's what makes the pipeline actually wait on the job rather than racing ahead. Siemens ships two integrations built exactly this way (DOCMGT-render-document-revision, CAE-simulation-process-launch-handler), so this is the supported pattern, not a workaround.

6. Fan-out / fan-in — running several stages in parallel with one gate

  • Fan-out: point several Do task templates' dependency_task_templates at the same predecessor, same action code (2, the normal predecessor→successor edge). Each gets its own marker dataset + handler, so each fires its own translation request independently once the predecessor completes.
  • Fan-in / gate: a Review task (or EPMOrTaskTemplate for a pure OR-join) whose predecessors are all the fan-out stages, same edge code 2. ★★★ Proven live, repeatedly, on TC 2606: this gate does not start until EVERY predecessor reaches Completed — not merely Started. Watched a review sit Pending through 0/3, 1/3, 2/3 predecessors complete, and flip to Started the instant the third did, timestamped to the second. The dependency-edge constant is literally named startDependencyTaskRef in the underlying PLMXML — don't let that name mislead you; behaviorally it waits for Complete.
  • Pass/fail routing: a task's dashed Fail path and an EPMOrTaskTemplate "Failure Accumulator" pattern let multiple independent failure sources (a rejected review, a failed condition, a stage that reports failure) converge on one terminal path — confirmed live via a real diagram capture: a rejected-review dashed edge and a failed-condition dashed edge landing on the identical downstream OR gate.
  • Rewiring dependency_task_templates on an already-built template is possible over SOA (createOrUpdateTemplate's additionalData, not PLMXML re-import) — useful for removing a stage or changing the graph after the fact without rebuilding from scratch. Full struct and the correction to an earlier "create-time only" claim are in tc-workflow-authoring.

Part 2 — The router pattern: one script, many pipeline stages

Registering a new translator.xml/Service.properties/preference triple per stage is real infrastructure work (Part 1, steps 1–3) each time. The router pattern trades that off: one Dispatcher service, one script, many stage behaviors selected by config, so adding a new pipeline stage becomes a config-file edit instead of a new service registration.

node dispatcher-entry.mjs --model <id> --rev-id <A> --name "<n>" --workdir <dir> \
                           --config <adapters.json>
  • dispatcher-entry.mjs reads --config's pipeline array and looks each name up in a fixed registry of stage functions (extract/resolve/checkin/verify/... — whatever your pipeline needs). Refuse an empty pipeline and refuse an unknown stage name outright rather than silently skipping — all([]) is true, so a pipeline with zero stages "succeeds" having done nothing, and a typo'd stage name silently dropped means the pipeline that ran isn't the one that was configured. Both are real, cheap mistakes to guard against explicitly.
  • Two Dispatcher services (two translator.xml entries, two Service.properties Prepare mappings, two Windows-service classpath regens) can point at the same script bytes, differing only in which adapters.json each service's translator.xml <TransExecutable> passes via --config. This is what makes "one router, N pipelines" actually true rather than aspirational — proven with three separate services sharing one dispatcher-entry.mjs.
  • Correlating which stage fired, without a custom clientoption: TSTK-CreateTranslationRequest makes the matched marker dataset the created request's primary object. The staged TranslationTask XML one level above the working directory carries <UserDefAttribute name="datasetname"> — read that, not keyValueArgs (which does not reach a translator as command-line options despite looking like it should — proven: a translator.xml <Options> block using optionkey receives empty values even with keyValueArgs populated). A router keyed off the marker's name lets several identically-shaped Do tasks (each with its own uniquely-named marker) all hit the same service and still behave differently per task.
  • Deliberate per-stage delay + pass/fail, for testing the pipeline shape itself: give the config a per-marker-name entry ({"delayMs": 12000, "fail": true}) and have the stage function setTimeout then throw/exit non-zero. This is how to prove staggered timing and a failure path actually propagate, independent of whatever real work a stage eventually does.

What "job success" looks like from the workflow's side

A Do task's own completion is a side effect of the handler firing on Start/Complete — it is NOT gated on the router script's actual exit code unless you deliberately wire it that way (Part 1, step 5, EPM-hold). Without EPM-hold, a Do task can show Completed in Teamcenter the instant the handler fires, while the actual job is still running (or has already failed) in the Dispatcher queue. If the pipeline needs "stage genuinely succeeded" to gate the next stage, EPM-hold + the job performing Complete itself (step 5) is the only mechanism that ties them together — read the task state alone and you're reading "the trigger fired," not "the job passed."

Verifying any of this actually ran — read the Dispatcher logs, not the workflow

getProperties on a DispatcherRequest returns almost nothing useful (current_state/provider_name/service_name don't exist as readable properties — see tc-dispatcher-requests for the queryDispatcherRequests workaround). The ground truth is on the Dispatcher host:

C:\apps\PLM\DSP\Logs\Dispatcher\task\<taskid>\<taskid>_dc.log

Grep for the service name across the newest task directories after a run (Get-ChildItem ... -Directory | Sort LastWriteTime -Descending). Real failure signatures to recognize immediately rather than re-diagnosing from scratch:

Log line contains Means
Cannot find task preparation class specified by service configuration for <Provider> <service> Provider/service casing mismatch, OR the Windows service's classpath predates a Service.properties change — check both before assuming a missing jar
Entering custom TaskPrep class for: ... TaskPrep@... with no following ERROR Class loaded and ran — a real signal of progress, check what follows
SoaHelper.getPreference(String)" is null (NullPointerException in BasicConfigHelper) The _ets_ds_types preference isn't resolving — re-check step 3, including the report-file trap
Dispatcher Module not available for Service: <name> Nothing has registered/regenerated the service on the Module side yet
Task = <id>, Status = Started Translation!!! then Completed successfully! The job genuinely ran and finished — this is what real success looks like, not a clean-looking Task Saved alone

Known-good vs still-open, as of this writing

Proven live, on real TC 2606 hardware: the fan-in gate behavior (Complete, not Start), the ProviderName casing fix, the Windows-service classpath regen fix, the router-serves-multiple-services pattern (three services, one script, confirmed via distinct translator.xml entries all resolving to the same .mjs file), and the site-preference CLI recipe including the no-BOM/env-script/report-file traps.

Still open, not proven end to end: a hand-registered custom service's _ets_ds_types preference resolving reliably at runtime — one instance of this got stuck with no root cause found despite a confirmed-correct database write, while a sibling service on the identical code path resolved fine. See tc-dispatcher-requests's dated entry for the full trail before re-deriving it. If you hit this, read the preferences_manager import report file before anything else — that lead was never actually exercised.

Related: tc-dispatcher-requests, tc-workflow-authoring, tc-soa-payload-shapes, tc-vm-operations, tc-verify-and-cleanup.


Generated from skills/tc-dispatcher-cicd-pipeline/SKILL.md in the tc-automation-skills library, which is the canonical copy and also serves as the agent skill set for Teamcenter work.