Building a Twin
Building a twin from a Teamcenter interface definition
The pipeline turns what Teamcenter records about a system's interfaces into a vsiBuild command file, with every assumption it had to make written down beside the output. Three scripts, in order; each one's output is a file you can read.
1. Extract the interface data (a Teamcenter read)
python extract_icd.py --profile vm2606 --item-range 005095 005142 --out wildfire-icd.json
Reads, strictly read-only and disconnecting when done, the interface objects
(Seg0Interface), the ports assigned to them (Seg0InterfaceAssignment, read per interface
with expandGRMRelationsForSecondary), the exchange allocations that give each interface
its signals (Seg0ExchangeAllocation to Seg0ItemFlow) and each port's direction
(fnd0Direction). Ports bind to interfaces through Seg0InterfaceAssignment, not through
Seg0Implements. The output JSON is the ICD the rest of the pipeline works from; the run
that produced Wildfire's saw 103 interfaces on the tier, selected 48, and found 464 of 476
ports carrying an assignment.
2. Find which logical block owns each port
python port_owners.py --icd wildfire-icd.json --out owners.json
Port ownership is BOM structure: each port is a line under the owning block revision's structure. The script opens a BOM window per block, expands all levels, and records the immediate owner of every port plus the block tree. Without this, a 152-port model becomes 152 components and vsiBuild refuses it (the cap is 64 including a slot the fabric keeps).
3. Generate the command file
python icd_to_vsi.py -i wildfire-icd.json -m gateway_map.json -o wildfire_grouped.vsi.cmd --twin WildfireDT --owners owners.json
Options that matter:
--ownersgroups ports under their owning block so one block is one component; ports of the same protocol on one block merge (Ethernet always, others when vsiBuild's one port per protocol per component rule requires it), and the merges are reported.--group-cap(default 63) is the component budget; the generator folds child blocks into parents to fit and lists what it folded.--python-blocks REGEXemits matching blocks as Python components (seepython-components). A matched block that carries an Ethernet port is kept C++ and the demotion is reported, because vsiBuild generates invalid Python for a component whose socket carries no connected signals.--exclude-portsand--exclude-blocksdrop by regex and list what they dropped; used for probe and duplicate objects on the MSP-2 tier.--workspacesets where vsiBuild will write the twin. Use a short root such asD:\wfpywhen any block is Python.
Every run writes OUT.assumptions.md next to the output. Read it before building. It
lists, per bus, what was derived rather than read: which port is the Ethernet hub and the
socket numbers chosen; that signals are typed int because Teamcenter records no type;
which signal directions were declared to satisfy connect signals although the recorded
direction said otherwise; frame layouts at 8 bits per signal; spokes omitted because both
ends landed on one merged port; and the blocks demoted from Python.
How protocols are decided
Teamcenter records no interface protocol. gateway_map.json maps interface class names to
VSI protocols and is deliberately a reviewable file rather than logic in code. The
generator prefers Seg0Implements on the port when it exists, falls back to the name rules
with a warning, and lists anything unresolved rather than dropping it. The grammar the
generator emits per protocol is in reference/command-grammar; the six places the vendor
PDF disagreed with the binary are marked there.
What comes out
For Wildfire: 63 components, 78 ports, 63 signal-definition lines, 118 TCP sockets in
hub-and-spoke form across 20 Ethernet buses, 22 CAN nodes on one frame id, and 30
connect signals lines across eight signal-wired buses. The file is the input to
running-and-controlling/build-and-run.
What the ICD does not carry, and how that shows up
- No signals cross Ethernet sockets, so 25 components are trace-only and the checker lists them as "nothing to check".
- No CAN bit layout, so frames are laid out 8 bits per signal in declaration order.
- No socket roles or port numbers, so the first port on a bus is the hub and numbers are handed out from 8800.
- No signal timing, so the glue's per-bus periods are a stimulus schedule, not the real rates.
Each of these is an open item against the Teamcenter model (roadmap rm-xq7x89ob), not against the generator, which reports them rather than inventing values.
Source: innexis-vsi/extract_icd.py, port_owners.py, icd_to_vsi.py, gateway_map.json, README.md (EXERCISED 2026-08-20 to 2026-09-08) · retrieved Tue Sep 08 2026 00:00:00 GMT+0000 (Coordinated Universal Time)