Skip to content

Commit

Permalink
Update routing logic and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoineGautier committed Jan 10, 2025
1 parent 21e691a commit 48036cf
Show file tree
Hide file tree
Showing 11 changed files with 69 additions and 73 deletions.
2 changes: 1 addition & 1 deletion server/bin/install-modelica-dependencies.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/sh
set -x

MODELICA_BUILDINGS_COMMIT=6263b4788b2013a5a5ac3bf5ef163d453995058c
MODELICA_BUILDINGS_COMMIT=5ebd8af4297bb2aabc57f50c2ab9db1f9590c9dc
MODELICA_JSON_COMMIT=a46a361c3047c0a2b3d1cfc9bc8b0a4ced16006a

parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )
Expand Down
72 changes: 34 additions & 38 deletions server/src/parser/loader.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import path from "path";
import fs from "fs";
import { execSync } from "child_process";
Expand All @@ -7,9 +6,9 @@ import { typeStore } from "./parser";
import config from "../../src/config";

// The following arrays are populated with the ***full class names***
// of templates (identified with class annotation __ctrlFlow(routing = "template")
// of templates identified with class annotation __ctrlFlow(routing="template")
// and all packages containing templates up to the root package
// (identified with class annotation __ctrlFlow(routing = "root").
// identified with class annotation __ctrlFlow(routing="root").
export const TEMPLATE_LIST: string[] = [];
export const PACKAGE_LIST: string[] = [];

Expand All @@ -36,6 +35,10 @@ type TemplateNode = {
parentName: string | null;
};

// Master list of TemplateNode objects representing all templates and packages.
// It is intended to be used in the future when the UI can handle nested packages.
const templateNodes: TemplateNode[] = [];

export function getClassNameFromRelativePath(filePath: string) {
filePath = filePath.endsWith(".json") ? filePath.slice(0, -5) : filePath;
return filePath.replace(/\//g, ".");
Expand All @@ -48,10 +51,6 @@ export function getClassNameFromJson(json: any): string {
);
}

function getWithinFromClassName(className: string): string {
return className.split(".").slice(0, -1).join(".");
}

/**
* Creates a TemplateNode object from a JSON representation of a Modelica class.
* @param json - The JSON object containing the Modelica class definition
Expand Down Expand Up @@ -91,48 +90,52 @@ function systemGrep(regExp: string, dirPath: string): string[] | null {
* Finds all entry points that contain the template identifier for a given package.
* - LIMITATION: This function requires that the package uses
* [Directory Hierarchy Mapping](https://specification.modelica.org/maint/3.6/packages.html#directory-hierarchy-mapping)
* - Currently, only entryPoints, TEMPLATE_LIST and PACKAGE_LIST are used.
* - In the future, when the UI can handle nested packages, entryPoints should be removed
* and templateNodes should be used to create the file tree structure.
* @param packageName - The Modelica class name of the package to search for entry points
* @returns An array of objects containing the path and parsed JSON for each entry point found
*/
export function findPackageEntryPoints(
packageName: string,
): { path: string; json: Object | undefined }[] {
const templates: any[] = [];
const templateNodes: TemplateNode[] = [];
const entryPoints: { path: string; json: Object | undefined }[] = [];
): { className: string; json: Object | undefined }[] {
const entryPoints: { className: string; json: Object | undefined }[] = [];
MODELICA_JSON_PATH.forEach((dir) => {
// We need a top directory to look up for entry points
// so we can simply convert the class name to a relative path
// without adding any file extension.
// We can simply convert the class name to a relative path without adding any file extension
// because we only need a top directory to look up for entry points.
const dirPath = path.resolve(dir, packageName.replace(/\./g, "/"));
if (fs.existsSync(dirPath)) {
// Find all template files.
const templatePaths = systemGrep(TEMPLATE_IDENTIFIER, dirPath);

// Populate arrays storing templates.
templatePaths?.forEach((p) => {
templates.push(require(p));
const templateJson = loader(p);
const templateNode = createTemplateNode(templateJson);
TEMPLATE_LIST.push(templateNode.className);
templateNodes.push(templateNode);
});

// Find root package.
const rootPackagePath = systemGrep(TEMPLATE_ROOT, dirPath)?.[0];
if (!rootPackagePath) {
console.error("Error: root package not found in: " + dirPath);
console.error("Error: root package not found in " + dirPath);
process.exit(1);
}
const rootPackageJson = require(rootPackagePath);
const rootPackageJson = loader(rootPackagePath);
const rootPackageName = getClassNameFromJson(rootPackageJson);

// Iterate over all template files up to the root package and populate the templateNodes array.
for (let template of templates) {
const templateNode = createTemplateNode(template);
TEMPLATE_LIST.push(templateNode.className);
templateNodes.push(templateNode);
let packageName = template.within;
// Iterate over all template files up to the root package and populate
// templateNodes, TEMPLATE_LIST, PACKAGE_LIST and entryPoints.

for (let templateJson of [...templateNodes.map(({json}) => json)]) {
let packageName = (templateJson as any).within;
while (packageName && packageName !== rootPackageName) {
const packagePath = getPathFromClassName(packageName, dirPath);
const packagePath = getPathFromClassName(packageName, dir);
if (!packagePath) {
break;
}
const packageJson = require(packagePath);
const packageJson = loader(packagePath);
// If the package is already in the templateNodes array, its parents have also been added.
if (
templateNodes
Expand All @@ -141,27 +144,20 @@ export function findPackageEntryPoints(
) {
break;
}
if (!packageJson) {
continue;
}
const packageNode = createTemplateNode(packageJson);
PACKAGE_LIST.push(packageNode.className);
templateNodes.push(packageNode);

// Temporary fix until the UI can handle nested packages:
// We only store in entryPoints the immediate parent package.
if (packageName === templateNode.parentName) {
const relativePath = path.relative(dir, packagePath);
entryPoints.push({
path: relativePath,
json: getClassNameFromRelativePath(relativePath),
})
}

packageName = packageJson.within;
packageName = (packageJson as any).within;
}
}
}
});

return entryPoints;
return templateNodes.map(({ className, json }) => { return { className, json }; });
}

/**
Expand Down Expand Up @@ -224,4 +220,4 @@ export function loader(className: string): Object | undefined {
}
}
}
}
}
19 changes: 8 additions & 11 deletions server/src/parser/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* that element is available if referenced by another piece of modelica-json.
*/

import { findPackageEntryPoints, loader, TEMPLATE_IDENTIFIER } from "./loader";
import { findPackageEntryPoints, loader, TEMPLATE_LIST, PACKAGE_LIST } from "./loader";
import { Template } from "./template";
import {
createModification,
Expand Down Expand Up @@ -390,18 +390,15 @@ export class InputGroup extends Element {
);
this.deadEnd = this.getLinkageKeywordValue() === false;

if (
this.annotation &&
this.annotation.find((m) => m.name === TEMPLATE_IDENTIFIER)
) {
if ([...TEMPLATE_LIST, ...PACKAGE_LIST].includes(this.modelicaPath)) {
this.entryPoint = true;
if (definition.class_prefixes === "model") {
if (TEMPLATE_LIST.includes(this.modelicaPath)) {
new Template(this);
}
}
}

/**
/**@
* Returns the list of elements with all inherited elements flattened
*/
getChildElements(useDeadEnd = false): Element[] {
Expand Down Expand Up @@ -953,9 +950,9 @@ function _constructElement(
export class File {
public package = ""; // only important part of a file
public elementList: Element[] = [];
constructor(obj: any, filePath: string) {
constructor(obj: any, className: string) {
this.package = obj.within;
const splitFilePath = filePath.split(".");
const splitFilePath = className.split(".");
if (this.package) {
// Check that each portion of the within path matches the file path
// a mismatch means an incorrectly typed or structured package
Expand Down Expand Up @@ -998,8 +995,8 @@ export const getFile = (className: string) => {
* @param packageName - The full class name of the package to load (e.g. "Library.Package.SubPackage")
*/
export const loadPackage = (packageName: string) => {
const paths = findPackageEntryPoints(packageName);
paths?.map(({ json, path }) => new File(json, path));
const entryPoints = findPackageEntryPoints(packageName);
entryPoints?.map(({ json, className }) => new File(json, className));

// Attempt to load project settings from a pre-defined path
const projectPath = "Buildings.Templates.Data.AllSystems";
Expand Down
36 changes: 22 additions & 14 deletions server/src/parser/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function getOptions(): {
export interface Option {
type: string;
name: string;
modelicaPath: string;
modelicaPath: string; // Modelica fully qualified class name
visible: boolean;
options?: string[];
group?: Literal | string;
Expand Down Expand Up @@ -180,19 +180,19 @@ function _mapInputToOption(input: parser.TemplateInput): Option {

export interface SystemTypeN {
description: string;
modelicaPath: string;
modelicaPath: string; // Modelica fully qualified class name
}

export interface SystemTemplateN {
modelicaPath: string;
modelicaPath: string; // Modelica fully qualified class name
pathModifiers: { [key: string]: string };
scheduleOptionPaths: string[];
systemTypes: string[];
name: string;
}

export interface ModifiersN {
modelicaPath: string;
modelicaPath: string; // Modelica fully qualified class name
value: any;
}

Expand Down Expand Up @@ -222,17 +222,25 @@ export class Template {
_extractSystemTypes(element: parser.Element) {
const path = element.modelicaPath.split(".");
path.pop();
while (path.length) {
const type = parser.findElement(path.join("."));
if (type && type.entryPoint) {
const systemType = {
description: type.description,
modelicaPath: type.modelicaPath,
};
this.systemTypes.unshift(systemType);
systemTypeStore.set(type.modelicaPath, systemType);

// Fix for https://github.com/lbl-srg/ctrl-flow-dev/issues/422
// Currently the UI does not support nested system types.
// Therefore, we only add the Modelica class name of the containing package.
const type = parser.findElement(path.join("."));
if (type && type.entryPoint) {
if (!type.description) {
console.error(
"Error: missing class description string in entry point " +
type.modelicaPath,
);
process.exit(1);
}
path.pop();
const systemType = {
description: type.description,
modelicaPath: type.modelicaPath,
};
this.systemTypes.unshift(systemType);
systemTypeStore.set(type.modelicaPath, systemType);
}
}

Expand Down
2 changes: 1 addition & 1 deletion server/tests/static-data/Buildings/Templates/package.mo
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
within Buildings;
package Templates "Mock Templates Folder"
extends Modelica.Icons.Package;

annotation(__ctrlFlow(routing="root"));
end Templates;
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ model SecondTemplate "Subcategory Template"
parameter String second_template_param="2nd Template Param"
"Second Template Parameter"
annotation (Evaluate=true, Dialog(group="Configuration"));
annotation (__ctrlFlow_template=true);
annotation (__ctrlFlow(routing="template"));
end SecondTemplate;
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
within TestPackage.NestedTemplate;
package Subcategory "Test Subcategory"
extends Modelica.Icons.Package;
annotation(__ctrlFlow_template=true);
end Subcategory;
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
within TestPackage;
package NestedTemplate "Nested Templates Package"
extends Modelica.Icons.Package;
annotation(__ctrlFlow_template=true);
end NestedTemplate;
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,5 @@ model TestTemplate "Test Template"
"Third Test Component")),
Dialog(group="Selectable Component"));

annotation (__ctrlFlow_template=true);

annotation(__ctrlFlow(routing="template"));
end TestTemplate;
2 changes: 0 additions & 2 deletions server/tests/static-data/TestPackage/Template/package.mo
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
within TestPackage;
package Template "Test Template"
extends Modelica.Icons.Package;

annotation (__ctrlFlow_template=true);
end Template;
2 changes: 1 addition & 1 deletion server/tests/static-data/TestPackage/package.mo
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
within ;
package TestPackage "Test Package for Modelica Templates"
extends Modelica.Icons.Package;

annotation(__ctrlFlow(routing="root"));
end TestPackage;

0 comments on commit 48036cf

Please sign in to comment.