From 78527cff31e2a977132ac2bf9be9a2ecb6f287b7 Mon Sep 17 00:00:00 2001 From: darenkeck-dev <94657107+darenkeck-dev@users.noreply.github.com> Date: Fri, 29 Jul 2022 12:37:40 -0700 Subject: [PATCH] #135: Initial work to Group Schedule Table Inputs (#144) * WIP: update 'dat' structure * Fix syntax error in dat subcomponents * Fix typos in test template * WIP: add debug for redeclaration modifiers * WIP: add TODO for expanding 'Modification' class for redeclares * Expand 'Modification' class to support redeclare modifiers * Add additional typing for redeclaration structures * WIP: add 'dat' specific test group * Correct replaceable param in Testpackage * WIP: Update how child options are populated * Fix bug in how child options are fetched for replaceable inputs * Add optionTree helper function * WIP: refactor modification factory method, add additional types * WIP: add mod unpacking helper method * Add 'Import' parser element * Handle package redeclarations * Remove option count test * WIP: split out schedule options from config options * Fix order-of-operations error in condition * Punch out on duplicate type references when splitting schedule options * Prevent recursive extraction of schedule options * Update system option tests * Remove debugging logs --- server/scripts/parse-template-package.ts | 5 +- server/scripts/parse-test-package.ts | 5 +- server/src/parser/index.ts | 5 +- server/src/parser/modification.ts | 156 ++++++++++++------ server/src/parser/parser.ts | 90 +++++++--- server/src/parser/template.ts | 94 +++++++++-- .../integration/parser/modifiers.test.ts | 3 + .../parser/parsed-elements.test.ts | 31 ++-- .../tests/integration/parser/template.test.ts | 12 +- server/tests/integration/parser/utils.ts | 15 ++ .../Component/Data/TestTemplateController.mo | 9 + .../static-data/TestPackage/Template/Data.mo | 28 ---- .../Template/Data/PartialTemplate.mo | 13 ++ .../TestPackage/Template/Data/TestTemplate.mo | 28 ++++ .../TestPackage/Template/TestTemplate.mo | 2 +- 15 files changed, 359 insertions(+), 137 deletions(-) create mode 100644 server/tests/static-data/TestPackage/Component/Data/TestTemplateController.mo delete mode 100644 server/tests/static-data/TestPackage/Template/Data.mo create mode 100644 server/tests/static-data/TestPackage/Template/Data/PartialTemplate.mo create mode 100644 server/tests/static-data/TestPackage/Template/Data/TestTemplate.mo diff --git a/server/scripts/parse-template-package.ts b/server/scripts/parse-template-package.ts index 3435e44c..b1b40abd 100644 --- a/server/scripts/parse-template-package.ts +++ b/server/scripts/parse-template-package.ts @@ -10,10 +10,13 @@ import { loadPackage("Buildings"); +const { options, scheduleOptions } = getOptions(); + const data = { templates: getTemplates(), systemTypes: getSystemTypes(), - options: getOptions(), + options: options, + scheduleOptions: scheduleOptions, }; const dest = path.resolve( diff --git a/server/scripts/parse-test-package.ts b/server/scripts/parse-test-package.ts index c365a836..f5b2bcf5 100644 --- a/server/scripts/parse-test-package.ts +++ b/server/scripts/parse-test-package.ts @@ -13,10 +13,13 @@ loadPackage(`${fullTempDirPath}/TestPackage`); // const buildDir = `${process.cwd()}/build/modelica-json/json`; // loadPackage(`${buildDir}/Buildings/Templates`); +const { options, scheduleOptions } = getOptions(); + const data = { templates: getTemplates(), systemTypes: getSystemTypes(), - options: getOptions(), + options: options, + scheduleOptions: scheduleOptions, }; const dest = path.resolve( diff --git a/server/src/parser/index.ts b/server/src/parser/index.ts index 9969bc34..b6007e37 100644 --- a/server/src/parser/index.ts +++ b/server/src/parser/index.ts @@ -28,6 +28,9 @@ export function getSystemTypes(): templates.SystemTypeN[] { return templates.getSystemTypes(); } -export function getOptions(): parser.OptionN[] { +export function getOptions(): { + options: parser.OptionN[]; + scheduleOptions: templates.ScheduleOption[]; +} { return templates.getOptions(); } diff --git a/server/src/parser/modification.ts b/server/src/parser/modification.ts index 75feca25..a0a5461c 100644 --- a/server/src/parser/modification.ts +++ b/server/src/parser/modification.ts @@ -1,3 +1,5 @@ +import { ShortClassSpecifier } from "./parser"; + /** * Modifications are places where there is an assignment, e.g. * 'my_param=5'. Modifications can also contain groups of modifications, e.g. @@ -23,22 +25,38 @@ const modStore: Map = new Map(); -type RedeclarationMod = { +type ComponentDeclaration1 = { + declaration: DeclarationBlock; + description?: DescriptionBlock; +}; + +type ComponentClause1 = { + type_specifier: string; // Modelica Path + component_declaration1: ComponentDeclaration1; +}; + +type ShortClassDefinition = { + class_prefixes: string; // //(PARTIAL)? (CLASS | MODEL | (OPERATOR)? RECORD | BLOCK | (EXPANDABLE)? CONNECTOR | TYPE | PACKAGE | ((PURE | IMPURE))? (OPERATOR)? FUNCTION | OPERATOR), + short_class_specifier: ShortClassSpecifier; // from 'parser.ts' +}; + +type ElementReplaceable = { + component_clause1: ComponentClause1; + short_class_definition: ShortClassDefinition; +}; + +type RedeclareMod = { element_redeclaration: { - element_replaceable: { - component_clause1: { - type_specifier: string; // Modelica Path - component_declaration1: { - declaration: DeclarationBlock; - description: DescriptionBlock; - }; - }; - } + each: boolean; + final: boolean; + short_class_definition?: ShortClassDefinition; + element_replaceable?: ElementReplaceable; + component_clause1?: ComponentClause1; }; }; type ClassMod = { - class_modification: (WrappedMod | RedeclarationMod)[]; + class_modification: (WrappedMod | RedeclareMod)[]; }; type Assignment = { @@ -57,12 +75,12 @@ export type WrappedMod = { export type Mod = { name: string; - modification: ClassMod | WrappedMod | Assignment | RedeclarationMod; + modification: ClassMod | WrappedMod | Assignment | RedeclareMod; }; export type DeclarationBlock = { identifier: string; - modification?: ClassMod | WrappedMod | Assignment | RedeclarationMod; + modification?: ClassMod | WrappedMod | Assignment | RedeclareMod; }; export type DescriptionBlock = { @@ -79,14 +97,16 @@ export function getModificationList( classMod: ClassMod, modelicaPath: string, name = "", -): Modification[] { - return classMod.class_modification.map((m) => - createModification({ - definition: m as WrappedMod, - basePath: modelicaPath, - name: name, - }), - ); +) { + return classMod.class_modification + .map((m) => + createModification({ + definition: m as WrappedMod, + basePath: modelicaPath, + name: name, + }), + ) + .filter((m) => m !== undefined) as Modification[]; } interface ModificationBasics { @@ -94,10 +114,11 @@ interface ModificationBasics { name?: string; value?: any; definition?: any; + type?: string; } interface ModificationWithDefinition extends ModificationBasics { - definition: WrappedMod | Mod | DeclarationBlock; + definition: WrappedMod | Mod | DeclarationBlock | RedeclareMod; value?: never; } @@ -109,6 +130,64 @@ interface ModificationWithValue extends ModificationBasics { type ModificationProps = ModificationWithDefinition | ModificationWithValue; +function unpackRedeclaration(props: ModificationProps) { + let { definition } = props; + const redeclaration = (definition as RedeclareMod).element_redeclaration; + if ("component_clause1" in redeclaration) { + const componentClause1 = + redeclaration.component_clause1 as ComponentClause1; + const type = componentClause1.type_specifier; + const redeclareDefinition = + componentClause1.component_declaration1.declaration; + const modProps = { ...props, type, definition: redeclareDefinition }; + const redeclareMod = createModification(modProps); + return redeclareMod; + } else if ("short_class_definition" in redeclaration) { + } else if ("element_replaceable" in redeclaration) { + } +} + +function unpackModblock(props: ModificationProps) { + let mods: Modification[] = []; + let value: string = ""; + let { definition, basePath = "", name } = props as ModificationWithDefinition; + + let modBlock = definition; + + modBlock = + "element_modification_or_replaceable" in definition + ? definition.element_modification_or_replaceable.element_modification + : definition; + + if ("name" in modBlock) { + name = modBlock.name; + } else if ("identifier" in modBlock) { + name = modBlock.identifier; + } + + let modelicaPath = basePath ? `${basePath}.${name}` : ""; + const mod = (modBlock as Mod).modification; + if (mod) { + // test if an assignment + if ("equal" in mod) { + // simple_expression can potentially be an expression + // TODO be ready to feed that into Expression generator + value = (mod as Assignment).expression.simple_expression; + } else if (name == "choice") { + const choiceMod = (mod as ClassMod).class_modification[0] as RedeclareMod; + if (choiceMod.element_redeclaration) { + const replaceable = choiceMod.element_redeclaration + .element_replaceable as ElementReplaceable; + value = replaceable.component_clause1.type_specifier; + } + } else if ("class_modification" in mod) { + mods = getModificationList(mod as ClassMod, modelicaPath); + } + } + + return new Modification(basePath, name, value, mods); +} + /** * Factory method that can create a Modification from two approaches: * @@ -118,40 +197,19 @@ type ModificationProps = ModificationWithDefinition | ModificationWithValue; * @param props: ModificationProps * @returns Modification */ -export function createModification(props: ModificationProps): Modification { +export function createModification( + props: ModificationProps, +): Modification | undefined { let mods: Modification[] = []; let { definition, value, basePath = "", name } = props; let modelicaPath = basePath ? `${basePath}.${name}` : ""; if (definition) { - const modBlock = - "element_modification_or_replaceable" in definition - ? definition.element_modification_or_replaceable.element_modification - : definition; - - if ("name" in modBlock) { - name = modBlock.name; - } else if ("identifier" in modBlock) { - name = modBlock.identifier; + if ("element_redeclaration" in definition) { + return unpackRedeclaration(props); } - const mod = modBlock.modification; - if (mod) { - // test if an assignment - if ("equal" in mod) { - // simple_expression can potentially be an expression - // TODO be ready to feed that into Expression generator - value = (mod as Assignment).expression.simple_expression; - } else if (name == "choice") { - const choiceMod = (mod as ClassMod) - .class_modification[0] as RedeclarationMod; - value = - choiceMod.element_redeclaration.element_replaceable.component_clause1.type_specifier; - } else if ("class_modification" in mod) { - // const type = ""; - mods = getModificationList(mod as ClassMod, modelicaPath); - } - } + return unpackModblock(props); } return new Modification(basePath, name, value, mods); diff --git a/server/src/parser/parser.ts b/server/src/parser/parser.ts index 387903c6..f61bdf14 100644 --- a/server/src/parser/parser.ts +++ b/server/src/parser/parser.ts @@ -19,7 +19,7 @@ import { getModificationList, } from "./modification"; -const EXTEND_NAME = "__extend"; +export const EXTEND_NAME = "__extend"; // TODO: templates *should* have all types defined within a template - however there will // be upcoming changes once unit changes are supported export const MODELICA_LITERALS = ["String", "Boolean", "Real", "Integer"]; @@ -43,7 +43,7 @@ interface Description { annotation: ClassModification; } -interface ShortClassSpecifier { +export interface ShortClassSpecifier { identifier: string; short_class_specifier_value: { base_prefix: string; @@ -215,9 +215,9 @@ export class InputGroup extends Element { this.type = this.modelicaPath; this.description = specifier.description_string; - this.elementList = specifier.composition.element_list.map((e: any) => - _constructElement(e, this.modelicaPath), - ); + this.elementList = specifier.composition.element_list + .map((e: any) => _constructElement(e, this.modelicaPath)) + .filter((e: Element | undefined) => e !== undefined); this.annotation = specifier.composition.annotation?.map( (m: Mod | WrappedMod) => createModification({ definition: m }), @@ -239,9 +239,9 @@ export class InputGroup extends Element { return options; } - const children = this.elementList.filter( - (el) => Object.keys(el.getOptions(options, recursive)).length > 0, - ); + const children = this.elementList.filter((el) => { + return Object.keys(el.getOptions(options)).length > 0; + }); options[this.modelicaPath] = { modelicaPath: this.modelicaPath, @@ -253,10 +253,6 @@ export class InputGroup extends Element { .filter((c) => !(c in MODELICA_LITERALS)), }; - children.map((el) => { - options = el.getOptions(options, recursive); - }); - return options; } @@ -377,9 +373,7 @@ export class Input extends Element { } const typeInstance = typeStore.get(this.type) || null; - const typeOptions = typeInstance - ? typeInstance.getOptions(options, false) - : {}; + const typeOptions = typeInstance ? typeInstance.getOptions({}, false) : {}; const childOptions = typeOptions[this.type]?.options || []; const visible = this._setOptionVisible(typeOptions[this.type]); @@ -395,7 +389,7 @@ export class Input extends Element { visible: visible, valueExpression: this.valueExpression, enable: this.enable, - options: childOptions, + options: childOptions, // TODO: try and just use type to link to child options to prevent duplicates }; if (recursive) { @@ -432,13 +426,15 @@ export class ReplaceableInput extends Input { // the default value is original type provided this.value = this.type; - this.mods.push( - createModification({ - name: this.name, - value: this.value, - basePath: basePath, - }), - ); + const mod = createModification({ + name: this.name, + value: this.value, + basePath: basePath, + }); + + if (mod) { + this.mods.push(mod); + } // modifiers for replaceables are specified in a constraining // interface. Check if one is present to extract modifiers @@ -473,19 +469,23 @@ export class ReplaceableInput extends Input { return options; } + // if an annotation has been provided, use the choices from that annotation + // otherwise fallback to using the parameter type + const childTypes = this.choices.length ? this.choices : [this.type]; + options[this.modelicaPath] = { modelicaPath: this.modelicaPath, type: this.type, value: this.value, name: this.description, - options: this.choices, + options: childTypes, group: this.group, tab: this.tab, visible: true, }; if (recursive) { - this.choices.map((c) => { + childTypes.map((c) => { const typeInstance = typeStore.get(c) || null; if (typeInstance) { options = typeInstance.getOptions(options); @@ -623,6 +623,31 @@ export class InputGroupExtend extends Element { } } +type ImportClause = { + identifier: string; + name: string; +}; + +export class Import extends Element { + value: string; + + constructor(definition: any, basePath: string) { + super(); + const importClause = definition.import_clause as ImportClause; // arbitrary name. Important that this will not collide with other param names + this.name = importClause.identifier; + this.value = importClause.name; // path to imported type + this.modelicaPath = `${basePath}.${this.name}`; + const registered = this.registerPath(this.modelicaPath); + if (!registered) { + return; // PUNCH-OUT! + } + } + + getOptions(options: { [key: string]: OptionN } = {}, recursive = true) { + return options; + } +} + /** * Given a list of elements, discovers and returns the formatted type * @@ -637,6 +662,7 @@ function _constructElement( const extend = "extends_clause"; const component = "component_clause"; const replaceable = "replaceable"; + const importClause = "import_clause"; definition = "class_definition" in definition ? definition.class_definition : definition; @@ -655,6 +681,8 @@ function _constructElement( elementType = replaceable; } else if (component in definition) { elementType = component; + } else if (importClause in definition) { + elementType = importClause; } let element: Element | undefined; @@ -683,9 +711,19 @@ function _constructElement( case replaceable: element = new ReplaceableInput(definition, basePath); break; + case importClause: + element = new Import(definition, basePath); + break; } - return element?.duplicate ? typeStore.get(element?.modelicaPath) : element; + const result = element?.duplicate + ? typeStore.get(element?.modelicaPath) + : element; + if (!result) { + // TODO: log `definition` that could not be parsed + // console.log(`Unable to parse the following block:\n${definition}`); + } + return result; } // diff --git a/server/src/parser/template.ts b/server/src/parser/template.ts index 574e2429..c9933277 100644 --- a/server/src/parser/template.ts +++ b/server/src/parser/template.ts @@ -5,7 +5,6 @@ * and provide accessor methods to extract what is needed in linkage schema format */ -import { access } from "fs"; import * as parser from "./parser"; const templateStore = new Map(); @@ -19,19 +18,31 @@ export function getSystemTypes() { return [...systemTypeStore.values()]; } -export function getOptions(): parser.OptionN[] { - const templates = [...templateStore.values()]; +type Options = { [key: string]: parser.OptionN }; +type ScheduleOptions = { [key: string]: ScheduleOption }; - // [{'asdf': OptionN}, {'asdf': OptionN}, {}] - const options = templates.reduce( - (acc: { [key: string]: parser.OptionN }, currentValue) => { - return { ...acc, ...currentValue.getOptions() }; - }, - {}, - ); - const optionsList = Object.values(options); +export function getOptions(): { + options: parser.OptionN[]; + scheduleOptions: ScheduleOption[]; +} { + const templates = [...templateStore.values()]; + let allConfigOptions = {}; + let allScheduleOptions = {}; + + templates.map((t) => { + const { options, scheduleOptions } = t.getOptions(); + allConfigOptions = { ...allConfigOptions, ...options }; + allScheduleOptions = { ...allScheduleOptions, ...scheduleOptions }; + }); + + return { + options: Object.values(allConfigOptions), + scheduleOptions: Object.values(allScheduleOptions), + }; +} - return optionsList; +export interface ScheduleOption extends parser.OptionN { + groups: string[]; } export interface SystemTypeN { @@ -41,6 +52,7 @@ export interface SystemTypeN { export interface SystemTemplateN { modelicaPath: string; + scheduleOptionPath: string; systemTypes: string[]; name: string; } @@ -51,6 +63,7 @@ export interface ModifiersN { } export class Template { + scheduleOptionPath: string = ""; systemTypes: SystemTypeN[] = []; constructor(public element: parser.Element) { @@ -81,8 +94,62 @@ export class Template { return this.element.description; } + /* Descends tree of options removing all nodes that originate + * from the 'dat' parameter and assigns that parameter to a + * schedule options dictionary + */ + splitOptions( + path: string, + options: { [key: string]: parser.OptionN }, + scheduleOptions: { [key: string]: ScheduleOption }, + ) { + if (path in scheduleOptions) { + return; // BREAK-OUT: path already split out + // TODO: this is necessary if 'dat' records re-use types + } + const option = options[path]; + + // TODO: build up group list + scheduleOptions[path] = { groups: [], ...option }; + delete options[option.modelicaPath]; + + // option.options?.map((o) => this.splitOptions(o, options, scheduleOptions)); + } + getOptions() { - return this.element.getOptions(); + const options = this.element.getOptions(); + const scheduleOptions: { [key: string]: ScheduleOption } = {}; + + // try and find 'dat' param by checking the class definition, + // then going through each extended class + let curPath = this.modelicaPath; + let dat: parser.OptionN | null = null; + + while (!dat) { + dat = options[`${curPath}.dat`]; + + if (dat) { + // NOTE: we could just remove 'dat' as a child option preventing + // traversal to all schedule table related options instead of going + // through and deleting keys if we run into issues with performance + break; + } else { + curPath = `${curPath}.${parser.EXTEND_NAME}`; + if (!(curPath in options)) { + break; + } + const extendOption = options[curPath]; + // use extend 'type' to get to extend class options + curPath = extendOption.type; + } + } + + if (dat) { + this.scheduleOptionPath = dat.modelicaPath; + this.splitOptions(dat.modelicaPath, options, scheduleOptions); + } + + return { options, scheduleOptions }; } getSystemTypes() { @@ -92,6 +159,7 @@ export class Template { getSystemTemplate(): SystemTemplateN { return { modelicaPath: this.modelicaPath, + scheduleOptionPath: this.scheduleOptionPath, systemTypes: this.systemTypes.map((t) => t.modelicaPath), name: this.description, }; diff --git a/server/tests/integration/parser/modifiers.test.ts b/server/tests/integration/parser/modifiers.test.ts index 2181fa4f..3b37859f 100644 --- a/server/tests/integration/parser/modifiers.test.ts +++ b/server/tests/integration/parser/modifiers.test.ts @@ -22,6 +22,9 @@ describe("Modifications", () => { // just check shape modifiers.map((m) => { expect(m.modelicaPath).toBeDefined(); + if (!m.value) { + console.log(m); + } expect(m.value).toBeDefined(); }); }); diff --git a/server/tests/integration/parser/parsed-elements.test.ts b/server/tests/integration/parser/parsed-elements.test.ts index 68056fef..9a13621d 100644 --- a/server/tests/integration/parser/parsed-elements.test.ts +++ b/server/tests/integration/parser/parsed-elements.test.ts @@ -1,5 +1,5 @@ import * as parser from "../../../src/parser/parser"; -import { initializeTestModelicaJson } from "./utils"; +import { initializeTestModelicaJson, optionTree } from "./utils"; const testModelicaFile = "TestPackage.Template.TestTemplate"; describe("Basic parser functionality", () => { @@ -26,7 +26,9 @@ describe("Basic parser functionality", () => { const file = parser.getFile(testModelicaFile) as parser.File; const template = file.elementList[0] as parser.InputGroup; const expectedPath = `${template.modelicaPath}.${paramName}`; - const element = template.elementList.find((e) => e.modelicaPath === expectedPath) + const element = template.elementList.find( + (e) => e.modelicaPath === expectedPath, + ); expect(element).toBeTruthy(); }); @@ -104,7 +106,7 @@ describe("Expected Options are extracted", () => { Check that instead of a value, the option has a value expression */ it("Extracts literal value expression", () => { - const file = parser.getFile(testModelicaFile) as parser.File; + const file = parser.getFile(testModelicaFile) as parser.File; const template = file.elementList[0] as parser.InputGroup; const options = template.getOptions(); const option = options[ @@ -181,19 +183,11 @@ describe("Expected Options are extracted", () => { expect(expectedValues.length).toBe(0); enumOption?.options?.map((o) => { - const childOption = options[o] + const childOption = options[o]; expect(childOption?.visible).toBeFalsy(); }); }); - it("Extracts the expected number of options for the TestTemplate", () => { - const optionTotal = 44; - const file = parser.getFile(testModelicaFile) as parser.File; - const template = file.elementList[0] as parser.InputGroup; - const options = template.getOptions(); - expect(Object.values(options).length).toBe(optionTotal); - }); - it("Extracts expected InputGroup options", () => { const file = parser.getFile(testModelicaFile) as parser.File; const inputGroup = file.elementList[0] as parser.InputGroup; @@ -219,16 +213,23 @@ describe("Expected Options are extracted", () => { const template = file.elementList[0] as parser.InputGroup; const options = template.getOptions(); + // debugging structure to track option generation + // const optionCount: { [key: string]: number } = {}; + Object.values(options).map((o) => { const childOptions = o.options; if (childOptions) { childOptions.map((option) => { - if (!(option in options)) { - console.log(o, option); - } expect(option in options).toBeTruthy(); + // optionCount[option] = optionCount[option] + // ? optionCount[option] + 1 + // : 1; + // if (optionCount[option] > 1) { + // console.log(`${o.modelicaPath} - ${option}`); + // } }); } }); + // console.log(optionCount); }); }); diff --git a/server/tests/integration/parser/template.test.ts b/server/tests/integration/parser/template.test.ts index de99642b..07b0ae2a 100644 --- a/server/tests/integration/parser/template.test.ts +++ b/server/tests/integration/parser/template.test.ts @@ -12,7 +12,7 @@ const templatePath = "TestPackage.Template.TestTemplate"; const nestedTemplatePath = "TestPackage.NestedTemplate.Subcategory.SecondTemplate"; -describe("Basic parser functionality", () => { +describe("Template wrapper class functionality", () => { beforeAll(() => { createTestModelicaJson(); loadPackage(`${fullTempDirPath}/TestPackage`); @@ -53,7 +53,7 @@ describe("Basic parser functionality", () => { ) as Template; const systemTemplate = template.getSystemTemplate(); - const options = getOptions(); + const { options, scheduleOptions } = getOptions(); const systemTemplateOptions = options.find( (o) => o.modelicaPath === systemTemplate.modelicaPath, @@ -63,6 +63,14 @@ describe("Basic parser functionality", () => { ); }); + it("Templates generate separate schedule options and configuration options", () => { + const datPath = 'TestPackage.Template.TestTemplate.dat'; + + const { scheduleOptions } = getOptions(); + const datScheduleOption = scheduleOptions.find( o => o.modelicaPath === datPath); + expect(datScheduleOption).toBeTruthy(); + }); + it("Keeps system types in correct order", () => { // The system types should match the directory order const templates = getTemplates(); diff --git a/server/tests/integration/parser/utils.ts b/server/tests/integration/parser/utils.ts index a4c71e11..c2322df7 100644 --- a/server/tests/integration/parser/utils.ts +++ b/server/tests/integration/parser/utils.ts @@ -20,3 +20,18 @@ export function initializeTestModelicaJson() { createTestModelicaJson(); parser.setPathPrefix(fullTempDirPath); } + +type SimpleOption = { + name: string; + path: string; + options: SimpleOption[]; +} + +/* + * De-normalizes options and returns the denormalized shape + */ +export function optionTree(optionsN: {[key: string]: parser.OptionN}, rootPath: string): SimpleOption { + const root = optionsN[rootPath]; + const options = (root.options) ? root.options.map(o => optionTree(optionsN, o)) : []; + return { name: root.name, path: root.modelicaPath, options: options}; +} diff --git a/server/tests/static-data/TestPackage/Component/Data/TestTemplateController.mo b/server/tests/static-data/TestPackage/Component/Data/TestTemplateController.mo new file mode 100644 index 00000000..2a45d5f4 --- /dev/null +++ b/server/tests/static-data/TestPackage/Component/Data/TestTemplateController.mo @@ -0,0 +1,9 @@ +within TestPackage.Component.Data; +record TestTemplateController "Record for TestTemplate Controller" + extends Modelica.Icons.Record; + + parameter Buildings.Templates.AirHandlersFans.Types.ReliefReturnSection typSecRel + "Relief/return air section type" + annotation (Evaluate=true, Dialog(group="Configuration", enable=true)); + +end TestTemplateController; diff --git a/server/tests/static-data/TestPackage/Template/Data.mo b/server/tests/static-data/TestPackage/Template/Data.mo deleted file mode 100644 index 42a3e562..00000000 --- a/server/tests/static-data/TestPackage/Template/Data.mo +++ /dev/null @@ -1,28 +0,0 @@ -within TestPackage.Template; -package Data "Test Data Package" - extends Modelica.Icons.Package; - - record TestRecord "Test Record" - extends Modelica.Icons.Record; - - parameter String record_parameter="Record Parameter" - annotation (Evaluate=true, Dialog(group="Configuration")); - - final parameter Boolean nested_bool=true; - - /* - Test propagating UP a configuration parameter from a subcomponent - (inner/outer declarations are not allowed in records so an explicit parameter binding is needed - i.e. we cannot access selectable_component.container as in TestPackage.Component.ThirdComponent) - */ - parameter TestPackage.Types.Container container_selectable_component - "Container Type" - annotation (Evaluate=true, Dialog(group="Configuration")); - - parameter Boolean flag_enabled(start=true) - "Uninitialized parameter with enable attribute and start value" - annotation(Dialog( - enable=container_selectable_component==TestPackage.Types.Container.Bowl)); - end TestRecord; - -end Data; diff --git a/server/tests/static-data/TestPackage/Template/Data/PartialTemplate.mo b/server/tests/static-data/TestPackage/Template/Data/PartialTemplate.mo new file mode 100644 index 00000000..066b1bb7 --- /dev/null +++ b/server/tests/static-data/TestPackage/Template/Data/PartialTemplate.mo @@ -0,0 +1,13 @@ +within TestPackage.Template.Data; +record PartialTemplate "Record for TestTemplate Interface Class" + extends Modelica.Icons.Record; + + replaceable parameter TestPackage.Component.Data.TestTemplateController + ctl(final typSecRel=typSecRel) + "Controller" + annotation (Dialog(group="Controls")); + + parameter Boolean partialData=false + "Partial Data Parameter" + annotation (Evaluate=true, Dialog(group="Configuration"), enable=true); +end PartialTemplate; \ No newline at end of file diff --git a/server/tests/static-data/TestPackage/Template/Data/TestTemplate.mo b/server/tests/static-data/TestPackage/Template/Data/TestTemplate.mo new file mode 100644 index 00000000..8b1ccb9d --- /dev/null +++ b/server/tests/static-data/TestPackage/Template/Data/TestTemplate.mo @@ -0,0 +1,28 @@ +within TestPackage.Template.Data; +record TestTemplate "Test Record" + extends TestPackage.Template.Data.PartialTemplate( + redeclare TestPackage.Component.Data.TestTemplateController + ctl( + final typSecRel=typSecRel + ) + ); + + parameter String record_parameter="Record Parameter" + annotation (Evaluate=true, Dialog(group="Configuration")); + + final parameter Boolean nested_bool=true; + + /* + Test propagating UP a configuration parameter from a subcomponent + (inner/outer declarations are not allowed in records so an explicit parameter binding is needed + i.e. we cannot access selectable_component.container as in TestPackage.Component.ThirdComponent) + */ + parameter TestPackage.Types.Container container_selectable_component + "Container Type" + annotation (Evaluate=true, Dialog(group="Configuration")); + + parameter Boolean flag_enabled(start=true) + "Uninitialized parameter with enable attribute and start value" + annotation(Dialog( + enable=container_selectable_component==TestPackage.Types.Container.Bowl)); +end TestRecord; diff --git a/server/tests/static-data/TestPackage/Template/TestTemplate.mo b/server/tests/static-data/TestPackage/Template/TestTemplate.mo index 200531be..e00ef93e 100644 --- a/server/tests/static-data/TestPackage/Template/TestTemplate.mo +++ b/server/tests/static-data/TestPackage/Template/TestTemplate.mo @@ -39,7 +39,7 @@ model TestTemplate "Test Template" /* Test Record */ - parameter TestPackage.Template.Data.TestRecord dat( + parameter TestPackage.Template.Data.TestTemplate dat( final container_selectable_component=selectable_component.container) "Record with additional parameters";