diff --git a/api/proto/sysml.pb.go b/api/proto/sysml.pb.go index c8d651e34a..8aa5d5f531 100644 --- a/api/proto/sysml.pb.go +++ b/api/proto/sysml.pb.go @@ -3723,11 +3723,14 @@ type ConvertRequest struct { // *ConvertRequest_Content // *ConvertRequest_ModelHash Source isConvertRequest_Source `protobuf_oneof:"source"` - // "sysml", "kerml", "text", "ttl", "turtle" or "rdf". Empty infers from - // file_path's extension, and is notation for a model_hash, since that is what - // parse reads; inline content has neither, so it must say. + // "sysml", "kerml", "text", "ttl", "turtle" or "rdf", or "xmi", "uml" or + // "mdzip" for a SysML v1 model, which is read and migrated to v2 and never + // written. Empty infers from file_path's extension, and is notation for a + // model_hash, since that is what parse reads; inline content has neither, so + // it must say. FromFormat string `protobuf:"bytes,3,opt,name=from_format,json=fromFormat,proto3" json:"from_format,omitempty"` - // Format to write, named as in from_format. Empty is rejected. + // Format to write, named as in from_format; the v1 names are refused, since + // a v2 model has no v1 form. Empty is rejected. ToFormat string `protobuf:"bytes,4,opt,name=to_format,json=toFormat,proto3" json:"to_format,omitempty"` // Write notation back out even when the parser could not read all of it, // reporting its syntax errors as diagnostics. Notation to notation only: @@ -3860,7 +3863,8 @@ type ConvertResponse struct { // Set when either format is RDF, whose mapping is experimental: it covers // model structure and the behavior its bodies state, refuses what it cannot // write back, and its vocabulary may change without a compatibility path. - // Notation to notation is stable and leaves this unset. + // Also set when the source is SysML v1, whose migration is experimental in + // the same sense. Notation to notation is stable and leaves this unset. Experimental bool `protobuf:"varint,6,opt,name=experimental,proto3" json:"experimental,omitempty"` // What is experimental about the conversion, in the wording every surface // reports it in. Empty when experimental is false. diff --git a/api/proto/sysml.proto b/api/proto/sysml.proto index a400763455..d656ffff7a 100644 --- a/api/proto/sysml.proto +++ b/api/proto/sysml.proto @@ -759,11 +759,14 @@ message ConvertRequest { string content = 2; string model_hash = 6; } - // "sysml", "kerml", "text", "ttl", "turtle" or "rdf". Empty infers from - // file_path's extension, and is notation for a model_hash, since that is what - // parse reads; inline content has neither, so it must say. + // "sysml", "kerml", "text", "ttl", "turtle" or "rdf", or "xmi", "uml" or + // "mdzip" for a SysML v1 model, which is read and migrated to v2 and never + // written. Empty infers from file_path's extension, and is notation for a + // model_hash, since that is what parse reads; inline content has neither, so + // it must say. string from_format = 3; - // Format to write, named as in from_format. Empty is rejected. + // Format to write, named as in from_format; the v1 names are refused, since + // a v2 model has no v1 form. Empty is rejected. string to_format = 4; // Write notation back out even when the parser could not read all of it, // reporting its syntax errors as diagnostics. Notation to notation only: @@ -786,7 +789,8 @@ message ConvertResponse { // Set when either format is RDF, whose mapping is experimental: it covers // model structure and the behavior its bodies state, refuses what it cannot // write back, and its vocabulary may change without a compatibility path. - // Notation to notation is stable and leaves this unset. + // Also set when the source is SysML v1, whose migration is experimental in + // the same sense. Notation to notation is stable and leaves this unset. bool experimental = 6; // What is experimental about the conversion, in the wording every surface // reports it in. Empty when experimental is false. diff --git a/changes/unreleased/sysml-v1-migration-docs.added.md b/changes/unreleased/sysml-v1-migration-docs.added.md new file mode 100644 index 0000000000..6185a617ce --- /dev/null +++ b/changes/unreleased/sysml-v1-migration-docs.added.md @@ -0,0 +1,2 @@ +- **SysML v1 migration is documented on every surface it reaches.** `ConvertRequest.from_format` and `ConvertResponse.experimental` in `api/proto/sysml.proto`, `docs/reference/wire-contract.md` and `docs/reference/api.md` now name `xmi`, `uml` and `mdzip` as input-only formats, the extensions they are inferred from, the canonical `xmi` a response answers, and that a migration marks the response experimental as an RDF conversion does; the Python client's `convert` docstrings and guide chapter 9 say the same. Guide chapter 11 walks one migration through — running it with `-migration-report`, reading the four verdicts, finishing units, behaviors and operations by hand, and checking the result as a v2 model — and the roadmap records the migration's open items: behaviors, operations and receptions, units and quantity kinds, the report over gRPC, and stable identity for a re-migration. +- **The Go client names SysML v1 as a conversion source.** `opensysml.FormatXMI` is the format a `Conversion` answers when `ConvertFile` read UML XMI, an Eclipse UML2 `.uml` file or a `.mdzip` archive; the `Conversion` is `Experimental` with the migration notice, and asking to write `FormatXMI` is refused with `CodeInvalidArgument`. diff --git a/client/opensysml/README.md b/client/opensysml/README.md index 6c22000f30..8afee96534 100644 --- a/client/opensysml/README.md +++ b/client/opensysml/README.md @@ -38,7 +38,7 @@ inst, err := client.Instantiate(ctx, model, "Demo::Vehicle") | Choose who answers | `ListEngines`, `WithEngine`, `Engine`, `CalcEngine` | | Search it | `Query`, `QueryOSLC` | | Report on it | `RunDocumentQuery`, `RenderDocument` | -| Write it out | `Convert`, `ConvertFile`, `ConvertSource` | +| Write it out, or migrate a SysML v1 model in | `Convert`, `ConvertFile`, `ConvertSource` | | Change its source | `ApplyEdits` | Execution and verification take the same handles the rest of the API takes: diff --git a/client/opensysml/authoring.go b/client/opensysml/authoring.go index afa7cde301..ceb76e0a66 100644 --- a/client/opensysml/authoring.go +++ b/client/opensysml/authoring.go @@ -10,10 +10,10 @@ import ( // Format is a representation a model is written in or read from. type Format string -// The formats conversion accepts. There are two canonical ones, FormatSysML and -// FormatTTL, and a Conversion answers by those names whichever alias was asked -// for. RDF, in any spelling, is an experimental mapping, which a Conversion -// reports. +// The formats conversion accepts. There are two canonical ones that are written, +// FormatSysML and FormatTTL, and a Conversion answers by those names whichever +// alias was asked for. RDF, in any spelling, is an experimental mapping, which a +// Conversion reports; so is migration from FormatXMI, which is only ever read. const ( FormatSysML Format = "sysml" FormatTTL Format = "ttl" @@ -25,6 +25,9 @@ const ( // serialization written. FormatTurtle Format = "turtle" FormatRDF Format = "rdf" + // FormatXMI is SysML v1 as UML XMI, an Eclipse UML2 .uml file or a .mdzip + // archive, migrated to v2 on the way in. Asking to write it is refused. + FormatXMI Format = "xmi" ) // ConvertOption configures Convert and ConvertFile. @@ -59,7 +62,7 @@ type Conversion struct { From Format To Format // Experimental is set when either format is RDF, whose vocabulary may change - // without a compatibility path. + // without a compatibility path, or the source is SysML v1, whose migration may. Experimental bool // ExperimentalNotice says what is experimental about the conversion, empty // when it is not. diff --git a/client/opensysml/surface_test.go b/client/opensysml/surface_test.go index d0795bd6a1..dd91b472cd 100644 --- a/client/opensysml/surface_test.go +++ b/client/opensysml/surface_test.go @@ -5,6 +5,7 @@ import ( "errors" "net/http" "net/http/httptest" + "path/filepath" "reflect" "strings" "testing" @@ -676,6 +677,29 @@ func TestAFormatAliasIsAnsweredCanonically(t *testing.T) { } } +func TestConvertFileMigratesSysMLv1(t *testing.T) { + client := newClient(t) + xmi := filepath.Join("..", "..", "internal", "core", "migrate", "testdata", "xmi", "vehicle.xmi") + conversion, err := client.ConvertFile(context.Background(), xmi, opensysml.FormatSysML) + if err != nil { + t.Fatalf("ConvertFile: %v", err) + } + if conversion.From != opensysml.FormatXMI || conversion.To != opensysml.FormatSysML { + t.Errorf("conversion = %s to %s, want xmi to sysml", conversion.From, conversion.To) + } + if !strings.Contains(conversion.Content, "part def Vehicle") { + t.Errorf("conversion does not carry the migrated model:\n%s", conversion.Content) + } + if !conversion.Experimental || !strings.Contains(conversion.ExperimentalNotice, "SysML v1 migration") { + t.Errorf("a migration does not report itself as experimental: %q", conversion.ExperimentalNotice) + } + + _, err = client.ConvertFile(context.Background(), xmi, opensysml.FormatXMI) + if !errors.Is(err, opensysml.CodeInvalidArgument) { + t.Errorf("writing xmi: err = %v, want CodeInvalidArgument", err) + } +} + func TestConvertSourceReadsInlineContent(t *testing.T) { client := newClient(t) conversion, err := client.ConvertSource(context.Background(), editableSource, opensysml.FormatSysML, diff --git a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ConvertRequest.java b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ConvertRequest.java index 9d5fe0599e..8c9f93dc17 100644 --- a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ConvertRequest.java +++ b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ConvertRequest.java @@ -256,9 +256,11 @@ public java.lang.String getModelHash() { private volatile java.lang.Object fromFormat_ = ""; /** *
- * "sysml", "kerml", "text", "ttl", "turtle" or "rdf". Empty infers from
- * file_path's extension, and is notation for a model_hash, since that is what
- * parse reads; inline content has neither, so it must say.
+ * "sysml", "kerml", "text", "ttl", "turtle" or "rdf", or "xmi", "uml" or
+ * "mdzip" for a SysML v1 model, which is read and migrated to v2 and never
+ * written. Empty infers from file_path's extension, and is notation for a
+ * model_hash, since that is what parse reads; inline content has neither, so
+ * it must say.
*
*
* string from_format = 3 [json_name = "fromFormat"];
@@ -279,9 +281,11 @@ public java.lang.String getFromFormat() {
}
/**
*
- * "sysml", "kerml", "text", "ttl", "turtle" or "rdf". Empty infers from
- * file_path's extension, and is notation for a model_hash, since that is what
- * parse reads; inline content has neither, so it must say.
+ * "sysml", "kerml", "text", "ttl", "turtle" or "rdf", or "xmi", "uml" or
+ * "mdzip" for a SysML v1 model, which is read and migrated to v2 and never
+ * written. Empty infers from file_path's extension, and is notation for a
+ * model_hash, since that is what parse reads; inline content has neither, so
+ * it must say.
*
*
* string from_format = 3 [json_name = "fromFormat"];
@@ -307,7 +311,8 @@ public java.lang.String getFromFormat() {
private volatile java.lang.Object toFormat_ = "";
/**
*
- * Format to write, named as in from_format. Empty is rejected.
+ * Format to write, named as in from_format; the v1 names are refused, since
+ * a v2 model has no v1 form. Empty is rejected.
*
*
* string to_format = 4 [json_name = "toFormat"];
@@ -328,7 +333,8 @@ public java.lang.String getToFormat() {
}
/**
*
- * Format to write, named as in from_format. Empty is rejected.
+ * Format to write, named as in from_format; the v1 names are refused, since
+ * a v2 model has no v1 form. Empty is rejected.
*
*
* string to_format = 4 [json_name = "toFormat"];
@@ -1112,9 +1118,11 @@ public Builder setModelHashBytes(
private java.lang.Object fromFormat_ = "";
/**
*
- * "sysml", "kerml", "text", "ttl", "turtle" or "rdf". Empty infers from
- * file_path's extension, and is notation for a model_hash, since that is what
- * parse reads; inline content has neither, so it must say.
+ * "sysml", "kerml", "text", "ttl", "turtle" or "rdf", or "xmi", "uml" or
+ * "mdzip" for a SysML v1 model, which is read and migrated to v2 and never
+ * written. Empty infers from file_path's extension, and is notation for a
+ * model_hash, since that is what parse reads; inline content has neither, so
+ * it must say.
*
*
* string from_format = 3 [json_name = "fromFormat"];
@@ -1134,9 +1142,11 @@ public java.lang.String getFromFormat() {
}
/**
*
- * "sysml", "kerml", "text", "ttl", "turtle" or "rdf". Empty infers from
- * file_path's extension, and is notation for a model_hash, since that is what
- * parse reads; inline content has neither, so it must say.
+ * "sysml", "kerml", "text", "ttl", "turtle" or "rdf", or "xmi", "uml" or
+ * "mdzip" for a SysML v1 model, which is read and migrated to v2 and never
+ * written. Empty infers from file_path's extension, and is notation for a
+ * model_hash, since that is what parse reads; inline content has neither, so
+ * it must say.
*
*
* string from_format = 3 [json_name = "fromFormat"];
@@ -1157,9 +1167,11 @@ public java.lang.String getFromFormat() {
}
/**
*
- * "sysml", "kerml", "text", "ttl", "turtle" or "rdf". Empty infers from
- * file_path's extension, and is notation for a model_hash, since that is what
- * parse reads; inline content has neither, so it must say.
+ * "sysml", "kerml", "text", "ttl", "turtle" or "rdf", or "xmi", "uml" or
+ * "mdzip" for a SysML v1 model, which is read and migrated to v2 and never
+ * written. Empty infers from file_path's extension, and is notation for a
+ * model_hash, since that is what parse reads; inline content has neither, so
+ * it must say.
*
*
* string from_format = 3 [json_name = "fromFormat"];
@@ -1176,9 +1188,11 @@ public Builder setFromFormat(
}
/**
*
- * "sysml", "kerml", "text", "ttl", "turtle" or "rdf". Empty infers from
- * file_path's extension, and is notation for a model_hash, since that is what
- * parse reads; inline content has neither, so it must say.
+ * "sysml", "kerml", "text", "ttl", "turtle" or "rdf", or "xmi", "uml" or
+ * "mdzip" for a SysML v1 model, which is read and migrated to v2 and never
+ * written. Empty infers from file_path's extension, and is notation for a
+ * model_hash, since that is what parse reads; inline content has neither, so
+ * it must say.
*
*
* string from_format = 3 [json_name = "fromFormat"];
@@ -1192,9 +1206,11 @@ public Builder clearFromFormat() {
}
/**
*
- * "sysml", "kerml", "text", "ttl", "turtle" or "rdf". Empty infers from
- * file_path's extension, and is notation for a model_hash, since that is what
- * parse reads; inline content has neither, so it must say.
+ * "sysml", "kerml", "text", "ttl", "turtle" or "rdf", or "xmi", "uml" or
+ * "mdzip" for a SysML v1 model, which is read and migrated to v2 and never
+ * written. Empty infers from file_path's extension, and is notation for a
+ * model_hash, since that is what parse reads; inline content has neither, so
+ * it must say.
*
*
* string from_format = 3 [json_name = "fromFormat"];
@@ -1214,7 +1230,8 @@ public Builder setFromFormatBytes(
private java.lang.Object toFormat_ = "";
/**
*
- * Format to write, named as in from_format. Empty is rejected.
+ * Format to write, named as in from_format; the v1 names are refused, since
+ * a v2 model has no v1 form. Empty is rejected.
*
*
* string to_format = 4 [json_name = "toFormat"];
@@ -1234,7 +1251,8 @@ public java.lang.String getToFormat() {
}
/**
*
- * Format to write, named as in from_format. Empty is rejected.
+ * Format to write, named as in from_format; the v1 names are refused, since
+ * a v2 model has no v1 form. Empty is rejected.
*
*
* string to_format = 4 [json_name = "toFormat"];
@@ -1255,7 +1273,8 @@ public java.lang.String getToFormat() {
}
/**
*
- * Format to write, named as in from_format. Empty is rejected.
+ * Format to write, named as in from_format; the v1 names are refused, since
+ * a v2 model has no v1 form. Empty is rejected.
*
*
* string to_format = 4 [json_name = "toFormat"];
@@ -1272,7 +1291,8 @@ public Builder setToFormat(
}
/**
*
- * Format to write, named as in from_format. Empty is rejected.
+ * Format to write, named as in from_format; the v1 names are refused, since
+ * a v2 model has no v1 form. Empty is rejected.
*
*
* string to_format = 4 [json_name = "toFormat"];
@@ -1286,7 +1306,8 @@ public Builder clearToFormat() {
}
/**
*
- * Format to write, named as in from_format. Empty is rejected.
+ * Format to write, named as in from_format; the v1 names are refused, since
+ * a v2 model has no v1 form. Empty is rejected.
*
*
* string to_format = 4 [json_name = "toFormat"];
diff --git a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ConvertRequestOrBuilder.java b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ConvertRequestOrBuilder.java
index a2ad392930..65f0cf03fe 100644
--- a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ConvertRequestOrBuilder.java
+++ b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ConvertRequestOrBuilder.java
@@ -63,9 +63,11 @@ public interface ConvertRequestOrBuilder extends
/**
*
- * "sysml", "kerml", "text", "ttl", "turtle" or "rdf". Empty infers from
- * file_path's extension, and is notation for a model_hash, since that is what
- * parse reads; inline content has neither, so it must say.
+ * "sysml", "kerml", "text", "ttl", "turtle" or "rdf", or "xmi", "uml" or
+ * "mdzip" for a SysML v1 model, which is read and migrated to v2 and never
+ * written. Empty infers from file_path's extension, and is notation for a
+ * model_hash, since that is what parse reads; inline content has neither, so
+ * it must say.
*
*
* string from_format = 3 [json_name = "fromFormat"];
@@ -74,9 +76,11 @@ public interface ConvertRequestOrBuilder extends
java.lang.String getFromFormat();
/**
*
- * "sysml", "kerml", "text", "ttl", "turtle" or "rdf". Empty infers from
- * file_path's extension, and is notation for a model_hash, since that is what
- * parse reads; inline content has neither, so it must say.
+ * "sysml", "kerml", "text", "ttl", "turtle" or "rdf", or "xmi", "uml" or
+ * "mdzip" for a SysML v1 model, which is read and migrated to v2 and never
+ * written. Empty infers from file_path's extension, and is notation for a
+ * model_hash, since that is what parse reads; inline content has neither, so
+ * it must say.
*
*
* string from_format = 3 [json_name = "fromFormat"];
@@ -87,7 +91,8 @@ public interface ConvertRequestOrBuilder extends
/**
*
- * Format to write, named as in from_format. Empty is rejected.
+ * Format to write, named as in from_format; the v1 names are refused, since
+ * a v2 model has no v1 form. Empty is rejected.
*
*
* string to_format = 4 [json_name = "toFormat"];
@@ -96,7 +101,8 @@ public interface ConvertRequestOrBuilder extends
java.lang.String getToFormat();
/**
*
- * Format to write, named as in from_format. Empty is rejected.
+ * Format to write, named as in from_format; the v1 names are refused, since
+ * a v2 model has no v1 form. Empty is rejected.
*
*
* string to_format = 4 [json_name = "toFormat"];
diff --git a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ConvertResponse.java b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ConvertResponse.java
index 75d7758ff3..daa89e444b 100644
--- a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ConvertResponse.java
+++ b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ConvertResponse.java
@@ -300,7 +300,8 @@ public org.openmbee.opensysml.proto.DiagnosticOrBuilder getDiagnosticsOrBuilder(
* Set when either format is RDF, whose mapping is experimental: it covers
* model structure and the behavior its bodies state, refuses what it cannot
* write back, and its vocabulary may change without a compatibility path.
- * Notation to notation is stable and leaves this unset.
+ * Also set when the source is SysML v1, whose migration is experimental in
+ * the same sense. Notation to notation is stable and leaves this unset.
*
*
* bool experimental = 6 [json_name = "experimental"];
@@ -1519,7 +1520,8 @@ public org.openmbee.opensysml.proto.Diagnostic.Builder addDiagnosticsBuilder(
* Set when either format is RDF, whose mapping is experimental: it covers
* model structure and the behavior its bodies state, refuses what it cannot
* write back, and its vocabulary may change without a compatibility path.
- * Notation to notation is stable and leaves this unset.
+ * Also set when the source is SysML v1, whose migration is experimental in
+ * the same sense. Notation to notation is stable and leaves this unset.
*
*
* bool experimental = 6 [json_name = "experimental"];
@@ -1534,7 +1536,8 @@ public boolean getExperimental() {
* Set when either format is RDF, whose mapping is experimental: it covers
* model structure and the behavior its bodies state, refuses what it cannot
* write back, and its vocabulary may change without a compatibility path.
- * Notation to notation is stable and leaves this unset.
+ * Also set when the source is SysML v1, whose migration is experimental in
+ * the same sense. Notation to notation is stable and leaves this unset.
*
*
* bool experimental = 6 [json_name = "experimental"];
@@ -1553,7 +1556,8 @@ public Builder setExperimental(boolean value) {
* Set when either format is RDF, whose mapping is experimental: it covers
* model structure and the behavior its bodies state, refuses what it cannot
* write back, and its vocabulary may change without a compatibility path.
- * Notation to notation is stable and leaves this unset.
+ * Also set when the source is SysML v1, whose migration is experimental in
+ * the same sense. Notation to notation is stable and leaves this unset.
*
*
* bool experimental = 6 [json_name = "experimental"];
diff --git a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ConvertResponseOrBuilder.java b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ConvertResponseOrBuilder.java
index aeec919b8a..0d5462537d 100644
--- a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ConvertResponseOrBuilder.java
+++ b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ConvertResponseOrBuilder.java
@@ -130,7 +130,8 @@ org.openmbee.opensysml.proto.DiagnosticOrBuilder getDiagnosticsOrBuilder(
* Set when either format is RDF, whose mapping is experimental: it covers
* model structure and the behavior its bodies state, refuses what it cannot
* write back, and its vocabulary may change without a compatibility path.
- * Notation to notation is stable and leaves this unset.
+ * Also set when the source is SysML v1, whose migration is experimental in
+ * the same sense. Notation to notation is stable and leaves this unset.
*
*
* bool experimental = 6 [json_name = "experimental"];
diff --git a/clients/node/src/generated/sysml_pb.ts b/clients/node/src/generated/sysml_pb.ts
index aa87f2f1d0..33972ef15b 100644
--- a/clients/node/src/generated/sysml_pb.ts
+++ b/clients/node/src/generated/sysml_pb.ts
@@ -2044,16 +2044,19 @@ export type ConvertRequest = Message<"sysml.ConvertRequest"> & {
} | { case: undefined; value?: undefined };
/**
- * "sysml", "kerml", "text", "ttl", "turtle" or "rdf". Empty infers from
- * file_path's extension, and is notation for a model_hash, since that is what
- * parse reads; inline content has neither, so it must say.
+ * "sysml", "kerml", "text", "ttl", "turtle" or "rdf", or "xmi", "uml" or
+ * "mdzip" for a SysML v1 model, which is read and migrated to v2 and never
+ * written. Empty infers from file_path's extension, and is notation for a
+ * model_hash, since that is what parse reads; inline content has neither, so
+ * it must say.
*
* @generated from field: string from_format = 3;
*/
fromFormat: string;
/**
- * Format to write, named as in from_format. Empty is rejected.
+ * Format to write, named as in from_format; the v1 names are refused, since
+ * a v2 model has no v1 form. Empty is rejected.
*
* @generated from field: string to_format = 4;
*/
@@ -2120,7 +2123,8 @@ export type ConvertResponse = Message<"sysml.ConvertResponse"> & {
* Set when either format is RDF, whose mapping is experimental: it covers
* model structure and the behavior its bodies state, refuses what it cannot
* write back, and its vocabulary may change without a compatibility path.
- * Notation to notation is stable and leaves this unset.
+ * Also set when the source is SysML v1, whose migration is experimental in
+ * the same sense. Notation to notation is stable and leaves this unset.
*
* @generated from field: bool experimental = 6;
*/
diff --git a/clients/python/opensysml/__init__.py b/clients/python/opensysml/__init__.py
index d2b41ba907..d8cb65d6cf 100644
--- a/clients/python/opensysml/__init__.py
+++ b/clients/python/opensysml/__init__.py
@@ -239,9 +239,10 @@ def convert(to_format, file_path=None, content=None, model_hash=None,
content (str, optional): Source carried inline
model_hash (str, optional): Hash of a loaded model, whose parsed source
is converted
- from_format (str, optional): Format to read the source as; inferred from
- file_path's extension when omitted, notation for a model_hash, and
- required for inline content
+ from_format (str, optional): Format to read the source as, one of the
+ to_format names or 'xmi', 'uml' or 'mdzip' for a SysML v1 model to
+ migrate; inferred from file_path's extension when omitted, notation
+ for a model_hash, and required for inline content
tolerate_syntax_errors (bool): Write notation back out even when the
parser could not read all of it
host (str): Service hostname, or a ``host:port`` address
@@ -252,7 +253,9 @@ def convert(to_format, file_path=None, content=None, model_hash=None,
Warns:
ExperimentalFeatureWarning: If either format is RDF, whose mapping is
- experimental — see ``docs/reference/rdf-mapping.md``
+ experimental (see ``docs/reference/rdf-mapping.md``), or the source
+ is SysML v1, whose migration is experimental too (see
+ ``docs/reference/sysml-v1-migration.md``)
Example:
>>> import opensysml
diff --git a/clients/python/opensysml/connection.py b/clients/python/opensysml/connection.py
index f9c29f5e2b..36f8f2c008 100644
--- a/clients/python/opensysml/connection.py
+++ b/clients/python/opensysml/connection.py
@@ -842,9 +842,11 @@ def convert(self, to_format, file_path=None, content=None, model_hash=None,
content (str, optional): Source carried inline
model_hash (str, optional): Hash of a loaded model, whose parsed
source is converted
- from_format (str, optional): Format to read the source as; inferred
- from file_path's extension when omitted, notation for a
- model_hash, and required for inline content
+ from_format (str, optional): Format to read the source as, one of
+ the to_format names or 'xmi', 'uml' or 'mdzip' for a SysML v1
+ model to migrate; inferred from file_path's extension when
+ omitted, notation for a model_hash, and required for inline
+ content
tolerate_syntax_errors (bool): Write notation back out even when the
parser could not read all of it, reporting its syntax errors as
the result's diagnostics. Notation to notation only: every other
@@ -857,7 +859,9 @@ def convert(self, to_format, file_path=None, content=None, model_hash=None,
Warns:
ExperimentalFeatureWarning: If either format is RDF, whose mapping is
- experimental — see ``docs/reference/rdf-mapping.md``
+ experimental (see ``docs/reference/rdf-mapping.md``), or the
+ source is SysML v1, whose migration is experimental too (see
+ ``docs/reference/sysml-v1-migration.md``)
Raises:
ValueError: If other than one of file_path, content and model_hash
diff --git a/clients/rust/conformance/sysml.descriptor.binpb b/clients/rust/conformance/sysml.descriptor.binpb
index bf6221c3b9..3ffc5b24a4 100644
Binary files a/clients/rust/conformance/sysml.descriptor.binpb and b/clients/rust/conformance/sysml.descriptor.binpb differ
diff --git a/clients/rust/opensysml/src/proto/sysml/sysml.rs b/clients/rust/opensysml/src/proto/sysml/sysml.rs
index 669829c0d1..c57d922188 100644
--- a/clients/rust/opensysml/src/proto/sysml/sysml.rs
+++ b/clients/rust/opensysml/src/proto/sysml/sysml.rs
@@ -880,12 +880,15 @@ pub struct ExecuteStateResponse {
/// change the answer; a file_path is read afresh and content is carried inline.
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ConvertRequest {
- /// "sysml", "kerml", "text", "ttl", "turtle" or "rdf". Empty infers from
- /// file_path's extension, and is notation for a model_hash, since that is what
- /// parse reads; inline content has neither, so it must say.
+ /// "sysml", "kerml", "text", "ttl", "turtle" or "rdf", or "xmi", "uml" or
+ /// "mdzip" for a SysML v1 model, which is read and migrated to v2 and never
+ /// written. Empty infers from file_path's extension, and is notation for a
+ /// model_hash, since that is what parse reads; inline content has neither, so
+ /// it must say.
#[prost(string, tag="3")]
pub from_format: ::prost::alloc::string::String,
- /// Format to write, named as in from_format. Empty is rejected.
+ /// Format to write, named as in from_format; the v1 names are refused, since
+ /// a v2 model has no v1 form. Empty is rejected.
#[prost(string, tag="4")]
pub to_format: ::prost::alloc::string::String,
/// Write notation back out even when the parser could not read all of it,
@@ -930,7 +933,8 @@ pub struct ConvertResponse {
/// Set when either format is RDF, whose mapping is experimental: it covers
/// model structure and the behavior its bodies state, refuses what it cannot
/// write back, and its vocabulary may change without a compatibility path.
- /// Notation to notation is stable and leaves this unset.
+ /// Also set when the source is SysML v1, whose migration is experimental in
+ /// the same sense. Notation to notation is stable and leaves this unset.
#[prost(bool, tag="6")]
pub experimental: bool,
/// What is experimental about the conversion, in the wording every surface
diff --git a/docs/guide/07-saving-and-rdf.md b/docs/guide/07-saving-and-rdf.md
index dc65838b66..aeb15e360c 100644
--- a/docs/guide/07-saving-and-rdf.md
+++ b/docs/guide/07-saving-and-rdf.md
@@ -89,8 +89,9 @@ sysml input.txt -convert ttl -from sysml
`-convert` and `-from` accept `sysml`, `kerml`, `text`, `ttl`, `turtle` and `rdf`; `-from` also
accepts `xmi` (or `uml`, `mdzip`) for a SysML v1 model exported as OMG XMI, an Eclipse `.uml`
file or a zip archive holding the XMI (a MagicDraw/Cameo `.mdzip` project), which is migrated
-to v2 on the way in — an experimental path, and every run that takes it says so on stderr; see
-[SysML v1 migration](../reference/sysml-v1-migration.md). The output path
+to v2 on the way in — an experimental path, and every run that takes it says so on stderr;
+[chapter 11](11-migrating-from-sysml-v1.md) walks one through, and
+[SysML v1 migration](../reference/sysml-v1-migration.md) states the mapping. The output path
plays no part in choosing the format, so a destination without an extension, such as `-o /dev/null`
or a FIFO, needs no extra flags.
diff --git a/docs/guide/09-clients.md b/docs/guide/09-clients.md
index bdd464c057..84c662e8fc 100644
--- a/docs/guide/09-clients.md
+++ b/docs/guide/09-clients.md
@@ -1002,6 +1002,22 @@ text, and `write(path)` saves it. Formats are named `sysml`, `kerml`, `text`, `t
`rdf`. A file path's format is inferred from its extension; inline `content` has no extension, so
it needs `from_format`.
+`convert` also reads a **SysML v1** model and migrates it, when the source is UML XMI, an Eclipse
+UML2 `.uml` file or a `.mdzip` archive: `from_format` is `xmi`, `uml` or `mdzip`, inferred from
+those extensions, and is an input only — asking to write it raises `InvalidRequestError`, since a
+v2 model has no v1 form. Migration is
+[experimental](../reference/sysml-v1-migration.md#status-experimental) and warns as the RDF
+direction does. The service does not return the migration report; run
+`sysml Model.xmi -convert sysml -migration-report Model.report.txt` for the element-by-element
+account, as [chapter 11](11-migrating-from-sysml-v1.md) walks through.
+
+```python
+migrated = opensysml.convert("sysml", file_path="Vehicle.mdzip") # ExperimentalFeatureWarning
+migrated.from_format, migrated.to_format # ('xmi', 'sysml')
+migrated.write("Vehicle.sysml")
+opensysml.convert("ttl", content=xmi_text, from_format="xmi") # straight to RDF
+```
+
A `Model` writes out the source the service parsed, identified by `model.hash`, so editing the file
between `load` and `save` does not change what is written: the model saved is the model you
inspected. `convert(file_path=…)` is the alternative, and reads the file as it is now. The
@@ -1025,7 +1041,8 @@ What each direction preserves:
Conversion is capability-negotiated: against a service that does not report the `convert`
capability, these calls raise `MissingCapabilityError` naming the required upgrade rather than
failing on an unimplemented method. For a service that does not report the RDF mapping's status,
-the status is worked out from the formats it reports, so an RDF conversion warns either way.
+the status is worked out from the formats it reports, so an RDF conversion or a v1 migration
+warns either way.
Suppress the warning with `warnings.simplefilter("ignore",
opensysml.ExperimentalFeatureWarning)`; no stable feature uses that warning class.
diff --git a/docs/guide/11-migrating-from-sysml-v1.md b/docs/guide/11-migrating-from-sysml-v1.md
new file mode 100644
index 0000000000..0016c3ccd7
--- /dev/null
+++ b/docs/guide/11-migrating-from-sysml-v1.md
@@ -0,0 +1,207 @@
+# 11. Migrating a SysML v1 model
+
+A SysML v1 model exported from its tool as UML XMI, an Eclipse UML2 `.uml` file or a
+MagicDraw/Cameo `.mdzip` archive can be read by `sysml -convert` and written as SysML v2 notation
+or RDF Turtle. This chapter walks one export through the migration: running it, reading the
+report it produces, finishing by hand what the mapping leaves behind, and checking the result as
+a v2 model. Every mapping rule, and the format of the report, is in
+[reference/sysml-v1-migration.md](../reference/sysml-v1-migration.md).
+
+Migration is **experimental**: the mapping covers structure, ports and connectors, requirements,
+constraints, instances and allocations; it does not yet migrate behaviors, operations or units;
+and what it writes for a v1 element may change between releases without a compatibility path.
+Every run says so on stderr, so a script sees the status without reading this page. It is also
+**one way**: a v2 model has no v1 form, so `-convert xmi` is refused, and the migrated notation is
+the place to keep working — not a copy to be re-migrated after editing the v1 model.
+
+## The example
+
+The model migrated below is the vehicle fixture the migration's own tests use,
+[`internal/core/migrate/testdata/xmi/vehicle.xmi`](../../internal/core/migrate/testdata/xmi/vehicle.xmi):
+a small vehicle design of blocks, value types, ports and connectors, a constraint block, an
+instance, a requirements package with satisfy, verify and derive relationships, and — so that the
+report has something to say — an activity, a state machine, an operation, a unit and a quantity
+kind. Copy it next to you as `Vehicle.xmi` to follow along; your own tool's export works the
+same way, whatever its extension:
+
+```bash
+sysml Vehicle.xmi -convert sysml -o Vehicle.sysml # OMG XMI 2.5.1 with the SysML profile
+sysml Vehicle.uml -convert sysml -o Vehicle.sysml # Eclipse UML2 / Papyrus
+sysml Vehicle.mdzip -convert sysml -o Vehicle.sysml # MagicDraw / Cameo project archive
+sysml export.xml -convert sysml -o Vehicle.sysml -from xmi # an extension that does not say
+```
+
+The input format is inferred from the extension and named with `-from` when the extension does
+not say. Diagrams and the exporting tool's private state are skipped; the model is what is read.
+
+## Running the migration
+
+Ask for the report alongside the notation. Without `-migration-report` only its one-line summary
+is printed:
+
+```console
+$ sysml Vehicle.xmi -convert sysml -o Vehicle.sysml -migration-report Vehicle.report.txt
+note: SysML v1 migration is experimental: the mapping covers structure, ports and connectors, requirements, constraints, instances and allocations, reports every element it approximates or leaves behind, and what it writes for a v1 element may change without a compatibility path; see docs/reference/sysml-v1-migration.md § Status
+wrote Vehicle.report.txt (migration report: migrated 89 element(s): 68 mapped, 13 approximated, 8 unmapped (3 skipped as profile or library content))
+wrote Vehicle.sysml (sysml, 5913 bytes)
+```
+
+The summary is the first thing to read: 89 v1 elements, of which 68 have a direct v2 form, 13
+were written as the nearest v2 construct, and 8 have none. The 3 skipped are the SysML profile
+application, the profile itself and a diagram — not model content, so they count against nothing.
+The command exits 0 when the notation was written, whatever the report says; it exits non-zero
+and writes nothing when the input cannot be read as XMI at all, or holds no model.
+
+A report named `.json` is written as JSON with the same entries, for a script that wants to gate
+on the verdicts rather than read them:
+
+```bash
+sysml Vehicle.xmi -convert sysml -o Vehicle.sysml -migration-report Vehicle.report.json
+```
+
+`-convert ttl` writes the migrated model as RDF Turtle in one step, through the same mapping and
+then [the RDF mapping](07-saving-and-rdf.md); the report describes the migration either way.
+
+## Reading the report
+
+The report accounts for every element of the v1 model under one of four verdicts, the ones that
+need attention first:
+
+```text
+# SysML v1 to v2 migration report: Vehicle.xmi
+# exported by Example UML Tool
+# migrated 89 element(s): 68 mapped, 13 approximated, 8 unmapped (3 skipped as profile or library content)
+
+## unmapped (8)
+«Verify» Abstraction Requirements::