From d0abc99ef950bf69a09143c922c58f759673d90b Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 16 Oct 2026 15:48:52 -0400 Subject: [PATCH 1/7] Stop offering a Description on LookupTable and Breakpoint, which MATLAB's classes do not have MATLAB R2027a has no Description property on Simulink.LookupTable or Simulink.Breakpoint: isprop is 0, and setting one fails with "Unrecognized property 'Description' for class 'Simulink.LookupTable'." Both nodes listed one anyway, so the table and the Property Inspector offered an editor, the edit was accepted, and both save paths wrote it as a top-level key that MATLAB opens without a warning and ignores. Both nodes now do what FunctionElement does. Description is not in getProperties or in the General layout, descriptionEditable is false, and setProperty('Description', ...) is refused in MATLAB's words and leaves the entry unmodified. A Description key the file already has is shown read-only (in the Description column and in the inspector's Other group) and is written back as read on both save paths. test/lutBreakpointNoDescription.test.ts covers both classes in both formats and a new entry. Existing tests changed only where they pinned the removed editor: typedef.fidelity's Description round-trip (now: refused, file unchanged), the General groups in piGeneralAllNodes and lookupTableFlatProps, the Breakpoint and LookupTable rows in absentPropertyWriteBack's table, and schemaLayoutResolvable's layout-key floors (182 to 180 and 100 to 98, the two removed description keys). The two class docs no longer claim a passing MATLAB gate for a Description edit. --- dist/datamodel/node/data/BreakpointNode.d.ts | 4 +- .../node/data/BreakpointNode.d.ts.map | 2 +- dist/datamodel/node/data/BreakpointNode.js | 14 +- .../datamodel/node/data/BreakpointNode.js.map | 2 +- dist/datamodel/node/data/LookupTableNode.d.ts | 4 +- .../node/data/LookupTableNode.d.ts.map | 2 +- dist/datamodel/node/data/LookupTableNode.js | 14 +- .../node/data/LookupTableNode.js.map | 2 +- dist/datamodel/schema/classes/breakpoint.json | 2 +- .../datamodel/schema/classes/lookupTable.json | 2 +- src/datamodel/node/data/BreakpointNode.ts | 21 +- src/datamodel/node/data/LookupTableNode.ts | 21 +- .../node/data/Simulink.Breakpoint.md | 53 +++-- .../node/data/Simulink.LookupTable.md | 49 +++-- src/datamodel/schema/classes/breakpoint.json | 2 +- src/datamodel/schema/classes/lookupTable.json | 2 +- test/absentPropertyWriteBack.test.ts | 43 ++--- test/lookupTableFlatProps.test.ts | 4 +- test/lutBreakpointNoDescription.test.ts | 181 ++++++++++++++++++ test/parity/fidelity/typedef.fidelity.test.ts | 50 ++--- test/schema/piGeneralAllNodes.test.ts | 15 +- test/schemaLayoutResolvable.test.ts | 10 +- 22 files changed, 377 insertions(+), 122 deletions(-) create mode 100644 test/lutBreakpointNoDescription.test.ts diff --git a/dist/datamodel/node/data/BreakpointNode.d.ts b/dist/datamodel/node/data/BreakpointNode.d.ts index fc40b274..4d5affca 100644 --- a/dist/datamodel/node/data/BreakpointNode.d.ts +++ b/dist/datamodel/node/data/BreakpointNode.d.ts @@ -1,6 +1,7 @@ import SimulinkObjectNode from '../SimulinkObjectNode.js'; import type { PropClass } from '../BaseNode.js'; import type BaseNode from '../BaseNode.js'; +import type { SetPropertyResult } from '../DataNode.js'; export default class BreakpointNode extends SimulinkObjectNode { Description: string; constructor(name: string, parent: BaseNode | null, props: Record, serial: Record); @@ -9,7 +10,8 @@ export default class BreakpointNode extends SimulinkObjectNode { get displayValue(): string; get valueEditable(): boolean; getProperties(): PropClass[]; - _serializedOverrides(): Record; + get descriptionEditable(): boolean; + setProperty(propName: string, stringValue: string): true | SetPropertyResult; static get defaultName(): string; static createDefault(name: string, parent: BaseNode | null): BreakpointNode; static parse(rawVal: Record, name: string, parent: BaseNode | null): BreakpointNode; diff --git a/dist/datamodel/node/data/BreakpointNode.d.ts.map b/dist/datamodel/node/data/BreakpointNode.d.ts.map index 450c8c42..dad49c9b 100644 --- a/dist/datamodel/node/data/BreakpointNode.d.ts.map +++ b/dist/datamodel/node/data/BreakpointNode.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BreakpointNode.d.ts","sourceRoot":"","sources":["../../../../src/datamodel/node/data/BreakpointNode.ts"],"names":[],"mappings":"AACA,OAAO,kBAAkB,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAK3C,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,kBAAkB;IAC1D,WAAW,EAAE,MAAM,CAAC;gBACR,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClH,IAAI,IAAI,IAAI,MAAM,CAAmC;IACrD,IAAI,SAAS,IAAI,MAAM,CAAuB;IAE9C,IAAI,YAAY,IAAI,MAAM,CAAe;IACzC,IAAI,aAAa,IAAI,OAAO,CAAkB;IAC9C,aAAa,IAAI,SAAS,EAAE;IAE5B,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC/C,MAAM,KAAK,WAAW,IAAI,MAAM,CAAyB;IAQzD,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,cAAc;IAC3E,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,cAAc;CACvG"} \ No newline at end of file +{"version":3,"file":"BreakpointNode.d.ts","sourceRoot":"","sources":["../../../../src/datamodel/node/data/BreakpointNode.ts"],"names":[],"mappings":"AACA,OAAO,kBAAkB,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAIxD,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,kBAAkB;IAO1D,WAAW,EAAE,MAAM,CAAC;gBACR,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClH,IAAI,IAAI,IAAI,MAAM,CAAmC;IACrD,IAAI,SAAS,IAAI,MAAM,CAAuB;IAE9C,IAAI,YAAY,IAAI,MAAM,CAAe;IACzC,IAAI,aAAa,IAAI,OAAO,CAAkB;IAC9C,aAAa,IAAI,SAAS,EAAE;IAG5B,IAAI,mBAAmB,IAAI,OAAO,CAAkB;IAGpD,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,iBAAiB;IAM5E,MAAM,KAAK,WAAW,IAAI,MAAM,CAAyB;IAQzD,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,cAAc;IAC3E,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,cAAc;CACvG"} \ No newline at end of file diff --git a/dist/datamodel/node/data/BreakpointNode.js b/dist/datamodel/node/data/BreakpointNode.js index a8c8e29d..b852a4a6 100644 --- a/dist/datamodel/node/data/BreakpointNode.js +++ b/dist/datamodel/node/data/BreakpointNode.js @@ -2,7 +2,6 @@ import SimulinkObjectNode from '../SimulinkObjectNode.js'; import PropName from '../../prop/PropName.js'; import PropDataType from '../../prop/PropDataType.js'; -import PropDescription from '../../prop/PropDescription.js'; const CLASS_NAME = 'Simulink.Breakpoint'; export default class BreakpointNode extends SimulinkObjectNode { constructor(name, parent, props, serial) { super(name, parent, serial); this.Description = props.Description || ''; } @@ -11,9 +10,18 @@ export default class BreakpointNode extends SimulinkObjectNode { // A Breakpoint has no scalar "value" — the Value column is empty and not editable. get displayValue() { return ''; } get valueEditable() { return false; } - getProperties() { return [PropName, PropDataType, PropDescription]; } + getProperties() { return [PropName, PropDataType]; } // PI layout: schema-driven "General" group (classes/breakpoint.json). - _serializedOverrides() { return this._gatedProps({ Description: this.Description }); } + // No editor in the table (`row._descriptionEditable`) or the Property Inspector. + get descriptionEditable() { return false; } + // An edit is refused in MATLAB's words, rather than accepted and saved as a key MATLAB + // ignores on load, which is what an edit here used to do. + setProperty(propName, stringValue) { + if (propName === 'Description') { + return { error: true, reason: `Unrecognized property 'Description' for class '${CLASS_NAME}'.`, invalidValue: stringValue, validValue: this.Description }; + } + return super.setProperty(propName, stringValue); + } static get defaultName() { return 'Breakpoint'; } // MEASURED from what MATLAB R2027a writes for `Simulink.Breakpoint` with nothing set, in // MATLAB's own alphabetical key order. Three of the four properties are themselves nested diff --git a/dist/datamodel/node/data/BreakpointNode.js.map b/dist/datamodel/node/data/BreakpointNode.js.map index c60009c4..1d082902 100644 --- a/dist/datamodel/node/data/BreakpointNode.js.map +++ b/dist/datamodel/node/data/BreakpointNode.js.map @@ -1 +1 @@ -{"version":3,"file":"BreakpointNode.js","sourceRoot":"","sources":["../../../../src/datamodel/node/data/BreakpointNode.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,OAAO,kBAAkB,MAAM,0BAA0B,CAAC;AAG1D,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAC9C,OAAO,YAAY,MAAM,4BAA4B,CAAC;AACtD,OAAO,eAAe,MAAM,+BAA+B,CAAC;AAC5D,MAAM,UAAU,GAAG,qBAAqB,CAAC;AACzC,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,kBAAkB;IAE1D,YAAY,IAAY,EAAE,MAAuB,EAAE,KAA8B,EAAE,MAA+B,IAAI,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,GAAI,KAAK,CAAC,WAAsB,IAAI,EAAE,CAAC,CAAC,CAAC;IAC5M,IAAI,IAAI,KAAa,OAAO,sBAAsB,CAAC,CAAC,CAAC;IACrD,IAAI,SAAS,KAAa,OAAO,UAAU,CAAC,CAAC,CAAC;IAC9C,mFAAmF;IACnF,IAAI,YAAY,KAAa,OAAO,EAAE,CAAC,CAAC,CAAC;IACzC,IAAI,aAAa,KAAc,OAAO,KAAK,CAAC,CAAC,CAAC;IAC9C,aAAa,KAAkB,OAAO,CAAC,QAAQ,EAAE,YAAY,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC;IAClF,sEAAsE;IACtE,oBAAoB,KAA8B,OAAO,IAAI,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/G,MAAM,KAAK,WAAW,KAAa,OAAO,YAAY,CAAC,CAAC,CAAC;IACzD,yFAAyF;IACzF,0FAA0F;IAC1F,8FAA8F;IAC9F,4FAA4F;IAC5F,oFAAoF;IACpF,wFAAwF;IACxF,oFAAoF;IACpF,MAAM,CAAC,aAAa,CAAC,IAAY,EAAE,MAAuB,IAAoB,MAAM,MAAM,GAAG,cAAc,CAAC,cAAc,CAAC,UAAU,EAAE,EAAE,WAAW,EAAE,EAAE,aAAa,EAAE,iCAAiC,EAAE,WAAW,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,cAAc,CAAC,iBAAiB,CAAC,WAAW,CAAC,EAAE,cAAc,EAAE,EAAE,aAAa,EAAE,qCAAqC,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,GAAG,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,IAAI,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAA6B,CAAC,CAAC,CAAC,CAAC;IACnuB,MAAM,CAAC,KAAK,CAAC,MAA+B,EAAE,IAAY,EAAE,MAAuB,IAAoB,MAAM,KAAK,GAAG,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,IAAI,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAA6B,CAAC,CAAC,CAAC,CAAC;CAC9Q"} \ No newline at end of file +{"version":3,"file":"BreakpointNode.js","sourceRoot":"","sources":["../../../../src/datamodel/node/data/BreakpointNode.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/datamodel/node/data/LookupTableNode.d.ts b/dist/datamodel/node/data/LookupTableNode.d.ts index 2f26aee7..e033f9bc 100644 --- a/dist/datamodel/node/data/LookupTableNode.d.ts +++ b/dist/datamodel/node/data/LookupTableNode.d.ts @@ -1,6 +1,7 @@ import SimulinkObjectNode from '../SimulinkObjectNode.js'; import type { PropClass } from '../BaseNode.js'; import type BaseNode from '../BaseNode.js'; +import type { SetPropertyResult } from '../DataNode.js'; export default class LookupTableNode extends SimulinkObjectNode { Description: string; constructor(name: string, parent: BaseNode | null, props: Record, serial: Record); @@ -9,7 +10,8 @@ export default class LookupTableNode extends SimulinkObjectNode { get displayValue(): string; get valueEditable(): boolean; getProperties(): PropClass[]; - _serializedOverrides(): Record; + get descriptionEditable(): boolean; + setProperty(propName: string, stringValue: string): true | SetPropertyResult; static get defaultName(): string; static createDefault(name: string, parent: BaseNode | null): LookupTableNode; static parse(rawVal: Record, name: string, parent: BaseNode | null): LookupTableNode; diff --git a/dist/datamodel/node/data/LookupTableNode.d.ts.map b/dist/datamodel/node/data/LookupTableNode.d.ts.map index c61a111f..e41508bf 100644 --- a/dist/datamodel/node/data/LookupTableNode.d.ts.map +++ b/dist/datamodel/node/data/LookupTableNode.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"LookupTableNode.d.ts","sourceRoot":"","sources":["../../../../src/datamodel/node/data/LookupTableNode.ts"],"names":[],"mappings":"AACA,OAAO,kBAAkB,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAK3C,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,kBAAkB;IAC3D,WAAW,EAAE,MAAM,CAAC;gBACR,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClH,IAAI,IAAI,IAAI,MAAM,CAAuB;IACzC,IAAI,SAAS,IAAI,MAAM,CAAuB;IAE9C,IAAI,YAAY,IAAI,MAAM,CAAe;IACzC,IAAI,aAAa,IAAI,OAAO,CAAkB;IAC9C,aAAa,IAAI,SAAS,EAAE;IAE5B,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC/C,MAAM,KAAK,WAAW,IAAI,MAAM,CAA0B;IAQ1D,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,eAAe;IAC5E,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,eAAe;CACxG"} \ No newline at end of file +{"version":3,"file":"LookupTableNode.d.ts","sourceRoot":"","sources":["../../../../src/datamodel/node/data/LookupTableNode.ts"],"names":[],"mappings":"AACA,OAAO,kBAAkB,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAIxD,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,kBAAkB;IAO3D,WAAW,EAAE,MAAM,CAAC;gBACR,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClH,IAAI,IAAI,IAAI,MAAM,CAAuB;IACzC,IAAI,SAAS,IAAI,MAAM,CAAuB;IAE9C,IAAI,YAAY,IAAI,MAAM,CAAe;IACzC,IAAI,aAAa,IAAI,OAAO,CAAkB;IAC9C,aAAa,IAAI,SAAS,EAAE;IAG5B,IAAI,mBAAmB,IAAI,OAAO,CAAkB;IAGpD,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,iBAAiB;IAM5E,MAAM,KAAK,WAAW,IAAI,MAAM,CAA0B;IAQ1D,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,eAAe;IAC5E,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,eAAe;CACxG"} \ No newline at end of file diff --git a/dist/datamodel/node/data/LookupTableNode.js b/dist/datamodel/node/data/LookupTableNode.js index 49572f65..c53bf370 100644 --- a/dist/datamodel/node/data/LookupTableNode.js +++ b/dist/datamodel/node/data/LookupTableNode.js @@ -2,7 +2,6 @@ import SimulinkObjectNode from '../SimulinkObjectNode.js'; import PropName from '../../prop/PropName.js'; import PropDataType from '../../prop/PropDataType.js'; -import PropDescription from '../../prop/PropDescription.js'; const CLASS_NAME = 'Simulink.LookupTable'; export default class LookupTableNode extends SimulinkObjectNode { constructor(name, parent, props, serial) { super(name, parent, serial); this.Description = props.Description || ''; } @@ -11,9 +10,18 @@ export default class LookupTableNode extends SimulinkObjectNode { // A LookupTable has no scalar "value" — the Value column is empty and not editable. get displayValue() { return ''; } get valueEditable() { return false; } - getProperties() { return [PropName, PropDataType, PropDescription]; } + getProperties() { return [PropName, PropDataType]; } // PI layout: schema-driven "General" group (classes/lookupTable.json). - _serializedOverrides() { return this._gatedProps({ Description: this.Description }); } + // No editor in the table (`row._descriptionEditable`) or the Property Inspector. + get descriptionEditable() { return false; } + // An edit is refused in MATLAB's words, rather than accepted and saved as a key MATLAB + // ignores on load, which is what an edit here used to do. + setProperty(propName, stringValue) { + if (propName === 'Description') { + return { error: true, reason: `Unrecognized property 'Description' for class '${CLASS_NAME}'.`, invalidValue: stringValue, validValue: this.Description }; + } + return super.setProperty(propName, stringValue); + } static get defaultName() { return 'LookupTable'; } // MEASURED from what MATLAB R2027a writes for `Simulink.LookupTable` with nothing set, in // MATLAB's own alphabetical key order. Four nested objects and two scalars, where the bag used diff --git a/dist/datamodel/node/data/LookupTableNode.js.map b/dist/datamodel/node/data/LookupTableNode.js.map index b3fbd150..3a4e7574 100644 --- a/dist/datamodel/node/data/LookupTableNode.js.map +++ b/dist/datamodel/node/data/LookupTableNode.js.map @@ -1 +1 @@ -{"version":3,"file":"LookupTableNode.js","sourceRoot":"","sources":["../../../../src/datamodel/node/data/LookupTableNode.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,OAAO,kBAAkB,MAAM,0BAA0B,CAAC;AAG1D,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAC9C,OAAO,YAAY,MAAM,4BAA4B,CAAC;AACtD,OAAO,eAAe,MAAM,+BAA+B,CAAC;AAC5D,MAAM,UAAU,GAAG,sBAAsB,CAAC;AAC1C,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,kBAAkB;IAE3D,YAAY,IAAY,EAAE,MAAuB,EAAE,KAA8B,EAAE,MAA+B,IAAI,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,GAAI,KAAK,CAAC,WAAsB,IAAI,EAAE,CAAC,CAAC,CAAC;IAC5M,IAAI,IAAI,KAAa,OAAO,UAAU,CAAC,CAAC,CAAC;IACzC,IAAI,SAAS,KAAa,OAAO,UAAU,CAAC,CAAC,CAAC;IAC9C,oFAAoF;IACpF,IAAI,YAAY,KAAa,OAAO,EAAE,CAAC,CAAC,CAAC;IACzC,IAAI,aAAa,KAAc,OAAO,KAAK,CAAC,CAAC,CAAC;IAC9C,aAAa,KAAkB,OAAO,CAAC,QAAQ,EAAE,YAAY,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC;IAClF,uEAAuE;IACvE,oBAAoB,KAA8B,OAAO,IAAI,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/G,MAAM,KAAK,WAAW,KAAa,OAAO,aAAa,CAAC,CAAC,CAAC;IAC1D,0FAA0F;IAC1F,+FAA+F;IAC/F,4FAA4F;IAC5F,0FAA0F;IAC1F,8FAA8F;IAC9F,8FAA8F;IAC9F,qFAAqF;IACrF,MAAM,CAAC,aAAa,CAAC,IAAY,EAAE,MAAuB,IAAqB,MAAM,MAAM,GAAG,eAAe,CAAC,cAAc,CAAC,UAAU,EAAE,EAAE,+DAA+D,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,aAAa,EAAE,iCAAiC,EAAE,WAAW,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,eAAe,CAAC,iBAAiB,CAAC,WAAW,CAAC,EAAE,cAAc,EAAE,EAAE,aAAa,EAAE,qCAAqC,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,kBAAkB,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,aAAa,EAAE,4BAA4B,EAAE,WAAW,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,IAAI,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAA6B,CAAC,CAAC,CAAC,CAAC;IACh9B,MAAM,CAAC,KAAK,CAAC,MAA+B,EAAE,IAAY,EAAE,MAAuB,IAAqB,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,IAAI,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAA6B,CAAC,CAAC,CAAC,CAAC;CACjR"} \ No newline at end of file +{"version":3,"file":"LookupTableNode.js","sourceRoot":"","sources":["../../../../src/datamodel/node/data/LookupTableNode.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/datamodel/schema/classes/breakpoint.json b/dist/datamodel/schema/classes/breakpoint.json index a36d4119..8cad95c0 100644 --- a/dist/datamodel/schema/classes/breakpoint.json +++ b/dist/datamodel/schema/classes/breakpoint.json @@ -8,7 +8,7 @@ "supportTunableSize" ], "layout": [ - { "group": "General", "items": ["name", "value", "dataType", "kind", "class", "description"] }, + { "group": "General", "items": ["name", "value", "dataType", "kind", "class"] }, { "group": "Code Generation", "items": ["storageClass", "structTypeName", "structTypeDataScope", "structTypeHeaderFile"] }, { "group": "Advanced", "items": ["supportTunableSize"] } ] diff --git a/dist/datamodel/schema/classes/lookupTable.json b/dist/datamodel/schema/classes/lookupTable.json index 2ec50ecb..fd31dbf8 100644 --- a/dist/datamodel/schema/classes/lookupTable.json +++ b/dist/datamodel/schema/classes/lookupTable.json @@ -10,7 +10,7 @@ "supportTunableSize" ], "layout": [ - { "group": "General", "items": ["name", "value", "dataType", "kind", "class", "description"] }, + { "group": "General", "items": ["name", "value", "dataType", "kind", "class"] }, { "group": "Value Properties", "items": ["breakpointsSpecification"] }, { "group": "Code Generation", "items": ["storageClass", "structTypeName", "structTypeDataScope", "structTypeHeaderFile"] }, { "group": "Advanced", "items": ["allowDifferentTableBpSizes", "supportTunableSize"] } diff --git a/src/datamodel/node/data/BreakpointNode.ts b/src/datamodel/node/data/BreakpointNode.ts index 2dbc99aa..ed60c3ee 100644 --- a/src/datamodel/node/data/BreakpointNode.ts +++ b/src/datamodel/node/data/BreakpointNode.ts @@ -2,11 +2,17 @@ import SimulinkObjectNode from '../SimulinkObjectNode.js'; import type { PropClass } from '../BaseNode.js'; import type BaseNode from '../BaseNode.js'; +import type { SetPropertyResult } from '../DataNode.js'; import PropName from '../../prop/PropName.js'; import PropDataType from '../../prop/PropDataType.js'; -import PropDescription from '../../prop/PropDescription.js'; const CLASS_NAME = 'Simulink.Breakpoint'; export default class BreakpointNode extends SimulinkObjectNode { + // What the FILE says, shown read-only. MATLAB's Simulink.Breakpoint has no Description: + // `isprop` is 0, setting one fails with MATLAB:noPublicFieldForClass (R2027a, measured + // 2026-10-16), and a dictionary MATLAB writes carries no such key. A key some other writer + // left is read here for the Description column and written back exactly as read: nothing + // overrides it (`_serializedOverrides` is the inherited empty one), so the file's own bag + // passes through. Simulink.FunctionElement is the precedent (ServiceBusNode.ts). Description: string; constructor(name: string, parent: BaseNode | null, props: Record, serial: Record) { super(name, parent, serial); this.Description = (props.Description as string) || ''; } get icon(): string { return 'wsSimulinkBreakpoint'; } @@ -14,9 +20,18 @@ export default class BreakpointNode extends SimulinkObjectNode { // A Breakpoint has no scalar "value" — the Value column is empty and not editable. get displayValue(): string { return ''; } get valueEditable(): boolean { return false; } - getProperties(): PropClass[] { return [PropName, PropDataType, PropDescription]; } + getProperties(): PropClass[] { return [PropName, PropDataType]; } // PI layout: schema-driven "General" group (classes/breakpoint.json). - _serializedOverrides(): Record { return this._gatedProps({ Description: this.Description }); } + // No editor in the table (`row._descriptionEditable`) or the Property Inspector. + get descriptionEditable(): boolean { return false; } + // An edit is refused in MATLAB's words, rather than accepted and saved as a key MATLAB + // ignores on load, which is what an edit here used to do. + setProperty(propName: string, stringValue: string): true | SetPropertyResult { + if (propName === 'Description') { + return { error: true, reason: `Unrecognized property 'Description' for class '${CLASS_NAME}'.`, invalidValue: stringValue, validValue: this.Description }; + } + return super.setProperty(propName, stringValue); + } static get defaultName(): string { return 'Breakpoint'; } // MEASURED from what MATLAB R2027a writes for `Simulink.Breakpoint` with nothing set, in // MATLAB's own alphabetical key order. Three of the four properties are themselves nested diff --git a/src/datamodel/node/data/LookupTableNode.ts b/src/datamodel/node/data/LookupTableNode.ts index 10ba25fe..a1a4ec9d 100644 --- a/src/datamodel/node/data/LookupTableNode.ts +++ b/src/datamodel/node/data/LookupTableNode.ts @@ -2,11 +2,17 @@ import SimulinkObjectNode from '../SimulinkObjectNode.js'; import type { PropClass } from '../BaseNode.js'; import type BaseNode from '../BaseNode.js'; +import type { SetPropertyResult } from '../DataNode.js'; import PropName from '../../prop/PropName.js'; import PropDataType from '../../prop/PropDataType.js'; -import PropDescription from '../../prop/PropDescription.js'; const CLASS_NAME = 'Simulink.LookupTable'; export default class LookupTableNode extends SimulinkObjectNode { + // What the FILE says, shown read-only. MATLAB's Simulink.LookupTable has no Description: + // `isprop` is 0, setting one fails with MATLAB:noPublicFieldForClass (R2027a, measured + // 2026-10-16), and a dictionary MATLAB writes carries no such key. A key some other writer + // left is read here for the Description column and written back exactly as read: nothing + // overrides it (`_serializedOverrides` is the inherited empty one), so the file's own bag + // passes through. Simulink.FunctionElement is the precedent (ServiceBusNode.ts). Description: string; constructor(name: string, parent: BaseNode | null, props: Record, serial: Record) { super(name, parent, serial); this.Description = (props.Description as string) || ''; } get icon(): string { return 'wsLookup'; } @@ -14,9 +20,18 @@ export default class LookupTableNode extends SimulinkObjectNode { // A LookupTable has no scalar "value" — the Value column is empty and not editable. get displayValue(): string { return ''; } get valueEditable(): boolean { return false; } - getProperties(): PropClass[] { return [PropName, PropDataType, PropDescription]; } + getProperties(): PropClass[] { return [PropName, PropDataType]; } // PI layout: schema-driven "General" group (classes/lookupTable.json). - _serializedOverrides(): Record { return this._gatedProps({ Description: this.Description }); } + // No editor in the table (`row._descriptionEditable`) or the Property Inspector. + get descriptionEditable(): boolean { return false; } + // An edit is refused in MATLAB's words, rather than accepted and saved as a key MATLAB + // ignores on load, which is what an edit here used to do. + setProperty(propName: string, stringValue: string): true | SetPropertyResult { + if (propName === 'Description') { + return { error: true, reason: `Unrecognized property 'Description' for class '${CLASS_NAME}'.`, invalidValue: stringValue, validValue: this.Description }; + } + return super.setProperty(propName, stringValue); + } static get defaultName(): string { return 'LookupTable'; } // MEASURED from what MATLAB R2027a writes for `Simulink.LookupTable` with nothing set, in // MATLAB's own alphabetical key order. Four nested objects and two scalars, where the bag used diff --git a/src/datamodel/node/data/Simulink.Breakpoint.md b/src/datamodel/node/data/Simulink.Breakpoint.md index 79fe8e6d..30c6a39f 100644 --- a/src/datamodel/node/data/Simulink.Breakpoint.md +++ b/src/datamodel/node/data/Simulink.Breakpoint.md @@ -4,8 +4,10 @@ **Node class:** `BreakpointNode` (`src/datamodel/node/data/BreakpointNode.ts`) **MATLAB class:** `Simulink.Breakpoint` -**Editable in our UI:** Name, Description, and Storage Class (the table's Storage Class -column, a 17-value dropdown). Everything else is a read-only projection of the source bag. +**Editable in our UI:** Name and Storage Class (the table's Storage Class column, a +17-value dropdown). Everything else is a read-only projection of the source bag. There is +no Description: MATLAB's class has none, so none is offered and an edit to one is refused +(see "Description" below). **Verified against:** MATLAB R2027a (probe_class('Simulink.Breakpoint')) ## Overview @@ -15,19 +17,19 @@ table blocks. It contains a Breakpoints sub-object (with Value, DataType, Dimensions, Unit), CoderInfo, StructTypeInfo, and a SupportTunableSize flag. In our UI the Breakpoint has no scalar value: the Value column is empty and not editable (`valueEditable = false`). Its Property Inspector groups (from -`schema/classes/breakpoint.json`) are General (Name, Value, Data Type, Kind, Class, -Description), Code Generation (Storage Class and the three StructTypeInfo keys under their +`schema/classes/breakpoint.json`) are General (Name, Value, Data Type, Kind, Class), +Code Generation (Storage Class and the three StructTypeInfo keys under their MATLAB labels Name / Data Scope / Header File) and Advanced (Support Tunable Size) — plus the "Other" catch-all for what stays unmodelled. There is no Value Properties group, -because a Breakpoint has no `BreakpointsSpecification`. Every PI row but Name and -Description is a label; Storage Class is editable in the TABLE, not the PI. +because a Breakpoint has no `BreakpointsSpecification`. Every PI row but Name is a +label; Storage Class is editable in the TABLE, not the PI. ## Property table | Property | MATLAB type | SetAccess | Editable here | Serialized key (JSON / binary) | Editor | Allowed values / constraint | |---------------------|-----------------------------------|-----------|---------------|--------------------------------|--------|-----------------------------| | Name | char | (entry) | yes | name / name | text | Valid MATLAB identifier, unique in namespace | -| Description | char | public | yes | (nested or top-level) | text | Any string | +| Description | — (not a property) | — | no (refused) | top-level key, only as read | — | A key the file already has is shown read-only and kept | | Breakpoints | Simulink.lookuptable.Breakpoint | public | no | Breakpoints / Breakpoints | — | Sub-object (Value, DataType, Dimensions, Unit, FieldName) | | CoderInfo | Simulink.CoderInfo | protected | no | CoderInfo / CoderInfo | — | Sub-object | | StructTypeInfo | Simulink.lookuptable.StructTypeInfo | public | no | StructTypeInfo / StructTypeInfo | — | Sub-object (DataScope, HeaderFileName, Name) | @@ -55,10 +57,23 @@ parentheses where the two differ. ### Description -- The Breakpoint's Description (when present) is stored at the top level or - within nested sub-objects. Non-string assignment in MATLAB produces "Value must be - a character vector or a string scalar." — unreachable from our string editor. - Type-guarded by the string editor. +- `Simulink.Breakpoint` has no Description property. In MATLAB R2027a (27.1.0.3409468, + measured 2026-10-16) `isprop(Simulink.Breakpoint, 'Description')` is 0, and setting one + fails with MATLAB:noPublicFieldForClass, "Unrecognized property 'Description' for class + 'Simulink.Breakpoint'." A dictionary MATLAB writes carries no top-level Description key, + in either format. The `Breakpoints` sub-object has a Description of its own; that one is + nested, unmodelled, and passes through as read. +- So none is offered, as for `Simulink.FunctionElement`: Description is not in + `getProperties` or in the General group, `descriptionEditable` is false (the table cell + has no editor), and `setProperty('Description', …)` is refused in MATLAB's words and + leaves the entry unmodified. It used to be offered, accepted, and written as a top-level + key. +- A top-level Description key the file already has (written by another tool, or by this + package before the change) is shown read-only, in the Description column and in the + Property Inspector's "Other" group, and is written back as read on both save paths. + MATLAB opens such a file cleanly and ignores the key: measured in both formats with a key + added by hand and with our former writer's output, 14 of 14 entries read and no warning. + Test: `test/lutBreakpointNoDescription.test.ts`. ### Sub-objects @@ -127,7 +142,9 @@ unmodelled. identifier, unique in namespace, max 63 chars, not a keyword). Test: `test/parity/fidelity/typedef.fidelity.test.ts`. -- No additional validation for Description (any string accepted). +- Description: refused outright by `BreakpointNode.setProperty`, with MATLAB's message, + because the class has no such property. + Test: `test/lutBreakpointNoDescription.test.ts`. - `storageClass`: enforced by `trySetSchemaProperty` against this class's own 17-value `options` list — the only one of the five with a write path at all. A value outside the @@ -146,7 +163,9 @@ unmodelled. Test: `test/parity/fidelity/typedef.fidelity.test.ts` (json format loop). - Binary sldd: same. Test: `test/parity/fidelity/typedef.fidelity.test.ts` (binary format loop). -- MATLAB gate: Description edit → MATLAB reads back. PASS. +- MATLAB gate: none for Description, which MATLAB does not have. An earlier line here read + "Description edit → MATLAB reads back. PASS.", but that gate asserted only the class. + A file carrying a top-level Description key opens in MATLAB, and the key is ignored. - The five flat properties, in-process only: a configured Breakpoint renders all five, including the two that travel through a nested sub-object, and a `storageClass` write lands at `CoderInfo.StorageClass`. **No MATLAB re-open gate was run for any of the five.** @@ -154,14 +173,6 @@ unmodelled. ## Open questions / deferred -- **Description not a MATLAB property**: MATLAB's Simulink.Breakpoint does NOT - expose a top-level `Description` property (attempting `obj.Description` errors: - "Unrecognized method, property, or field 'Description' for class - 'Simulink.Breakpoint'"). Our node stores/restores Description via the JSON - `_properties` for in-process round-trip fidelity, but the MATLAB gate cannot - assert it via the standard property-read path. The field is preserved in the - serialized file but is not accessible from the MATLAB API at the top level. - - **Breakpoint array editing**: The numeric data in Breakpoints.Value is a 1-D array that requires a dedicated editor UX. Not exposed via the scalar text editor; fully deferred. diff --git a/src/datamodel/node/data/Simulink.LookupTable.md b/src/datamodel/node/data/Simulink.LookupTable.md index c63e26f5..3be11472 100644 --- a/src/datamodel/node/data/Simulink.LookupTable.md +++ b/src/datamodel/node/data/Simulink.LookupTable.md @@ -4,8 +4,10 @@ **Node class:** `LookupTableNode` (`src/datamodel/node/data/LookupTableNode.ts`) **MATLAB class:** `Simulink.LookupTable` -**Editable in our UI:** Name, Description, and Storage Class (the table's Storage Class -column, a 17-value dropdown). Everything else is a read-only projection of the source bag. +**Editable in our UI:** Name and Storage Class (the table's Storage Class column, a +17-value dropdown). Everything else is a read-only projection of the source bag. There is +no Description: MATLAB's class has none, so none is offered and an edit to one is refused +(see "Description" below). **Verified against:** MATLAB R2027a (probe_class('Simulink.LookupTable')) ## Overview @@ -15,18 +17,18 @@ blocks. It contains a Table sub-object, one or more Breakpoint sub-objects, CoderInfo, StructTypeInfo, and configuration flags. In our UI the LookupTable has no scalar value: the Value column is empty and not editable (`valueEditable = false`). Its Property Inspector groups (from `schema/classes/lookupTable.json`) are General (Name, -Value, Data Type, Kind, Class, Description), Value Properties (Breakpoints +Value, Data Type, Kind, Class), Value Properties (Breakpoints Specification), Code Generation (Storage Class, and the three StructTypeInfo keys under their MATLAB labels Name / Data Scope / Header File) and Advanced (the two tunable-size switches) — plus the "Other" catch-all for what stays unmodelled. Every PI row but Name -and Description is a label; Storage Class is editable in the TABLE, not the PI. +is a label; Storage Class is editable in the TABLE, not the PI. ## Property table | Property | MATLAB type | SetAccess | Editable here | Serialized key (JSON / binary) | Editor | Allowed values / constraint | |---------------------|------------------------------|-----------|---------------|--------------------------------|--------|-----------------------------| | Name | char | (entry) | yes | name / name | text | Valid MATLAB identifier, unique in namespace | -| Description | char | public | yes | (nested in sub-objects) | text | Any string | +| Description | — (not a property) | — | no (refused) | top-level key, only as read | — | A key the file already has is shown read-only and kept | | Table | Simulink.lookuptable.Table | public | no | Table / Table | — | Sub-object (Value, DataType, Dimensions, Unit, FieldName) | | Breakpoints | Simulink.lookuptable.Breakpoint | public | no | Breakpoints / Breakpoints | — | Sub-object (Value, DataType, Dimensions, Unit, FieldName) | | CoderInfo | Simulink.CoderInfo | protected | no | CoderInfo / CoderInfo | — | Sub-object | @@ -56,10 +58,23 @@ in parentheses where the two differ. ### Description -- The LookupTable's Description property is stored at the top-level _properties - (when present). Non-string assignment in MATLAB produces "Value must be a - character vector or a string scalar." — unreachable from our string editor. - Type-guarded by the string editor. +- `Simulink.LookupTable` has no Description property. In MATLAB R2027a (27.1.0.3409468, + measured 2026-10-16) `isprop(Simulink.LookupTable, 'Description')` is 0, and setting one + fails with MATLAB:noPublicFieldForClass, "Unrecognized property 'Description' for class + 'Simulink.LookupTable'." A dictionary MATLAB writes carries no top-level Description key, + in either format. The `Table` and `Breakpoints` sub-objects each have a Description of + their own; those are nested, unmodelled, and pass through as read. +- So none is offered, as for `Simulink.FunctionElement`: Description is not in + `getProperties` or in the General group, `descriptionEditable` is false (the table cell + has no editor), and `setProperty('Description', …)` is refused in MATLAB's words and + leaves the entry unmodified. It used to be offered, accepted, and written as a top-level + key. +- A top-level Description key the file already has (written by another tool, or by this + package before the change) is shown read-only, in the Description column and in the + Property Inspector's "Other" group, and is written back as read on both save paths. + MATLAB opens such a file cleanly and ignores the key: measured in both formats with a key + added by hand and with our former writer's output, 14 of 14 entries read and no warning. + Test: `test/lutBreakpointNoDescription.test.ts`. ### Sub-objects @@ -175,7 +190,9 @@ read-only and unmodelled. identifier, unique in namespace, max 63 chars, not a keyword). Test: `test/parity/fidelity/typedef.fidelity.test.ts`. -- No additional validation for Description (any string accepted). +- Description: refused outright by `LookupTableNode.setProperty`, with MATLAB's message, + because the class has no such property. + Test: `test/lutBreakpointNoDescription.test.ts`. - `storageClass`: enforced by `trySetSchemaProperty` against the class's own 17-value `options` list, returning `{ error: true, reason: 'Invalid value for Storage Class' }` on @@ -194,7 +211,9 @@ read-only and unmodelled. Test: `test/parity/fidelity/typedef.fidelity.test.ts` (json format loop). - Binary sldd: same. Test: `test/parity/fidelity/typedef.fidelity.test.ts` (binary format loop). -- MATLAB gate: Description edit → MATLAB reads back. PASS. +- MATLAB gate: none for Description, which MATLAB does not have. An earlier line here read + "Description edit → MATLAB reads back. PASS.", but that gate asserted only the class. + A file carrying a top-level Description key opens in MATLAB, and the key is ignored. - The six flat properties, in-process only: a LookupTable that carries them renders every value including the two nested paths; one that omits them renders each descriptor default; and both bags keep exactly the keys they arrived with on both save paths after @@ -204,14 +223,6 @@ read-only and unmodelled. ## Open questions / deferred -- **Description not a MATLAB property**: MATLAB's Simulink.LookupTable does NOT - expose a top-level `Description` property (attempting `obj.Description` errors: - "Unrecognized method, property, or field 'Description' for class - 'Simulink.LookupTable'"). Our node stores/restores Description via the JSON - `_properties` for in-process round-trip fidelity, but the MATLAB gate cannot - assert it via the standard property-read path. The field is preserved in the - serialized file but is not accessible from the MATLAB API at the top level. - - **Table/Breakpoint array editing**: The numeric data in Table.Value and Breakpoints[].Value is complex multi-dimensional data that requires a dedicated editor UX. Not exposed via the scalar text editor; fully deferred. diff --git a/src/datamodel/schema/classes/breakpoint.json b/src/datamodel/schema/classes/breakpoint.json index 6e8f1c73..978ec515 100644 --- a/src/datamodel/schema/classes/breakpoint.json +++ b/src/datamodel/schema/classes/breakpoint.json @@ -8,7 +8,7 @@ "supportTunableSize" ], "layout": [ - { "group": "General", "items": ["name", "value", "dataType", "kind", "class", "description"] }, + { "group": "General", "items": ["name", "value", "dataType", "kind", "class"] }, { "group": "Code Generation", "items": ["storageClass", "structTypeName", "structTypeDataScope", "structTypeHeaderFile"] }, { "group": "Advanced", "items": ["supportTunableSize"] } ] diff --git a/src/datamodel/schema/classes/lookupTable.json b/src/datamodel/schema/classes/lookupTable.json index eb7f687c..9cf333d4 100644 --- a/src/datamodel/schema/classes/lookupTable.json +++ b/src/datamodel/schema/classes/lookupTable.json @@ -10,7 +10,7 @@ "supportTunableSize" ], "layout": [ - { "group": "General", "items": ["name", "value", "dataType", "kind", "class", "description"] }, + { "group": "General", "items": ["name", "value", "dataType", "kind", "class"] }, { "group": "Value Properties", "items": ["breakpointsSpecification"] }, { "group": "Code Generation", "items": ["storageClass", "structTypeName", "structTypeDataScope", "structTypeHeaderFile"] }, { "group": "Advanced", "items": ["allowDifferentTableBpSizes", "supportTunableSize"] } diff --git a/test/absentPropertyWriteBack.test.ts b/test/absentPropertyWriteBack.test.ts index 217a8e4a..c064ce4e 100644 --- a/test/absentPropertyWriteBack.test.ts +++ b/test/absentPropertyWriteBack.test.ts @@ -1,10 +1,10 @@ // Copyright 2026 The MathWorks, Inc. // -// ONE WRITE-BACK RULE, TWO SAVE PATHS, ELEVEN CLASSES. +// ONE WRITE-BACK RULE, TWO SAVE PATHS, NINE CLASSES. // -// The eleven, so the count above can be checked rather than trusted: NumericType, -// AliasType, ValueType, Signal, Breakpoint, LookupTable and the registered CustomObject -// are the `CASES` table below; Parameter, BusElement, ConfigSet and ConfigSetRef have +// The nine, so the count above can be checked rather than trusted: NumericType, +// AliasType, ValueType, Signal and the registered CustomObject are the `CASES` table +// below; Parameter, BusElement, ConfigSet and ConfigSetRef have // their own blocks, because a bound and an ungated key are not the table's shape. A bus // is not counted separately — it appears only as the thing a BusElement is saved inside. // @@ -122,8 +122,6 @@ import AliasTypeNode from '../src/datamodel/node/data/AliasTypeNode.js'; import ValueTypeNode from '../src/datamodel/node/data/ValueTypeNode.js'; import SignalNode from '../src/datamodel/node/data/SignalNode.js'; import ParameterNode from '../src/datamodel/node/data/ParameterNode.js'; -import BreakpointNode from '../src/datamodel/node/data/BreakpointNode.js'; -import LookupTableNode from '../src/datamodel/node/data/LookupTableNode.js'; import CustomObjectNode from '../src/datamodel/node/data/CustomObjectNode.js'; import ConfigSetNode from '../src/datamodel/node/data/ConfigSetNode.js'; import ConfigSetRefNode from '../src/datamodel/node/data/ConfigSetRefNode.js'; @@ -213,29 +211,16 @@ const CASES: Case[] = [ key: 'Description', typed: 'commanded torque', }, - // The three classes below hold nothing but a Description, so today their two save - // paths are character-for-character the same gate. That identity is not a property of - // the code, though — it is still two hand-written copies — so the next person who - // teaches one of these classes a second property, or hoists one of its paths onto a - // shared helper, can change one method and leave the other behind with nothing - // objecting. Being trivially correct today is the reason to pin them here, not a - // reason to leave them out. - { - label: 'Simulink.Breakpoint', - className: 'Simulink.Breakpoint', - parse: (raw, name) => BreakpointNode.parse(raw, name, null), - prop: 'Description', - key: 'Description', - typed: 'engine speed breakpoints, 0:500:6000 rpm', - }, - { - label: 'Simulink.LookupTable', - className: 'Simulink.LookupTable', - parse: (raw, name) => LookupTableNode.parse(raw, name, null), - prop: 'Description', - key: 'Description', - typed: 'engine torque vs. speed and throttle', - }, + // The class below holds nothing but a Description, so today its two save paths are + // character-for-character the same gate. That identity is not a property of the code, + // though — it is still two hand-written copies — so the next person who teaches this + // class a second property, or hoists one of its paths onto a shared helper, can change + // one method and leave the other behind with nothing objecting. Being trivially correct + // today is the reason to pin it here, not a reason to leave it out. + // + // Breakpoint and LookupTable were rows here until 2026-10-16. MATLAB's two classes have + // no Description property, so neither node takes an edit to one any more, and each + // writes back the file's bag as read (test/lutBreakpointNoDescription.test.ts). { // Not a Simulink class name: `CustomObject` is the literal `_array_class` this // package registers a node for, and the Class column shows it verbatim. diff --git a/test/lookupTableFlatProps.test.ts b/test/lookupTableFlatProps.test.ts index 64da2330..70899596 100644 --- a/test/lookupTableFlatProps.test.ts +++ b/test/lookupTableFlatProps.test.ts @@ -198,7 +198,7 @@ describe('PI layout — groups and ordered keys, matching MATLAB', () => { it('LookupTable: General, Value Properties, Code Generation, Advanced', () => { const n = lut(LUT_CONFIGURED()); expect(groupTitles(n)).toEqual(['General', 'Value Properties', 'Code Generation', 'Advanced', 'Other']); - expect(itemsIn(n, 'General')).toEqual(['Name', 'Value', 'DataType', 'Kind', 'Class', 'Description']); + expect(itemsIn(n, 'General')).toEqual(['Name', 'Value', 'DataType', 'Kind', 'Class']); expect(itemsIn(n, 'Value Properties')).toEqual(['breakpointsSpecification']); expect(itemsIn(n, 'Code Generation')).toEqual([ 'storageClass', 'structTypeName', 'structTypeDataScope', 'structTypeHeaderFile', @@ -209,7 +209,7 @@ describe('PI layout — groups and ordered keys, matching MATLAB', () => { it('Breakpoint: General, Code Generation, Advanced (no Value Properties)', () => { const n = bp(BP_CONFIGURED()); expect(groupTitles(n)).toEqual(['General', 'Code Generation', 'Advanced', 'Other']); - expect(itemsIn(n, 'General')).toEqual(['Name', 'Value', 'DataType', 'Kind', 'Class', 'Description']); + expect(itemsIn(n, 'General')).toEqual(['Name', 'Value', 'DataType', 'Kind', 'Class']); expect(itemsIn(n, 'Code Generation')).toEqual([ 'storageClass', 'structTypeName', 'structTypeDataScope', 'structTypeHeaderFile', ]); diff --git a/test/lutBreakpointNoDescription.test.ts b/test/lutBreakpointNoDescription.test.ts new file mode 100644 index 00000000..25cd7434 --- /dev/null +++ b/test/lutBreakpointNoDescription.test.ts @@ -0,0 +1,181 @@ +// Copyright 2026 The MathWorks, Inc. +// +// A Simulink.LookupTable and a Simulink.Breakpoint have no Description, so neither offers one. +// +// Measured in R2027a (27.1.0.3409468): `isprop(Simulink.LookupTable, 'Description')` is 0, and +// the same for `Simulink.Breakpoint`. Setting one fails with MATLAB:noPublicFieldForClass, +// "Unrecognized property 'Description' for class 'Simulink.LookupTable'." The class properties +// are Table, BreakpointsSpecification, Breakpoints, AllowMultipleInstancesOf..., SupportTunable- +// Size, CoderInfo and StructTypeInfo, and a Breakpoint has four of them. A dictionary MATLAB +// writes carries no top-level Description key for either class, in either format. +// +// Both nodes used to list one anyway, so the table and the Property Inspector offered an +// editor, `setProperty('Description', …)` returned true, and both save paths wrote the text as +// a top-level key. MATLAB opens such a file without a warning and ignores the key, so the edit +// was accepted, saved, and had no effect. The ruling (2026-10-16, group E, theme 2: follow +// MATLAB) is what Simulink.FunctionElement already does (ServiceBusNode.ts): no Description +// property, an edit to one refused, and a key the file already has written back as it was read. +// +// Driven through the session API a host uses — `ingest`, the node's own `setProperty`, `toRow` +// and `toPIObject`, and `serializeSource` — over the MATLAB-written parity dictionaries in both +// formats, and over `createDefault`, which is what a new entry starts as. +import { describe, it, expect } from 'vitest'; +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { strFromU8, strToU8, unzipSync, zipSync } from 'fflate'; +import { createSession, ingest, type SerializedSource } from '../src/index.js'; +import LookupTableNode from '../src/datamodel/node/data/LookupTableNode.js'; +import BreakpointNode from '../src/datamodel/node/data/BreakpointNode.js'; +import { resolveSourcePath } from '../src/datamodel/schema/index.js'; +import type DataNode from '../src/datamodel/node/DataNode.js'; +import '../src/datamodel/node/data/NodeClassMap.js'; + +type Format = 'text' | 'binary'; + +const CLASSES = [ + { className: 'Simulink.LookupTable', entry: 'MyLUT', make: (n: string) => LookupTableNode.createDefault(n, null) }, + { className: 'Simulink.Breakpoint', entry: 'MyBkpt', make: (n: string) => BreakpointNode.createDefault(n, null) }, +]; +const FORMATS: Format[] = ['text', 'binary']; + +const GENERAL = ['Name', 'Value', 'DataType', 'Kind', 'Class']; + +function fixture(format: Format): Uint8Array { + return new Uint8Array(readFileSync(fileURLToPath(new URL(`./parity/artifacts/${format}/params.sldd`, import.meta.url)))); +} + +// The same dictionary with a top-level `Description` key added to one entry's bag, as some +// other writer (or this package, before this change) leaves it. Added to the bytes, so the +// reader is what puts it in the node's bag. +function withDescription(format: Format, entry: string, className: string, text: string): Uint8Array | string { + const bytes = fixture(format); + if (format === 'text') { + const json = JSON.parse(strFromU8(bytes)); + const entries = json.__MW_TEXT_PARTS__['__MW_TEXT_PART__/data/chunk0'].__MW_TEXT_content.entries as any[]; + const hit = entries.find((e) => e.name === entry); + expect(hit, `no ${entry} in the text fixture`).toBeDefined(); + const bag = hit.value._elements[0]._properties as Record; + expect(bag).not.toHaveProperty('Description'); + bag.Description = text; + return JSON.stringify(json, null, '\t'); + } + const parts = unzipSync(bytes); + const xml = strFromU8(parts['data/chunk0.xml']); + const open = ``; + expect(xml.split(open).length - 1, `one ${className} in the binary fixture`).toBe(1); + parts['data/chunk0.xml'] = strToU8(xml.replace(open, `${open}\n

${text}

`)); + return zipSync(parts); +} + +function entryIn(root: any, name: string): DataNode { + const stack = [root]; + while (stack.length) { + const n = stack.shift(); + if (n?.name === name && n.className && !String(n.id ?? '').startsWith('section:')) { + return n as DataNode; + } + stack.push(...(n?.children ?? [])); + } + throw new Error(`no entry "${name}"`); +} + +function open(content: Uint8Array | string) { + const session = createSession(); + const source: any = ingest(session, content, { filename: 'params.sldd' }); + return { session, source }; +} + +function saved(session: any, source: any): Uint8Array | string { + const out = session.serializeSource(source.name) as SerializedSource; + return out.kind === 'binary' ? out.bytes : out.text; +} + +// The entry's top-level property bag as the saved file carries it, read back by the reader. +function savedBag(content: Uint8Array | string, entry: string): Record { + return (entryIn(open(content).source, entry) as any).serial._properties as Record; +} + +// The two save paths, each reduced to the bag it puts in the file. +const binaryPath = (node: DataNode) => node._getSerializedProperties(); +const textPath = (node: DataNode) => + JSON.parse(JSON.stringify((node.serializeValue() as any)._elements[0]._properties)) as Record; + +function generalItems(node: DataNode): string[] { + const groups = node.toPIObject()!.propertySheet.groups as any[]; + return groups.find((g) => g.displayName === 'General').items.map((i: any) => i.name); +} + +function expectRefused(node: DataNode, className: string, text: string) { + const result = node.setProperty('Description', text); + expect(result).not.toBe(true); + expect(result).toMatchObject({ error: true, invalidValue: text }); + expect((result as { reason: string }).reason).toContain( + `Unrecognized property 'Description' for class '${className}'.`, + ); +} + +for (const c of CLASSES) { + describe(`${c.className} has no Description`, () => { + for (const format of FORMATS) { + describe(`${c.entry}, ${format} dictionary`, () => { + it('refuses a Description edit, and no save writes one', () => { + const { session, source } = open(fixture(format)); + const node = entryIn(source, c.entry); + expect(node.className).toBe(c.className); + + expectRefused(node, c.className, 'throttle map'); + expect(node.status).not.toBe('Modified'); + expect(binaryPath(node)).not.toHaveProperty('Description'); + expect(textPath(node)).not.toHaveProperty('Description'); + + // An unrelated edit rewrites the entry from the node, so the save reaches the writer + // (a binary entry nobody edited is written as the bytes it was read from). + expect(node.setProperty('storageClass', 'ExportedGlobal')).toBe(true); + const bag = savedBag(saved(session, source), c.entry); + expect(resolveSourcePath(bag, 'CoderInfo.StorageClass')).toBe('ExportedGlobal'); + expect(bag).not.toHaveProperty('Description'); + }); + + it('offers no Description editor in the table or the Property Inspector', () => { + const node = entryIn(open(fixture(format)).source, c.entry); + expect(node.getProperties().map((p) => p.key)).not.toContain('Description'); + expect(node.toRow()!._descriptionEditable).toBe(false); + expect(generalItems(node)).toEqual(GENERAL); + const piProps = node.toPIObject()!.propertySheet.properties as any[]; + expect(piProps.filter((p) => p.editable && /Description/.test(p.name))).toEqual([]); + }); + + it('keeps a Description the file already has, as read and read-only, on both save paths', () => { + const { session, source } = open(withDescription(format, c.entry, c.className, 'kept')); + const node = entryIn(source, c.entry); + + const row = node.toRow()!; + expect(row.Description).toBe('kept'); + expect(row._descriptionEditable).toBe(false); + const pi = node.toPIObject()!; + expect((pi.objects[0] as Record)['Other.Description']).toBe('kept'); + const other = (pi.propertySheet.properties as any[]).find((p) => p.name === 'Other.Description'); + expect(other.editable).toBe(false); + + expectRefused(node, c.className, 'changed'); + expect(node.setProperty('storageClass', 'ExportedGlobal')).toBe(true); + expect(binaryPath(node).Description).toBe('kept'); + expect(textPath(node).Description).toBe('kept'); + + const bag = savedBag(saved(session, source), c.entry); + expect(resolveSourcePath(bag, 'CoderInfo.StorageClass')).toBe('ExportedGlobal'); + expect(bag.Description).toBe('kept'); + }); + }); + } + + it('a new entry (createDefault) refuses one and saves none', () => { + const node = c.make('n'); + expectRefused(node, c.className, 'x'); + expect(node.toRow()!._descriptionEditable).toBe(false); + expect(generalItems(node)).toEqual(GENERAL); + expect(binaryPath(node)).not.toHaveProperty('Description'); + expect(textPath(node)).not.toHaveProperty('Description'); + }); + }); +} diff --git a/test/parity/fidelity/typedef.fidelity.test.ts b/test/parity/fidelity/typedef.fidelity.test.ts index a25cddd9..72e65f66 100644 --- a/test/parity/fidelity/typedef.fidelity.test.ts +++ b/test/parity/fidelity/typedef.fidelity.test.ts @@ -107,22 +107,25 @@ for (const format of ['json', 'binary'] as SlddFormat[]) { expect(entry.displayValue).toBe(''); }); - it('Description edit round-trips in-process', { timeout: MATLAB_TIMEOUT }, () => { + it('Description edit is refused; the file\'s key is unchanged', { timeout: MATLAB_TIMEOUT }, () => { const { model, entry } = freshLUT(); - expect(entry.setProperty('Description', 'throttle map')).toBe(true); - expect(entry.Description).toBe('throttle map'); + // MATLAB's Simulink.LookupTable has no Description property: setting one fails with + // "Unrecognized property 'Description' for class 'Simulink.LookupTable'." (R2027a, + // measured 2026-10-16), so the edit is refused, and the file, which has no + // top-level Description key for this entry, still has none after a save. + // test/lutBreakpointNoDescription.test.ts covers a file that does carry one. + const result = entry.setProperty('Description', 'throttle map'); + expect(result).not.toBe(true); + expect(result.error).toBe(true); + expect(entry.Description).toBe(''); const bytes = serializeModel(model, format); const fresh = reparseEntry(bytes, format, 'params.sldd', 'MyLUT'); - expect(fresh.Description).toBe('throttle map'); - - // NOTE: MATLAB's Simulink.LookupTable does NOT expose a top-level - // Description property (it errors: "Unrecognized method, property, or - // field 'Description' for class 'Simulink.LookupTable'"). The - // Description we serialize is a node-level field preserved by our - // round-trip but not readable via obj.Description in MATLAB. Gate only - // asserts class. + expect(fresh.Description).toBe(''); + expect(fresh.serial._properties).not.toHaveProperty('Description'); + + // The gate asserts the class: there is no Description for it to read back. if (matlabAvailable()) { const out = matlabAssertRoundTrip(bytes, 'MyLUT', { __class__: 'Simulink.LookupTable', @@ -148,22 +151,25 @@ for (const format of ['json', 'binary'] as SlddFormat[]) { expect(entry.displayValue).toBe(''); }); - it('Description edit round-trips in-process', { timeout: MATLAB_TIMEOUT }, () => { + it('Description edit is refused; the file\'s key is unchanged', { timeout: MATLAB_TIMEOUT }, () => { const { model, entry } = freshBkpt(); - expect(entry.setProperty('Description', 'speed breakpoints')).toBe(true); - expect(entry.Description).toBe('speed breakpoints'); + // MATLAB's Simulink.Breakpoint has no Description property: setting one fails with + // "Unrecognized property 'Description' for class 'Simulink.Breakpoint'." (R2027a, + // measured 2026-10-16), so the edit is refused, and the file, which has no + // top-level Description key for this entry, still has none after a save. + // test/lutBreakpointNoDescription.test.ts covers a file that does carry one. + const result = entry.setProperty('Description', 'speed breakpoints'); + expect(result).not.toBe(true); + expect(result.error).toBe(true); + expect(entry.Description).toBe(''); const bytes = serializeModel(model, format); const fresh = reparseEntry(bytes, format, 'params.sldd', 'MyBkpt'); - expect(fresh.Description).toBe('speed breakpoints'); - - // NOTE: MATLAB's Simulink.Breakpoint does NOT expose a top-level - // Description property (it errors: "Unrecognized method, property, or - // field 'Description' for class 'Simulink.Breakpoint'"). The - // Description we serialize is a node-level field preserved by our - // round-trip but not readable via obj.Description in MATLAB. Gate only - // asserts class. + expect(fresh.Description).toBe(''); + expect(fresh.serial._properties).not.toHaveProperty('Description'); + + // The gate asserts the class: there is no Description for it to read back. if (matlabAvailable()) { const out = matlabAssertRoundTrip(bytes, 'MyBkpt', { __class__: 'Simulink.Breakpoint', diff --git a/test/schema/piGeneralAllNodes.test.ts b/test/schema/piGeneralAllNodes.test.ts index 1a761884..87582bb7 100644 --- a/test/schema/piGeneralAllNodes.test.ts +++ b/test/schema/piGeneralAllNodes.test.ts @@ -83,11 +83,20 @@ describe('common "General" identity group — schema-driven classes', () => { }); }); - it('LookupTable / Breakpoint / CustomObject keep Description in General', () => { - for (const M of [LookupTableNode, BreakpointNode, CustomObjectNode]) { + it('CustomObject keeps Description in General', () => { + expect(firstGroup(CustomObjectNode.createDefault('n', null))).toEqual({ + name: 'General', + items: ['Name', 'Value', 'DataType', 'Kind', 'Class', 'Description'], + }); + }); + + it('LookupTable / Breakpoint open with the identity five and no Description', () => { + // MATLAB's Simulink.LookupTable and Simulink.Breakpoint have no Description property + // (isprop is 0 in R2027a), so neither shows one. test/lutBreakpointNoDescription.test.ts. + for (const M of [LookupTableNode, BreakpointNode]) { expect(firstGroup(M.createDefault('n', null))).toEqual({ name: 'General', - items: ['Name', 'Value', 'DataType', 'Kind', 'Class', 'Description'], + items: ['Name', 'Value', 'DataType', 'Kind', 'Class'], }); } }); diff --git a/test/schemaLayoutResolvable.test.ts b/test/schemaLayoutResolvable.test.ts index db7dc8e3..21b3bc76 100644 --- a/test/schemaLayoutResolvable.test.ts +++ b/test/schemaLayoutResolvable.test.ts @@ -537,15 +537,17 @@ describe('the guard is actually reading the schema tree', () => { }); it('sweeps the number of (class, key) pairs it claims to', () => { - // Measured on the tree as it stands: 20 classes, 182 (class, key) pairs, 85 of them - // computed-and-exempt, 97 with a source location — which become 100 probe cases, + // Measured on the tree as it stands: 20 classes, 180 (class, key) pairs, 85 of them + // computed-and-exempt, 95 with a source location — which become 98 probe cases, // because `unit` declares two raw spellings and each is a location a real file uses. + // (182, 97 and 100 until 2026-10-16, when the LookupTable and Breakpoint layouts lost + // their `description` key: MATLAB's two classes have no Description property.) expect(SCHEMA_CLASSES.length).toBeGreaterThanOrEqual(20); - expect(allPairs.length).toBeGreaterThanOrEqual(182); + expect(allPairs.length).toBeGreaterThanOrEqual(180); expect(exemptPairs.length).toBeGreaterThanOrEqual(85); // The floor that matters most: this is the number of pairs actually PROBED. Every // other count can hold while this one collapses. - expect(sweepCases.length).toBeGreaterThanOrEqual(100); + expect(sweepCases.length).toBeGreaterThanOrEqual(98); // And the two halves account for the WHOLE: every pair enumerated is either exempt or // probed, so one cannot fall out of both. Counted through a Set because `unit` // contributes two cases for one pair, and compared for EQUALITY — a floor here would From bbbb4f194223d0f5e3b9f0ef1d86f29bda73a50b Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 16 Oct 2026 16:02:19 -0400 Subject: [PATCH 2/7] Stop offering and writing a Value on VariantBank, which MATLAB's class does not have MATLAB R2027a has no Value property on Simulink.VariantBank: its properties are Name, Description, VariantConditions, AllChoicesCoderInfo and ActiveChoiceCoderInfo, isprop(..., 'Value') is 0, and setting one fails with "Unrecognized property 'Value' for class 'Simulink.VariantBank'." The node offered one anyway: the cell took an editor, setProperty('Value', ...) was accepted, and both save paths wrote the text beside the saveobj envelope. MATLAB opens such a file with no error or warning, builds a bank with no Value, and drops the key on its next save, so the edit had no effect. Every bank MATLAB wrote also showed '', a char value the class does not have. The node now does what a bus element does (#113). valueEditable is false, a Value edit is refused ("'' is a Simulink.VariantBank, which has no Value, so none can be set on it.") and leaves the entry unmodified, and _serializedOverrides is empty, so no save writes a Value. A Value key a file already carries (only ever this package's) is shown quoted, read-only, and written back as read. A bank with no Value key shows an empty cell. The Value column stays, read-only, for that key. test/variantBankNoValue.test.ts covers the bank MATLAB wrote, in both formats, and a new entry. Its fixtures are test/fixtures/classes_r2027a_text.sldd and classes_r2027a_binary.sldd, written by MATLAB R2027a, with their generator, make_classes_r2027a.m. A re-run gives the same files apart from UUIDs and timestamps. Existing tests changed only where they pinned the removed edit. In variantLeafNodes, the VariantBank row of the shared contract now checks that an edit is refused and the bag is written back as read, instead of checking that the edit is written. Its two cases in the quoted-display regression, which commit a Value, are removed. The class doc records the measurement, and two comments that cited VariantBank's Value as written are corrected. --- dist/datamodel/node/data/VariantBankNode.d.ts | 4 + .../node/data/VariantBankNode.d.ts.map | 2 +- dist/datamodel/node/data/VariantBankNode.js | 44 +- .../node/data/VariantBankNode.js.map | 2 +- .../VariantConfigurationDataNode.d.ts.map | 2 +- .../node/data/VariantConfigurationDataNode.js | 3 +- .../data/VariantConfigurationDataNode.js.map | 2 +- dist/datamodel/prop/PropValue.js | 2 +- dist/datamodel/prop/PropValue.js.map | 2 +- .../node/data/Simulink.VariantBank.md | 65 +- src/datamodel/node/data/VariantBankNode.ts | 50 +- .../node/data/VariantConfigurationDataNode.ts | 3 +- src/datamodel/prop/PropValue.ts | 2 +- test/fixtures/classes_r2027a_binary.sldd | Bin 0 -> 4465 bytes test/fixtures/classes_r2027a_text.sldd | 715 ++++++++++++++++++ test/fixtures/make_classes_r2027a.m | 70 ++ test/variantBankNoValue.test.ts | 242 ++++++ test/variantLeafNodes.test.ts | 33 +- 18 files changed, 1181 insertions(+), 62 deletions(-) create mode 100644 test/fixtures/classes_r2027a_binary.sldd create mode 100644 test/fixtures/classes_r2027a_text.sldd create mode 100644 test/fixtures/make_classes_r2027a.m create mode 100644 test/variantBankNoValue.test.ts diff --git a/dist/datamodel/node/data/VariantBankNode.d.ts b/dist/datamodel/node/data/VariantBankNode.d.ts index 9c1f9cde..bdf67af3 100644 --- a/dist/datamodel/node/data/VariantBankNode.d.ts +++ b/dist/datamodel/node/data/VariantBankNode.d.ts @@ -1,13 +1,17 @@ import SimulinkObjectNode from '../SimulinkObjectNode.js'; import type { PropClass } from '../BaseNode.js'; import type BaseNode from '../BaseNode.js'; +import type { SetPropertyResult } from '../DataNode.js'; export default class VariantBankNode extends SimulinkObjectNode { Value: unknown; + readonly _valueInFile: boolean; constructor(name: string, parent: BaseNode | null, props: Record, serial: Record); get icon(): string; get className(): string; get displayValue(): string; + get valueEditable(): boolean; getProperties(): PropClass[]; + setProperty(propName: string, stringValue: string): true | SetPropertyResult; _serializedOverrides(): Record; static get defaultName(): string; static createDefault(name: string, parent: BaseNode | null): VariantBankNode; diff --git a/dist/datamodel/node/data/VariantBankNode.d.ts.map b/dist/datamodel/node/data/VariantBankNode.d.ts.map index a90cbf3e..97b51140 100644 --- a/dist/datamodel/node/data/VariantBankNode.d.ts.map +++ b/dist/datamodel/node/data/VariantBankNode.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"VariantBankNode.d.ts","sourceRoot":"","sources":["../../../../src/datamodel/node/data/VariantBankNode.ts"],"names":[],"mappings":"AACA,OAAO,kBAAkB,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAK3C,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,kBAAkB;IAC3D,KAAK,EAAE,OAAO,CAAC;gBACH,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClH,IAAI,IAAI,IAAI,MAAM,CAAgC;IAClD,IAAI,SAAS,IAAI,MAAM,CAAuB;IAC9C,IAAI,YAAY,IAAI,MAAM,CAAyC;IACnE,aAAa,IAAI,SAAS,EAAE;IAI5B,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC/C,MAAM,KAAK,WAAW,IAAI,MAAM,CAA0B;IAM1D,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,eAAe;IAC5E,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,eAAe;CACxG"} \ No newline at end of file +{"version":3,"file":"VariantBankNode.d.ts","sourceRoot":"","sources":["../../../../src/datamodel/node/data/VariantBankNode.ts"],"names":[],"mappings":"AACA,OAAO,kBAAkB,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAcxD,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,kBAAkB;IAC3D,KAAK,EAAE,OAAO,CAAC;IAGf,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;gBACnB,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClH,IAAI,IAAI,IAAI,MAAM,CAAgC;IAClD,IAAI,SAAS,IAAI,MAAM,CAAuB;IAC9C,IAAI,YAAY,IAAI,MAAM,CAAkE;IAE5F,IAAI,aAAa,IAAI,OAAO,CAAkB;IAC9C,aAAa,IAAI,SAAS,EAAE;IAK5B,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,iBAAiB;IAe5E,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC/C,MAAM,KAAK,WAAW,IAAI,MAAM,CAA0B;IAK1D,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,eAAe;IAC5E,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,eAAe;CACxG"} \ No newline at end of file diff --git a/dist/datamodel/node/data/VariantBankNode.js b/dist/datamodel/node/data/VariantBankNode.js index 41f07d48..c324a364 100644 --- a/dist/datamodel/node/data/VariantBankNode.js +++ b/dist/datamodel/node/data/VariantBankNode.js @@ -4,22 +4,46 @@ import PropName from '../../prop/PropName.js'; import PropValue from '../../prop/PropValue.js'; import PropDataType from '../../prop/PropDataType.js'; const CLASS_NAME = 'Simulink.VariantBank'; +// MATLAB's Simulink.VariantBank has no Value (R2027a: `isprop(Simulink.VariantBank, 'Value')` +// is 0, and setting one fails with "Unrecognized property 'Value' for class +// 'Simulink.VariantBank'."). Its properties are Name, Description, VariantConditions, +// AllChoicesCoderInfo and ActiveChoiceCoderInfo, and the saveobj envelope a dictionary holds it +// as adds BankCoderInfo; none of them is modelled here yet. So the Value column offers no editor +// and a Value edit is refused, as a bus element's is (BaseBusElementNode). A Value key a file +// already carries, which only this package ever wrote (MATLAB reads such a file without a +// warning, builds a bank with no Value, and drops the key on its next save), is shown and +// written back as it was read, read-only. export default class VariantBankNode extends SimulinkObjectNode { - constructor(name, parent, props, serial) { super(name, parent, serial); this.Value = props.Value !== undefined ? props.Value : ''; } + constructor(name, parent, props, serial) { super(name, parent, serial); this._valueInFile = props.Value !== undefined; this.Value = this._valueInFile ? props.Value : ''; } get icon() { return 'wsParameters_bank'; } get className() { return CLASS_NAME; } - get displayValue() { return PropValue.format(this.Value); } + get displayValue() { return this._valueInFile ? PropValue.format(this.Value) : ''; } + // The column stays, read-only, for a Value a file already carries. + get valueEditable() { return false; } getProperties() { return [PropName, PropValue, PropDataType]; } // PI layout: schema-driven "General" group (classes/variantBank.json). - // UNGATED: the Value is what the bank IS, so it is written whether or not the file - // carried the key. - _serializedOverrides() { return { Value: this.Value }; } + // Refused rather than taken: DataNode's generic tail would keep the text, mark the entry + // Modified and answer `true`, and the file would gain a key MATLAB ignores. One still in the + // encoded stream it was read from keeps that refusal's own reason. + setProperty(propName, stringValue) { + if (this._resolveProperty(propName) === 'Value') { + return (this._refuseEncodedEdit(propName, stringValue) ?? { + error: true, + reason: `'${this.displayName}' is a ${this.className}, which has no Value, so none can be set on it.`, + invalidValue: stringValue, + validValue: this.displayValue, + }); + } + return super.setProperty(propName, stringValue); + } + // Nothing overridden: the bag is written as it was read, so a file's own Value key (and + // only that) passes through `_mergeProps` unchanged, and no new one is ever written. + _serializedOverrides() { return {}; } static get defaultName() { return 'VariantBank'; } - // A saveobj envelope, for the reason VariantVariableNode's carries one. MATLAB has no - // `Value` property on this class at all, so the `{ Value: '' }` this used to write was pure - // invention; under an envelope `_mergeProps` drops an empty override, so the - // `_serializedOverrides` above now writes nothing until the user gives Value a value. - // Fields, order and defaults measured from a MATLAB-written dictionary. + // A saveobj envelope, for the reason VariantVariableNode's carries one, and nothing beside + // it: MATLAB has no `Value` property on this class at all, so the `{ Value: '' }` this used + // to write was pure invention. Fields, order and defaults measured from a MATLAB-written + // dictionary. static createDefault(name, parent) { const rawVal = VariantBankNode._defaultCustomSaveRawVal(CLASS_NAME, ['Name', 'Description', 'VariantConditions', 'AllChoicesCoderInfo', 'ActiveChoiceCoderInfo', 'BankCoderInfo'], { Name: '', Description: '', VariantConditions: VariantBankNode._emptyCell(), AllChoicesCoderInfo: '', ActiveChoiceCoderInfo: '', BankCoderInfo: '' }); const props = VariantBankNode._propsOf(rawVal); return new VariantBankNode(name, parent, props, { _rawVal: rawVal, _properties: props }); } static parse(rawVal, name, parent) { const props = VariantBankNode._propsOf(rawVal); return new VariantBankNode(name, parent, props, { _rawVal: rawVal, _properties: props }); } } diff --git a/dist/datamodel/node/data/VariantBankNode.js.map b/dist/datamodel/node/data/VariantBankNode.js.map index 3c85207d..43eee4c2 100644 --- a/dist/datamodel/node/data/VariantBankNode.js.map +++ b/dist/datamodel/node/data/VariantBankNode.js.map @@ -1 +1 @@ -{"version":3,"file":"VariantBankNode.js","sourceRoot":"","sources":["../../../../src/datamodel/node/data/VariantBankNode.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,OAAO,kBAAkB,MAAM,0BAA0B,CAAC;AAG1D,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAC9C,OAAO,SAAS,MAAM,yBAAyB,CAAC;AAChD,OAAO,YAAY,MAAM,4BAA4B,CAAC;AACtD,MAAM,UAAU,GAAG,sBAAsB,CAAC;AAC1C,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,kBAAkB;IAE3D,YAAY,IAAY,EAAE,MAAuB,EAAE,KAA8B,EAAE,MAA+B,IAAI,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/M,IAAI,IAAI,KAAa,OAAO,mBAAmB,CAAC,CAAC,CAAC;IAClD,IAAI,SAAS,KAAa,OAAO,UAAU,CAAC,CAAC,CAAC;IAC9C,IAAI,YAAY,KAAa,OAAO,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnE,aAAa,KAAkB,OAAO,CAAC,QAAQ,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IAC5E,uEAAuE;IACvE,mFAAmF;IACnF,mBAAmB;IACnB,oBAAoB,KAA8B,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACjF,MAAM,KAAK,WAAW,KAAa,OAAO,aAAa,CAAC,CAAC,CAAC;IAC1D,sFAAsF;IACtF,4FAA4F;IAC5F,6EAA6E;IAC7E,sFAAsF;IACtF,wEAAwE;IACxE,MAAM,CAAC,aAAa,CAAC,IAAY,EAAE,MAAuB,IAAqB,MAAM,MAAM,GAAG,eAAe,CAAC,wBAAwB,CAAC,UAAU,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,eAAe,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,iBAAiB,EAAE,eAAe,CAAC,UAAU,EAAE,EAAE,mBAAmB,EAAE,EAAE,EAAE,qBAAqB,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,IAAI,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAA6B,CAAC,CAAC,CAAC,CAAC;IAChkB,MAAM,CAAC,KAAK,CAAC,MAA+B,EAAE,IAAY,EAAE,MAAuB,IAAqB,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,IAAI,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAA6B,CAAC,CAAC,CAAC,CAAC;CACjR"} \ No newline at end of file +{"version":3,"file":"VariantBankNode.js","sourceRoot":"","sources":["../../../../src/datamodel/node/data/VariantBankNode.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/datamodel/node/data/VariantConfigurationDataNode.d.ts.map b/dist/datamodel/node/data/VariantConfigurationDataNode.d.ts.map index 30c5cfb1..380f49db 100644 --- a/dist/datamodel/node/data/VariantConfigurationDataNode.d.ts.map +++ b/dist/datamodel/node/data/VariantConfigurationDataNode.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"VariantConfigurationDataNode.d.ts","sourceRoot":"","sources":["../../../../src/datamodel/node/data/VariantConfigurationDataNode.ts"],"names":[],"mappings":"AACA,OAAO,kBAAkB,MAAM,0BAA0B,CAAC;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAI3C,MAAM,CAAC,OAAO,OAAO,4BAA6B,SAAQ,kBAAkB;IACxE,KAAK,EAAE,OAAO,CAAC;gBACH,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClH,IAAI,IAAI,IAAI,MAAM,CAA8B;IAGhD,IAAI,SAAS,IAAI,MAAM,CAAmI;IAE1J,IAAI,YAAY,IAAI,MAAM,CAAe;IACzC,IAAI,aAAa,IAAI,OAAO,CAAkB;IAC9C,aAAa,IAAI,SAAS,EAAE;IAG5B,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC/C,MAAM,KAAK,WAAW,IAAI,MAAM,CAAuC;IAqCvE,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,4BAA4B;IAwBzF,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,4BAA4B;CACrH"} \ No newline at end of file +{"version":3,"file":"VariantConfigurationDataNode.d.ts","sourceRoot":"","sources":["../../../../src/datamodel/node/data/VariantConfigurationDataNode.ts"],"names":[],"mappings":"AACA,OAAO,kBAAkB,MAAM,0BAA0B,CAAC;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAI3C,MAAM,CAAC,OAAO,OAAO,4BAA6B,SAAQ,kBAAkB;IACxE,KAAK,EAAE,OAAO,CAAC;gBACH,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClH,IAAI,IAAI,IAAI,MAAM,CAA8B;IAGhD,IAAI,SAAS,IAAI,MAAM,CAAmI;IAE1J,IAAI,YAAY,IAAI,MAAM,CAAe;IACzC,IAAI,aAAa,IAAI,OAAO,CAAkB;IAC9C,aAAa,IAAI,SAAS,EAAE;IAI5B,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC/C,MAAM,KAAK,WAAW,IAAI,MAAM,CAAuC;IAqCvE,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,4BAA4B;IAwBzF,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,4BAA4B;CACrH"} \ No newline at end of file diff --git a/dist/datamodel/node/data/VariantConfigurationDataNode.js b/dist/datamodel/node/data/VariantConfigurationDataNode.js index faeb3b31..556dca66 100644 --- a/dist/datamodel/node/data/VariantConfigurationDataNode.js +++ b/dist/datamodel/node/data/VariantConfigurationDataNode.js @@ -15,7 +15,8 @@ export default class VariantConfigurationDataNode extends SimulinkObjectNode { get valueEditable() { return false; } getProperties() { return [PropName, PropDataType]; } // PI layout: schema-driven "General" group (classes/variantConfigurationData.json). - // UNGATED, as VariantBankNode's Value is. + // UNGATED. (VariantBankNode's Value was too, until it stopped writing one at all: MATLAB's + // Simulink.VariantBank has no Value.) _serializedOverrides() { return { Value: this.Value }; } static get defaultName() { return 'VariantConfigurationData'; } // The one entry the Add gallery produced that MATLAB REFUSED to load, in both formats: diff --git a/dist/datamodel/node/data/VariantConfigurationDataNode.js.map b/dist/datamodel/node/data/VariantConfigurationDataNode.js.map index 3978ed30..ab4df3e8 100644 --- a/dist/datamodel/node/data/VariantConfigurationDataNode.js.map +++ b/dist/datamodel/node/data/VariantConfigurationDataNode.js.map @@ -1 +1 @@ -{"version":3,"file":"VariantConfigurationDataNode.js","sourceRoot":"","sources":["../../../../src/datamodel/node/data/VariantConfigurationDataNode.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"VariantConfigurationDataNode.js","sourceRoot":"","sources":["../../../../src/datamodel/node/data/VariantConfigurationDataNode.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/datamodel/prop/PropValue.js b/dist/datamodel/prop/PropValue.js index b888279f..a688a01e 100644 --- a/dist/datamodel/prop/PropValue.js +++ b/dist/datamodel/prop/PropValue.js @@ -55,7 +55,7 @@ export default class PropValue { } // The inverse of the quoting above — see PropClass.unformat. Consulted only by // DataNode's generic string path, i.e. by the nodes that store a bare string - // and let this atom decorate it (Simulink.VariantBank and friends). The classes + // and let this atom decorate it (Simulink.VariantExpression and friends). The classes // whose Value is a real MATLAB expression (Parameter, a MATLAB variable, // VariantControl) override setProperty and parse the text themselves, so their // quotes stay meaningful and this is never reached for them. diff --git a/dist/datamodel/prop/PropValue.js.map b/dist/datamodel/prop/PropValue.js.map index 6e2e9ae3..4759cfa4 100644 --- a/dist/datamodel/prop/PropValue.js.map +++ b/dist/datamodel/prop/PropValue.js.map @@ -1 +1 @@ -{"version":3,"file":"PropValue.js","sourceRoot":"","sources":["../../../src/datamodel/prop/PropValue.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAGrC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACzG,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAE3G,MAAM,CAAC,OAAO,OAAO,SAAS;aACnB,QAAG,GAAG,OAAO,CAAC;aACd,gBAAW,GAAG,OAAO,CAAC;aACtB,WAAM,GAAG,MAAM,CAAC;aAChB,WAAM,GAAG,OAAO,CAAC;IAExB,MAAM,CAAC,SAAS,CAAC,IAAc;QAC3B,OAAO,IAAI,CAAC,YAAY,CAAC;IAC7B,CAAC;IAED,MAAM,CAAC,MAAM,CAAC,KAAc;QACxB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxC,OAAO,aAAa,CAAC;QACzB,CAAC;QACD,0EAA0E;QAC1E,0EAA0E;QAC1E,0EAA0E;QAC1E,qEAAqE;QACrE,iBAAiB;QACjB,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC5B,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;QAClC,CAAC;QACD,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;YAC7B,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;QACD,4EAA4E;QAC5E,0EAA0E;QAC1E,4EAA4E;QAC5E,uEAAuE;QACvE,mEAAmE;QACnE,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC5B,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACvB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAAC,OAAO,aAAa,CAAC;YAAC,CAAC;YACjD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;gBACrD,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACxC,CAAC;YACD,uEAAuE;YACvE,uEAAuE;YACvE,sEAAsE;YACtE,sEAAsE;YACtE,MAAM,IAAI,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;YAC/B,IAAI,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;gBAAC,OAAO,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YAAC,CAAC;YAC/D,MAAM,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;YAChE,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QACzE,CAAC;QACD,OAAO,EAAE,CAAC;IACd,CAAC;IAED,+EAA+E;IAC/E,6EAA6E;IAC7E,gFAAgF;IAChF,yEAAyE;IACzE,+EAA+E;IAC/E,6DAA6D;aACtD,aAAQ,GAAG,iBAAiB,CAAC"} \ No newline at end of file +{"version":3,"file":"PropValue.js","sourceRoot":"","sources":["../../../src/datamodel/prop/PropValue.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAGrC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACzG,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAE3G,MAAM,CAAC,OAAO,OAAO,SAAS;aACnB,QAAG,GAAG,OAAO,CAAC;aACd,gBAAW,GAAG,OAAO,CAAC;aACtB,WAAM,GAAG,MAAM,CAAC;aAChB,WAAM,GAAG,OAAO,CAAC;IAExB,MAAM,CAAC,SAAS,CAAC,IAAc;QAC3B,OAAO,IAAI,CAAC,YAAY,CAAC;IAC7B,CAAC;IAED,MAAM,CAAC,MAAM,CAAC,KAAc;QACxB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxC,OAAO,aAAa,CAAC;QACzB,CAAC;QACD,0EAA0E;QAC1E,0EAA0E;QAC1E,0EAA0E;QAC1E,qEAAqE;QACrE,iBAAiB;QACjB,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC5B,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;QAClC,CAAC;QACD,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;YAC7B,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;QACD,4EAA4E;QAC5E,0EAA0E;QAC1E,4EAA4E;QAC5E,uEAAuE;QACvE,mEAAmE;QACnE,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC5B,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACvB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAAC,OAAO,aAAa,CAAC;YAAC,CAAC;YACjD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;gBACrD,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACxC,CAAC;YACD,uEAAuE;YACvE,uEAAuE;YACvE,sEAAsE;YACtE,sEAAsE;YACtE,MAAM,IAAI,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;YAC/B,IAAI,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;gBAAC,OAAO,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YAAC,CAAC;YAC/D,MAAM,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;YAChE,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QACzE,CAAC;QACD,OAAO,EAAE,CAAC;IACd,CAAC;IAED,+EAA+E;IAC/E,6EAA6E;IAC7E,sFAAsF;IACtF,yEAAyE;IACzE,+EAA+E;IAC/E,6DAA6D;aACtD,aAAQ,GAAG,iBAAiB,CAAC"} \ No newline at end of file diff --git a/src/datamodel/node/data/Simulink.VariantBank.md b/src/datamodel/node/data/Simulink.VariantBank.md index 00d5513a..38cf2033 100644 --- a/src/datamodel/node/data/Simulink.VariantBank.md +++ b/src/datamodel/node/data/Simulink.VariantBank.md @@ -4,43 +4,60 @@ **Node class:** `VariantBankNode` (`src/datamodel/node/data/VariantBankNode.ts`) **MATLAB class:** `Simulink.VariantBank` -**Editable in our UI:** no (Value column exposed via text editor but no fixture to round-trip) -**Verified against:** not probed (no params.sldd fixture instantiates this class) +**Editable in our UI:** no. The Value column is read-only, and a Value edit is refused: the +MATLAB class has no Value. +**Verified against:** MATLAB R2027a (27.1.0.3409468), 2026-10-16, and the bank `vbD` MATLAB +wrote into `test/fixtures/classes_r2027a_{text,binary}.sldd` (`make_classes_r2027a.m`) ## Overview -A Simulink.VariantBank stores variant bank configuration data. The node exposes -PropName, PropValue, and PropDataType in its property table, and its Value editor -is technically `text` (inherited from DataNode). However, no fixture in the test -suite (`params.sldd`) contains a VariantBank entry, so round-trip fidelity cannot -be validated without generating new MATLAB fixtures (out of scope for this phase). +A Simulink.VariantBank stores variant bank configuration data. MATLAB's class has five +properties: Name, Description, VariantConditions, AllChoicesCoderInfo and +ActiveChoiceCoderInfo. A dictionary holds a bank as a saveobj envelope of those five plus +BankCoderInfo, in both formats, with nothing beside it. None of the six is modelled as a +property of the node yet; the envelope is written back as it was read. -The node has a `createDefault` static that produces a minimal in-memory instance -with an empty-string Value. It serializes through `_serializeSimulinkObject`. +The node lists PropName, PropValue and PropDataType. PropValue stays as a read-only column, for +a Value key a file already carries (see below). `createDefault` makes the envelope MATLAB writes +for a new bank, with its fields, order and defaults measured from a MATLAB-written dictionary, +and nothing beside it. ## Property table | Property | Editor | Serialized key | Notes | |------------|--------|--------------------------|-------------------------------| | Name | text | entry name | MATLAB identifier | -| Value | text | Value (in _properties) | Exposed but no fixture to validate round-trip | +| Value | none | written back as read | Not a property of the class; shown only where the file carries one | | DataType | label | (className display) | Read-only label | -## Read-only / no-fixture status +## Value: the class has none -- The node does NOT explicitly override `valueEditable` to false; it inherits the - BaseNode default (true unless displayValue is a `<...>` string). With an empty - Value, `displayValue` returns `''`, so `valueEditable` would be true in theory. -- However, no `params.sldd` test fixture instantiates a `Simulink.VariantBank`, - so we have no round-trip path to validate edits against MATLAB. -- **Contract-lock**: pin the className and property shape via `createDefault` in - `test/parity/fidelity/hostnodes.fidelity.test.ts`. If a future phase adds a - fixture, a full fidelity doc and round-trip test should be written. +Measured in R2027a: `isprop(Simulink.VariantBank, 'Value')` is 0, and `vb.Value = 'x'` fails +with "Unrecognized property 'Value' for class 'Simulink.VariantBank'." + +This node used to offer one anyway. The cell took a text editor, `setProperty('Value', …)` +returned true, and both save paths wrote the text beside the envelope: `_properties: {Value: …}` +in a text dictionary, `

…

` after the saveobj `P` in a binary +one. MATLAB opens both files with no error and no warning, reads every entry, and builds a bank +with no Value; a MATLAB re-save of the entry drops the key. So the edit was accepted, saved, and +had no effect. Every MATLAB-written bank also showed `''`, which implies a char value the class +does not have. + +Following MATLAB (the 2026-10-16 ruling, group E, C-168), as a bus element does +(`BaseBusElementNode`): + +- `valueEditable` is false, so neither the table nor the Property Inspector offers an editor. +- `setProperty('Value', …)` is refused: "'' is a Simulink.VariantBank, which has no Value, + so none can be set on it." An entry still in an encoded stream keeps that refusal's reason. +- `_serializedOverrides` is empty, so no save writes a Value. A Value key a file already carries + (only ever this package's) passes through `_mergeProps` unchanged, and is shown quoted. +- A bank whose file carries no Value key, which is every bank MATLAB writes, shows an empty cell. + +Locked by `test/variantBankNoValue.test.ts` (both formats, and `createDefault`) and the +`READ_ONLY` row of `test/variantLeafNodes.test.ts`. ## Open questions / deferred -- **Fixture generation**: creating a `Simulink.VariantBank` entry in MATLAB and - exporting it to a test `.sldd` would enable full round-trip testing. Deferred. -- **Value semantics**: the exact MATLAB constraints on VariantBank.Value are - undocumented here (no probe was run). Conservative: do not add editing validation - until probed. +- **The class's own fields** (C-168 option (b)): show and edit Name, Description, + VariantConditions, AllChoicesCoderInfo and ActiveChoiceCoderInfo out of the envelope. The + MATLAB-written fixtures this was waiting for now exist (`vbD` above). diff --git a/src/datamodel/node/data/VariantBankNode.ts b/src/datamodel/node/data/VariantBankNode.ts index be32e958..8c031f06 100644 --- a/src/datamodel/node/data/VariantBankNode.ts +++ b/src/datamodel/node/data/VariantBankNode.ts @@ -2,27 +2,57 @@ import SimulinkObjectNode from '../SimulinkObjectNode.js'; import type { PropClass } from '../BaseNode.js'; import type BaseNode from '../BaseNode.js'; +import type { SetPropertyResult } from '../DataNode.js'; import PropName from '../../prop/PropName.js'; import PropValue from '../../prop/PropValue.js'; import PropDataType from '../../prop/PropDataType.js'; const CLASS_NAME = 'Simulink.VariantBank'; +// MATLAB's Simulink.VariantBank has no Value (R2027a: `isprop(Simulink.VariantBank, 'Value')` +// is 0, and setting one fails with "Unrecognized property 'Value' for class +// 'Simulink.VariantBank'."). Its properties are Name, Description, VariantConditions, +// AllChoicesCoderInfo and ActiveChoiceCoderInfo, and the saveobj envelope a dictionary holds it +// as adds BankCoderInfo; none of them is modelled here yet. So the Value column offers no editor +// and a Value edit is refused, as a bus element's is (BaseBusElementNode). A Value key a file +// already carries, which only this package ever wrote (MATLAB reads such a file without a +// warning, builds a bank with no Value, and drops the key on its next save), is shown and +// written back as it was read, read-only. export default class VariantBankNode extends SimulinkObjectNode { Value: unknown; - constructor(name: string, parent: BaseNode | null, props: Record, serial: Record) { super(name, parent, serial); this.Value = props.Value !== undefined ? props.Value : ''; } + // Whether the file carried a Value key at all. MATLAB's own banks never do, and showing + // their absent one as `''` implied a char value the class does not have. + readonly _valueInFile: boolean; + constructor(name: string, parent: BaseNode | null, props: Record, serial: Record) { super(name, parent, serial); this._valueInFile = props.Value !== undefined; this.Value = this._valueInFile ? props.Value : ''; } get icon(): string { return 'wsParameters_bank'; } get className(): string { return CLASS_NAME; } - get displayValue(): string { return PropValue.format(this.Value); } + get displayValue(): string { return this._valueInFile ? PropValue.format(this.Value) : ''; } + // The column stays, read-only, for a Value a file already carries. + get valueEditable(): boolean { return false; } getProperties(): PropClass[] { return [PropName, PropValue, PropDataType]; } // PI layout: schema-driven "General" group (classes/variantBank.json). - // UNGATED: the Value is what the bank IS, so it is written whether or not the file - // carried the key. - _serializedOverrides(): Record { return { Value: this.Value }; } + // Refused rather than taken: DataNode's generic tail would keep the text, mark the entry + // Modified and answer `true`, and the file would gain a key MATLAB ignores. One still in the + // encoded stream it was read from keeps that refusal's own reason. + setProperty(propName: string, stringValue: string): true | SetPropertyResult { + if (this._resolveProperty(propName) === 'Value') { + return ( + this._refuseEncodedEdit(propName, stringValue) ?? { + error: true, + reason: `'${this.displayName}' is a ${this.className}, which has no Value, so none can be set on it.`, + invalidValue: stringValue, + validValue: this.displayValue, + } + ); + } + return super.setProperty(propName, stringValue); + } + // Nothing overridden: the bag is written as it was read, so a file's own Value key (and + // only that) passes through `_mergeProps` unchanged, and no new one is ever written. + _serializedOverrides(): Record { return {}; } static get defaultName(): string { return 'VariantBank'; } - // A saveobj envelope, for the reason VariantVariableNode's carries one. MATLAB has no - // `Value` property on this class at all, so the `{ Value: '' }` this used to write was pure - // invention; under an envelope `_mergeProps` drops an empty override, so the - // `_serializedOverrides` above now writes nothing until the user gives Value a value. - // Fields, order and defaults measured from a MATLAB-written dictionary. + // A saveobj envelope, for the reason VariantVariableNode's carries one, and nothing beside + // it: MATLAB has no `Value` property on this class at all, so the `{ Value: '' }` this used + // to write was pure invention. Fields, order and defaults measured from a MATLAB-written + // dictionary. static createDefault(name: string, parent: BaseNode | null): VariantBankNode { const rawVal = VariantBankNode._defaultCustomSaveRawVal(CLASS_NAME, ['Name', 'Description', 'VariantConditions', 'AllChoicesCoderInfo', 'ActiveChoiceCoderInfo', 'BankCoderInfo'], { Name: '', Description: '', VariantConditions: VariantBankNode._emptyCell(), AllChoicesCoderInfo: '', ActiveChoiceCoderInfo: '', BankCoderInfo: '' }); const props = VariantBankNode._propsOf(rawVal); return new VariantBankNode(name, parent, props, { _rawVal: rawVal, _properties: props } as Record); } static parse(rawVal: Record, name: string, parent: BaseNode | null): VariantBankNode { const props = VariantBankNode._propsOf(rawVal); return new VariantBankNode(name, parent, props, { _rawVal: rawVal, _properties: props } as Record); } } diff --git a/src/datamodel/node/data/VariantConfigurationDataNode.ts b/src/datamodel/node/data/VariantConfigurationDataNode.ts index 5f592290..c5b566d4 100644 --- a/src/datamodel/node/data/VariantConfigurationDataNode.ts +++ b/src/datamodel/node/data/VariantConfigurationDataNode.ts @@ -18,7 +18,8 @@ export default class VariantConfigurationDataNode extends SimulinkObjectNode { get valueEditable(): boolean { return false; } getProperties(): PropClass[] { return [PropName, PropDataType]; } // PI layout: schema-driven "General" group (classes/variantConfigurationData.json). - // UNGATED, as VariantBankNode's Value is. + // UNGATED. (VariantBankNode's Value was too, until it stopped writing one at all: MATLAB's + // Simulink.VariantBank has no Value.) _serializedOverrides(): Record { return { Value: this.Value }; } static get defaultName(): string { return 'VariantConfigurationData'; } diff --git a/src/datamodel/prop/PropValue.ts b/src/datamodel/prop/PropValue.ts index f45b7f22..e52cef02 100644 --- a/src/datamodel/prop/PropValue.ts +++ b/src/datamodel/prop/PropValue.ts @@ -57,7 +57,7 @@ export default class PropValue { // The inverse of the quoting above — see PropClass.unformat. Consulted only by // DataNode's generic string path, i.e. by the nodes that store a bare string - // and let this atom decorate it (Simulink.VariantBank and friends). The classes + // and let this atom decorate it (Simulink.VariantExpression and friends). The classes // whose Value is a real MATLAB expression (Parameter, a MATLAB variable, // VariantControl) override setProperty and parse the text themselves, so their // quotes stay meaningful and this is never reached for them. diff --git a/test/fixtures/classes_r2027a_binary.sldd b/test/fixtures/classes_r2027a_binary.sldd new file mode 100644 index 0000000000000000000000000000000000000000..545db5f09761f0351669bb634065e65b4b54d931 GIT binary patch literal 4465 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 literal 0 HcmV?d00001 diff --git a/test/fixtures/classes_r2027a_text.sldd b/test/fixtures/classes_r2027a_text.sldd new file mode 100644 index 00000000..a66baae0 --- /dev/null +++ b/test/fixtures/classes_r2027a_text.sldd @@ -0,0 +1,715 @@ +{ + "__MW_TEXT_COREPROPERTIES__": { + "release": "R2027a" + }, + "__MW_TEXT_PARTS__": { + "__MW_TEXT_PART__/data/chunk0": { + "__MW_TEXT_content": { + "entries": [ + { + "name": "bpD", + "metadata": { + "uuid": "9465308b-bf22-40bb-addd-808bd65f72a1", + "namespace": "dacaf35e-55a5-454d-a7c1-93db038a210e", + "lastmod": "20261016T191456.001240", + "modifiedby": "weiwang", + "isderived": "0" + }, + "value": { + "_array_class": "Simulink.Breakpoint", + "_dimensions": [ + 1, + 1 + ], + "_elements": [ + { + "_id": "1", + "_properties": { + "Breakpoints": { + "_id": "2", + "_object_class": "Simulink.lookuptable.Breakpoint", + "_properties": { + "DataType": "auto", + "Description": "", + "Dimensions": [ + 0, + 0 + ], + "FieldName": "BP", + "TunableSizeName": "N", + "TunableSizeValue": -1, + "Unit": "" + } + }, + "CoderInfo": { + "_id": "3", + "_object_class": "Simulink.CoderInfo", + "_properties": { + "CSCPackageName": "Simulink", + "CustomAttributes": { + "_id": "4", + "_object_class": "SimulinkCSC.AttribClass_Simulink_Default", + "_properties": {} + }, + "CustomStorageClass": "Default", + "ParameterOrSignal": "Parameter", + "StorageClass": "Auto" + } + }, + "StructTypeInfo": { + "_id": "5", + "_object_class": "Simulink.lookuptable.StructTypeInfo", + "_properties": { + "DataScope": "Auto", + "HeaderFileName": "", + "Name": "" + } + }, + "SupportTunableSize": false + } + } + ], + "_mw_element_type": "MATLABArray" + } + }, + { + "name": "busAl8", + "metadata": { + "uuid": "2a268469-ee64-4fb5-bb96-856f31077990", + "namespace": "dacaf35e-55a5-454d-a7c1-93db038a210e", + "lastmod": "20261016T191456.009331", + "modifiedby": "weiwang", + "isderived": "0" + }, + "value": { + "_array_class": "Simulink.Bus", + "_dimensions": [ + 1, + 1 + ], + "_elements": [ + { + "_id": "1", + "_properties": { + "Alignment": 8, + "DataScope": "Auto", + "Description": "", + "Elements_internal": [], + "HeaderFile": "", + "PreserveElementDimensions": false + } + } + ], + "_mw_element_type": "MATLABArray" + } + }, + { + "name": "lutD", + "metadata": { + "uuid": "6b74d389-e89c-482e-b7f8-1196f25ea9c8", + "namespace": "dacaf35e-55a5-454d-a7c1-93db038a210e", + "lastmod": "20261016T191455.996084", + "modifiedby": "weiwang", + "isderived": "0" + }, + "value": { + "_array_class": "Simulink.LookupTable", + "_dimensions": [ + 1, + 1 + ], + "_elements": [ + { + "_id": "1", + "_properties": { + "AllowMultipleInstancesOfTypeToHaveDifferentTableBreakpointSizes": false, + "Breakpoints": { + "_id": "3", + "_object_class": "Simulink.lookuptable.Breakpoint", + "_properties": { + "DataType": "auto", + "Description": "", + "Dimensions": [ + 0, + 0 + ], + "FieldName": "BP1", + "TunableSizeName": "N1", + "TunableSizeValue": -1, + "Unit": "" + } + }, + "CoderInfo": { + "_id": "4", + "_object_class": "Simulink.CoderInfo", + "_properties": { + "CSCPackageName": "Simulink", + "CustomAttributes": { + "_id": "5", + "_object_class": "SimulinkCSC.AttribClass_Simulink_Default", + "_properties": {} + }, + "CustomStorageClass": "Default", + "ParameterOrSignal": "Parameter", + "StorageClass": "Auto" + } + }, + "StructTypeInfo": { + "_id": "6", + "_object_class": "Simulink.lookuptable.StructTypeInfo", + "_properties": { + "DataScope": "Auto", + "HeaderFileName": "", + "Name": "" + } + }, + "SupportTunableSize": false, + "Table": { + "_id": "2", + "_object_class": "Simulink.lookuptable.Table", + "_properties": { + "DataType": "auto", + "Description": "", + "Dimensions": [ + 0, + 0 + ], + "FieldName": "Table", + "Unit": "" + } + } + } + } + ], + "_mw_element_type": "MATLABArray" + } + }, + { + "name": "pAl8", + "metadata": { + "uuid": "ff951763-b858-4398-96c3-284c2ecd7059", + "namespace": "dacaf35e-55a5-454d-a7c1-93db038a210e", + "lastmod": "20261016T191456.006202", + "modifiedby": "weiwang", + "isderived": "0" + }, + "value": { + "_array_class": "Simulink.Parameter", + "_dimensions": [ + 1, + 1 + ], + "_elements": [ + { + "_id": "1", + "_properties": { + "CoderInfo": { + "_id": "2", + "_object_class": "Simulink.CoderInfo", + "_properties": { + "Alignment": { + "_type": "int32", + "_value": "8" + }, + "CSCPackageName": "Simulink", + "CustomAttributes": { + "_id": "3", + "_object_class": "SimulinkCSC.AttribClass_Simulink_Default", + "_properties": {} + }, + "CustomStorageClass": "Default", + "ParameterOrSignal": "Parameter", + "StorageClass": "ExportedGlobal" + } + }, + "Complexity": "real", + "Dimensions": [ + 1, + 1 + ], + "Value": 1 + } + } + ], + "_mw_element_type": "MATLABArray" + } + }, + { + "name": "pAlM1", + "metadata": { + "uuid": "cef41108-7d32-4a4b-88aa-2ac906315fe5", + "namespace": "dacaf35e-55a5-454d-a7c1-93db038a210e", + "lastmod": "20261016T191456.007650", + "modifiedby": "weiwang", + "isderived": "0" + }, + "value": { + "_array_class": "Simulink.Parameter", + "_dimensions": [ + 1, + 1 + ], + "_elements": [ + { + "_id": "1", + "_properties": { + "CoderInfo": { + "_id": "2", + "_object_class": "Simulink.CoderInfo", + "_properties": { + "CSCPackageName": "Simulink", + "CustomAttributes": { + "_id": "3", + "_object_class": "SimulinkCSC.AttribClass_Simulink_Default", + "_properties": {} + }, + "CustomStorageClass": "Default", + "ParameterOrSignal": "Parameter", + "StorageClass": "ExportedGlobal" + } + }, + "Complexity": "real", + "Dimensions": [ + 1, + 1 + ], + "Value": 1 + } + } + ], + "_mw_element_type": "MATLABArray" + } + }, + { + "name": "pNo", + "metadata": { + "uuid": "6b79f286-7cb5-4c37-bc33-72bb8614e4af", + "namespace": "dacaf35e-55a5-454d-a7c1-93db038a210e", + "lastmod": "20261016T191456.008218", + "modifiedby": "weiwang", + "isderived": "0" + }, + "value": { + "_array_class": "Simulink.Parameter", + "_dimensions": [ + 1, + 1 + ], + "_elements": [ + { + "_id": "1", + "_properties": { + "CoderInfo": { + "_id": "2", + "_object_class": "Simulink.CoderInfo", + "_properties": { + "CSCPackageName": "Simulink", + "CustomAttributes": { + "_id": "3", + "_object_class": "SimulinkCSC.AttribClass_Simulink_Default", + "_properties": {} + }, + "CustomStorageClass": "Default", + "ParameterOrSignal": "Parameter", + "StorageClass": "Auto" + } + }, + "Complexity": "real", + "Dimensions": [ + 1, + 1 + ], + "Value": 1 + } + } + ], + "_mw_element_type": "MATLABArray" + } + }, + { + "name": "pSC", + "metadata": { + "uuid": "f296894c-f820-4684-9d94-03561e8e3b38", + "namespace": "dacaf35e-55a5-454d-a7c1-93db038a210e", + "lastmod": "20261016T191456.007410", + "modifiedby": "weiwang", + "isderived": "0" + }, + "value": { + "_array_class": "Simulink.Parameter", + "_dimensions": [ + 1, + 1 + ], + "_elements": [ + { + "_id": "1", + "_properties": { + "CoderInfo": { + "_id": "2", + "_object_class": "Simulink.CoderInfo", + "_properties": { + "CSCPackageName": "Simulink", + "CustomAttributes": { + "_id": "3", + "_object_class": "SimulinkCSC.AttribClass_Simulink_Default", + "_properties": {} + }, + "CustomStorageClass": "Default", + "ParameterOrSignal": "Parameter", + "StorageClass": "ExportedGlobal" + } + }, + "Complexity": "real", + "Dimensions": [ + 1, + 1 + ], + "Value": 1 + } + } + ], + "_mw_element_type": "MATLABArray" + } + }, + { + "name": "sAl16", + "metadata": { + "uuid": "23ec5395-cc10-42b7-a493-a700ac0b6f59", + "namespace": "dacaf35e-55a5-454d-a7c1-93db038a210e", + "lastmod": "20261016T191456.008919", + "modifiedby": "weiwang", + "isderived": "0" + }, + "value": { + "_array_class": "Simulink.Signal", + "_dimensions": [ + 1, + 1 + ], + "_elements": [ + { + "_id": "1", + "_properties": { + "CoderInfo": { + "_id": "3", + "_object_class": "Simulink.CoderInfo", + "_properties": { + "Alignment": { + "_type": "int32", + "_value": "16" + }, + "CSCPackageName": "Simulink", + "CustomAttributes": { + "_id": "4", + "_object_class": "SimulinkCSC.AttribClass_Simulink_Default", + "_properties": {} + }, + "CustomStorageClass": "Default", + "ParameterOrSignal": "Signal", + "StorageClass": "ExportedGlobal" + } + }, + "LoggingInfo": { + "_id": "2", + "_object_class": "Simulink.LoggingInfo", + "_properties": {} + } + } + } + ], + "_mw_element_type": "MATLABArray" + } + }, + { + "name": "sigA", + "metadata": { + "uuid": "d1b3fcc1-fc53-43d7-a3c9-a38838c7eef9", + "namespace": "dacaf35e-55a5-454d-a7c1-93db038a210e", + "lastmod": "20261016T191456.004163", + "modifiedby": "weiwang", + "isderived": "0" + }, + "value": { + "_array_class": "Simulink.Signal", + "_dimensions": [ + 1, + 1 + ], + "_elements": [ + { + "_id": "1", + "_properties": { + "CoderInfo": { + "_id": "3", + "_object_class": "Simulink.CoderInfo", + "_properties": { + "CSCPackageName": "Simulink", + "CustomAttributes": { + "_id": "4", + "_object_class": "SimulinkCSC.AttribClass_Simulink_Default", + "_properties": {} + }, + "CustomStorageClass": "Default", + "ParameterOrSignal": "Signal", + "StorageClass": "Auto" + } + }, + "LoggingInfo": { + "_id": "2", + "_object_class": "Simulink.LoggingInfo", + "_properties": {} + } + } + } + ], + "_mw_element_type": "MATLABArray" + } + }, + { + "name": "sigC", + "metadata": { + "uuid": "f2e39f44-f6ec-455c-a5e1-a1b3698e0c6f", + "namespace": "dacaf35e-55a5-454d-a7c1-93db038a210e", + "lastmod": "20261016T191456.002924", + "modifiedby": "weiwang", + "isderived": "0" + }, + "value": { + "_array_class": "Simulink.Signal", + "_dimensions": [ + 1, + 1 + ], + "_elements": [ + { + "_id": "1", + "_properties": { + "CoderInfo": { + "_id": "3", + "_object_class": "Simulink.CoderInfo", + "_properties": { + "CSCPackageName": "Simulink", + "CustomAttributes": { + "_id": "4", + "_object_class": "SimulinkCSC.AttribClass_Simulink_Default", + "_properties": {} + }, + "CustomStorageClass": "Default", + "ParameterOrSignal": "Signal", + "StorageClass": "Auto" + } + }, + "Complexity": "complex", + "DimensionsMode": "Variable", + "LoggingInfo": { + "_id": "2", + "_object_class": "Simulink.LoggingInfo", + "_properties": {} + } + } + } + ], + "_mw_element_type": "MATLABArray" + } + }, + { + "name": "sigNo", + "metadata": { + "uuid": "165a4559-8f23-4d32-99cb-d8de6f2837b8", + "namespace": "dacaf35e-55a5-454d-a7c1-93db038a210e", + "lastmod": "20261016T191456.004707", + "modifiedby": "weiwang", + "isderived": "0" + }, + "value": { + "_array_class": "Simulink.Signal", + "_dimensions": [ + 1, + 1 + ], + "_elements": [ + { + "_id": "1", + "_properties": { + "CoderInfo": { + "_id": "3", + "_object_class": "Simulink.CoderInfo", + "_properties": { + "CSCPackageName": "Simulink", + "CustomAttributes": { + "_id": "4", + "_object_class": "SimulinkCSC.AttribClass_Simulink_Default", + "_properties": {} + }, + "CustomStorageClass": "Default", + "ParameterOrSignal": "Signal", + "StorageClass": "Auto" + } + }, + "LoggingInfo": { + "_id": "2", + "_object_class": "Simulink.LoggingInfo", + "_properties": {} + } + } + } + ], + "_mw_element_type": "MATLABArray" + } + }, + { + "name": "sigR", + "metadata": { + "uuid": "8d072d25-781d-42f5-b017-4d772b671dfb", + "namespace": "dacaf35e-55a5-454d-a7c1-93db038a210e", + "lastmod": "20261016T191456.003566", + "modifiedby": "weiwang", + "isderived": "0" + }, + "value": { + "_array_class": "Simulink.Signal", + "_dimensions": [ + 1, + 1 + ], + "_elements": [ + { + "_id": "1", + "_properties": { + "CoderInfo": { + "_id": "3", + "_object_class": "Simulink.CoderInfo", + "_properties": { + "CSCPackageName": "Simulink", + "CustomAttributes": { + "_id": "4", + "_object_class": "SimulinkCSC.AttribClass_Simulink_Default", + "_properties": {} + }, + "CustomStorageClass": "Default", + "ParameterOrSignal": "Signal", + "StorageClass": "Auto" + } + }, + "Complexity": "real", + "DimensionsMode": "Fixed", + "LoggingInfo": { + "_id": "2", + "_object_class": "Simulink.LoggingInfo", + "_properties": {} + } + } + } + ], + "_mw_element_type": "MATLABArray" + } + }, + { + "name": "vbD", + "metadata": { + "uuid": "a2583ad4-4847-45b4-bad9-83c9830a0f3b", + "namespace": "dacaf35e-55a5-454d-a7c1-93db038a210e", + "lastmod": "20261016T191456.011733", + "modifiedby": "weiwang", + "isderived": "0" + }, + "value": { + "_array_class": "Simulink.VariantBank", + "_dimensions": [ + 1, + 1 + ], + "_elements": [ + { + "_custom_save": { + "_array_type": "Struct", + "_dimensions": [ + 1, + 1 + ], + "_elements": [ + { + "ActiveChoiceCoderInfo": "", + "AllChoicesCoderInfo": "", + "BankCoderInfo": "", + "Description": "", + "Name": "", + "VariantConditions": { + "_array_type": "Cell", + "_dimensions": [ + 1, + 0 + ], + "_elements": [] + } + } + ], + "_fields": [ + "Name", + "Description", + "VariantConditions", + "AllChoicesCoderInfo", + "ActiveChoiceCoderInfo", + "BankCoderInfo" + ] + }, + "_id": "1" + } + ], + "_mw_element_type": "MATLABArray" + } + }, + { + "name": "vbciD", + "metadata": { + "uuid": "43c8b8f7-361e-48ba-bd2b-693a8ae93745", + "namespace": "dacaf35e-55a5-454d-a7c1-93db038a210e", + "lastmod": "20261016T191456.013688", + "modifiedby": "weiwang", + "isderived": "0" + }, + "value": { + "_array_class": "Simulink.VariantBankCoderInfo", + "_dimensions": [ + 1, + 1 + ], + "_elements": [ + { + "_custom_save": { + "_array_type": "Struct", + "_dimensions": [ + 1, + 1 + ], + "_elements": [ + { + "DefinitionFile": "", + "HeaderFile": "", + "PostStatement": "", + "PreStatement": "", + "Qualifier": "None" + } + ], + "_fields": [ + "HeaderFile", + "DefinitionFile", + "PreStatement", + "PostStatement", + "Qualifier" + ] + }, + "_id": "1" + } + ], + "_mw_element_type": "MATLABArray" + } + } + ], + "Dictionary References": [], + "AllowAccessBWS": false + } + } + } +} \ No newline at end of file diff --git a/test/fixtures/make_classes_r2027a.m b/test/fixtures/make_classes_r2027a.m new file mode 100644 index 00000000..1a1e2f82 --- /dev/null +++ b/test/fixtures/make_classes_r2027a.m @@ -0,0 +1,70 @@ +function make_classes_r2027a(outDir) + % Copyright 2026 The MathWorks, Inc. + % Authors classes_r2027a_text.sldd and classes_r2027a_binary.sldd: one dictionary, in each + % format, holding a MATLAB-written default (or lightly set) instance of each class below, so + % a test can read what MATLAB itself stores for them. Run (one line: mw splits -batch on + % newlines): + % + % mw -using Bmain matlab -nodesktop -batch "cd(''); make_classes_r2027a" + % + % `outDir` defaults to this folder. The committed files were written by R2027a + % (27.1.0.3409468) on 2026-10-16; a re-run gives new UUIDs and timestamps, and the same + % values. + % + % THE ENTRIES (Design Data): + % lutD, bpD Simulink.LookupTable and Simulink.Breakpoint, defaults + % sigC Simulink.Signal, Complexity 'complex', DimensionsMode 'Variable' + % sigR Simulink.Signal, Complexity 'real', DimensionsMode 'Fixed' + % sigA Simulink.Signal, Complexity set to 'complex' and back to 'auto' + % sigNo Simulink.Signal, default + % pAl8 Simulink.Parameter(1), StorageClass 'ExportedGlobal', Alignment 8 + % pAlM1 Simulink.Parameter(1), StorageClass 'ExportedGlobal', Alignment 8 and + % back to -1 + % pSC Simulink.Parameter(1), StorageClass 'ExportedGlobal' + % pNo Simulink.Parameter(1) + % sAl16 Simulink.Signal, StorageClass 'ExportedGlobal', Alignment 16 + % busAl8 Simulink.Bus, Alignment 8 + % vbD Simulink.VariantBank, default (a saveobj envelope, and no Value: the class + % has none) + % vbciD Simulink.VariantBankCoderInfo, default + if nargin < 1 || isempty(outDir) + outDir = fileparts(mfilename('fullpath')); + end + Simulink.data.dictionary.closeAll('-discard'); + formats = {'uncompressed-text', 'text'; 'compressed-binary', 'binary'}; + for i = 1:size(formats, 1) + fn = fullfile(outDir, ['classes_r2027a_' formats{i, 2} '.sldd']); + if exist(fn, 'file'), delete(fn); end + dd = Simulink.data.dictionary.create(fn); + dd.FileFormat = formats{i, 1}; + ds = dd.getSection('Design Data'); + ds.addEntry('lutD', Simulink.LookupTable); + ds.addEntry('bpD', Simulink.Breakpoint); + sC = Simulink.Signal; sC.Complexity = 'complex'; sC.DimensionsMode = 'Variable'; + ds.addEntry('sigC', sC); + sR = Simulink.Signal; sR.Complexity = 'real'; sR.DimensionsMode = 'Fixed'; + ds.addEntry('sigR', sR); + sA = Simulink.Signal; sA.Complexity = 'complex'; sA.Complexity = 'auto'; + ds.addEntry('sigA', sA); + ds.addEntry('sigNo', Simulink.Signal); + pA = Simulink.Parameter(1); pA.CoderInfo.StorageClass = 'ExportedGlobal'; pA.CoderInfo.Alignment = 8; + ds.addEntry('pAl8', pA); + pM = Simulink.Parameter(1); pM.CoderInfo.StorageClass = 'ExportedGlobal'; pM.CoderInfo.Alignment = 8; pM.CoderInfo.Alignment = -1; + pSC = Simulink.Parameter(1); pSC.CoderInfo.StorageClass = 'ExportedGlobal'; + ds.addEntry('pSC', pSC); + ds.addEntry('pAlM1', pM); + ds.addEntry('pNo', Simulink.Parameter(1)); + sAl = Simulink.Signal; sAl.CoderInfo.StorageClass = 'ExportedGlobal'; sAl.CoderInfo.Alignment = 16; + ds.addEntry('sAl16', sAl); + bA = Simulink.Bus; bA.Alignment = 8; + ds.addEntry('busAl8', bA); + ds.addEntry('vbD', Simulink.VariantBank); + ds.addEntry('vbciD', Simulink.VariantBankCoderInfo); + clear ds + dd.saveChanges(); + dd.close(); + fprintf('WROTE %s\n', fn); + end + Simulink.data.dictionary.closeAll('-discard'); + fprintf('MAKE_CLASSES_R2027A_DONE\n'); +end diff --git a/test/variantBankNoValue.test.ts b/test/variantBankNoValue.test.ts new file mode 100644 index 00000000..96ddbe1f --- /dev/null +++ b/test/variantBankNoValue.test.ts @@ -0,0 +1,242 @@ +// Copyright 2026 The MathWorks, Inc. +// +// A Simulink.VariantBank has no Value, so the entry offers none, takes none, and writes none. +// +// Measured in R2027a (27.1.0.3409468): `properties(Simulink.VariantBank)` is Name, Description, +// VariantConditions, AllChoicesCoderInfo and ActiveChoiceCoderInfo (the saveobj envelope adds +// BankCoderInfo), `isprop(Simulink.VariantBank, 'Value')` is 0, and `vb.Value = 'x'` fails with +// "Unrecognized property 'Value' for class 'Simulink.VariantBank'." A dictionary MATLAB writes +// holds the bank as that envelope alone, in both formats. +// +// The node used to offer a Value anyway: the cell took an editor, `setProperty('Value', …)` +// returned true, and both save paths wrote the text next to the envelope (`_properties.Value` in +// a text dictionary, `

` in a binary one). MATLAB opens such a file +// without an error or a warning, the object it builds has no Value, and a MATLAB re-save drops +// the key; so the edit was accepted, saved, and had no effect. Every MATLAB-written bank showed +// `''`, a char value the class does not have. The ruling (2026-10-16, group E, theme 2: follow +// MATLAB) is what #113 did for a bus element (BaseBusElementNode): no editor, a Value edit +// refused, and a Value key the file already carries (only ever this package's) shown and written +// back as it was read. +// +// Driven through the session API a host uses — `ingest`, the node's own `setProperty`, `toRow` +// and `toPIObject`, and `serializeSource` — over vbD, a bank MATLAB wrote into +// test/fixtures/classes_r2027a_{text,binary}.sldd (test/fixtures/make_classes_r2027a.m), and +// over `createDefault`, which is what a new entry starts as. +import { describe, it, expect } from 'vitest'; +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { strFromU8, strToU8, unzipSync, zipSync } from 'fflate'; +import { createSession, ingest, type SerializedSource } from '../src/index.js'; +import VariantBankNode from '../src/datamodel/node/data/VariantBankNode.js'; +import type DataNode from '../src/datamodel/node/DataNode.js'; +import '../src/datamodel/node/data/NodeClassMap.js'; + +type Format = 'text' | 'binary'; +const FORMATS: Format[] = ['text', 'binary']; +const CLASS = 'Simulink.VariantBank'; +const ENTRY = 'vbD'; +const RENAMED = 'vbE'; + +function fixture(format: Format): Uint8Array { + return new Uint8Array(readFileSync(fileURLToPath(new URL(`./fixtures/classes_r2027a_${format}.sldd`, import.meta.url)))); +} + +// The text dictionary's entry list, and the binary one's chunk XML, of whatever bytes a save gave. +function textEntries(text: string): any[] { + return JSON.parse(text).__MW_TEXT_PARTS__['__MW_TEXT_PART__/data/chunk0'].__MW_TEXT_content.entries as any[]; +} +function chunkXml(bytes: Uint8Array): string { + return strFromU8(unzipSync(bytes)['data/chunk0.xml']); +} +// The `` of one entry, as the binary file spells it. +function bankXml(xml: string, entry: string): string { + const at = xml.indexOf(`

${entry}

`); + expect(at, `no entry ${entry} in the chunk`).toBeGreaterThan(-1); + const open = xml.indexOf(``, at); + const close = xml.indexOf('', at); + expect(open > at && open < close, `${entry} is not a ${CLASS}`).toBe(true); + return xml.slice(open, close); +} + +// vbD with a sibling Value 'b1' added to its bytes, in the place and the spelling this package's +// old writer put one (MATLAB, given that file, opened it without a warning). Added to the bytes, +// so the reader is what puts it in the node's bag. +function withValue(format: Format, text: string): Uint8Array | string { + const bytes = fixture(format); + if (format === 'text') { + const json = JSON.parse(strFromU8(bytes)); + const entries = json.__MW_TEXT_PARTS__['__MW_TEXT_PART__/data/chunk0'].__MW_TEXT_content.entries as any[]; + const elem = entries.find((e) => e.name === ENTRY).value._elements[0] as Record; + expect(elem).not.toHaveProperty('_properties'); + expect(elem).toHaveProperty('_custom_save'); + elem._properties = { Value: text }; + return JSON.stringify(json, null, '\t'); + } + const parts = unzipSync(bytes); + const xml = strFromU8(parts['data/chunk0.xml']); + const envelopeEnd = '

\n \n

'; + expect(xml.split(envelopeEnd).length - 1, 'one VariantBank envelope in the binary fixture').toBe(1); + parts['data/chunk0.xml'] = strToU8( + xml.replace(envelopeEnd, `${envelopeEnd}\n

${text}

`), + ); + return zipSync(parts); +} + +function entryIn(root: any, name: string): DataNode { + const stack = [root]; + while (stack.length) { + const n = stack.shift(); + if (n?.name === name && n.className && !String(n.id ?? '').startsWith('section:')) { + return n as DataNode; + } + stack.push(...(n?.children ?? [])); + } + throw new Error(`no entry "${name}"`); +} + +function open(content: Uint8Array | string) { + const session = createSession(); + const source: any = ingest(session, content, { filename: 'classes.sldd' }); + return { session, source }; +} + +function saved(session: any, source: any): Uint8Array | string { + const out = session.serializeSource(source.name) as SerializedSource; + return out.kind === 'binary' ? out.bytes : out.text; +} + +// What a save wrote for the entry: its bag as the reader reads the saved file back, and the +// entry's own spelling in that file (the text entry's element, or the binary entry's XML). +function savedEntry(content: Uint8Array | string, entry: string) { + const bag = (entryIn(open(content).source, entry) as any).serial._properties as Record; + const spelled = + typeof content === 'string' + ? JSON.stringify(textEntries(content).find((e) => e.name === entry).value) + : bankXml(chunkXml(content), entry); + return { bag, spelled }; +} + +// The two save paths, each reduced to the bag it puts in the file. +const binaryPath = (node: DataNode) => node._getSerializedProperties(); +const textElem = (node: DataNode) => JSON.parse(JSON.stringify((node.serializeValue() as any)._elements[0])) as Record; + +function piValue(node: DataNode) { + const pi = node.toPIObject()!; + const item = (pi.propertySheet.properties as any[]).find((p) => p.name === 'Value'); + return { shown: (pi.objects[0] as Record).Value, editable: item.editable }; +} + +function expectRefused(node: DataNode, text: string) { + const result = node.setProperty('Value', text); + expect(result).not.toBe(true); + expect(result).toMatchObject({ + error: true, + reason: `'${node.name}' is a ${CLASS}, which has no Value, so none can be set on it.`, + invalidValue: text, + validValue: node.displayValue, + }); +} + +describe(`${CLASS} has no Value`, () => { + for (const format of FORMATS) { + describe(`${ENTRY}, the bank MATLAB wrote, ${format} dictionary`, () => { + it('shows no Value, and offers no editor in the table or the Property Inspector', () => { + const node = entryIn(open(fixture(format)).source, ENTRY); + expect(node.className).toBe(CLASS); + // Not `''`, which is a char value the class does not have. + expect(node.displayValue).toBe(''); + expect(node.valueEditable).toBe(false); + const row = node.toRow()!; + expect(row.Value).toBe(''); + expect(row._valueEditable).toBe(false); + expect(piValue(node)).toEqual({ shown: '', editable: false }); + }); + + it('refuses a Value edit, and no save writes one', () => { + const { session, source } = open(fixture(format)); + const node = entryIn(source, ENTRY); + + expectRefused(node, 'x'); + // The quoted form the cell would have been seeded with, and the empty text it shows. + expectRefused(node, "'x'"); + expectRefused(node, ''); + expect(node.status).not.toBe('Modified'); + expect(node.displayValue).toBe(''); + expect(binaryPath(node)).not.toHaveProperty('Value'); + expect(textElem(node)).not.toHaveProperty('_properties'); + + // A rename rewrites the entry from the node, so the save reaches the writer (a binary + // entry nobody edited is written as the bytes it was read from). + expect(node.setProperty('Name', RENAMED)).toBe(true); + const { bag, spelled } = savedEntry(saved(session, source), RENAMED); + expect(bag).not.toHaveProperty('Value'); + expect(spelled).not.toContain('"Value"'); + expect(spelled).not.toContain('

{ + // A guard: this held before the ruling too, because an EMPTY Value was never written + // under an envelope (DataNode._mergeProps). It pins that the change keeps it so. + const read = open(fixture(format)); + const before = JSON.parse(JSON.stringify((entryIn(read.source, ENTRY) as any).serial._properties)); + const untouched = saved(read.session, read.source); + if (format === 'text') { + expect(untouched).toBe(strFromU8(fixture('text'))); + } + expect(savedEntry(untouched, ENTRY).bag).toEqual(before); + + expect(entryIn(read.source, ENTRY).setProperty('Name', RENAMED)).toBe(true); + const renamed = saved(read.session, read.source); + expect(savedEntry(renamed, RENAMED).bag).toEqual(before); + const ownSpelling = savedEntry(renamed, RENAMED).spelled; + if (format === 'text') { + const original = textEntries(strFromU8(fixture('text'))).find((e) => e.name === ENTRY).value; + expect(JSON.parse(ownSpelling)).toEqual(original); + } else { + // The writer's own XML spelling of the envelope (an empty cell as an open and a close + // tag, say) is not this item's; what is, is that nothing beside the envelope is added. + expect(ownSpelling).toContain('

{ + const { session, source } = open(withValue(format, 'b1')); + const node = entryIn(source, ENTRY); + + expect(node.displayValue).toBe("'b1'"); + expect(node.valueEditable).toBe(false); + const row = node.toRow()!; + expect(row.Value).toBe("'b1'"); + expect(row._valueEditable).toBe(false); + expect(piValue(node)).toEqual({ shown: "'b1'", editable: false }); + + expectRefused(node, 'changed'); + expectRefused(node, "'b1'"); + expect(node.status).not.toBe('Modified'); + expect(binaryPath(node).Value).toBe('b1'); + expect(textElem(node)._properties).toEqual({ Value: 'b1' }); + + expect(node.setProperty('Name', RENAMED)).toBe(true); + const out = saved(session, source); + const { bag, spelled } = savedEntry(out, RENAMED); + expect(bag.Value).toBe('b1'); + expect(spelled).toContain(format === 'text' ? '"_properties":{"Value":"b1"}' : '

b1

'); + expect(entryIn(open(out).source, RENAMED).displayValue).toBe("'b1'"); + }); + }); + } + + it('a new entry (createDefault) shows none, refuses one and saves none', () => { + const node = VariantBankNode.createDefault('n', null); + expect(node.displayValue).toBe(''); + expect(node.valueEditable).toBe(false); + expect(node.toRow()!._valueEditable).toBe(false); + expect(piValue(node)).toEqual({ shown: '', editable: false }); + expectRefused(node, 'x'); + expect(binaryPath(node)).not.toHaveProperty('Value'); + expect(textElem(node)).not.toHaveProperty('_properties'); + expect(node.serializeXml('P', { Name: 'Value' }, 0)).not.toContain('

(['Simulink.VariantBank']); + // The `_array_class` a NEW entry is written with, where it differs from the class the node // reports for a parsed one. Exactly one class differs, and it differs for a reason MATLAB // enforced: a dictionary's config section accepts the CONTAINER class @@ -71,13 +78,21 @@ describe('single-property leaf nodes — shared contract', () => { expect(parsed()[prop]).toBe(''); }); - it('writes an edit back through the array wrapper', () => { - // _serializeSimulinkObject rebuilds _elements[0]._properties from the live - // field; without it the loaded raw value is written and the edit vanishes. - const n = parsed({ [prop]: 'old', Untouched: 7 }); - expect(n.setProperty(prop, 'new')).toBe(true); - expect(n.serializeValue()).toEqual(wrap(cls, { [prop]: 'new', Untouched: 7 })); - }); + if (READ_ONLY.has(cls)) { + it('refuses an edit, and writes the property back as it was read', () => { + const n = parsed({ [prop]: 'old', Untouched: 7 }); + expect(n.setProperty(prop, 'new')).toMatchObject({ error: true }); + expect(n.serializeValue()).toEqual(wrap(cls, { [prop]: 'old', Untouched: 7 })); + }); + } else { + it('writes an edit back through the array wrapper', () => { + // _serializeSimulinkObject rebuilds _elements[0]._properties from the live + // field; without it the loaded raw value is written and the edit vanishes. + const n = parsed({ [prop]: 'old', Untouched: 7 }); + expect(n.setProperty(prop, 'new')).toBe(true); + expect(n.serializeValue()).toEqual(wrap(cls, { [prop]: 'new', Untouched: 7 })); + }); + } it('createDefault produces a node that serializes to a valid wrapper', () => { const n = (Node as any).createDefault('n', null); @@ -195,10 +210,10 @@ describe('REGRESSION: a quoted display round-trips through an edit', () => { // edit wrapped them again ('a == 1' → ''a == 1'' → …), until the saved .sldd // held a condition MATLAB could no longer evaluate. Note the edit is committed // in the 'Value' COLUMN, which is how the table addresses these props. + // Simulink.VariantBank was a row here until it stopped taking a Value edit at all (READ_ONLY). const QUOTED = [ { cls: 'Simulink.VariantExpression', Node: VariantExpressionNode, prop: 'Condition', raw: "strcmp(mode,'fast')" }, { cls: 'Simulink.VariantVariable', Node: VariantVariableNode, prop: 'Specification', raw: "p('x')" }, - { cls: 'Simulink.VariantBank', Node: VariantBankNode, prop: 'Value', raw: "bank'1" }, ] as const; for (const { cls, Node, prop, raw } of QUOTED) { From 1d2be05da8b44e97052f4ffcf6af2ff802eaf48f Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 16 Oct 2026 16:09:56 -0400 Subject: [PATCH 3/7] Stop offering and writing a Value on VariantBankCoderInfo, which MATLAB's class does not have MATLAB R2027a has no Value property on Simulink.VariantBankCoderInfo. Its properties are HeaderFile, DefinitionFile, PreStatement, PostStatement and Qualifier, isprop(..., 'Value') is 0, and setting one fails with "Unrecognized property 'Value' for class 'Simulink.VariantBankCoderInfo'." The node offered one anyway: the cell took an editor, setProperty('Value', ...) was accepted, and both save paths wrote the text beside the saveobj envelope. MATLAB opens such a file with no error or warning and builds an object with no Value, so the edit had no effect. Every entry MATLAB wrote also showed '', a char value the class does not have. The node now does what VariantBank does (the previous commit) and a bus element does (#113). valueEditable is false, a Value edit is refused ("'' is a Simulink.VariantBankCoderInfo, which has no Value, so none can be set on it.") and leaves the entry unmodified, and _serializedOverrides is empty, so no save writes a Value. A Value key a file already carries (only ever this package's) is shown quoted, read-only, and written back as read. An entry with no Value key shows an empty cell. The Value column stays, read-only, for that key. test/variantBankNoValue.test.ts now runs its cases over a table of both classes, adding vbciD (MATLAB-written, in both formats of the existing classes_r2027a fixtures) and a new entry. The VariantBank cases and their titles are unchanged. Existing tests changed only where they pinned the removed edit. In variantLeafNodes, the VariantBankCoderInfo row of the shared contract joins VariantBank in READ_ONLY: it now checks that an edit is refused and the bag is written back as read, instead of checking that the edit is written. The quoted-display case for the two classes is retitled, since "like MATLAB" no longer holds for a key MATLAB's classes do not have; its assertions are unchanged. The class doc records the measurement. --- .../node/data/VariantBankCoderInfoNode.d.ts | 4 + .../data/VariantBankCoderInfoNode.d.ts.map | 2 +- .../node/data/VariantBankCoderInfoNode.js | 37 ++- .../node/data/VariantBankCoderInfoNode.js.map | 2 +- .../data/Simulink.VariantBankCoderInfo.md | 67 +++-- .../node/data/VariantBankCoderInfoNode.ts | 43 ++- test/variantBankNoValue.test.ts | 265 ++++++++++-------- test/variantLeafNodes.test.ts | 12 +- 8 files changed, 275 insertions(+), 157 deletions(-) diff --git a/dist/datamodel/node/data/VariantBankCoderInfoNode.d.ts b/dist/datamodel/node/data/VariantBankCoderInfoNode.d.ts index b3abc00b..79563bdc 100644 --- a/dist/datamodel/node/data/VariantBankCoderInfoNode.d.ts +++ b/dist/datamodel/node/data/VariantBankCoderInfoNode.d.ts @@ -1,13 +1,17 @@ import SimulinkObjectNode from '../SimulinkObjectNode.js'; import type { PropClass } from '../BaseNode.js'; import type BaseNode from '../BaseNode.js'; +import type { SetPropertyResult } from '../DataNode.js'; export default class VariantBankCoderInfoNode extends SimulinkObjectNode { Value: unknown; + readonly _valueInFile: boolean; constructor(name: string, parent: BaseNode | null, props: Record, serial: Record); get icon(): string; get className(): string; get displayValue(): string; + get valueEditable(): boolean; getProperties(): PropClass[]; + setProperty(propName: string, stringValue: string): true | SetPropertyResult; _serializedOverrides(): Record; static get defaultName(): string; static createDefault(name: string, parent: BaseNode | null): VariantBankCoderInfoNode; diff --git a/dist/datamodel/node/data/VariantBankCoderInfoNode.d.ts.map b/dist/datamodel/node/data/VariantBankCoderInfoNode.d.ts.map index 9f952c11..6aad5303 100644 --- a/dist/datamodel/node/data/VariantBankCoderInfoNode.d.ts.map +++ b/dist/datamodel/node/data/VariantBankCoderInfoNode.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"VariantBankCoderInfoNode.d.ts","sourceRoot":"","sources":["../../../../src/datamodel/node/data/VariantBankCoderInfoNode.ts"],"names":[],"mappings":"AACA,OAAO,kBAAkB,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAK3C,MAAM,CAAC,OAAO,OAAO,wBAAyB,SAAQ,kBAAkB;IACpE,KAAK,EAAE,OAAO,CAAC;gBACH,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClH,IAAI,IAAI,IAAI,MAAM,CAAyC;IAC3D,IAAI,SAAS,IAAI,MAAM,CAAuB;IAC9C,IAAI,YAAY,IAAI,MAAM,CAAyC;IACnE,aAAa,IAAI,SAAS,EAAE;IAG5B,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC/C,MAAM,KAAK,WAAW,IAAI,MAAM,CAAmC;IAKnE,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,wBAAwB;IACrF,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,wBAAwB;CACjH"} \ No newline at end of file +{"version":3,"file":"VariantBankCoderInfoNode.d.ts","sourceRoot":"","sources":["../../../../src/datamodel/node/data/VariantBankCoderInfoNode.ts"],"names":[],"mappings":"AACA,OAAO,kBAAkB,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAcxD,MAAM,CAAC,OAAO,OAAO,wBAAyB,SAAQ,kBAAkB;IACpE,KAAK,EAAE,OAAO,CAAC;IAGf,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;gBACnB,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClH,IAAI,IAAI,IAAI,MAAM,CAAyC;IAC3D,IAAI,SAAS,IAAI,MAAM,CAAuB;IAC9C,IAAI,YAAY,IAAI,MAAM,CAAkE;IAE5F,IAAI,aAAa,IAAI,OAAO,CAAkB;IAC9C,aAAa,IAAI,SAAS,EAAE;IAK5B,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,iBAAiB;IAe5E,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC/C,MAAM,KAAK,WAAW,IAAI,MAAM,CAAmC;IAMnE,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,wBAAwB;IACrF,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,wBAAwB;CACjH"} \ No newline at end of file diff --git a/dist/datamodel/node/data/VariantBankCoderInfoNode.js b/dist/datamodel/node/data/VariantBankCoderInfoNode.js index 16b27ce5..0b05dd72 100644 --- a/dist/datamodel/node/data/VariantBankCoderInfoNode.js +++ b/dist/datamodel/node/data/VariantBankCoderInfoNode.js @@ -4,17 +4,44 @@ import PropName from '../../prop/PropName.js'; import PropValue from '../../prop/PropValue.js'; import PropDataType from '../../prop/PropDataType.js'; const CLASS_NAME = 'Simulink.VariantBankCoderInfo'; +// MATLAB's Simulink.VariantBankCoderInfo has no Value (R2027a: `isprop(Simulink.VariantBankCoderInfo, +// 'Value')` is 0, and setting one fails with "Unrecognized property 'Value' for class +// 'Simulink.VariantBankCoderInfo'."). Its properties are HeaderFile, DefinitionFile, PreStatement, +// PostStatement and Qualifier, which are also the fields of the saveobj envelope a dictionary +// holds it as; none of them is modelled here yet. So, as for VariantBankNode (and a bus element, +// BaseBusElementNode), the Value column offers no editor and a Value edit is refused. A Value key +// a file already carries, which only this package ever wrote (MATLAB reads such a file without a +// warning, builds the object with no Value, and drops the key on its next save), is shown and +// written back as it was read, read-only. export default class VariantBankCoderInfoNode extends SimulinkObjectNode { - constructor(name, parent, props, serial) { super(name, parent, serial); this.Value = props.Value !== undefined ? props.Value : ''; } + constructor(name, parent, props, serial) { super(name, parent, serial); this._valueInFile = props.Value !== undefined; this.Value = this._valueInFile ? props.Value : ''; } get icon() { return 'wsParameters_bankCoderInfo'; } get className() { return CLASS_NAME; } - get displayValue() { return PropValue.format(this.Value); } + get displayValue() { return this._valueInFile ? PropValue.format(this.Value) : ''; } + // The column stays, read-only, for a Value a file already carries. + get valueEditable() { return false; } getProperties() { return [PropName, PropValue, PropDataType]; } // PI layout: schema-driven "General" group (classes/variantBankCoderInfo.json). - // UNGATED, as VariantBankNode's Value is. - _serializedOverrides() { return { Value: this.Value }; } + // Refused rather than taken: DataNode's generic tail would keep the text, mark the entry + // Modified and answer `true`, and the file would gain a key MATLAB ignores. One still in the + // encoded stream it was read from keeps that refusal's own reason. + setProperty(propName, stringValue) { + if (this._resolveProperty(propName) === 'Value') { + return (this._refuseEncodedEdit(propName, stringValue) ?? { + error: true, + reason: `'${this.displayName}' is a ${this.className}, which has no Value, so none can be set on it.`, + invalidValue: stringValue, + validValue: this.displayValue, + }); + } + return super.setProperty(propName, stringValue); + } + // Nothing overridden: the bag is written as it was read, so a file's own Value key (and + // only that) passes through `_mergeProps` unchanged, and no new one is ever written. + _serializedOverrides() { return {}; } static get defaultName() { return 'VariantBankCoderInfo'; } - // A saveobj envelope, as VariantBankNode's is, and the invented `Value` goes the same way. + // A saveobj envelope, as VariantBankNode's is, and nothing beside it: MATLAB has no `Value` + // property on this class, so none is written. // The only field here whose default is not empty is Qualifier, which MATLAB defaults to the // string 'None' — every field is a plain char, so this class's envelope is spelled // identically in both formats. Measured from a MATLAB-written dictionary. diff --git a/dist/datamodel/node/data/VariantBankCoderInfoNode.js.map b/dist/datamodel/node/data/VariantBankCoderInfoNode.js.map index 669f4982..e894843a 100644 --- a/dist/datamodel/node/data/VariantBankCoderInfoNode.js.map +++ b/dist/datamodel/node/data/VariantBankCoderInfoNode.js.map @@ -1 +1 @@ -{"version":3,"file":"VariantBankCoderInfoNode.js","sourceRoot":"","sources":["../../../../src/datamodel/node/data/VariantBankCoderInfoNode.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,OAAO,kBAAkB,MAAM,0BAA0B,CAAC;AAG1D,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAC9C,OAAO,SAAS,MAAM,yBAAyB,CAAC;AAChD,OAAO,YAAY,MAAM,4BAA4B,CAAC;AACtD,MAAM,UAAU,GAAG,+BAA+B,CAAC;AACnD,MAAM,CAAC,OAAO,OAAO,wBAAyB,SAAQ,kBAAkB;IAEpE,YAAY,IAAY,EAAE,MAAuB,EAAE,KAA8B,EAAE,MAA+B,IAAI,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/M,IAAI,IAAI,KAAa,OAAO,4BAA4B,CAAC,CAAC,CAAC;IAC3D,IAAI,SAAS,KAAa,OAAO,UAAU,CAAC,CAAC,CAAC;IAC9C,IAAI,YAAY,KAAa,OAAO,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnE,aAAa,KAAkB,OAAO,CAAC,QAAQ,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IAC5E,gFAAgF;IAChF,0CAA0C;IAC1C,oBAAoB,KAA8B,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACjF,MAAM,KAAK,WAAW,KAAa,OAAO,sBAAsB,CAAC,CAAC,CAAC;IACnE,2FAA2F;IAC3F,4FAA4F;IAC5F,mFAAmF;IACnF,0EAA0E;IAC1E,MAAM,CAAC,aAAa,CAAC,IAAY,EAAE,MAAuB,IAA8B,MAAM,MAAM,GAAG,wBAAwB,CAAC,wBAAwB,CAAC,UAAU,EAAE,CAAC,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,eAAe,EAAE,WAAW,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,GAAG,wBAAwB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,IAAI,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAA6B,CAAC,CAAC,CAAC,CAAC;IAC9gB,MAAM,CAAC,KAAK,CAAC,MAA+B,EAAE,IAAY,EAAE,MAAuB,IAA8B,MAAM,KAAK,GAAG,wBAAwB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,IAAI,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAA6B,CAAC,CAAC,CAAC,CAAC;CAC5S"} \ No newline at end of file +{"version":3,"file":"VariantBankCoderInfoNode.js","sourceRoot":"","sources":["../../../../src/datamodel/node/data/VariantBankCoderInfoNode.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/datamodel/node/data/Simulink.VariantBankCoderInfo.md b/src/datamodel/node/data/Simulink.VariantBankCoderInfo.md index 920cbd79..bfdc73ac 100644 --- a/src/datamodel/node/data/Simulink.VariantBankCoderInfo.md +++ b/src/datamodel/node/data/Simulink.VariantBankCoderInfo.md @@ -4,40 +4,65 @@ **Node class:** `VariantBankCoderInfoNode` (`src/datamodel/node/data/VariantBankCoderInfoNode.ts`) **MATLAB class:** `Simulink.VariantBankCoderInfo` -**Editable in our UI:** no (Value column exposed via text editor but no fixture to round-trip) -**Verified against:** not probed (no params.sldd fixture instantiates this class) +**Editable in our UI:** no. The Value column is read-only, and a Value edit is refused: the +MATLAB class has no Value. +**Verified against:** MATLAB R2027a (27.1.0.3409468), 2026-10-16, and the entry `vbciD` MATLAB +wrote into `test/fixtures/classes_r2027a_{text,binary}.sldd` (`make_classes_r2027a.m`) ## Overview -A Simulink.VariantBankCoderInfo stores code-generation settings for variant banks. -The node exposes PropName, PropValue, and PropDataType, with a text editor on Value -(inherited from DataNode). Like VariantBank, no fixture in the test suite -(`params.sldd`) contains a VariantBankCoderInfo entry, so round-trip fidelity -cannot be validated without generating new MATLAB fixtures (out of scope). +A Simulink.VariantBankCoderInfo stores code-generation settings for variant banks. MATLAB's +class has five properties: HeaderFile, DefinitionFile, PreStatement, PostStatement and +Qualifier. All five are char, and all default to `''` except Qualifier, which defaults to +`'None'`. A dictionary holds the object as a saveobj envelope of those five, in both formats, +with nothing beside it. None of the five is modelled as a property of the node yet; the +envelope is written back as it was read. -The node has a `createDefault` static that produces a minimal in-memory instance -with an empty-string Value. It serializes through `_serializeSimulinkObject`. +The node lists PropName, PropValue and PropDataType. PropValue stays as a read-only column, for +a Value key a file already carries (see below). `createDefault` makes the envelope MATLAB writes +for a new entry, with its fields, order and defaults measured from a MATLAB-written dictionary, +and nothing beside it. ## Property table | Property | Editor | Serialized key | Notes | |------------|--------|--------------------------|-------------------------------| | Name | text | entry name | MATLAB identifier | -| Value | text | Value (in _properties) | Exposed but no fixture to validate round-trip | +| Value | none | written back as read | Not a property of the class; shown only where the file carries one | | DataType | label | (className display) | Read-only label | -## Read-only / no-fixture status +## Value: the class has none -- The node does NOT explicitly override `valueEditable` to false; BaseNode default - applies (true unless displayValue is `<...>`). With an empty Value, the editor - would be available. -- However, no `params.sldd` test fixture instantiates this class, so there is no - round-trip validation path. -- **Contract-lock**: pin the className and property shape via `createDefault` in - `test/parity/fidelity/hostnodes.fidelity.test.ts`. +Measured in R2027a: `isprop(Simulink.VariantBankCoderInfo, 'Value')` is 0, and setting one fails +with "Unrecognized property 'Value' for class 'Simulink.VariantBankCoderInfo'." + +This node used to offer one anyway. The cell took a text editor (`valueEditable` was inherited, +and true), `setProperty('Value', …)` returned true, and both save paths wrote the text beside +the envelope: `_properties: {Value: …}` in a text dictionary, `

…

` +after the saveobj `P` in a binary one. MATLAB opens both files with no error and no warning, +reads every entry, and builds an object with no Value, its Qualifier still `'None'`; the value +is silently dropped. So the edit was accepted, saved, and had no effect. Every MATLAB-written +entry also showed `''`, which implies a char value the class does not have. + +Following MATLAB (the 2026-10-16 ruling, group E, C-169), as `Simulink.VariantBank` does (C-168) +and a bus element does (`BaseBusElementNode`): + +- `valueEditable` is false, so neither the table nor the Property Inspector offers an editor. +- `setProperty('Value', …)` is refused: "'' is a Simulink.VariantBankCoderInfo, which has + no Value, so none can be set on it." An entry still in an encoded stream keeps that refusal's + reason. +- `_serializedOverrides` is empty, so no save writes a Value. A Value key a file already carries + (only ever this package's) passes through `_mergeProps` unchanged, and is shown quoted. +- An entry whose file carries no Value key, which is every one MATLAB writes, shows an empty + cell. + +Locked by `test/variantBankNoValue.test.ts` (both formats, and `createDefault`) and the +`READ_ONLY` row of `test/variantLeafNodes.test.ts`. The envelope's fields are pinned by +`test/newEntryEnvelopeBothFormats.test.ts`, and the property list by +`test/parity/fidelity/hostnodes.fidelity.test.ts`. ## Open questions / deferred -- **Fixture generation**: needs a MATLAB-created `.sldd` with a VariantBankCoderInfo - entry. Deferred. -- **Value semantics**: undocumented; no MATLAB probe was run. +- **The class's own fields** (C-169 option (b)): show and edit HeaderFile, DefinitionFile, + PreStatement, PostStatement and Qualifier out of the envelope. The MATLAB-written fixtures + this was waiting for now exist (`vbciD` above). diff --git a/src/datamodel/node/data/VariantBankCoderInfoNode.ts b/src/datamodel/node/data/VariantBankCoderInfoNode.ts index 21fa87aa..ec3c4ce3 100644 --- a/src/datamodel/node/data/VariantBankCoderInfoNode.ts +++ b/src/datamodel/node/data/VariantBankCoderInfoNode.ts @@ -2,22 +2,55 @@ import SimulinkObjectNode from '../SimulinkObjectNode.js'; import type { PropClass } from '../BaseNode.js'; import type BaseNode from '../BaseNode.js'; +import type { SetPropertyResult } from '../DataNode.js'; import PropName from '../../prop/PropName.js'; import PropValue from '../../prop/PropValue.js'; import PropDataType from '../../prop/PropDataType.js'; const CLASS_NAME = 'Simulink.VariantBankCoderInfo'; +// MATLAB's Simulink.VariantBankCoderInfo has no Value (R2027a: `isprop(Simulink.VariantBankCoderInfo, +// 'Value')` is 0, and setting one fails with "Unrecognized property 'Value' for class +// 'Simulink.VariantBankCoderInfo'."). Its properties are HeaderFile, DefinitionFile, PreStatement, +// PostStatement and Qualifier, which are also the fields of the saveobj envelope a dictionary +// holds it as; none of them is modelled here yet. So, as for VariantBankNode (and a bus element, +// BaseBusElementNode), the Value column offers no editor and a Value edit is refused. A Value key +// a file already carries, which only this package ever wrote (MATLAB reads such a file without a +// warning, builds the object with no Value, and drops the key on its next save), is shown and +// written back as it was read, read-only. export default class VariantBankCoderInfoNode extends SimulinkObjectNode { Value: unknown; - constructor(name: string, parent: BaseNode | null, props: Record, serial: Record) { super(name, parent, serial); this.Value = props.Value !== undefined ? props.Value : ''; } + // Whether the file carried a Value key at all. MATLAB's own entries never do, and showing + // their absent one as `''` implied a char value the class does not have. + readonly _valueInFile: boolean; + constructor(name: string, parent: BaseNode | null, props: Record, serial: Record) { super(name, parent, serial); this._valueInFile = props.Value !== undefined; this.Value = this._valueInFile ? props.Value : ''; } get icon(): string { return 'wsParameters_bankCoderInfo'; } get className(): string { return CLASS_NAME; } - get displayValue(): string { return PropValue.format(this.Value); } + get displayValue(): string { return this._valueInFile ? PropValue.format(this.Value) : ''; } + // The column stays, read-only, for a Value a file already carries. + get valueEditable(): boolean { return false; } getProperties(): PropClass[] { return [PropName, PropValue, PropDataType]; } // PI layout: schema-driven "General" group (classes/variantBankCoderInfo.json). - // UNGATED, as VariantBankNode's Value is. - _serializedOverrides(): Record { return { Value: this.Value }; } + // Refused rather than taken: DataNode's generic tail would keep the text, mark the entry + // Modified and answer `true`, and the file would gain a key MATLAB ignores. One still in the + // encoded stream it was read from keeps that refusal's own reason. + setProperty(propName: string, stringValue: string): true | SetPropertyResult { + if (this._resolveProperty(propName) === 'Value') { + return ( + this._refuseEncodedEdit(propName, stringValue) ?? { + error: true, + reason: `'${this.displayName}' is a ${this.className}, which has no Value, so none can be set on it.`, + invalidValue: stringValue, + validValue: this.displayValue, + } + ); + } + return super.setProperty(propName, stringValue); + } + // Nothing overridden: the bag is written as it was read, so a file's own Value key (and + // only that) passes through `_mergeProps` unchanged, and no new one is ever written. + _serializedOverrides(): Record { return {}; } static get defaultName(): string { return 'VariantBankCoderInfo'; } - // A saveobj envelope, as VariantBankNode's is, and the invented `Value` goes the same way. + // A saveobj envelope, as VariantBankNode's is, and nothing beside it: MATLAB has no `Value` + // property on this class, so none is written. // The only field here whose default is not empty is Qualifier, which MATLAB defaults to the // string 'None' — every field is a plain char, so this class's envelope is spelled // identically in both formats. Measured from a MATLAB-written dictionary. diff --git a/test/variantBankNoValue.test.ts b/test/variantBankNoValue.test.ts index 96ddbe1f..b5541612 100644 --- a/test/variantBankNoValue.test.ts +++ b/test/variantBankNoValue.test.ts @@ -1,12 +1,16 @@ // Copyright 2026 The MathWorks, Inc. // -// A Simulink.VariantBank has no Value, so the entry offers none, takes none, and writes none. +// A Simulink.VariantBank has no Value, and neither has a Simulink.VariantBankCoderInfo, so the +// entry offers none, takes none, and writes none. // // Measured in R2027a (27.1.0.3409468): `properties(Simulink.VariantBank)` is Name, Description, // VariantConditions, AllChoicesCoderInfo and ActiveChoiceCoderInfo (the saveobj envelope adds // BankCoderInfo), `isprop(Simulink.VariantBank, 'Value')` is 0, and `vb.Value = 'x'` fails with // "Unrecognized property 'Value' for class 'Simulink.VariantBank'." A dictionary MATLAB writes -// holds the bank as that envelope alone, in both formats. +// holds the bank as that envelope alone, in both formats. `properties(Simulink.VariantBankCoderInfo)` +// is HeaderFile, DefinitionFile, PreStatement, PostStatement and Qualifier, all char ('' but for +// Qualifier, 'None'), and the same two answers hold: `isprop(…, 'Value')` is 0, and setting one +// fails with "Unrecognized property 'Value' for class 'Simulink.VariantBankCoderInfo'." // // The node used to offer a Value anyway: the cell took an editor, `setProperty('Value', …)` // returned true, and both save paths wrote the text next to the envelope (`_properties.Value` in @@ -14,28 +18,49 @@ // without an error or a warning, the object it builds has no Value, and a MATLAB re-save drops // the key; so the edit was accepted, saved, and had no effect. Every MATLAB-written bank showed // `''`, a char value the class does not have. The ruling (2026-10-16, group E, theme 2: follow -// MATLAB) is what #113 did for a bus element (BaseBusElementNode): no editor, a Value edit -// refused, and a Value key the file already carries (only ever this package's) shown and written -// back as it was read. +// MATLAB; C-168 for the bank, C-169 for its coder info) is what #113 did for a bus element +// (BaseBusElementNode): no editor, a Value edit refused, and a Value key the file already carries +// (only ever this package's) shown and written back as it was read. // // Driven through the session API a host uses — `ingest`, the node's own `setProperty`, `toRow` -// and `toPIObject`, and `serializeSource` — over vbD, a bank MATLAB wrote into -// test/fixtures/classes_r2027a_{text,binary}.sldd (test/fixtures/make_classes_r2027a.m), and -// over `createDefault`, which is what a new entry starts as. +// and `toPIObject`, and `serializeSource` — over vbD and vbciD, a bank and a bank coder info +// MATLAB wrote into test/fixtures/classes_r2027a_{text,binary}.sldd +// (test/fixtures/make_classes_r2027a.m), and over `createDefault`, which is what a new entry +// starts as. import { describe, it, expect } from 'vitest'; import { readFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import { strFromU8, strToU8, unzipSync, zipSync } from 'fflate'; import { createSession, ingest, type SerializedSource } from '../src/index.js'; import VariantBankNode from '../src/datamodel/node/data/VariantBankNode.js'; +import VariantBankCoderInfoNode from '../src/datamodel/node/data/VariantBankCoderInfoNode.js'; import type DataNode from '../src/datamodel/node/DataNode.js'; import '../src/datamodel/node/data/NodeClassMap.js'; type Format = 'text' | 'binary'; const FORMATS: Format[] = ['text', 'binary']; -const CLASS = 'Simulink.VariantBank'; -const ENTRY = 'vbD'; -const RENAMED = 'vbE'; + +// Each class, the entry MATLAB wrote of it, the name a rename gives that entry, and the last +// lines of its saveobj `P` in the binary file (where this package's old writer put a Value after). +const CASES = [ + { + CLASS: 'Simulink.VariantBank', + Node: VariantBankNode, + what: 'the bank', + ENTRY: 'vbD', + RENAMED: 'vbE', + envelopeEnd: '

\n \n

', + }, + { + CLASS: 'Simulink.VariantBankCoderInfo', + Node: VariantBankCoderInfoNode, + what: 'the bank coder info', + ENTRY: 'vbciD', + RENAMED: 'vbciE', + envelopeEnd: '

None

\n
\n

', + }, +] as const; +type Case = (typeof CASES)[number]; function fixture(format: Format): Uint8Array { return new Uint8Array(readFileSync(fileURLToPath(new URL(`./fixtures/classes_r2027a_${format}.sldd`, import.meta.url)))); @@ -48,8 +73,8 @@ function textEntries(text: string): any[] { function chunkXml(bytes: Uint8Array): string { return strFromU8(unzipSync(bytes)['data/chunk0.xml']); } -// The `` of one entry, as the binary file spells it. -function bankXml(xml: string, entry: string): string { +// The `` of one entry, as the binary file spells it. +function bankXml(xml: string, entry: string, CLASS: string): string { const at = xml.indexOf(`

${entry}

`); expect(at, `no entry ${entry} in the chunk`).toBeGreaterThan(-1); const open = xml.indexOf(``, at); @@ -58,10 +83,10 @@ function bankXml(xml: string, entry: string): string { return xml.slice(open, close); } -// vbD with a sibling Value 'b1' added to its bytes, in the place and the spelling this package's -// old writer put one (MATLAB, given that file, opened it without a warning). Added to the bytes, -// so the reader is what puts it in the node's bag. -function withValue(format: Format, text: string): Uint8Array | string { +// The entry with a sibling Value 'b1' added to its bytes, in the place and the spelling this +// package's old writer put one (MATLAB, given that file, opened it without a warning). Added to +// the bytes, so the reader is what puts it in the node's bag. +function withValue({ CLASS, ENTRY, envelopeEnd }: Case, format: Format, text: string): Uint8Array | string { const bytes = fixture(format); if (format === 'text') { const json = JSON.parse(strFromU8(bytes)); @@ -74,8 +99,7 @@ function withValue(format: Format, text: string): Uint8Array | string { } const parts = unzipSync(bytes); const xml = strFromU8(parts['data/chunk0.xml']); - const envelopeEnd = '

\n \n

'; - expect(xml.split(envelopeEnd).length - 1, 'one VariantBank envelope in the binary fixture').toBe(1); + expect(xml.split(envelopeEnd).length - 1, `one ${CLASS} envelope in the binary fixture`).toBe(1); parts['data/chunk0.xml'] = strToU8( xml.replace(envelopeEnd, `${envelopeEnd}\n

${text}

`), ); @@ -107,12 +131,12 @@ function saved(session: any, source: any): Uint8Array | string { // What a save wrote for the entry: its bag as the reader reads the saved file back, and the // entry's own spelling in that file (the text entry's element, or the binary entry's XML). -function savedEntry(content: Uint8Array | string, entry: string) { +function savedEntry(content: Uint8Array | string, entry: string, CLASS: string) { const bag = (entryIn(open(content).source, entry) as any).serial._properties as Record; const spelled = typeof content === 'string' ? JSON.stringify(textEntries(content).find((e) => e.name === entry).value) - : bankXml(chunkXml(content), entry); + : bankXml(chunkXml(content), entry, CLASS); return { bag, spelled }; } @@ -126,7 +150,7 @@ function piValue(node: DataNode) { return { shown: (pi.objects[0] as Record).Value, editable: item.editable }; } -function expectRefused(node: DataNode, text: string) { +function expectRefused(CLASS: string, node: DataNode, text: string) { const result = node.setProperty('Value', text); expect(result).not.toBe(true); expect(result).toMatchObject({ @@ -137,106 +161,109 @@ function expectRefused(node: DataNode, text: string) { }); } -describe(`${CLASS} has no Value`, () => { - for (const format of FORMATS) { - describe(`${ENTRY}, the bank MATLAB wrote, ${format} dictionary`, () => { - it('shows no Value, and offers no editor in the table or the Property Inspector', () => { - const node = entryIn(open(fixture(format)).source, ENTRY); - expect(node.className).toBe(CLASS); - // Not `''`, which is a char value the class does not have. - expect(node.displayValue).toBe(''); - expect(node.valueEditable).toBe(false); - const row = node.toRow()!; - expect(row.Value).toBe(''); - expect(row._valueEditable).toBe(false); - expect(piValue(node)).toEqual({ shown: '', editable: false }); - }); +for (const c of CASES) { + const { CLASS, Node, what, ENTRY, RENAMED } = c; + describe(`${CLASS} has no Value`, () => { + for (const format of FORMATS) { + describe(`${ENTRY}, ${what} MATLAB wrote, ${format} dictionary`, () => { + it('shows no Value, and offers no editor in the table or the Property Inspector', () => { + const node = entryIn(open(fixture(format)).source, ENTRY); + expect(node.className).toBe(CLASS); + // Not `''`, which is a char value the class does not have. + expect(node.displayValue).toBe(''); + expect(node.valueEditable).toBe(false); + const row = node.toRow()!; + expect(row.Value).toBe(''); + expect(row._valueEditable).toBe(false); + expect(piValue(node)).toEqual({ shown: '', editable: false }); + }); - it('refuses a Value edit, and no save writes one', () => { - const { session, source } = open(fixture(format)); - const node = entryIn(source, ENTRY); - - expectRefused(node, 'x'); - // The quoted form the cell would have been seeded with, and the empty text it shows. - expectRefused(node, "'x'"); - expectRefused(node, ''); - expect(node.status).not.toBe('Modified'); - expect(node.displayValue).toBe(''); - expect(binaryPath(node)).not.toHaveProperty('Value'); - expect(textElem(node)).not.toHaveProperty('_properties'); - - // A rename rewrites the entry from the node, so the save reaches the writer (a binary - // entry nobody edited is written as the bytes it was read from). - expect(node.setProperty('Name', RENAMED)).toBe(true); - const { bag, spelled } = savedEntry(saved(session, source), RENAMED); - expect(bag).not.toHaveProperty('Value'); - expect(spelled).not.toContain('"Value"'); - expect(spelled).not.toContain('

{ + const { session, source } = open(fixture(format)); + const node = entryIn(source, ENTRY); - it('a save writes the bank back as MATLAB wrote it', () => { - // A guard: this held before the ruling too, because an EMPTY Value was never written - // under an envelope (DataNode._mergeProps). It pins that the change keeps it so. - const read = open(fixture(format)); - const before = JSON.parse(JSON.stringify((entryIn(read.source, ENTRY) as any).serial._properties)); - const untouched = saved(read.session, read.source); - if (format === 'text') { - expect(untouched).toBe(strFromU8(fixture('text'))); - } - expect(savedEntry(untouched, ENTRY).bag).toEqual(before); - - expect(entryIn(read.source, ENTRY).setProperty('Name', RENAMED)).toBe(true); - const renamed = saved(read.session, read.source); - expect(savedEntry(renamed, RENAMED).bag).toEqual(before); - const ownSpelling = savedEntry(renamed, RENAMED).spelled; - if (format === 'text') { - const original = textEntries(strFromU8(fixture('text'))).find((e) => e.name === ENTRY).value; - expect(JSON.parse(ownSpelling)).toEqual(original); - } else { - // The writer's own XML spelling of the envelope (an empty cell as an open and a close - // tag, say) is not this item's; what is, is that nothing beside the envelope is added. - expect(ownSpelling).toContain('

{ + // A guard: this held before the ruling too, because an EMPTY Value was never written + // under an envelope (DataNode._mergeProps). It pins that the change keeps it so. + const read = open(fixture(format)); + const before = JSON.parse(JSON.stringify((entryIn(read.source, ENTRY) as any).serial._properties)); + const untouched = saved(read.session, read.source); + if (format === 'text') { + expect(untouched).toBe(strFromU8(fixture('text'))); + } + expect(savedEntry(untouched, ENTRY, CLASS).bag).toEqual(before); + + expect(entryIn(read.source, ENTRY).setProperty('Name', RENAMED)).toBe(true); + const renamed = saved(read.session, read.source); + expect(savedEntry(renamed, RENAMED, CLASS).bag).toEqual(before); + const ownSpelling = savedEntry(renamed, RENAMED, CLASS).spelled; + if (format === 'text') { + const original = textEntries(strFromU8(fixture('text'))).find((e) => e.name === ENTRY).value; + expect(JSON.parse(ownSpelling)).toEqual(original); + } else { + // The writer's own XML spelling of the envelope (an empty cell as an open and a close + // tag, say) is not this item's; what is, is that nothing beside the envelope is added. + expect(ownSpelling).toContain('

{ + const { session, source } = open(withValue(c, format, 'b1')); + const node = entryIn(source, ENTRY); - it('keeps a Value the file already carries, as read and read-only, on both save paths', () => { - const { session, source } = open(withValue(format, 'b1')); - const node = entryIn(source, ENTRY); - - expect(node.displayValue).toBe("'b1'"); - expect(node.valueEditable).toBe(false); - const row = node.toRow()!; - expect(row.Value).toBe("'b1'"); - expect(row._valueEditable).toBe(false); - expect(piValue(node)).toEqual({ shown: "'b1'", editable: false }); - - expectRefused(node, 'changed'); - expectRefused(node, "'b1'"); - expect(node.status).not.toBe('Modified'); - expect(binaryPath(node).Value).toBe('b1'); - expect(textElem(node)._properties).toEqual({ Value: 'b1' }); - - expect(node.setProperty('Name', RENAMED)).toBe(true); - const out = saved(session, source); - const { bag, spelled } = savedEntry(out, RENAMED); - expect(bag.Value).toBe('b1'); - expect(spelled).toContain(format === 'text' ? '"_properties":{"Value":"b1"}' : '

b1

'); - expect(entryIn(open(out).source, RENAMED).displayValue).toBe("'b1'"); + expect(node.displayValue).toBe("'b1'"); + expect(node.valueEditable).toBe(false); + const row = node.toRow()!; + expect(row.Value).toBe("'b1'"); + expect(row._valueEditable).toBe(false); + expect(piValue(node)).toEqual({ shown: "'b1'", editable: false }); + + expectRefused(CLASS, node, 'changed'); + expectRefused(CLASS, node, "'b1'"); + expect(node.status).not.toBe('Modified'); + expect(binaryPath(node).Value).toBe('b1'); + expect(textElem(node)._properties).toEqual({ Value: 'b1' }); + + expect(node.setProperty('Name', RENAMED)).toBe(true); + const out = saved(session, source); + const { bag, spelled } = savedEntry(out, RENAMED, CLASS); + expect(bag.Value).toBe('b1'); + expect(spelled).toContain(format === 'text' ? '"_properties":{"Value":"b1"}' : '

b1

'); + expect(entryIn(open(out).source, RENAMED).displayValue).toBe("'b1'"); + }); }); - }); - } + } - it('a new entry (createDefault) shows none, refuses one and saves none', () => { - const node = VariantBankNode.createDefault('n', null); - expect(node.displayValue).toBe(''); - expect(node.valueEditable).toBe(false); - expect(node.toRow()!._valueEditable).toBe(false); - expect(piValue(node)).toEqual({ shown: '', editable: false }); - expectRefused(node, 'x'); - expect(binaryPath(node)).not.toHaveProperty('Value'); - expect(textElem(node)).not.toHaveProperty('_properties'); - expect(node.serializeXml('P', { Name: 'Value' }, 0)).not.toContain('

{ + const node = Node.createDefault('n', null); + expect(node.displayValue).toBe(''); + expect(node.valueEditable).toBe(false); + expect(node.toRow()!._valueEditable).toBe(false); + expect(piValue(node)).toEqual({ shown: '', editable: false }); + expectRefused(CLASS, node, 'x'); + expect(binaryPath(node)).not.toHaveProperty('Value'); + expect(textElem(node)).not.toHaveProperty('_properties'); + expect(node.serializeXml('P', { Name: 'Value' }, 0)).not.toContain('

(['Simulink.VariantBank']); +// already carries but takes no edit of it: Simulink.VariantBank has no Value, and nor has +// Simulink.VariantBankCoderInfo (R2027a; the 2026-10-16 ruling, group E, C-168 and C-169). +// test/variantBankNoValue.test.ts covers both over the entries MATLAB wrote. +const READ_ONLY = new Set(['Simulink.VariantBank', 'Simulink.VariantBankCoderInfo']); // The `_array_class` a NEW entry is written with, where it differs from the class the node // reports for a parsed one. Exactly one class differs, and it differs for a reason MATLAB @@ -113,7 +113,9 @@ describe('single-property leaf nodes — shared contract', () => { }); describe('per-class display differences', () => { - it('VariantBank and VariantBankCoderInfo quote their Value like MATLAB', () => { + it('VariantBank and VariantBankCoderInfo show a Value their file carries quoted', () => { + // Neither MATLAB class has a Value (READ_ONLY above); this is the key only this package's + // old writer put in a file, shown read-only. for (const Node of [VariantBankNode, VariantBankCoderInfoNode]) { const n = Node.parse(wrap('Simulink.VariantBank', { Value: 'b1' }), 'n', null) as any; expect(n.displayValue).toBe("'b1'"); From cca1e6ef6bba46086d0e9c990b3b66e475d31408 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 16 Oct 2026 16:32:57 -0400 Subject: [PATCH 4/7] Offer a Signal's Complexity and DimensionsMode as dropdowns over MATLAB's own sets MATLAB R2027a's Simulink.Signal (and mpt.Signal) takes Complexity from auto|real|complex and DimensionsMode from auto|Fixed|Variable, both defaulting to auto, and refuses anything else with "There is no enumerated value named 'X'." Both showed as read-only labels, while BusElement and ValueType already offered the same two properties as dropdowns. A setProperty('complexity', ...) also answered true and changed nothing: the text went onto a stray lowercase field that nothing shows or saves. SignalNode now uses BusElement's and ValueType's mechanism with the Signal's sets. Two new atoms (PropSignalComplexity, PropSignalDimensionsMode) replace the two schema descriptors in place, both in the table columns and in the Property Inspector layout, so both panes read the node field an edit writes. The column order and the schema's layout order are unchanged. setProperty refuses any other value through DataNode._rejectUnknownEnumeral, but only for these two properties, so Storage Class keeps its own refusal. Both save paths write the key when the file had it or the value is not auto. An untouched Signal, or one set back to auto, therefore saves with no new key in a text dictionary, which is how MATLAB writes it. A binary dictionary always has the key. A cleared cell shows auto and is saved as auto, never as ''. Dimensions stays a label, and so does Parameter's Complexity, which comes from its Value. test/signalEnumProps.test.ts drives the session and the node over Signals MATLAB wrote (sigC, sigR, sigA and sigNo in classes_r2027a_{text,binary}.sldd, and sig1 in params.sldd), plus an mpt.Signal and a new Signal. A default Signal set to complex/Variable saves as what MATLAB wrote for sigC: the text bag is equal as a value (the new keys come last, where MATLAB sorts them), and the binary element matches character for character. Set back to auto, it saves as sigA. The test also covers refusals, a clear, undo through the session, and the Property Inspector layout. Existing tests changed only where they pinned the removed label: - schemaColumns.test's "read-only Dimensions Mode" now expects the select cell. - mptClasses.test's typed columns now read the cells' text. - propAtoms.test: one comment. Simulink.Signal.md and the two shared atoms' comments now describe the new behaviour. --- dist/datamodel/node/data/SignalNode.d.ts | 6 +- dist/datamodel/node/data/SignalNode.d.ts.map | 2 +- dist/datamodel/node/data/SignalNode.js | 56 ++- dist/datamodel/node/data/SignalNode.js.map | 2 +- dist/datamodel/prop/PropComplexity.d.ts.map | 2 +- dist/datamodel/prop/PropComplexity.js | 9 +- dist/datamodel/prop/PropComplexity.js.map | 2 +- .../prop/PropDimensionsMode.d.ts.map | 2 +- dist/datamodel/prop/PropDimensionsMode.js | 8 +- dist/datamodel/prop/PropDimensionsMode.js.map | 2 +- dist/datamodel/prop/PropSignalComplexity.d.ts | 14 + .../prop/PropSignalComplexity.d.ts.map | 1 + dist/datamodel/prop/PropSignalComplexity.js | 36 ++ .../prop/PropSignalComplexity.js.map | 1 + .../prop/PropSignalDimensionsMode.d.ts | 14 + .../prop/PropSignalDimensionsMode.d.ts.map | 1 + .../prop/PropSignalDimensionsMode.js | 36 ++ .../prop/PropSignalDimensionsMode.js.map | 1 + src/datamodel/node/data/SignalNode.ts | 60 ++- src/datamodel/node/data/Simulink.Signal.md | 57 ++- src/datamodel/prop/PropComplexity.ts | 9 +- src/datamodel/prop/PropDimensionsMode.ts | 8 +- src/datamodel/prop/PropSignalComplexity.ts | 42 +++ .../prop/PropSignalDimensionsMode.ts | 42 +++ test/mptClasses.test.ts | 5 +- test/propAtoms.test.ts | 6 +- test/schema/schemaColumns.test.ts | 11 +- test/signalEnumProps.test.ts | 343 ++++++++++++++++++ 28 files changed, 725 insertions(+), 53 deletions(-) create mode 100644 dist/datamodel/prop/PropSignalComplexity.d.ts create mode 100644 dist/datamodel/prop/PropSignalComplexity.d.ts.map create mode 100644 dist/datamodel/prop/PropSignalComplexity.js create mode 100644 dist/datamodel/prop/PropSignalComplexity.js.map create mode 100644 dist/datamodel/prop/PropSignalDimensionsMode.d.ts create mode 100644 dist/datamodel/prop/PropSignalDimensionsMode.d.ts.map create mode 100644 dist/datamodel/prop/PropSignalDimensionsMode.js create mode 100644 dist/datamodel/prop/PropSignalDimensionsMode.js.map create mode 100644 src/datamodel/prop/PropSignalComplexity.ts create mode 100644 src/datamodel/prop/PropSignalDimensionsMode.ts create mode 100644 test/signalEnumProps.test.ts diff --git a/dist/datamodel/node/data/SignalNode.d.ts b/dist/datamodel/node/data/SignalNode.d.ts index a11449e7..48b8015b 100644 --- a/dist/datamodel/node/data/SignalNode.d.ts +++ b/dist/datamodel/node/data/SignalNode.d.ts @@ -1,6 +1,6 @@ import DataNode from '../DataNode.js'; import type { SetPropertyResult } from '../DataNode.js'; -import type { PropClass } from '../BaseNode.js'; +import type { PropClass, PIGroupDef } from '../BaseNode.js'; import type BaseNode from '../BaseNode.js'; export default class SignalNode extends DataNode { DataType: string; @@ -8,6 +8,8 @@ export default class SignalNode extends DataNode { Max: number | undefined; Unit: string; Description: string; + Complexity: string; + DimensionsMode: string; constructor(name: string, parent: BaseNode | null, props: Record, serial: Record); get icon(): string; get className(): string; @@ -15,9 +17,11 @@ export default class SignalNode extends DataNode { get valueEditable(): boolean; get dataType(): string; getProperties(): PropClass[]; + getPILayout(): PIGroupDef[] | null; setProperty(propName: string, stringValue: string): true | SetPropertyResult; _getSerializedProperties(): Record; serializeValue(): unknown; + _enumOverrides(sp: Record): Record; static get defaultName(): string; static createDefault(name: string, parent: BaseNode | null): SignalNode; static parse(rawVal: Record, name: string, parent: BaseNode | null): SignalNode; diff --git a/dist/datamodel/node/data/SignalNode.d.ts.map b/dist/datamodel/node/data/SignalNode.d.ts.map index 49fca774..83e258ce 100644 --- a/dist/datamodel/node/data/SignalNode.d.ts.map +++ b/dist/datamodel/node/data/SignalNode.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"SignalNode.d.ts","sourceRoot":"","sources":["../../../../src/datamodel/node/data/SignalNode.ts"],"names":[],"mappings":"AAEA,OAAO,QAAgC,MAAM,gBAAgB,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAa3C,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,QAAQ;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;gBAER,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAgBlH,IAAI,IAAI,IAAI,MAAM,CAA8D;IAKhF,IAAI,SAAS,IAAI,MAAM,CAAmI;IAE1J,IAAI,YAAY,IAAI,MAAM,CAAe;IACzC,IAAI,aAAa,IAAI,OAAO,CAAkB;IAM9C,IAAI,QAAQ,IAAI,MAAM,CAA0B;IAEhD,aAAa,IAAI,SAAS,EAAE;IAI5B,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,iBAAiB;IAgB5E,wBAAwB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAWnD,cAAc,IAAI,OAAO;IAczB,MAAM,KAAK,WAAW,IAAI,MAAM,CAAqB;IAErD,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,UAAU;IAUvE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,UAAU;CAMnG"} \ No newline at end of file +{"version":3,"file":"SignalNode.d.ts","sourceRoot":"","sources":["../../../../src/datamodel/node/data/SignalNode.ts"],"names":[],"mappings":"AAEA,OAAO,QAAgC,MAAM,gBAAgB,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AA0B3C,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,QAAQ;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IAGpB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;gBAEX,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAkBlH,IAAI,IAAI,IAAI,MAAM,CAA8D;IAKhF,IAAI,SAAS,IAAI,MAAM,CAAmI;IAE1J,IAAI,YAAY,IAAI,MAAM,CAAe;IACzC,IAAI,aAAa,IAAI,OAAO,CAAkB;IAM9C,IAAI,QAAQ,IAAI,MAAM,CAA0B;IAEhD,aAAa,IAAI,SAAS,EAAE;IAI5B,WAAW,IAAI,UAAU,EAAE,GAAG,IAAI;IAKlC,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,iBAAiB;IAyB5E,wBAAwB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAYnD,cAAc,IAAI,OAAO;IAuBzB,cAAc,CAAC,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IASpE,MAAM,KAAK,WAAW,IAAI,MAAM,CAAqB;IAErD,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,UAAU;IAUvE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,UAAU;CAMnG"} \ No newline at end of file diff --git a/dist/datamodel/node/data/SignalNode.js b/dist/datamodel/node/data/SignalNode.js index 729f9074..a06a0aa8 100644 --- a/dist/datamodel/node/data/SignalNode.js +++ b/dist/datamodel/node/data/SignalNode.js @@ -7,10 +7,22 @@ import PropMin from '../../prop/PropMin.js'; import PropMax from '../../prop/PropMax.js'; import PropUnit from '../../prop/PropUnit.js'; import PropDescription from '../../prop/PropDescription.js'; +import PropSignalComplexity from '../../prop/PropSignalComplexity.js'; +import PropSignalDimensionsMode from '../../prop/PropSignalDimensionsMode.js'; import { withSourceKeys } from './BaseBusNode.js'; import { schemaColumns } from '../schemaBridge.js'; import { oneWrite, writeUnderWay } from '../../parser/MatWriter.js'; const CLASS_NAME = 'Simulink.Signal'; +// Complexity and DimensionsMode are editable node fields, so the table and the Property Inspector +// read them through these atoms (the Signal's own enums, auto included) rather than through the +// schema descriptors, which read the source bag and are labels. In place, by key, so the column +// order and the schema layout's order stay what the schema authored. The schema keeps both +// descriptors: Parameter's complexity is one too, and stays a label, derived from its Value. +const ENUM_ATOMS = new Map([ + ['complexity', PropSignalComplexity], + ['dimensionsMode', PropSignalDimensionsMode], +]); +const withEnumAtoms = (props) => props.map((p) => ENUM_ATOMS.get(p.key) ?? p); export default class SignalNode extends DataNode { constructor(name, parent, props, serial) { super(name, parent, serial); @@ -26,6 +38,8 @@ export default class SignalNode extends DataNode { this.Max = props.Max; this.Unit = props.DocUnits || props.Unit || ''; this.Description = props.Description || ''; + this.Complexity = props.Complexity || 'auto'; + this.DimensionsMode = props.DimensionsMode || 'auto'; } get icon() { return this.isDerived ? 'serviceInterfaces' : 'wsSignal'; } // Report the class the FILE actually holds — same reason as ParameterNode's: @@ -41,9 +55,14 @@ export default class SignalNode extends DataNode { // Data Type column. Without this the column and the PI row were blank for // every Signal in every channel even though all four carry the key. get dataType() { return this.DataType; } - getProperties() { return [PropName, withSourceKeys(PropDataType, ['DataType', 'DataType_internal']), PropMin, PropMax, PropUnit, PropDescription, ...schemaColumns(this.className)]; } - // PI layout is now declarative — see schema/classes/signal.json `layout`, - // resolved by the inherited BaseNode.getPILayout via buildPILayout. + getProperties() { return [PropName, withSourceKeys(PropDataType, ['DataType', 'DataType_internal']), PropMin, PropMax, PropUnit, PropDescription, ...withEnumAtoms(schemaColumns(this.className))]; } + // PI layout is declarative (schema/classes/signal.json `layout`, resolved by buildPILayout), + // with the two enum atoms in place of their descriptors, so an edit the table makes shows in + // the PI too (ValueTypeNode.getPILayout gives the full account of that gap). + getPILayout() { + const layout = super.getPILayout(); + return layout && layout.map((g) => ({ ...g, items: withEnumAtoms(g.items) })); + } setProperty(propName, stringValue) { // Its own shown text, set back, is no edit (DataNode._isUnchangedCommit). if (this._isUnchangedCommit(propName, stringValue)) { @@ -57,6 +76,15 @@ export default class SignalNode extends DataNode { if (propName === 'Min' || propName === 'Max') { return this._setMinMax(propName, stringValue); } + // Complexity and DimensionsMode take only the Signal's own enumerals, as MATLAB does; + // the shared check reads the set off the atom the dropdown is built from. Only these + // two: Storage Class is a select too, and the schema path refuses it in its own words. + if (ENUM_ATOMS.has(propName)) { + const notAnEnumeral = this._rejectUnknownEnumeral(propName, stringValue); + if (notAnEnumeral) { + return notAnEnumeral; + } + } return DataNode.prototype.setProperty.call(this, propName, stringValue); } _getSerializedProperties() { @@ -75,6 +103,7 @@ export default class SignalNode extends DataNode { if ('Description' in sp || this.Description) { props.Description = this.Description; } + Object.assign(props, this._enumOverrides(sp)); return props; } serializeValue() { @@ -96,8 +125,29 @@ export default class SignalNode extends DataNode { if ('Description' in sp || this.Description) { overrides.Description = this.Description; } + Object.assign(overrides, this._enumOverrides(sp)); return this._serializeSimulinkObject(overrides); } + // Complexity and DimensionsMode, as both save paths write them: the key when the file had + // it, or when the value is not MATLAB's default. MATLAB's text form has a non-default value + // as a top-level key and no key at 'auto', even for a Signal set to 'complex' and back + // (measured in R2027a), so a Signal whose text file had no key saves with none until it + // leaves 'auto'. A key the file had stays, at 'auto' if set there, which MATLAB reads the + // same; a binary file always has both keys. A clear stores '' (DataNode._rejectUnknownEnumeral), + // which is no value either property has, so it is written as 'auto', what an absent key + // means, and never as ''. + _enumOverrides(sp) { + const overrides = {}; + const complexity = this.Complexity || 'auto'; + if ('Complexity' in sp || complexity !== 'auto') { + overrides.Complexity = complexity; + } + const dimensionsMode = this.DimensionsMode || 'auto'; + if ('DimensionsMode' in sp || dimensionsMode !== 'auto') { + overrides.DimensionsMode = dimensionsMode; + } + return overrides; + } static get defaultName() { return 'Signal'; } static createDefault(name, parent) { // Verified property-for-property against MATLAB R2027a's own default `Simulink.Signal`: diff --git a/dist/datamodel/node/data/SignalNode.js.map b/dist/datamodel/node/data/SignalNode.js.map index 501f1dbe..289be7f4 100644 --- a/dist/datamodel/node/data/SignalNode.js.map +++ b/dist/datamodel/node/data/SignalNode.js.map @@ -1 +1 @@ -{"version":3,"file":"SignalNode.js","sourceRoot":"","sources":["../../../../src/datamodel/node/data/SignalNode.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"SignalNode.js","sourceRoot":"","sources":["../../../../src/datamodel/node/data/SignalNode.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAErC,OAAO,QAAQ,EAAE,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAE9D,OAAO,kBAAkB,MAAM,0BAA0B,CAAC;AAG1D,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAC9C,OAAO,YAAY,MAAM,4BAA4B,CAAC;AACtD,OAAO,OAAO,MAAM,uBAAuB,CAAC;AAC5C,OAAO,OAAO,MAAM,uBAAuB,CAAC;AAC5C,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAC9C,OAAO,eAAe,MAAM,+BAA+B,CAAC;AAC5D,OAAO,oBAAoB,MAAM,oCAAoC,CAAC;AACtE,OAAO,wBAAwB,MAAM,wCAAwC,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAEpE,MAAM,UAAU,GAAG,iBAAiB,CAAC;AAErC,kGAAkG;AAClG,gGAAgG;AAChG,gGAAgG;AAChG,2FAA2F;AAC3F,6FAA6F;AAC7F,MAAM,UAAU,GAAG,IAAI,GAAG,CAAoB;IAC1C,CAAC,YAAY,EAAE,oBAA4C,CAAC;IAC5D,CAAC,gBAAgB,EAAE,wBAAgD,CAAC;CACvE,CAAC,CAAC;AACH,MAAM,aAAa,GAAG,CAAC,KAAkB,EAAe,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;AAExG,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,QAAQ;IAW5C,YAAY,IAAY,EAAE,MAAuB,EAAE,KAA8B,EAAE,MAA+B;QAC9G,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAC5B,uEAAuE;QACvE,4EAA4E;QAC5E,0EAA0E;QAC1E,sEAAsE;QACtE,yEAAyE;QACzE,kEAAkE;QAClE,MAAM,WAAW,GAAG,KAAK,CAAC,iBAAiB,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC;QACrG,IAAI,CAAC,QAAQ,GAAI,WAAsB,IAAI,MAAM,CAAC;QAClD,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,GAAyB,CAAC;QAC3C,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,GAAyB,CAAC;QAC3C,IAAI,CAAC,IAAI,GAAI,KAAK,CAAC,QAAmB,IAAK,KAAK,CAAC,IAAe,IAAI,EAAE,CAAC;QACvE,IAAI,CAAC,WAAW,GAAI,KAAK,CAAC,WAAsB,IAAI,EAAE,CAAC;QACvD,IAAI,CAAC,UAAU,GAAI,KAAK,CAAC,UAAqB,IAAI,MAAM,CAAC;QACzD,IAAI,CAAC,cAAc,GAAI,KAAK,CAAC,cAAyB,IAAI,MAAM,CAAC;IACrE,CAAC;IAED,IAAI,IAAI,KAAa,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IAChF,6EAA6E;IAC7E,6EAA6E;IAC7E,gFAAgF;IAChF,uCAAuC;IACvC,IAAI,SAAS,KAAa,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,OAA8C,CAAC,CAAC,OAAO,CAAC,GAAG,IAAI,kBAAkB,CAAC,GAAG,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC;IAC1J,+EAA+E;IAC/E,IAAI,YAAY,KAAa,OAAO,EAAE,CAAC,CAAC,CAAC;IACzC,IAAI,aAAa,KAAc,OAAO,KAAK,CAAC,CAAC,CAAC;IAE9C,8EAA8E;IAC9E,8EAA8E;IAC9E,0EAA0E;IAC1E,oEAAoE;IACpE,IAAI,QAAQ,KAAa,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEhD,aAAa,KAAkB,OAAO,CAAC,QAAQ,EAAE,cAAc,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,mBAAmB,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,aAAa,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClN,6FAA6F;IAC7F,6FAA6F;IAC7F,6EAA6E;IAC7E,WAAW;QACP,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QACnC,OAAO,MAAM,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAClF,CAAC;IAED,WAAW,CAAC,QAAgB,EAAE,WAAmB;QAC7C,0EAA0E;QAC1E,IAAI,IAAI,CAAC,kBAAkB,CAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,CAAC;YACjD,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,mGAAmG;QACnG,MAAM,cAAc,GAAG,IAAI,CAAC,kBAAkB,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QACtE,IAAI,cAAc,EAAE,CAAC;YACjB,OAAO,cAAc,CAAC;QAC1B,CAAC;QACD,IAAI,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;YAC3C,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QAClD,CAAC;QACD,sFAAsF;QACtF,qFAAqF;QACrF,uFAAuF;QACvF,IAAI,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3B,MAAM,aAAa,GAAG,IAAI,CAAC,sBAAsB,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;YACzE,IAAI,aAAa,EAAE,CAAC;gBAChB,OAAO,aAAa,CAAC;YACzB,CAAC;QACL,CAAC;QACD,OAAO,QAAQ,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;IAC5E,CAAC;IAED,wBAAwB;QACpB,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,WAAsC,CAAC;QAC9D,MAAM,OAAO,GAAG,UAAU,IAAI,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC;QACvD,MAAM,KAAK,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC;QACxB,IAAI,KAAK,IAAI,EAAE,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAAC,CAAC;QAClG,IAAI,KAAK,IAAI,EAAE,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAAC,CAAC;QAClG,IAAI,OAAO,IAAI,EAAE,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAAC,KAAK,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC;QAAC,CAAC;QAC/D,IAAI,aAAa,IAAI,EAAE,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAAC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;QAAC,CAAC;QACtF,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,CAAC;QAC9C,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,cAAc;QACV,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;YACnB,OAAO,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;QACjD,CAAC;QACD,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,WAAsC,CAAC;QAC9D,MAAM,OAAO,GAAG,UAAU,IAAI,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC;QACvD,MAAM,SAAS,GAA4B,EAAE,CAAC;QAC9C,IAAI,KAAK,IAAI,EAAE,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAAC,SAAS,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QAAC,CAAC;QACxE,IAAI,KAAK,IAAI,EAAE,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAAC,SAAS,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QAAC,CAAC;QACxE,IAAI,OAAO,IAAI,EAAE,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAAC,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC;QAAC,CAAC;QACnE,IAAI,aAAa,IAAI,EAAE,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;QAAC,CAAC;QAC1F,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC,wBAAwB,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;IAED,0FAA0F;IAC1F,4FAA4F;IAC5F,uFAAuF;IACvF,wFAAwF;IACxF,0FAA0F;IAC1F,iGAAiG;IACjG,wFAAwF;IACxF,0BAA0B;IAC1B,cAAc,CAAC,EAA2B;QACtC,MAAM,SAAS,GAA4B,EAAE,CAAC;QAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC;QAC7C,IAAI,YAAY,IAAI,EAAE,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;YAAC,SAAS,CAAC,UAAU,GAAG,UAAU,CAAC;QAAC,CAAC;QACvF,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,IAAI,MAAM,CAAC;QACrD,IAAI,gBAAgB,IAAI,EAAE,IAAI,cAAc,KAAK,MAAM,EAAE,CAAC;YAAC,SAAS,CAAC,cAAc,GAAG,cAAc,CAAC;QAAC,CAAC;QACvG,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,MAAM,KAAK,WAAW,KAAa,OAAO,QAAQ,CAAC,CAAC,CAAC;IAErD,MAAM,CAAC,aAAa,CAAC,IAAY,EAAE,MAAuB;QACtD,wFAAwF;QACxF,gFAAgF;QAChF,2FAA2F;QAC3F,iBAAiB;QACjB,MAAM,MAAM,GAAG,kBAAkB,CAAC,cAAc,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,kBAAkB,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,WAAW,EAAE,EAAE,aAAa,EAAE,sBAAsB,EAAE,WAAW,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QACrM,MAAM,KAAK,GAAG,kBAAkB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAClD,OAAO,IAAI,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAA6B,CAAC,CAAC;IACnH,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,MAA+B,EAAE,IAAY,EAAE,MAAuB;QAC/E,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,IAAK,MAAM,CAAC,SAAuB,CAAC,CAAC,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,IAAK,IAAgC,CAAC,WAAW,CAAC,IAAI,EAAE,CAA4B,CAAC;QACzG,MAAM,MAAM,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;QACvD,OAAO,IAAI,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAiC,CAAC,CAAC;IAClF,CAAC;CACJ"} \ No newline at end of file diff --git a/dist/datamodel/prop/PropComplexity.d.ts.map b/dist/datamodel/prop/PropComplexity.d.ts.map index 536f56f5..caecc804 100644 --- a/dist/datamodel/prop/PropComplexity.d.ts.map +++ b/dist/datamodel/prop/PropComplexity.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"PropComplexity.d.ts","sourceRoot":"","sources":["../../../src/datamodel/prop/PropComplexity.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAgC7C,MAAM,CAAC,OAAO,OAAO,cAAc;IAC/B,MAAM,CAAC,GAAG,SAAgB;IAC1B,MAAM,CAAC,WAAW,SAAgB;IAClC,MAAM,CAAC,MAAM,SAAY;IACzB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAgB;IAE5C,MAAM,CAAC,YAAY,SAAgB;IAGnC,MAAM,CAAC,UAAU,WAAkB;IAEnC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM;IAIxC,MAAM,CAAC,WAAW,IAAI,MAAM,EAAE;IAI9B,MAAM,CAAC,MAAM,oBAAc;CAC9B"} \ No newline at end of file +{"version":3,"file":"PropComplexity.d.ts","sourceRoot":"","sources":["../../../src/datamodel/prop/PropComplexity.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAiC7C,MAAM,CAAC,OAAO,OAAO,cAAc;IAC/B,MAAM,CAAC,GAAG,SAAgB;IAC1B,MAAM,CAAC,WAAW,SAAgB;IAClC,MAAM,CAAC,MAAM,SAAY;IACzB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAgB;IAE5C,MAAM,CAAC,YAAY,SAAgB;IAGnC,MAAM,CAAC,UAAU,WAAkB;IAEnC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM;IAIxC,MAAM,CAAC,WAAW,IAAI,MAAM,EAAE;IAI9B,MAAM,CAAC,MAAM,oBAAc;CAC9B"} \ No newline at end of file diff --git a/dist/datamodel/prop/PropComplexity.js b/dist/datamodel/prop/PropComplexity.js index cdc5a759..01c86d26 100644 --- a/dist/datamodel/prop/PropComplexity.js +++ b/dist/datamodel/prop/PropComplexity.js @@ -24,10 +24,11 @@ import { formatText } from './formatText.js'; // DEX_MATLAB_CMD set, "MATLAB accepts what we write here" is an expectation, not a // result. // -// Only Simulink.BusElement uses this atom. The `complexity` COLUMN is also filled -// for Parameter/Signal/ValueType, but through the declarative schema -// (schema/props/dataObject.json), which still declares it a label — deliberately, -// because those classes admit a third value ('auto') and are a separate unlock. +// Simulink.BusElement and Simulink.ValueType use this atom. A Simulink.Signal's +// Complexity admits a third value, 'auto' (its default), so it has its own atom over +// its own set, PropSignalComplexity. A Parameter's `complexity` column still comes +// from the declarative schema (schema/props/dataObject.json), as a label, because a +// Parameter's Complexity is derived from its Value. const OPTIONS = ['real', 'complex']; export default class PropComplexity { static { this.key = 'complexity'; } diff --git a/dist/datamodel/prop/PropComplexity.js.map b/dist/datamodel/prop/PropComplexity.js.map index a5f340da..729dd9b9 100644 --- a/dist/datamodel/prop/PropComplexity.js.map +++ b/dist/datamodel/prop/PropComplexity.js.map @@ -1 +1 @@ -{"version":3,"file":"PropComplexity.js","sourceRoot":"","sources":["../../../src/datamodel/prop/PropComplexity.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAGrC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,2EAA2E;AAC3E,kFAAkF;AAClF,gFAAgF;AAChF,8EAA8E;AAC9E,8EAA8E;AAC9E,8EAA8E;AAC9E,kFAAkF;AAClF,+EAA+E;AAC/E,oDAAoD;AACpD,EAAE;AACF,mFAAmF;AACnF,wEAAwE;AACxE,8EAA8E;AAC9E,+EAA+E;AAC/E,gBAAgB;AAChB,EAAE;AACF,mFAAmF;AACnF,mFAAmF;AACnF,6EAA6E;AAC7E,mFAAmF;AACnF,sEAAsE;AACtE,mFAAmF;AACnF,UAAU;AACV,EAAE;AACF,kFAAkF;AAClF,qEAAqE;AACrE,kFAAkF;AAClF,gFAAgF;AAChF,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAEpC,MAAM,CAAC,OAAO,OAAO,cAAc;aACxB,QAAG,GAAG,YAAY,CAAC;aACnB,gBAAW,GAAG,YAAY,CAAC;aAC3B,WAAM,GAAG,QAAQ,CAAC;aAClB,WAAM,GAAkB,YAAY,CAAC;IAC5C,oEAAoE;aAC7D,iBAAY,GAAG,YAAY,CAAC;IACnC,yEAAyE;IACzE,gDAAgD;aACzC,eAAU,GAAG,CAAC,YAAY,CAAC,CAAC;IAEnC,MAAM,CAAC,SAAS,CAAC,IAAc;QAC3B,OAAO,CAAE,IAA2C,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;IAC3E,CAAC;IAED,MAAM,CAAC,WAAW;QACd,OAAO,OAAO,CAAC;IACnB,CAAC;aAEM,WAAM,GAAG,UAAU,CAAC"} \ No newline at end of file +{"version":3,"file":"PropComplexity.js","sourceRoot":"","sources":["../../../src/datamodel/prop/PropComplexity.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAGrC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,2EAA2E;AAC3E,kFAAkF;AAClF,gFAAgF;AAChF,8EAA8E;AAC9E,8EAA8E;AAC9E,8EAA8E;AAC9E,kFAAkF;AAClF,+EAA+E;AAC/E,oDAAoD;AACpD,EAAE;AACF,mFAAmF;AACnF,wEAAwE;AACxE,8EAA8E;AAC9E,+EAA+E;AAC/E,gBAAgB;AAChB,EAAE;AACF,mFAAmF;AACnF,mFAAmF;AACnF,6EAA6E;AAC7E,mFAAmF;AACnF,sEAAsE;AACtE,mFAAmF;AACnF,UAAU;AACV,EAAE;AACF,gFAAgF;AAChF,qFAAqF;AACrF,mFAAmF;AACnF,oFAAoF;AACpF,oDAAoD;AACpD,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAEpC,MAAM,CAAC,OAAO,OAAO,cAAc;aACxB,QAAG,GAAG,YAAY,CAAC;aACnB,gBAAW,GAAG,YAAY,CAAC;aAC3B,WAAM,GAAG,QAAQ,CAAC;aAClB,WAAM,GAAkB,YAAY,CAAC;IAC5C,oEAAoE;aAC7D,iBAAY,GAAG,YAAY,CAAC;IACnC,yEAAyE;IACzE,gDAAgD;aACzC,eAAU,GAAG,CAAC,YAAY,CAAC,CAAC;IAEnC,MAAM,CAAC,SAAS,CAAC,IAAc;QAC3B,OAAO,CAAE,IAA2C,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;IAC3E,CAAC;IAED,MAAM,CAAC,WAAW;QACd,OAAO,OAAO,CAAC;IACnB,CAAC;aAEM,WAAM,GAAG,UAAU,CAAC"} \ No newline at end of file diff --git a/dist/datamodel/prop/PropDimensionsMode.d.ts.map b/dist/datamodel/prop/PropDimensionsMode.d.ts.map index 9aa4c08e..053c560f 100644 --- a/dist/datamodel/prop/PropDimensionsMode.d.ts.map +++ b/dist/datamodel/prop/PropDimensionsMode.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"PropDimensionsMode.d.ts","sourceRoot":"","sources":["../../../src/datamodel/prop/PropDimensionsMode.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAgC7C,MAAM,CAAC,OAAO,OAAO,kBAAkB;IACnC,MAAM,CAAC,GAAG,SAAoB;IAC9B,MAAM,CAAC,WAAW,SAAqB;IACvC,MAAM,CAAC,MAAM,SAAY;IACzB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAoB;IAEhD,MAAM,CAAC,YAAY,SAAoB;IAGvC,MAAM,CAAC,UAAU,WAAsB;IAEvC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM;IAIxC,MAAM,CAAC,WAAW,IAAI,MAAM,EAAE;IAI9B,MAAM,CAAC,MAAM,oBAAc;CAC9B"} \ No newline at end of file +{"version":3,"file":"PropDimensionsMode.d.ts","sourceRoot":"","sources":["../../../src/datamodel/prop/PropDimensionsMode.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA8B7C,MAAM,CAAC,OAAO,OAAO,kBAAkB;IACnC,MAAM,CAAC,GAAG,SAAoB;IAC9B,MAAM,CAAC,WAAW,SAAqB;IACvC,MAAM,CAAC,MAAM,SAAY;IACzB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAoB;IAEhD,MAAM,CAAC,YAAY,SAAoB;IAGvC,MAAM,CAAC,UAAU,WAAsB;IAEvC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM;IAIxC,MAAM,CAAC,WAAW,IAAI,MAAM,EAAE;IAI9B,MAAM,CAAC,MAAM,oBAAc;CAC9B"} \ No newline at end of file diff --git a/dist/datamodel/prop/PropDimensionsMode.js b/dist/datamodel/prop/PropDimensionsMode.js index 72691b28..10a1f209 100644 --- a/dist/datamodel/prop/PropDimensionsMode.js +++ b/dist/datamodel/prop/PropDimensionsMode.js @@ -23,11 +23,9 @@ import { formatText } from './formatText.js'; // DEX_MATLAB_CMD set, "MATLAB accepts what we write here" is an expectation, not a // result. // -// Only Simulink.BusElement uses this atom. The `dimensionsMode` COLUMN is also -// filled for Simulink.Signal, but through the declarative schema -// (schema/props/dataObject.json), which still declares it a label — deliberately, -// because a Signal's DimensionsMode defaults to 'auto', a value this set does not -// contain, so the two are separate unlocks with separate enums. +// Simulink.BusElement and Simulink.ValueType use this atom. A Simulink.Signal's +// DimensionsMode defaults to 'auto', a value this set does not contain, so it has +// its own atom over its own set, PropSignalDimensionsMode. const OPTIONS = ['Fixed', 'Variable']; export default class PropDimensionsMode { static { this.key = 'dimensionsMode'; } diff --git a/dist/datamodel/prop/PropDimensionsMode.js.map b/dist/datamodel/prop/PropDimensionsMode.js.map index d3daf674..8ac51e5b 100644 --- a/dist/datamodel/prop/PropDimensionsMode.js.map +++ b/dist/datamodel/prop/PropDimensionsMode.js.map @@ -1 +1 @@ -{"version":3,"file":"PropDimensionsMode.js","sourceRoot":"","sources":["../../../src/datamodel/prop/PropDimensionsMode.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAGrC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,+EAA+E;AAC/E,uEAAuE;AACvE,kFAAkF;AAClF,iFAAiF;AACjF,mFAAmF;AACnF,uEAAuE;AACvE,kFAAkF;AAClF,yEAAyE;AACzE,EAAE;AACF,mFAAmF;AACnF,4EAA4E;AAC5E,8EAA8E;AAC9E,iFAAiF;AACjF,sBAAsB;AACtB,EAAE;AACF,mFAAmF;AACnF,mFAAmF;AACnF,6EAA6E;AAC7E,mFAAmF;AACnF,sEAAsE;AACtE,mFAAmF;AACnF,UAAU;AACV,EAAE;AACF,+EAA+E;AAC/E,iEAAiE;AACjE,kFAAkF;AAClF,kFAAkF;AAClF,gEAAgE;AAChE,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAEtC,MAAM,CAAC,OAAO,OAAO,kBAAkB;aAC5B,QAAG,GAAG,gBAAgB,CAAC;aACvB,gBAAW,GAAG,iBAAiB,CAAC;aAChC,WAAM,GAAG,QAAQ,CAAC;aAClB,WAAM,GAAkB,gBAAgB,CAAC;IAChD,oEAAoE;aAC7D,iBAAY,GAAG,gBAAgB,CAAC;IACvC,yEAAyE;IACzE,gDAAgD;aACzC,eAAU,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAEvC,MAAM,CAAC,SAAS,CAAC,IAAc;QAC3B,OAAO,CAAE,IAA+C,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;IACnF,CAAC;IAED,MAAM,CAAC,WAAW;QACd,OAAO,OAAO,CAAC;IACnB,CAAC;aAEM,WAAM,GAAG,UAAU,CAAC"} \ No newline at end of file +{"version":3,"file":"PropDimensionsMode.js","sourceRoot":"","sources":["../../../src/datamodel/prop/PropDimensionsMode.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAGrC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,+EAA+E;AAC/E,uEAAuE;AACvE,kFAAkF;AAClF,iFAAiF;AACjF,mFAAmF;AACnF,uEAAuE;AACvE,kFAAkF;AAClF,yEAAyE;AACzE,EAAE;AACF,mFAAmF;AACnF,4EAA4E;AAC5E,8EAA8E;AAC9E,iFAAiF;AACjF,sBAAsB;AACtB,EAAE;AACF,mFAAmF;AACnF,mFAAmF;AACnF,6EAA6E;AAC7E,mFAAmF;AACnF,sEAAsE;AACtE,mFAAmF;AACnF,UAAU;AACV,EAAE;AACF,gFAAgF;AAChF,kFAAkF;AAClF,2DAA2D;AAC3D,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAEtC,MAAM,CAAC,OAAO,OAAO,kBAAkB;aAC5B,QAAG,GAAG,gBAAgB,CAAC;aACvB,gBAAW,GAAG,iBAAiB,CAAC;aAChC,WAAM,GAAG,QAAQ,CAAC;aAClB,WAAM,GAAkB,gBAAgB,CAAC;IAChD,oEAAoE;aAC7D,iBAAY,GAAG,gBAAgB,CAAC;IACvC,yEAAyE;IACzE,gDAAgD;aACzC,eAAU,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAEvC,MAAM,CAAC,SAAS,CAAC,IAAc;QAC3B,OAAO,CAAE,IAA+C,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;IACnF,CAAC;IAED,MAAM,CAAC,WAAW;QACd,OAAO,OAAO,CAAC;IACnB,CAAC;aAEM,WAAM,GAAG,UAAU,CAAC"} \ No newline at end of file diff --git a/dist/datamodel/prop/PropSignalComplexity.d.ts b/dist/datamodel/prop/PropSignalComplexity.d.ts new file mode 100644 index 00000000..39fb7d9a --- /dev/null +++ b/dist/datamodel/prop/PropSignalComplexity.d.ts @@ -0,0 +1,14 @@ +import type BaseNode from '../node/BaseNode.js'; +import { formatText } from './formatText.js'; +export default class PropSignalComplexity { + static key: string; + static displayName: string; + static editor: string; + static column: string | null; + static nodeProperty: string; + static sourceKeys: string[]; + static readValue(node: BaseNode): string; + static readOptions(): string[]; + static format: typeof formatText; +} +//# sourceMappingURL=PropSignalComplexity.d.ts.map \ No newline at end of file diff --git a/dist/datamodel/prop/PropSignalComplexity.d.ts.map b/dist/datamodel/prop/PropSignalComplexity.d.ts.map new file mode 100644 index 00000000..cc8f8a0f --- /dev/null +++ b/dist/datamodel/prop/PropSignalComplexity.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"PropSignalComplexity.d.ts","sourceRoot":"","sources":["../../../src/datamodel/prop/PropSignalComplexity.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAmB7C,MAAM,CAAC,OAAO,OAAO,oBAAoB;IACrC,MAAM,CAAC,GAAG,SAAgB;IAC1B,MAAM,CAAC,WAAW,SAAgB;IAClC,MAAM,CAAC,MAAM,SAAY;IACzB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAgB;IAE5C,MAAM,CAAC,YAAY,SAAgB;IAEnC,MAAM,CAAC,UAAU,WAAkB;IAEnC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM;IAIxC,MAAM,CAAC,WAAW,IAAI,MAAM,EAAE;IAI9B,MAAM,CAAC,MAAM,oBAAc;CAC9B"} \ No newline at end of file diff --git a/dist/datamodel/prop/PropSignalComplexity.js b/dist/datamodel/prop/PropSignalComplexity.js new file mode 100644 index 00000000..dfd5aa9d --- /dev/null +++ b/dist/datamodel/prop/PropSignalComplexity.js @@ -0,0 +1,36 @@ +// Copyright 2026 The MathWorks, Inc. +import { formatText } from './formatText.js'; +// A Simulink.Signal's Complexity: an editable dropdown over the Signal's own enum, +// 'auto' | 'real' | 'complex'. Measured in R2027a: `set(Simulink.Signal, 'Complexity')` is +// exactly that set, mpt.Signal's is the same, the default is 'auto', and any other value is +// refused with "There is no enumerated value named 'X'." So this is PropComplexity's mechanism +// with the Signal's set. PropComplexity stays BusElement's and ValueType's, whose classes +// have no 'auto'; the two share the column and nothing else. +// +// SignalNode.setProperty validates an edit against readOptions (DataNode._rejectUnknownEnumeral), +// so the dropdown and the rule that refuses a typed-in value are one list. `nodeProperty` sends +// an edit to the node field, which both panes read: SignalNode puts this atom in the table and +// in the Property Inspector, in place of the schema descriptor that reads the source bag. +// +// readValue falls back to 'auto', not '': a cleared cell stores '' (the licence +// _rejectUnknownEnumeral gives a clear), which SignalNode saves as 'auto', so the cell shows +// what the save writes. +const OPTIONS = ['auto', 'real', 'complex']; +export default class PropSignalComplexity { + static { this.key = 'complexity'; } + static { this.displayName = 'Complexity'; } + static { this.editor = 'select'; } + static { this.column = 'complexity'; } + // The node field (and raw `_properties` key) an edit is written to. + static { this.nodeProperty = 'Complexity'; } + // Raw _properties key, so the PI "Other" catch-all treats it as already shown. + static { this.sourceKeys = ['Complexity']; } + static readValue(node) { + return (node.Complexity) || 'auto'; + } + static readOptions() { + return OPTIONS; + } + static { this.format = formatText; } +} +//# sourceMappingURL=PropSignalComplexity.js.map \ No newline at end of file diff --git a/dist/datamodel/prop/PropSignalComplexity.js.map b/dist/datamodel/prop/PropSignalComplexity.js.map new file mode 100644 index 00000000..da7fe65a --- /dev/null +++ b/dist/datamodel/prop/PropSignalComplexity.js.map @@ -0,0 +1 @@ +{"version":3,"file":"PropSignalComplexity.js","sourceRoot":"","sources":["../../../src/datamodel/prop/PropSignalComplexity.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAGrC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,mFAAmF;AACnF,2FAA2F;AAC3F,4FAA4F;AAC5F,+FAA+F;AAC/F,0FAA0F;AAC1F,6DAA6D;AAC7D,EAAE;AACF,kGAAkG;AAClG,gGAAgG;AAChG,+FAA+F;AAC/F,0FAA0F;AAC1F,EAAE;AACF,gFAAgF;AAChF,6FAA6F;AAC7F,wBAAwB;AACxB,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;AAE5C,MAAM,CAAC,OAAO,OAAO,oBAAoB;aAC9B,QAAG,GAAG,YAAY,CAAC;aACnB,gBAAW,GAAG,YAAY,CAAC;aAC3B,WAAM,GAAG,QAAQ,CAAC;aAClB,WAAM,GAAkB,YAAY,CAAC;IAC5C,oEAAoE;aAC7D,iBAAY,GAAG,YAAY,CAAC;IACnC,+EAA+E;aACxE,eAAU,GAAG,CAAC,YAAY,CAAC,CAAC;IAEnC,MAAM,CAAC,SAAS,CAAC,IAAc;QAC3B,OAAO,CAAE,IAA2C,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC;IAC/E,CAAC;IAED,MAAM,CAAC,WAAW;QACd,OAAO,OAAO,CAAC;IACnB,CAAC;aAEM,WAAM,GAAG,UAAU,CAAC"} \ No newline at end of file diff --git a/dist/datamodel/prop/PropSignalDimensionsMode.d.ts b/dist/datamodel/prop/PropSignalDimensionsMode.d.ts new file mode 100644 index 00000000..9d59ab82 --- /dev/null +++ b/dist/datamodel/prop/PropSignalDimensionsMode.d.ts @@ -0,0 +1,14 @@ +import type BaseNode from '../node/BaseNode.js'; +import { formatText } from './formatText.js'; +export default class PropSignalDimensionsMode { + static key: string; + static displayName: string; + static editor: string; + static column: string | null; + static nodeProperty: string; + static sourceKeys: string[]; + static readValue(node: BaseNode): string; + static readOptions(): string[]; + static format: typeof formatText; +} +//# sourceMappingURL=PropSignalDimensionsMode.d.ts.map \ No newline at end of file diff --git a/dist/datamodel/prop/PropSignalDimensionsMode.d.ts.map b/dist/datamodel/prop/PropSignalDimensionsMode.d.ts.map new file mode 100644 index 00000000..9931c8fd --- /dev/null +++ b/dist/datamodel/prop/PropSignalDimensionsMode.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"PropSignalDimensionsMode.d.ts","sourceRoot":"","sources":["../../../src/datamodel/prop/PropSignalDimensionsMode.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAmB7C,MAAM,CAAC,OAAO,OAAO,wBAAwB;IACzC,MAAM,CAAC,GAAG,SAAoB;IAC9B,MAAM,CAAC,WAAW,SAAqB;IACvC,MAAM,CAAC,MAAM,SAAY;IACzB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAoB;IAEhD,MAAM,CAAC,YAAY,SAAoB;IAEvC,MAAM,CAAC,UAAU,WAAsB;IAEvC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM;IAIxC,MAAM,CAAC,WAAW,IAAI,MAAM,EAAE;IAI9B,MAAM,CAAC,MAAM,oBAAc;CAC9B"} \ No newline at end of file diff --git a/dist/datamodel/prop/PropSignalDimensionsMode.js b/dist/datamodel/prop/PropSignalDimensionsMode.js new file mode 100644 index 00000000..652d0e05 --- /dev/null +++ b/dist/datamodel/prop/PropSignalDimensionsMode.js @@ -0,0 +1,36 @@ +// Copyright 2026 The MathWorks, Inc. +import { formatText } from './formatText.js'; +// A Simulink.Signal's DimensionsMode: an editable dropdown over the Signal's own enum, +// 'auto' | 'Fixed' | 'Variable'. Measured in R2027a: `set(Simulink.Signal, 'DimensionsMode')` +// is exactly that set, with that casing, mpt.Signal's is the same, the default is 'auto', and +// any other value is refused with "There is no enumerated value named 'X'." So this is +// PropDimensionsMode's mechanism with the Signal's set. PropDimensionsMode stays BusElement's +// and ValueType's, whose classes have no 'auto'. +// +// SignalNode.setProperty validates an edit against readOptions (DataNode._rejectUnknownEnumeral), +// so the dropdown and the rule that refuses a typed-in value are one list. `nodeProperty` sends +// an edit to the node field, which both panes read: SignalNode puts this atom in the table and +// in the Property Inspector, in place of the schema descriptor that reads the source bag. +// +// readValue falls back to 'auto', not '': a cleared cell stores '' (the licence +// _rejectUnknownEnumeral gives a clear), which SignalNode saves as 'auto', so the cell shows +// what the save writes. +const OPTIONS = ['auto', 'Fixed', 'Variable']; +export default class PropSignalDimensionsMode { + static { this.key = 'dimensionsMode'; } + static { this.displayName = 'Dimensions Mode'; } + static { this.editor = 'select'; } + static { this.column = 'dimensionsMode'; } + // The node field (and raw `_properties` key) an edit is written to. + static { this.nodeProperty = 'DimensionsMode'; } + // Raw _properties key, so the PI "Other" catch-all treats it as already shown. + static { this.sourceKeys = ['DimensionsMode']; } + static readValue(node) { + return (node.DimensionsMode) || 'auto'; + } + static readOptions() { + return OPTIONS; + } + static { this.format = formatText; } +} +//# sourceMappingURL=PropSignalDimensionsMode.js.map \ No newline at end of file diff --git a/dist/datamodel/prop/PropSignalDimensionsMode.js.map b/dist/datamodel/prop/PropSignalDimensionsMode.js.map new file mode 100644 index 00000000..a8d211a1 --- /dev/null +++ b/dist/datamodel/prop/PropSignalDimensionsMode.js.map @@ -0,0 +1 @@ +{"version":3,"file":"PropSignalDimensionsMode.js","sourceRoot":"","sources":["../../../src/datamodel/prop/PropSignalDimensionsMode.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAGrC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,uFAAuF;AACvF,8FAA8F;AAC9F,8FAA8F;AAC9F,uFAAuF;AACvF,8FAA8F;AAC9F,iDAAiD;AACjD,EAAE;AACF,kGAAkG;AAClG,gGAAgG;AAChG,+FAA+F;AAC/F,0FAA0F;AAC1F,EAAE;AACF,gFAAgF;AAChF,6FAA6F;AAC7F,wBAAwB;AACxB,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;AAE9C,MAAM,CAAC,OAAO,OAAO,wBAAwB;aAClC,QAAG,GAAG,gBAAgB,CAAC;aACvB,gBAAW,GAAG,iBAAiB,CAAC;aAChC,WAAM,GAAG,QAAQ,CAAC;aAClB,WAAM,GAAkB,gBAAgB,CAAC;IAChD,oEAAoE;aAC7D,iBAAY,GAAG,gBAAgB,CAAC;IACvC,+EAA+E;aACxE,eAAU,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAEvC,MAAM,CAAC,SAAS,CAAC,IAAc;QAC3B,OAAO,CAAE,IAA+C,CAAC,cAAc,CAAC,IAAI,MAAM,CAAC;IACvF,CAAC;IAED,MAAM,CAAC,WAAW;QACd,OAAO,OAAO,CAAC;IACnB,CAAC;aAEM,WAAM,GAAG,UAAU,CAAC"} \ No newline at end of file diff --git a/src/datamodel/node/data/SignalNode.ts b/src/datamodel/node/data/SignalNode.ts index 263a1eaf..2abdf3fe 100644 --- a/src/datamodel/node/data/SignalNode.ts +++ b/src/datamodel/node/data/SignalNode.ts @@ -3,7 +3,7 @@ import DataNode, { objectValueClassOf } from '../DataNode.js'; import type { SetPropertyResult } from '../DataNode.js'; import SimulinkObjectNode from '../SimulinkObjectNode.js'; -import type { PropClass } from '../BaseNode.js'; +import type { PropClass, PIGroupDef } from '../BaseNode.js'; import type BaseNode from '../BaseNode.js'; import PropName from '../../prop/PropName.js'; import PropDataType from '../../prop/PropDataType.js'; @@ -11,18 +11,35 @@ import PropMin from '../../prop/PropMin.js'; import PropMax from '../../prop/PropMax.js'; import PropUnit from '../../prop/PropUnit.js'; import PropDescription from '../../prop/PropDescription.js'; +import PropSignalComplexity from '../../prop/PropSignalComplexity.js'; +import PropSignalDimensionsMode from '../../prop/PropSignalDimensionsMode.js'; import { withSourceKeys } from './BaseBusNode.js'; import { schemaColumns } from '../schemaBridge.js'; import { oneWrite, writeUnderWay } from '../../parser/MatWriter.js'; const CLASS_NAME = 'Simulink.Signal'; +// Complexity and DimensionsMode are editable node fields, so the table and the Property Inspector +// read them through these atoms (the Signal's own enums, auto included) rather than through the +// schema descriptors, which read the source bag and are labels. In place, by key, so the column +// order and the schema layout's order stay what the schema authored. The schema keeps both +// descriptors: Parameter's complexity is one too, and stays a label, derived from its Value. +const ENUM_ATOMS = new Map([ + ['complexity', PropSignalComplexity as unknown as PropClass], + ['dimensionsMode', PropSignalDimensionsMode as unknown as PropClass], +]); +const withEnumAtoms = (props: PropClass[]): PropClass[] => props.map((p) => ENUM_ATOMS.get(p.key) ?? p); + export default class SignalNode extends DataNode { DataType: string; Min: number | undefined; Max: number | undefined; Unit: string; Description: string; + // MATLAB's default for both is 'auto', and a text dictionary leaves the key out at 'auto' + // (measured in R2027a), so an absent key reads as 'auto'. + Complexity: string; + DimensionsMode: string; constructor(name: string, parent: BaseNode | null, props: Record, serial: Record) { super(name, parent, serial); @@ -38,6 +55,8 @@ export default class SignalNode extends DataNode { this.Max = props.Max as number | undefined; this.Unit = (props.DocUnits as string) || (props.Unit as string) || ''; this.Description = (props.Description as string) || ''; + this.Complexity = (props.Complexity as string) || 'auto'; + this.DimensionsMode = (props.DimensionsMode as string) || 'auto'; } get icon(): string { return this.isDerived ? 'serviceInterfaces' : 'wsSignal'; } @@ -56,9 +75,14 @@ export default class SignalNode extends DataNode { // every Signal in every channel even though all four carry the key. get dataType(): string { return this.DataType; } - getProperties(): PropClass[] { return [PropName, withSourceKeys(PropDataType, ['DataType', 'DataType_internal']), PropMin, PropMax, PropUnit, PropDescription, ...schemaColumns(this.className)]; } - // PI layout is now declarative — see schema/classes/signal.json `layout`, - // resolved by the inherited BaseNode.getPILayout via buildPILayout. + getProperties(): PropClass[] { return [PropName, withSourceKeys(PropDataType, ['DataType', 'DataType_internal']), PropMin, PropMax, PropUnit, PropDescription, ...withEnumAtoms(schemaColumns(this.className))]; } + // PI layout is declarative (schema/classes/signal.json `layout`, resolved by buildPILayout), + // with the two enum atoms in place of their descriptors, so an edit the table makes shows in + // the PI too (ValueTypeNode.getPILayout gives the full account of that gap). + getPILayout(): PIGroupDef[] | null { + const layout = super.getPILayout(); + return layout && layout.map((g) => ({ ...g, items: withEnumAtoms(g.items) })); + } setProperty(propName: string, stringValue: string): true | SetPropertyResult { // Its own shown text, set back, is no edit (DataNode._isUnchangedCommit). @@ -73,6 +97,15 @@ export default class SignalNode extends DataNode { if (propName === 'Min' || propName === 'Max') { return this._setMinMax(propName, stringValue); } + // Complexity and DimensionsMode take only the Signal's own enumerals, as MATLAB does; + // the shared check reads the set off the atom the dropdown is built from. Only these + // two: Storage Class is a select too, and the schema path refuses it in its own words. + if (ENUM_ATOMS.has(propName)) { + const notAnEnumeral = this._rejectUnknownEnumeral(propName, stringValue); + if (notAnEnumeral) { + return notAnEnumeral; + } + } return DataNode.prototype.setProperty.call(this, propName, stringValue); } @@ -84,6 +117,7 @@ export default class SignalNode extends DataNode { if ('Max' in sp || this.Max !== undefined) { props.Max = this.Max !== undefined ? this.Max : []; } if (unitKey in sp || this.Unit) { props[unitKey] = this.Unit; } if ('Description' in sp || this.Description) { props.Description = this.Description; } + Object.assign(props, this._enumOverrides(sp)); return props; } @@ -98,9 +132,27 @@ export default class SignalNode extends DataNode { if ('Max' in sp || this.Max !== undefined) { overrides.Max = this.Max; } if (unitKey in sp || this.Unit) { overrides[unitKey] = this.Unit; } if ('Description' in sp || this.Description) { overrides.Description = this.Description; } + Object.assign(overrides, this._enumOverrides(sp)); return this._serializeSimulinkObject(overrides); } + // Complexity and DimensionsMode, as both save paths write them: the key when the file had + // it, or when the value is not MATLAB's default. MATLAB's text form has a non-default value + // as a top-level key and no key at 'auto', even for a Signal set to 'complex' and back + // (measured in R2027a), so a Signal whose text file had no key saves with none until it + // leaves 'auto'. A key the file had stays, at 'auto' if set there, which MATLAB reads the + // same; a binary file always has both keys. A clear stores '' (DataNode._rejectUnknownEnumeral), + // which is no value either property has, so it is written as 'auto', what an absent key + // means, and never as ''. + _enumOverrides(sp: Record): Record { + const overrides: Record = {}; + const complexity = this.Complexity || 'auto'; + if ('Complexity' in sp || complexity !== 'auto') { overrides.Complexity = complexity; } + const dimensionsMode = this.DimensionsMode || 'auto'; + if ('DimensionsMode' in sp || dimensionsMode !== 'auto') { overrides.DimensionsMode = dimensionsMode; } + return overrides; + } + static get defaultName(): string { return 'Signal'; } static createDefault(name: string, parent: BaseNode | null): SignalNode { diff --git a/src/datamodel/node/data/Simulink.Signal.md b/src/datamodel/node/data/Simulink.Signal.md index 8d3f2759..a6a46f68 100644 --- a/src/datamodel/node/data/Simulink.Signal.md +++ b/src/datamodel/node/data/Simulink.Signal.md @@ -16,8 +16,10 @@ dimensions, sampling mode, etc.) that a Simulink model can reference. Unlike and `valueEditable` is `false`. The only directly-editable numeric properties surfaced in our UI are **Min** and -**Max** (validated by `_setMinMax` on `DataNode`). All other properties are either -read-only labels or delegated to the schema bridge (Code Generation columns). +**Max** (validated by `_setMinMax` on `DataNode`). **Complexity** and +**DimensionsMode** are editable dropdowns over the Signal's own enums (validated by +`_rejectUnknownEnumeral` on `DataNode`). All other properties are either read-only +labels or delegated to the schema bridge (Code Generation columns). ## Property table @@ -30,8 +32,8 @@ read-only labels or delegated to the schema bridge (Code Generation columns). | Unit | char | public | no | DocUnits or Unit | label | unit expression (parser too complex to mirror) | | Description | char | public | yes | Description | textArea | any string | | Dimensions | double/char | public | no | — | — | not an editable column (complex multi-type acceptance) | -| DimensionsMode | char (enum) | public | no | — | — | enum {Fixed, Variable, auto}; not surfaced as editable column | -| Complexity | char (enum) | public | no | — | — | enum {real, complex, auto}; not surfaced as editable column | +| DimensionsMode | char (enum) | public | yes | DimensionsMode | select | enum {auto, Fixed, Variable}; default auto | +| Complexity | char (enum) | public | yes | Complexity | select | enum {auto, real, complex}; default auto | | SampleTime | double | public | no | — | — | not surfaced | | SamplingMode | char (enum) | public | no | — | — | enum {auto}; not surfaced | | InitialValue | char | public | no | — | — | free-form char; not surfaced as editable column | @@ -87,8 +89,29 @@ These accept only specific tokens (case-insensitive on first char for some): - **Complexity**: `real`, `complex`, `auto`. Same error pattern. - **SamplingMode**: `auto` only. Same error pattern. -These are NOT editable columns in our UI — they are either non-projected or -read-only. Validation not mirrored. +Measured in R2027a (27.1.0.3409468, 2026-10-16): `set(Simulink.Signal, 'Complexity')` +is `auto|real|complex` and `set(Simulink.Signal, 'DimensionsMode')` is +`auto|Fixed|Variable`, both defaulting to `auto`, and `mpt.Signal` has the same two. +`''` is refused too. A text `.sldd` holds a non-default value as a top-level key +(`"Complexity": "complex"`) and has no key at `auto`, even for a Signal set to +`complex` and back; a binary one always holds the key +(`

auto

`). + +**Complexity** and **DimensionsMode** are editable dropdowns (C-030, the 2026-10-16 +group E ruling): `PropSignalComplexity` and `PropSignalDimensionsMode`, in the table +and in the Property Inspector, both reading the node field. `setProperty` refuses any +other value with MATLAB's message. Two deliberate differences: +- MATLAB matches case-insensitively and stores the canonical spelling (`'Complex'` → + `complex`, `'fixed'` → `Fixed`). We match exactly, as for BusElement and ValueType; + the dropdown only offers canonical spellings. +- An empty value is a CLEAR (the licence `_rejectUnknownEnumeral` gives every enum), + so an undo of an edit can restore absence. It shows and saves as `auto`, never as `''`. + +Both save paths write the key when the file had it or the value is not `auto`, so an +untouched Signal, or one set back to `auto`, saves with no new key, as MATLAB does. +Test: `test/signalEnumProps.test.ts`. + +**SamplingMode** is still a label; its validation is not mirrored. ### Dimensions — complex multi-type @@ -110,9 +133,13 @@ validation is not mirrored. which enforces the finite-real-double-scalar rule. Returns `{error, reason: "