Skip to content

Latest commit

 

History

History
947 lines (819 loc) · 68.7 KB

File metadata and controls

947 lines (819 loc) · 68.7 KB

OpenSysML MDK: a Cameo Systems Modeler plugin with OpenSysML as the execution engine

Date: 2026-09-20 Status: Discovery and design, then built — editors/mdk/ is the plugin (first proposed as editors/cameo/, renamed when it took the OpenSysML MDK name; §1.2) Scope: the editors/mdk/ plugin; the Java client (client/java/opensysml-client); Migrate (internal/translate/xmi, internal/translate/migrate); the results a Cameo user sees on their own elements


1. What this is

Cameo Systems Modeler (and MagicDraw, Magic Cyber Systems Engineer and Magic Systems of Systems Architect, which share one platform and one OpenAPI) is where a large share of SysML v1 models live. OpenSysML already reads those models — OMG UML XMI 2.5 with the SysML profile, and a MagicDraw/Cameo .mdzip opened in place — and migrates them to SysML v2 notation (docs/reference/sysml-v1-migration.md); it runs, verifies and analyzes the result over sysml-grpc, and the Java client wraps every RPC (docs/reference/java-api.md). The missing piece is the plugin that lets a Cameo user select a package, choose Run with OpenSysML, and read the verdicts on their own elements.

This note answers the discovery questions a plugin design depends on, from the vendor's public documentation and Javadoc and from a migration benchmark run over the models that are publicly available, and then proposes the plugin. Every external claim carries the URL it was read from. Claims that could not be confirmed from a public page are marked unverified. No Cameo installation was available: nothing here was run against the tool.

1.1 The release this targets

The vendor's current documentation site describes 2026x Refresh1 as the latest release of every CATIA Magic / No Magic product, released on June 26, 2026 (version news). The last release of the previous line is 2024x Refresh3 (2024x Refresh3 version news). The two lines differ in the JDK they ship:

Release Bundled / recommended JDK Source
2026x Refresh1 Eclipse Temurin (AdoptOpenJDK) 21.0.10+7, HotSpot, all OSs Java version support, 2026x Refresh1
2026x Temurin 21.0.8+9 HotSpot, all OSs Java version support, 2026x
2024x Refresh3 Temurin 17.0.14 HotSpot Java version support for 2024x Refresh3

Pin: the plugin targets Cameo Systems Modeler 2026x Refresh1 with its bundled Eclipse Temurin 21.0.10+7 HotSpot — the latest publicly documented release. Every OpenAPI class the design relies on (§2–§6, §8) is cited from the 2026x Refresh1 Javadoc (https://jdocs.nomagic.com/2026xRefresh1/) or the current developer guide, and every "no such OpenAPI class exists" statement below was checked against the 2026x Refresh1 class index (https://jdocs.nomagic.com/2026xRefresh1/allclasses-index.html).

JDK. The OpenSysML Java client is built to a JDK 17 baseline and documents that on JDK 21 the same code runs unchanged (docs/reference/java-api.md); a JDK 21 host adds nothing the client needs and removes nothing it uses. The plugin is compiled with --release 17, so the one jar loads on the pinned JDK 21 and on the JDK 17 of the minimum release below. It must not use JDK 21 language or library features until the minimum moves.

Minimum supported: 2024x Refresh3 (Temurin 17.0.14), the last release of the previous line. The v1 path (§2–§6) uses only OpenAPI classes that the 2024x Refresh3 Javadoc (https://jdocs.nomagic.com/2024xRefresh3/) also lists — each such class is named where it matters — so the same jar runs there; the SysML v2 path (§8) uses classes that exist only in 2026x and is absent on 2024x Refresh3 by design (§10.1).

2. Plugin mechanics

2.1 Descriptor: plugin.xml

A plugin is a directory under the tool's plugins/ folder holding a plugin.xml descriptor, its jar(s) and any libraries (Plugin descriptor, current developer guide; the 2024x Refresh2 page, here, tabulates the attributes and is unchanged in substance). The fields the design uses:

Element / attribute Meaning (from the descriptor page)
plugin id, name, version, provider-name identity shown in the Resource/Plugin Manager
plugin class the fully qualified com.nomagic.magicdraw.plugins.Plugin subclass loaded by the plugin manager
plugin internalVersion integer compared when the same plugin is installed twice
requires-api the OpenAPI version the plugin needs; requires-api="1.0" in the vendor examples
requires / requires-plugin other plugins that must be loaded first (the SysML plugin, for a plugin that reads SysML stereotypes)
runtime / library name="…jar" the jars the plugin's classloader sees
ownClassloader="true" give this plugin its own classloader (default false)
class-lookup="LocalFirst" with ownClassloader, prefer the plugin's copies of classes over the tool's

PluginDescriptor exposes the same data at run time (PluginDescriptor, 2026x Refresh1; also in 2024x Refresh3): notably getPluginDirectory(), which is how the plugin finds the native sysml-grpc binary it ships (§2.4).

2.2 Lifecycle: com.nomagic.magicdraw.plugins.Plugin

The abstract class has three methods (Plugin, 2026x Refresh1; Plugin classes, current developer guide):

  • isSupported() — called first; the plugin is initialized only if it returns true. The OpenSysML plugin returns false when no sysml-grpc binary for the host platform is available (§2.4), with a log line saying why, rather than failing later.
  • init() — called at tool start-up; where action configurators, project windows and listeners are registered. It must not start the sysml-grpc child: the Java client starts one lazily on first Connection use, and an idle child at every Cameo start is what a user would notice. init() only wires UI.
  • close() — called before exit; returning false vetoes exit. The plugin closes its Connections and calls Connection.stopSharedServices(), which ends the child (§12), and returns true.

ResourceDependentPlugin is a second interface for plugins that own a profile the project depends on (Javadoc). The OpenSysML plugin writes nothing into the model (§6), so it does not implement it.

2.3 Class loading — the one-child-per-classloader consequence

All modeling tool plugins (classes and runtime libraries) are loaded by the one classloader. If there are plugins that cannot be loaded by the same classloader … their descriptors should be defined to use own classloaders. — Plugin class loading, 2026x

So by default every plugin shares one classloader. The Java client starts one private sysml-grpc child per classloader (docs/reference/java-api.md, "The service binary"). Two consequences:

  1. If two plugins each shade the OpenSysML client into the shared classloader, the second copy's classes collide with the first's — a plain Java problem, not an OpenSysML one. If they share one copy on the plugin classpath, they share one child and one parse cache.
  2. The OpenSysML plugin should set ownClassloader="true" and class-lookup="LocalFirst". Its dependencies (the client, its protobuf-JSON bodies) then cannot conflict with the tool's or other plugins' versions, and the classloader boundary makes the child's ownership explicit: one plugin, one classloader, one child, closed in Plugin.close().

The tool's own PluginUtils.getPlugins() lists loaded plugins (Javadoc), which is how a future second OpenSysML-based plugin could find and reuse this one's connection instead of starting a child of its own.

2.4 Native binaries per platform

Nothing in the vendor's plugin documentation addresses native executables; a plugin directory is an ordinary directory, so the plugin can lay out binaries however it likes and locate them from PluginDescriptor.getPluginDirectory(). The Java client resolves the service binary in this order: ConnectionOptions.binaryPath(...), then $OPENSYSML_GRPC_BINARY, then ~/.opensysml/bin/sysml-grpc (downloading a release pinned by version and verifying its signed manifest when absent), then $PATH (docs/reference/java-api.md). The release artifacts are named sysml-grpc-<goos>-<goarch>[.exe] for linux, darwin and windows (client/java/opensysml-client/src/main/java/org/openmbee/opensysml/internal/ReleasePlatform.java).

Design: the plugin ships all platform binaries under <plugin dir>/bin/ and passes the one for the host through ConnectionOptions.binaryPath(...), falling back to the client's download path only when bin/ is absent (a "thin" build for users whose IT forbids bundled executables but allows the signed download). Bundling all platforms keeps one Resource Manager .zip (§2.5) for every OS; the vendor ships one .zip per resource, not per OS.

Two platform facts matter and are recorded as risks (§12): the Java client itself drops the POSIX execute bit on filesystems that keep none ("Windows and some network filesystems keep no POSIX mode", BinaryDownloader.java), and a zip extracted by the Resource Manager may not preserve modes on macOS/Linux — the plugin must chmod +x (Java Files.setPosixFilePermissions) before first launch. On macOS, an executable extracted from a downloaded zip carries the quarantine attribute and Gatekeeper may refuse it unless it is notarized — unverified for this specific path, as the vendor documents nothing about it; the release binary is sigstore-signed but not Apple-notarized.

2.5 Distribution: Resource Manager .zip and descriptor

Plugins are distributed as a zip whose internal layout mirrors the tool's installation directory (plugins/<id>/plugin.xml, plugins/<id>/*.jar) plus a resource-manager descriptor at data/resourcemanager/MDR_Plugin_<id>_<n>_descriptor.xml; the Resource/Plugin Manager (Help ▸ Resource/Plugin Manager) installs it from a local file, a network share or a web server, and "supports zip archives only" (How to distribute resources, 2024x Refresh1; Creating required files and folders structure, 2024x Refresh2; Resource Manager, 2024x). The vendor also offers a Resource Builder wizard (Tools ▸ Development Tools ▸ Build Custom Resource…) that assembles the same zip. The editors/mdk/ build produces this zip directly (§8.2), the same way editors/vscode produces a .vsix, and the nightly attaches it beside the .vsix (docs/project/nightly.md).

3. UI contribution points

Every class in this section is in the 2026x Refresh1 Javadoc. ActionsProvider, ActionsConfiguratorsManager, BrowserContextAMConfigurator, DiagramContextAMConfigurator, ProjectWindow and RunnableWithProgress are also listed in the 2024x Refresh3 index (https://jdocs.nomagic.com/2024xRefresh3/allclasses-index.html), which is what the minimum release of §1.1 rests on.

Context menus. Actions live in ActionsManagers configured by configurators registered with ActionsConfiguratorsManager from Plugin.init(). Three interfaces matter (Creating new actions, 2024x Refresh2; ActionsConfiguratorsManager, 2026x Refresh1):

  • BrowserContextAMConfigurator.configure(ActionsManager, Tree) — the containment-tree shortcut menu; registered with addContainmentBrowserContextConfigurator (BrowserContextAMConfigurator, 2026x Refresh1). The Tree gives the selected nodes, so Run with OpenSysML is offered on a Package, Class (a Block), a Behavior or a Constraint/Requirement.
  • DiagramContextAMConfigurator.configure(ActionsManager, DiagramPresentationElement, PresentationElement[], PresentationElement) — a diagram's shortcut menu with the selected symbols; registered per diagram type with addDiagramContextConfigurator(String diagramType, …) (DiagramContextAMConfigurator, 2026x Refresh1).
  • AMConfigurator — main menu and toolbars (addMainMenuConfigurator), for a Tools ▸ OpenSysML menu with Run…, Verify…, Sweep… and Show results.

Actions are MDAction/DefaultBrowserAction/DefaultDiagramAction subclasses added to an MDActionsCategory; the category, not the action, is what the configurator adds.

ActionsProvider is the other side of the same mechanism: "the singleton class used for accessing actions in different parts (diagrams, browsers, main menu and etc.)", with getContainmentBrowserContextActions(BrowserTabTree), getDiagramContextActions(String diagramType, DiagramPresentationElement, PresentationElement[], PresentationElement) and getDiagramShortcutActions(...) (ActionsProvider, 2026x Refresh1). It reads the configured managers; a plugin contributes through the configurators above and only needs ActionsProvider to invoke or inspect an existing action (for example, to run the tool's own Validate after the results are in). The design registers configurators and does not call ActionsProvider directly.

Docking results panel. ProjectWindowsManager (from Application.getInstance().getMainFrame().getProjectWindowsManager()) adds a ProjectWindow — a Swing component described by a WindowComponentInfo (id, name, icon, side, docking state) — to the active project, and ProjectWindowsManager.ConfiguratorRegistry.addConfigurator(...) from init() makes its docking state persist with the project (ProjectWindowsManager, 2026x Refresh1; ProjectWindow, 2026x Refresh1; ProjectWindowsConfigurator, 2026x Refresh1). This is where the OpenSysML Results table (§6) lives, beside the tool's own Validation Results window. GUILog (Application.getInstance().getGUILog()) is the message/notification window for one-line status and hyperlinks (GUILog).

Progress and cancellation. ProgressStatusRunner.runWithProgressStatus(RunnableWithProgress, String description, boolean allowCancel, int millisToShow) runs a task with the tool's progress dialog; the runnable receives a ProgressStatus and is expected to poll isCancel() (ProgressStatusRunner, 2026x Refresh1; RunnableWithProgress, 2026x Refresh1). The RPCs are unary, so "cancel" means: stop waiting, discard the answer when it arrives, and — for a run that will not return — stop the child and start over. The client has one deadline per connection: ConnectionOptions.requestTimeout (default 60 s) applies to every RPC made on that Connection, and no call takes a deadline of its own (client/java/opensysml-client/src/main/java/org/openmbee/opensysml/ConnectionOptions.java; docs/reference/java-api.md). A single connection therefore cannot give Migrate thirty seconds and a run ten minutes, and a cancelled Migrate on a ten-minute connection blocks for ten minutes. The design opens two connections in the plugin's classloader with different timeouts: a short one for Migrate and ParseSources, a long one for execution and verification. Both share the one private child, so the model the short connection parsed is adopted on the long one by hash (connection.model(model.hash())) without a second parse. Because the child is reference-counted across connections (ServiceRegistry.release stops it only when the last connection closes), closing the long connection alone would leave the child — and the run — alive under the short one's reference; so the one cancellation path is: call Connection.stopSharedServices(), close both Connection objects, and recreate both together before the next operation, which pays the child start and the parse again. A per-call deadline in the Java client would collapse the two connections into one and is listed as a phase 2 prerequisite (§11). A finer cancel — a streaming or session RPC — is the surface-parity note's session API (api-surface-parity.md), not this plugin's to invent.

Diagram highlighting (for step-debug later). Two mechanisms exist. Annotations (com.nomagic.magicdraw.annotation.Annotation, AnnotationManager) attach a severity, kind, text and actions to a BaseElement or a PresentationElement; they are runtime-only ("not stored in the project"), the manager "takes care of drawing decorations around symbols with annotations", and the caller must update() after adding or removing them and remove them afterwards (Annotation, 2026x Refresh1; AnnotationManager, 2026x Refresh1). A custom AnnotationPainter (Annotation.addPainter) can draw the decoration itself. This is enough to mark "current state", "fired transition" and "failed constraint" on an open diagram without touching the model. The second mechanism — the Simulation Toolkit's own animation of active states and tokens — is not in the OpenAPI class index (its public classes are the SimulationProfile stereotype constants and SimulationManager/SimulationHelper, which drive its engine); reusing that animation for a foreign engine is unverified and assumed unavailable.

4. Getting the model out: in-memory XMI, or .mdzip

4.1 What the OpenAPI offers

The user-facing exporters are File ▸ Export To ▸ UML XMI 2.5 file, Eclipse UML2 (v2–v5) XMI, MOF XMI, EMF Ecore and MagicDraw Native XML (Exporting UML models, 2024x Refresh2). Of these, only the Eclipse UML2 exporter is in the OpenAPI: BaseEmfUml2XmiPlugin.exportXMI(Project, String destinationDir[, ProgressStatus]) and exportModel(Project) on the versioned EmfUml2XmiPlugin singletons (BaseEmfUml2XmiPlugin, 2026x Refresh1; v4 EmfUml2XmiPlugin). It writes to a directory, not to memory, and it writes the Eclipse UML2 dialect — which OpenSysML reads (Papyrus .uml), but with the SysML profile in the Eclipse namespace and the whole project, not a selection. No OpenAPI entry point for the "UML XMI 2.5 file" exporter was found in the 2026x Refresh1 class index — the com.nomagic.magicdraw.export package there holds image export only, and com.nomagic.persistence.XmiExporterDescription describes a format's version and required resources rather than performing an export. Treat "export selected package as OMG XMI 2.5 without a dialog" as unverified / not available.

ProjectsManager (Application.getInstance().getProjectsManager()) offers what the native format needs (ProjectsManager, 2026x Refresh1):

  • saveProject(ProjectDescriptor, boolean silent) — saves the project to the descriptor's location; ProjectDescriptorsFactory.createLocalProjectDescriptor(Project, File) makes a descriptor for an arbitrary file (ProjectDescriptorsFactory).
  • exportModule(Project, Collection<Package> packages, String description, ProjectDescriptor) — "Export local (not teamwork) module into given descriptor": a subset of packages written as a .mdzip of its own.

Whether saveProject to a different file re-points the open project at that file (as "Save As" does) is unverified; exportModule does not have that problem and is the primary route for a selection.

4.2 Decision: .mdzip on disk is the reliable route

OpenSysML opens a .mdzip in place and reads its com.nomagic.magicdraw.uml_model.model entries as one document; profile-application and stereotype classification only trust the OMG namespaces (http://www.omg.org/spec/UML/…, …/SysML/…) and Papyrus's (docs/reference/sysml-v1-migration.md). The benchmark (§9) settles the namespace question empirically: DocGen.mdzip, a Cameo project saved by the tool, yields 810 mapped and 388 approximated entries out of 1907 — Blocks, value properties, requirements and constraints are recognized — so the native .mdzip carries SysML stereotype applications in the OMG namespace, as the vendor's save format is itself XMI with the standard profiles. No in-memory step is needed, and the export the plugin performs is:

selection (packages)  → ProjectsManager.exportModule(project, packages, "OpenSysML run", tmp.mdzip)
whole project         → ProjectsManager.saveProject(createLocalProjectDescriptor(project, tmp.mdzip), true)
                        (or, when the project is already saved locally and clean, its own file)
tmp.mdzip             → Connection.migrateFile(tmp.mdzip, "sysml")        (file_path over the wire)

MigrateRequest.file_path is read by the sysml-grpc child, which runs on the same machine as Cameo, so a temporary file is enough. Inline content with from_format: "xmi" is also accepted by the service (a conformance case parses vehicle.xmi inline), and is the route for an XMI text a future exporter API produces — but a .mdzip is a zip and content is a string, so the archive goes by path.

Two limitations carry over from the migration reference: elements of used projects (modules) are not loaded, so references into them stay unmapped — the plugin should export the used modules too and pass every file to Migrate once the service accepts several sources for one migration (§11, phase 3); and Teamwork Cloud projects have no local .mdzip, so saveProject to a local descriptor (or exportModule) is the only route for them.

5. Element identity: from a Cameo element to a v2 qualified name

Cameo side. Every BaseElement has getID(), the persistent element ID that is the xmi:id in the saved project; Element.getHumanName() and getQualifiedName() on NamedElement give the display and qualified names. (The Javadoc for BaseElement.getID() was not fetched; that getID() is the persisted xmi:id is the vendor's long-standing contract and is verified only indirectly here — the .mdzip fixtures under tests/migrate/testdata/xmi and the benchmark archives carry xmi:id values in MagicDraw's _18_0_… form, and the report records them as id.)

OpenSysML side. The migration report is the per-element accounting (internal/translate/migrate/report.go): one Entry per source element with

field content
id the source xmi:id
kind the applied stereotype(s) or UML metaclass, e.g. «Block» Class
name the source qualified name
target the v2 qualified name the conversion wrote, when it wrote one
verdict mapped, approximated, unmapped, skipped
note why, for anything but mapped

with Report.Source, an optional Exporter (read from xmi:Documentation), and Count(), Unreferenced() and Summary() over the entries. id → target is exactly the map the plugin needs: a verdict OpenSysML reports on Demo::Vehicle::massLight is looked up by target, and the entry's id is the Cameo element to annotate. Elements the conversion renamed (duplicate member names, reserved words — the migration reference lists the cases as approximated) are still found this way, which is why the plugin must never reconstruct the name itself.

What the service returns today. MigrateResponse carries the report: its summary and counts always, and every entry (id, kind, name, target, verdict, note) when MigrateRequest.report is set (docs/reference/sysml-v1-migration.md; api/proto/sysml.proto, MigrationReport). The Java client hands it over as Migration.report(). The plugin surfaces the summary as a diagnostic of the run but does not yet read the entries, so it still matches by name and loses renamed elements; §11 makes reading them the prerequisite of the results UI's identity map.

6. Reporting results on the elements

Three vendor mechanisms, used together:

  1. Annotations (§3) — a runtime Annotation(severity, kind, text, target) per verdict, AnnotationManager.update(removed, added), and the tool draws the decoration on every diagram symbol of the element and in the browser. Severity is an EnumerationLiteral of the tool's severity enumeration (Annotation.ERROR/WARNING/INFO name the kinds); a failed assert constraint is an error, a requirement not satisfied a warning, a holding verdict an info that is off by default. Annotations carry NMActions, so each decoration offers Show in OpenSysML Results and Re-run.
  2. The Validation Results window — ValidationHelper.openValidationWindow(ValidationRunData, String windowID, Collection<RuleViolationResult>) "opens validation window and displays RuleViolationResult in it" (ValidationHelper, 2026x Refresh1; ValidationRunData, 2026x Refresh1). A RuleViolationResult pairs an Annotation with the Constraint (a validation rule) it violates, so this route needs a rule element in the model — an OpenSysML validation suite profile with one rule per verdict kind (constraint failed, requirement unsatisfied, run error) shipped as a read-only module. It buys the tool's own grouping, filtering, and "select in browser/diagram" for free. (com.nomagic.reportwizard.tools.validation.ValidationResult is the Report Wizard's object, not this one.)
  3. A custom results panel (§3) — the ProjectWindow table with columns the validation window lacks: engine (run, explore, solve, sweep), answer strength, schedule seed, replay command, sweep row; double-click selects the element (SelectionUtilities/ Application.getInstance().getMainFrame().getBrowser()), and Copy as sysml command gives the replayable CLI line.

Verdicts arrive from the Java client as Verification (constraint/requirement, with verdict, bounded, the checked object's instancePath), Satisfaction (every assert satisfy), Validation (every constraint of one object) and Sweep rows (docs/reference/java-api.md, Verification and Parameter sweeps). Each names the v2 element; §5 maps it back.

7. Positioning against the Cameo Simulation Toolkit

7.1 What the Simulation Toolkit covers

Magic Model Analyst / Cameo Simulation Toolkit is "an extendable model execution framework based on OMG fUML and W3C SCXML standards" that executes, animates and debugs SysML models, including parametrics, with mock-up user interfaces (documentation home, 2026x). Specifically:

  • fUML 1.3 activity semantics, with the action kinds it supports enumerated (Activity simulation engine, 2026x).
  • State machines on W3C SCXML semantics (per the home page above). PSSM conformance is not claimed on any page found; treat "PSSM" as unverified for the Toolkit. PSCS conformance likewise unverified; composite-structure behavior (ports, connectors, flows) is simulated, but no page found names the PSCS specification.
  • Parametrics: a built-in math solver (Octave-like syntax) as the default parametric evaluator, plus external evaluators — MATLAB, Mathematica, Dymola — and scripting languages (Built-in Math; Integration with external Evaluators, 2026x; Specifying the language for the expression). The solver evaluates; it does not search for values that satisfy a constraint set.
  • Alf comes from the separate Alf Plugin, which compiles Alf to fUML activities that the Toolkit executes ("Full Conformance" level) (Alf Plugin, 2026x; Running a model with Alf, 2024x Refresh3).
  • Recent additions are the HTML UI, the Result Player, server-side simulation, and Modelica export; "certain outdated integrations have been discontinued" in 2026x (2026x version news for the Toolkit).

7.2 What OpenSysML adds, and what it does not replace

The Toolkit is the interactive simulator of the SysML v1 model as drawn: animation, mock-up UIs, external solvers, timelines. OpenSysML executes the v2 model that the migration (or, for v2 projects, the textual export — §8) produces, and adds what the Toolkit has no counterpart for:

OpenSysML capability Where it is described Toolkit counterpart
Deterministic, replayable schedules — every unordered choice recorded as a choice point; declared / seed:<n> replay; explore enumerates every linearization within a budget scheduling.md, region-order-scheduling.md none: one interactive run at a time; no page found describes replaying a schedule
Batch verification — VerifyConstraint, VerifyRequirement, VerifySatisfaction, ValidateInstance over a whole model in one call, no UI api/proto/sysml.proto; java-api.md validation suites check well-formedness, not requirement satisfaction under execution
RunSweep — a parameter grid or sample, one row per run with inputs, outputs and verdicts java-api.md, Parameter sweeps none found (the Toolkit runs one configuration; trade studies are a separate product)
SMT-backed constraint solving — the solve engine finds values satisfying a constraint set, and the bounded model checkers ask a solver whether any schedule violates a requirement analysis-framework.md, smt-model-checking.md the parametric evaluators compute a value from given inputs; they do not solve for unknowns
RDF export — Convert to Turtle; OSLC-shaped Query docs/reference/rdf-mapping.md none
Answer strength — proved / bounded / witnessed / observed / not covered on every result analysis-framework.md none

What OpenSysML does not offer and the plugin must not pretend to: diagram animation of the v1 model, mock-up UIs, MATLAB/Mathematica/Dymola evaluators, and Alf. A user with a Toolkit-dependent model keeps using the Toolkit; the OpenSysML plugin sits beside it for batch, sweep, solve and replay.

8. SysML v2 in Cameo — the pinned release has it, and it changes the plugin

The pinned release, 2026x Refresh1, ships a SysML v2 Plugin with a SysML v2 project type, a textual editor and two-way synchronization between text and diagrams, a SysML v2 Evaluation Plugin for static evaluation, and a free Community Edition capped at 500 elements (SysML v2 Plugin documentation, 2026x; CATIA Magic/Cameo SysML v2 Solution). SysML v1 and v2 are chosen per project, in one installation (same page). Concretely:

  • Textual import/export in the UI: File ▸ Export To ▸ SysML v2 Textual Notation writes selected root namespaces as .sysml files — "You can export SysML v2 project namespaces into .sysml textual notation files which you can later import into your projects" — and File ▸ Import From ▸ SysML v2 Textual Notation imports a .sysml file into a separate root namespace (Textual notation import/export, 2026x Refresh1).
  • Textual import/export in the OpenAPI (2026x Refresh1): SysMLTextualNotationService.exportTextual(Namespace) → String and importTextual(ModelElementProject, String) (Javadoc), and SysMLProjectHelper to create or open v2 ("UPS") projects (Javadoc). The v2 metamodel is a separate API (com.dassault_systemes.modeler.kerml.model.kerml.Namespace, the com.dassault_systemes.modeler.sysml.libraries.standard.* library classes), not the UML Element tree.
  • Their own v1→v2 transformation (File ▸ Export To ▸ SysML v2 Model) is "a work in progress": the 2026x Refresh1 page says it covers "over 80% of the SysML v1 to SysML v2 Transformation specification", where the 2026x page said "about 20% of the metamodel"; it writes an .xlsx of not-migrated elements and does not migrate diagrams (Performing SysML v1 to v2 model transformation, 2026x Refresh1; Migration from SysML v1 to SysML v2, 2026x). The two percentages measure different things (a specification's clauses versus the metamodel) and are not comparable with each other or with the benchmark in §9.

Consequence. On the pinned release the plugin has two front ends and one engine:

Project kind How the model reaches OpenSysML Identity map
SysML v1 .mdzip → Migrate(xmi→sysml) → ParseSources (§4) migration report id → target (§5)
SysML v2 (SysML v2 Plugin installed) SysMLTextualNotationService.exportTextual(root) → ParseSources — no migration v2 qualified names are the same on both sides; OpenSysML's Symbol answers carry them

For v2 projects the vendor's parser is the one the user authored against and OpenSysML is a second parser and the execution engine of the same text; disagreements between the two parsers are themselves findings (the Diagnostics from ParseSources land in the results panel). Whether exportTextual emits element IDs as comments or @id metadata that would give a stronger identity than names is unverified.

A third route exists for v1 projects on the pinned release — the vendor's own transformation to a v2 project, then exportTextual — and the design does not take it as the primary path: the transformation is a user-driven export that produces a second project, its not-migrated list is an .xlsx rather than a per-element map, and nothing ties a v2 element it creates back to the xmi:id of the v1 element, so verdicts could not land on the user's v1 elements (§5–§6). OpenSysML's own migration keeps that map. The vendor route is offered as an opt-in ("Run the SysML v2 project instead") for users who have already transformed, and its coverage is compared with §9 in the first licensed run (§10.3).

On the minimum release, 2024x Refresh3, no SysML v2 project type or .sysml import was found: its version news (2024x Refresh3 Version News) announces none, and its Javadoc index lists no com.dassault_systemes.modeler.magic.sysml.textual or .core package, only a handful of diagram classes under com.dassault_systemes.modeler.magic. Treat "2024x Refresh3 has no SysML v2 support" as unverified (not found) rather than established. There OpenSysML is the only SysML v2 parser the plugin has and every model reaches it through the v1 migration (§4–§5). Because the v2 front end's vendor types are absent on the minimum release, it is a separate module loaded by reflection or a second plugin (§10.1), and its isSupported() checks for the SysML v2 Plugin.

9. Migration benchmark

Question. Is Migrate from v1 XMI/.mdzip good enough to be the plugin's primary path for v1 projects?

Method. OpenSysML built with make build. Twenty models were gathered: the ten fixtures under tests/migrate/testdata/xmi/, seven Papyrus SysML 1.1 test models (github.com/bmaggi/Papyrus-SysML11, tests/…/model/*.uml and samples/*.uml), two Cameo .mdzip projects from Open-MBEE's MDK (github.com/Open-MBEE/exec-cameo-mdk: src/main/dist/samples/MDK/DocGen.mdzip, src/test/resources/CSyncTest.mdzip), and the OMG SysML 1.6 profile itself (https://www.omg.org/spec/SysML/20181001/SysML.xmi; a profile, not a user model, included as the only OMG XMI artifact that resolved). For each:

bin/sysml <model> -migrate sysml -o <out>.sysml -migration-report <out>.report.json
bin/sysml <out>.sysml -validate

-validate is the CLI's check-and-exit flag (bin/sysml -h; there is no -check flag). Diagnostics were counted as lines matching error: and warning: case-insensitively, excluding the no errors summary. Attempts that did not resolve, so the set is what it is: github.com/eclipse-papyrus/org.eclipse.papyrus-sysml16 and …-sysml11 (404), github.com/Open-MBEE/mms-test (404), the bmaggi/SysML14-Gendoc-Example model (listed by the API, raw download 404), https://www.omg.org/spec/SysML/1.6/SysML.xmi (404). No vendor sample .mdzip is downloadable without an installation.

Results. Every migration and every validation exited 0; no panics, no error: diagnostics on any migrated model.

file source entries mapped approx. unmapped skipped check errors check warnings top unmapped kinds
acquisition.xmi repo fixture 65 52 5 6 2 0 0 DurationObservation (6)
heater_receptions.xmi repo fixture 81 69 10 2 0 0 0 Parameter, Reception
meter.xmi repo fixture 130 97 33 0 0 0 0 —
plant.xmi repo fixture 62 56 6 0 0 0 0 —
plant_states.xmi repo fixture 112 103 5 3 1 0 3 TimeEvent, Trigger
ported_calls.xmi repo fixture 53 45 8 0 0 0 0 —
reactor.xmi repo fixture 79 56 23 0 0 0 0 —
rig_interactions.xmi repo fixture 88 75 6 6 1 0 0 Interaction (3), MessageOccurrenceSpecification (2)
simconfig.xmi repo fixture 38 30 6 2 0 0 0 Slot (2)
vehicle.xmi repo fixture (exporter "Example UML Tool") 95 78 11 3 3 0 2 «Unit», «QuantityKind» InstanceSpecification
DocGen.mdzip Cameo, Open-MBEE MDK 1907 810 388 336 373 0 28 Constraint (123), «Viewpoint» Class (116)
CSyncTest.mdzip Cameo, Open-MBEE MDK 9 6 2 0 1 0 0 —
SysML_Allocate_TEST.uml Papyrus 1.1 12 1 8 0 3 0 0 —
SysML_DeriveReqt_TEST.uml Papyrus 1.1 12 1 8 0 3 0 0 —
SysML_Satisfy_TEST.uml Papyrus 1.1 13 1 9 0 3 0 0 —
SysML_Verify_TEST.uml Papyrus 1.1 16 1 5 7 3 0 0 «TestCase» (4), «Verify» Abstraction (3)
ModelWithBDD.uml Papyrus 1.1 12 1 0 0 11 0 0 —
ModelWithIBD.uml Papyrus 1.1 13 1 1 0 11 0 0 —
ModelWithPD.uml Papyrus 1.1 14 1 2 0 11 0 0 —
SysML.xmi OMG profile 2 0 0 1 1 0 0 Tag

Reading it.

  • On Cameo's own format the classifier works. DocGen.mdzip — a real, 1907-entry Cameo project — converts with 63 % of entries mapped or approximated and validates with no errors. Its unmapped entries are dominated by two kinds that are out of scope for execution: 122 Constraints whose specification is a UML Expression tree ("a UML Expression tree has no v2 form") and 116 «Viewpoint» classes plus «Expose» dependencies ("viewpoints are not migrated yet") — DocGen is a document-generation profile, not a system model. The 28 warnings are all Duplicate of inherited member name from generalizations the migration kept. The 373 skipped entries are profile applications, imports and notation, as the report's Unreferenced() separates them.
  • On system models, the fixtures, 80–95 % of entries map cleanly, the remainder are approximated with a note (return parameters as out, untyped properties as reference usages, partitions as comments), and the unmapped kinds are exactly what the migration reference lists as not yet migrated: interactions, duration observations, time events, units. The three plant_states warnings are OpenSysML's own extension notice for history/junction pseudostates it wrote (pseudostates.md) — a fact about the target notation, not a migration loss.
  • The Papyrus 1.1 rows are a namespace finding, not a migration finding. Those files declare xmlns:uml="http://www.eclipse.org/uml2/3.0.0/UML" and the 2010-era SysML profile namespace, which the reader does not recognize (it accepts the OMG namespaces and Papyrus SysML 1.6's); every element but the root is skipped as an unrecognized profile application. Papyrus 1.6 samples would have been the fair test; none resolved. A Cameo plugin never sees this dialect.
  • Units are not migrated («Unit»/«QuantityKind» instance specifications in vehicle.xmi), as the migration reference states; a v1 model whose constraints depend on unit conversion evaluates differently until they are.

Verdict. For a v1 system model saved by Cameo, Migrate is good enough to be the primary path: it never fails, every element is accounted for with a verdict and a note, and what is lost (interactions, viewpoints, units, expression-tree constraints) is nameable and shown to the user per element rather than silently dropped. The plugin must present approximated and unmapped counts before the first run (the pre-flight in §10.4) so the user knows what the engine did not see; that is the report-over-service prerequisite of §11, phase 3. The benchmark's weakness is its sample — two Cameo projects, neither a behavioral system model; the plugin's first integration test against a licensed Cameo (§10.3) should migrate the vendor's bundled samples (samples/SysML/*.mdzip in an installation) and re-run this table.

10. Architecture of editors/mdk/

10.1 Module layout

editors/mdk/
  README.md                       build, licence-free CI, model paths, distribution, the DocGen bridge
  pom.xml                         Maven reactor; Java 17; pins the OpenSysML client version
  plugin/                         the plugin — compiles with --release 17 against the 2026x Refresh1
                                  OpenAPI, using only classes also present in 2024x Refresh3 (§1.1)
    src/main/java/org/openmbee/opensysml/mdk/
      OpenSysMLPlugin.java            Plugin: isSupported / init / close; the docGen facade (§14)
      actions/                        OperationActions configurator, one MDAction per Operation,
                                      CalcArguments, the DocGen stereotype installer action
      annotations/                    AnnotationPlanner / Annotations: results → validation annotations
      bin/                            HostBinary: sysml-grpc resolution from the plugin directory
      bridge/                         DocGenBridge: the facade's implementation (§14)
      docgen/                         DocGenExtensions: creates the «JavaExtension» stereotypes
      engine/                         Engine, Operation, RunRequest: the two Connections (§3)
      identity/                       IdentityResolver: v2 qualified name → Cameo element
      results/                        RunResult and the outcome/diagnostic model
      selection/                      SelectionResolver, Selection: what was right-clicked
      source/                         ModelSource: .mdzip export or v2 textual export
      ui/                             ResultsWindow (ProjectWindow)
  mdk-bridge/                     the DocGen «JavaExtension» queries MDK loads (§14); depends on
                                  neither the client nor the plugin at run time
  openapi-stubs/                  compile-only stubs of the OpenAPI classes the plugin touches (§10.3)
  mdk-api-stubs/                  compile-only stubs of the MDK classes the bridge extends (§14)
  tools/                          BinaryStager, PluginDescriptorWriter (build-time)
  dist/                           the Resource Manager .zip

The plugin depends on the published org.openmbee:opensysml Java client (java-api.md) at the version pom.xml pins; the client is installed into the local repository first (editors/mdk/README.md).

10.2 Build

Gradle (Kotlin DSL), matching the Java client's build rather than Maven; the vendor's own examples are Ant/Gradle. Two source sets need OpenAPI jars on the compile classpath:

  • Licensed developer machine: -PcameoHome=/opt/Cameo puts <cameoHome>/lib/*.jar and <cameoHome>/plugins/**/*.jar on the compile classpath (compileOnly). Nothing from the installation is copied into the artifact.
  • CI: §10.3.

The distZip task lays out plugins/org.openmbee.opensysml/{plugin.xml,*.jar,bin/*} and data/resourcemanager/MDR_Plugin_org_openmbee_opensysml_<n>_descriptor.xml and zips it; the nightly attaches it beside the .vsix with the same <version>-nightly-<date>-<commit> scheme.

10.3 Compiling without a licence

The OpenAPI jars are not on Maven Central and the licence forbids redistributing them (the vendor's public GitHub examples, e.g. the MDK at github.com/Open-MBEE/exec-cameo-mdk, resolve them from a local installation). Two ways to keep CI honest:

  1. Compile-only stubs (openapi-stubs/): hand-written classes with the signatures the plugin calls — Plugin, PluginDescriptor, ActionsConfiguratorsManager, the three configurator interfaces, MDAction/MDActionsCategory, Application, Project, ProjectsManager, ProjectDescriptorsFactory, ProjectWindowsManager, ProjectWindow, WindowComponentInfo, ProgressStatusRunner, RunnableWithProgress, ProgressStatus, Annotation, AnnotationManager, ValidationHelper, ValidationRunData, RuleViolationResult, BaseElement/Element/NamedElement/Package, GUILog. Every stub method body is throw new UnsupportedOperationException("stub"). The stubs compile the plugin and let its unit tests run against Mockito mocks of the same types; the Javadoc pages cited here are the specification each stub is written from. A stub drifts silently when the vendor changes a signature — so:
  2. A licensed agent for the integration lane: a self-hosted runner with a Cameo installation and a floating licence runs the same build with -PcameoHome, which fails to compile if a stub lied, and then runs the plugin headless. Cameo supports headless execution of a plugin through com.nomagic.magicdraw.commandline.CommandLine/ProjectCommandLine (Javadoc), which is how the integration test opens each sample .mdzip, runs export → migrate → parse → verify, and asserts the identity map is total over the report's mapped entries. This lane is optional and non-blocking for outside contributors, and required for release. Whether the vendor's licence terms permit an unattended CI seat is unverified; a maintainer with the licence agreement must confirm before the lane exists.

10.4 The "Run with OpenSysML" sequence

user: right-click Package P (or a Block, Behavior, Requirement) ▸ OpenSysML ▸ Run…
  1  configurator resolves the selection to packages, or the owning package of a single element
  2  ProgressStatusRunner.runWithProgressStatus(task, "OpenSysML", allowCancel=true, 0)
  3  task, phase "export":   Exporter → tmp/<project>-<hash>.mdzip        (exportModule / saveProject)
  4  task, phase "migrate":  Migration c = connection.migrateFile(tmp, "sysml")
                             c.experimentalNotice → GUILog once per session
                             c.report.summary → results panel
     pre-flight:             c.report → counts (mapped/approximated/unmapped) and the
                             unmapped kinds; shown in the panel header; user may stop here
  5  task, phase "parse":    Model m = connection.parseSources(List.of(SourceDocument.inline("model.sysml", c.content())))
                             parse diagnostics → results panel (a v1 model that migrates but does
                             not parse is a migration bug to report upstream — its .sysml is kept)
  6  task, phase "run":      per the action chosen:
                               Run       → m.instantiate(target); m.runAction / runState (schedule policy from the dialog)
                               Verify    → m.verifySatisfaction(scope) + verifyConstraint/Requirement per selected element
                               Sweep     → m.runSweep(calc, ranges, options) from a small dialog
     each answer's element names → identity map → Cameo elements
  7  results:  Annotations on the mapped elements; RuleViolationResults into the validation
               window (when the suite module is loaded); every row into the OpenSysML Results panel
  8  the tmp .mdzip and .sysml are kept under the tool's temp dir until the next run, with a
     "Reveal migrated model" action, because the user will want to read what the engine read

isCancel() is polled between phases and the current connection's deadline bounds the wait inside one (§3: the short connection for phases 4–5, the long one for phase 6). The first run after start-up pays the child start (the client starts it lazily); later runs share the parse cache when the exported archive is unchanged (the client hashes sources).

10.5 Results mapping

ElementMap is built once per conversion from the report: Map<String xmiId, Entry> and Map<String target, List<Entry>>; a Cameo Element is fetched by ID with Project.getElementByID(String) (Project, 2026x Refresh1). The reverse map is a list because a target is not a unique key: the migration deliberately writes some source elements into a declaration owned by another — an operation's method behavior is recorded against the operation's target ("written as the body of the operation", internal/translate/migrate/behavior.go), and a renamed duplicate member may share a target with the element it collided with. Answers carry v2 qualified names (Symbol, Verification.constraintId/requirementId, SweepRow) and are resolved through that list; when it has more than one entry the result kind picks by Entry.kind, whose vocabulary is the source's UML metaclass with an optional stereotype prefix (kindOf in internal/translate/migrate/classify.go writes «Block» Class, Activity, Operation, never an abstract Behavior): an action or state verdict prefers the entry whose metaclass, after the «…» prefix, is one of Activity, StateMachine, OpaqueBehavior, FunctionBehavior or Interaction; a constraint verdict prefers Constraint; a value prefers Property or Port. When the kind still does not decide, the verdict is linked to every candidate and the panel shows them all rather than choosing one silently. A normalized target-kind field on the report entry would replace this string matching and belongs with the report-over-service change (§11, phase 3). A name with no entry — a library element, or a name the conversion synthesized — is shown in the panel without an element link, never dropped.

11. Phased plan

Phase Delivers Prerequisites in OpenSysML
1. Migration-based execution editors/mdk/plugin skeleton; plugin.xml; browser/diagram/menu actions; export → Migrate → ParseSources → instantiate/run/verify; results in GUILog and a plain table; stubs lane in CI; nightly .zip none — everything used is on the wire today
2. Results UI docking ProjectWindow table; Annotations on elements; validation-suite module and RuleViolationResult bridge; Reveal migrated model; per-phase deadlines and cancel none for the UI (two connections give per-phase deadlines, §3; a per-call deadline in the Java client would replace them); identity requires phase 3's report — until then names only
3. Units and report-over-service pre-flight counts and per-element migration notes in the panel; unit-bearing constraints evaluated correctly Migrate returns the migration report (MigrateResponse.report, the Entry shape of report.go — on the wire today) and accepts several source files for one migration (used modules); unit migration in internal/translate/migrate
4. Step-debug step a behavior from Cameo; highlight current state / fired transition / token on the open diagram through AnnotationPainters; choice-point display and reseed the debugger session API of api-surface-parity.md on the wire and in the Java client
v2 front end (in parallel from phase 1; needs the SysML v2 Plugin on the developer machine) plugin-v2: exportTextual → ParseSources, no migration; parser-disagreement report; opt-in run of a vendor-transformed v2 project none

11.1 Phase 1 as built

The implementation under editors/mdk/ follows §10 with these deviations:

  • Maven, not Gradle. The module is a Maven reactor (openapi-stubs, tools, plugin, dist) so it builds with the same toolchain as client/java; the client is consumed as an installed artifact rather than an included build. CAMEO_HOME (a shell script, not a Gradle property) compiles the plugin against a licensed installation.
  • One module for both paths. plugin-v2 is folded into plugin (org.openmbee.opensysml.mdk.v2) because the SysML v2 classes it needs (SysMLTextualNotationService, KerML Element, Namespace) are stubbed like the rest of the OpenAPI, so nothing forces a second source set. At runtime the v2 path is taken only when the textual service class loads and the selection is a SysML v2 element; otherwise the selection is exported as a .mdzip.
  • Identity by qualified name on both paths. The service does not return the migration report (phase 3), so v1 results are matched by the Cameo qualified name normalized the way the migration writes it (quoted segments unquoted). Every candidate for an ambiguous name is kept and annotated; a name the migration synthesized (unnamed2, renamed duplicates) is shown in the results window without an element link. The Entry.kind preference of §10.5 waits for the report.
  • Actions per operation, not a Run dialog. The context menu group holds six actions (Instantiate, Execute action, Execute state machine, Verify requirement/constraint, Evaluate calc, Run analysis), enabled for exactly one selected element; Verify picks verifyRequirement, verifySatisfaction or verifyConstraint from the symbol's kind. Sweep, the schedule dialog and Reveal migrated model are deferred with phase 2's remaining items.
  • Whole-project export. exportModule is given the primary model, since a selected element alone loses the references the conversion needs; a saved, unmodified .mdzip is read in place.
  • Phase 2's results window and annotations are in phase 1: a docking ProjectWindow per project with outcomes, diagnostics, final time and schedule, double-click selecting the element in the containment tree, and Annotations replaced per project on every run. The RuleViolationResult bridge and the validation-suite module are not.
  • One connection, cancel by close. A single Connection is opened lazily and reused; the progress dialog's isCancel() is polled every 250 ms and cancelling closes the connection, which aborts the in-flight RPC. The next run reopens it. Per-phase deadlines come from the client's request timeout rather than two connections.
  • Distribution. mvn -f editors/mdk/pom.xml -Dopensysml.version=vX.Y.Z package downloads the five sysml-grpc release assets, verifies each against the digest table the client jar ships, generates plugin.xml listing every runtime jar, and zips plugins/org.openmbee.opensysml.mdk/ with data/resourcemanager/.

12. Unknowns and risks

Each item says what is known, what is not, and what would settle it.

  1. Windows child process. PrivateService starts sysml-grpc -exit-with-parent, holds the write end of the child's stdin pipe and never writes; the child exits on end of file, which the kernel delivers when the parent dies. The source states that Windows anonymous pipes give the same guarantee (PrivateService.java, class comment). Two things are unverified on Windows inside Cameo: whether the tool's own process shutdown (a crash, or close() never called because another plugin vetoed exit and the user killed the process) still closes the handle promptly, and whether a Cameo launched from the vendor's .exe launcher inherits handles in a way that leaves a second holder of the pipe. Settle by: a licensed Windows integration run that kills csm.exe and asserts no sysml-grpc.exe remains after a few seconds. Mitigation if it fails: the child's -exit-with-parent gains a parent-PID poll on Windows.
  2. Executable bits and quarantine. Resource Manager extraction may not preserve POSIX modes; macOS Gatekeeper may refuse an un-notarized binary from a downloaded zip (§2.4, unverified). Settle on licensed macOS/Linux runs; mitigation: chmod before launch, and document xattr -d com.apple.quarantine or ship notarized binaries.
  3. No OpenAPI OMG-XMI exporter. The design relies on .mdzip (§4.2). If exportModule refuses packages that reference elements outside the selection, or saveProject to a new descriptor re-points the open project, the export falls back to saving the whole project to a temp file and passing the selection as ParseOptions/scope to OpenSysML instead. Settle with a licensed run.
  4. Migration report not on the wire (§5). Without it the identity map is by name and renamed elements are lost; phase 2's per-element results are only as good as names.
  5. Used projects / Teamwork Cloud. Modules are not loaded by the reader; a TWC project has no local file. Both need the multi-source Migrate of phase 3 and a licensed TWC test.
  6. Units. Not migrated; a model whose constraints depend on unit conversion is wrong, not just approximate, until internal/translate/migrate handles «Unit»/«QuantityKind».
  7. Two JDKs, two API surfaces. The pinned 2026x Refresh1 runs the plugin on JDK 21, the minimum 2024x Refresh3 on JDK 17; --release 17 covers both and the Java client's JDK 17 baseline runs unchanged on 21 (§1.1). The plugin-v2 module's vendor types exist only on the 2026x line, so it must be loaded reflectively or shipped as a second plugin with requires-plugin on the SysML v2 Plugin. Dropping the minimum release later removes the split.
  8. Licence terms for CI (§10.3): unverified whether an unattended seat is permitted.
  9. Simulation Toolkit conformance claims (§7.1): PSSM and PSCS conformance are not claimed on any page found; the positioning table says "SCXML-based state machines" and no more. If a page is found, the alignment note (precise-semantics-alignment.md) is the place to compare.
  10. Vendor .mdzip samples untested. The benchmark's Cameo rows are two Open-MBEE projects; the vendor's bundled samples were not available. First licensed run re-does §9 over them.

13. Every unverified claim, in one list

  • That the Resource Manager preserves the POSIX execute bit when extracting a plugin zip on macOS/Linux (§2.4) — the design chmods regardless.
  • macOS Gatekeeper behaviour for a sysml-grpc binary extracted by the Resource Manager (§2.4).
  • Reuse of the Simulation Toolkit's diagram animation for a foreign engine (§3) — assumed unavailable.
  • Any OpenAPI entry point for File ▸ Export To ▸ UML XMI 2.5 file (§4.1) — none found.
  • ProjectsManager.saveProject to a new local descriptor leaving the open project's own location unchanged (§4.1).
  • BaseElement.getID() being the persisted xmi:id (§5) — verified only through the fixtures' IDs, not from the Javadoc page.
  • The Simulation Toolkit's PSSM and PSCS conformance (§7.1).
  • That the minimum release, 2024x Refresh3, has no SysML v2 project type or textual import (§8) — not found in its version news or Javadoc index, which is absence of evidence only.
  • Whether SysMLTextualNotationService.exportTextual emits element identity beyond names (§8).
  • Windows pipe semantics of the child under Cameo's launcher and abnormal shutdown (§12.1).
  • Whether the vendor's licence permits an unattended CI seat (§10.3).

14. The MDK DocGen bridge

OpenMBEE's MDK (Open-MBEE/mdk) is the plugin a large share of Cameo users already run for MMS synchronisation and DocGen documents. OpenSysML MDK is positioned as its successor (docs/project/mdk-parity.md tracks the gap), and the first concrete step is to let an MDK document run OpenSysML.

14.1 The hook MDK offers

DocGen's «JavaExtension» stereotype (profile SysML Extensions, URI http://openmbee.org/mdk/sysml-extensions) tells DocGen to instantiate a Java class — named by the applied stereotype's name — as a org.openmbee.mdk.model.Query, call setTargets, initialize, then visit(forViewEditor, outputDir), and splice the returned DocumentElements into the document. The class is resolved with Class.forName(name, true, MDKPlugin.extensionsClassloader), a URLClassLoader over every jar in plugins/org.openmbee.mdk/extensions/ whose parent is MDK's own classloader. This is an extension point MDK ships for exactly this purpose, so no change to MDK is needed.

14.2 Two plugins, two classloaders, one facade

The extension classloader cannot see the OpenSysML plugin's jars (ownClassloader="true", class-lookup="LocalFirst", §2.3), and bundling the Java client, protobuf and the service into the bridge would mean a second service process and a second copy of every dependency. The bridge therefore stays small and calls back:

mdk-bridge (MDK's extension classloader)        plugin (its own classloader)
  <Operation>Query.visit(…)
    PluginLocator: PluginUtils.getPlugins()
      → descriptor id org.openmbee.opensysml.mdk
      → Method docGen(BaseElement, String, String) ─►  OpenSysMLPlugin.docGen
                                                        DocGenBridge: SelectionResolver → Engine.run
                                                        RunResult → Map<String,Object> (JDK types only)
    BridgeResult.parse(map) ◄───────────────────────────┘
    DocBookRenderer → DBParagraph / DBTable

Only Cameo OpenAPI types and JDK types (String, Map, List, Long) cross the boundary, both of which are loaded once, by Cameo's shared classloader, so no ClassCastException can arise. The target is typed com.nomagic.magicdraw.uml.BaseElement, the supertype of both the UML Element and the KerML API Element, so a SysML v2 target takes the textual path through the same SelectionResolver as the context menu (§7) and the bridge never loads a KerML class. The facade is a single public method on the Plugin instance, found reflectively, so the bridge needs no compile-time dependency on the plugin and an older plugin without the method fails with a readable message rather than a NoSuchMethodError.

Failure handling is per target: a target that is not a BaseElement, an operation the plugin does not know, a service failure or a failed export each become one error paragraph; the rest of the document still generates. A result whose temporary export could not be removed carries an export-cleanup warning diagnostic, as the context-menu path does.

14.3 The stereotypes

MDK resolves the extension class from the applied stereotype's name, so the model needs one stereotype per operation, each specialising «JavaExtension». Shipping them as a profile as a module would pin a profile .mdzip into the distribution; instead the plugin creates them in the project on demand (OpenSysML ▸ Add MDK DocGen extension stereotypes on the model root): a Profile named OpenSysML MDK DocGen under the primary model, applied to it with StereotypesHelper.applyProfile so its stereotypes are applicable, with stereotypes org.openmbee.opensysml.mdk.docgen.{Instantiate,ExecuteAction,ExecuteState,Verify,EvaluateCalc,RunAnalysis}, each extending Activity and CallBehaviorAction with a generalization to «JavaExtension»; EvaluateCalc adds a String tag arguments. The action is idempotent and runs inside one SessionManager session so it is a single undo step.

14.4 Build and packaging

The bridge compiles against mdk-api-stubs, compile-only copies of the six MDK classes it touches (Query, DocGenElement, Generatable, DocumentElement, DBParagraph, DBText, DBTable), written from MDK's develop source because MDK publishes no API artifact. As with the OpenAPI stubs (§10.3) only the members the bridge uses are declared, and they are never shipped. The distribution assembly adds the bridge jar at plugins/org.openmbee.mdk/extensions/ with no transitive dependencies — the bridge has none at run time — and lists it in no plugin.xml, so Cameo without MDK never loads it.

14.5 Unverified

Everything above the unit tests is reasoned from MDK's source and the vendor's Javadoc, not run: no Cameo installation and no MDK installation were available. Specifically unverified are that MDK's develop Query API is what the next MDK release ships; that PluginUtils.getPlugins() returns the OpenSysML Plugin instance (rather than a proxy) so the reflective call reaches docGen; that the UML String type is at UML Standard Profile::UML2 Metamodel::PrimitiveTypes::String in 2026x (the installer leaves the arguments tag untyped and says so if not); and the DocBook rendering of the tables in View Editor as opposed to the PDF path.