Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
836 changes: 120 additions & 716 deletions client/src/data/templates.json

Large diffs are not rendered by default.

78 changes: 61 additions & 17 deletions docs/linkage-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ Read-Only Data types (static data extracted from modelica packages)
- `SystemType`: template category
- `Template`: Modelica Template
- `Option`: a node of info from the template that either specifies how to render a dropdown or bit of UI, or links to a list of child `Option`s
- `ScheduleOption`: Like an `Option` but it also has a list of `ScheduleCategory`s
- `ScheduleCategory`: Category for `ScheduleOption`
- `ScheduleOption`: a `Table` object representing the column headers and cells of a schedule table derived from a Modelica record class

Write Data types (data created from user input):

Expand Down Expand Up @@ -152,24 +151,67 @@ export type Expression = {
`Operator`: a string indicating which operation. `none` is a special operator indiciating the operand is a `Literal` that requires no evaluation.
`Operands`: a list that can either be a literal value (like the number `4`) or another expression to resolve

### Schedule Table reorganization
### Schedule Table

Inputs for the schedule table are generically extracted in the same option tree used for configurations. The parser needs to be updated to unpack the tree into more convenient format as well as include grouping information for the schedule table.
Scheduled parameters are extracted from Modelica record classes specified for each template using the class annotation `__ctrlFlow(schedule={"modelica://..."})`.

`ScheduleGroup`:
The schedule parser transforms these records into a structured `Table` object that represents both the header structure (columns) and data cells.

`LeafColumn` — a concrete parameter column:

```typescript
interface ScheduleGroup {
modelicaPath: string;
name: string;
interface LeafColumn {
kind: "leaf";
instanceName: string; // relative instance name, e.g. "ctl.k"
label: string; // display label, e.g. "Air flow rate"
type: string;
value?: number | string | boolean | Expression; // default binding
final?: boolean; // true if locked by a final modifier
unit?: string;
displayUnit?: string;
min?: number | Expression;
max?: number | Expression;
start?: number | Expression;
enable?: boolean | Expression;
}
```

`GroupColumn` — a named column group, nestable arbitrarily deep:
- Created from `Dialog(group=...)` or `Dialog(tab=...)` annotations
- Created for record-typed component instances (children are the record's parameters)

```typescript
interface GroupColumn {
kind: "group";
label: string; // e.g. "Cooling coil"
instanceName?: string; // relative instance name, present only for record instances
type?: string;
children: Column[]; // nested LeafColumn or GroupColumn entries
enable?: boolean | Expression;
}
```

```typescript
type Column = LeafColumn | GroupColumn;
```

`Cell` — a single data value for a given row and column:

```typescript
interface Cell {
rowIndex: number;
columnInstanceName: string; // matches a LeafColumn instanceName
value: string | number | boolean | null;
}
```

`ScheduleOption`:
`Table` — the complete schedule table:

```typescript
interface ScheduleOption extends OptionInterface {
ScheduleGroups: string[]; // in-order list of schedule groups
interface Table {
configuration: string; // effective type path of the 'cfg' component
columns: Column[]; // tree of headers
cells: Cell[]; // data cells, filled at UI runtime
}
```

Expand All @@ -178,15 +220,17 @@ From this example table:
```
| group1 |
| subgroup1 | subgroup2 | subgroup3 |
|--------|-----------------|--------|--------|-----------------|
| param1 | param2 | param3 | param4 | param5 | param6 | param7 |
Row |--------|-----------------|--------|--------|-----------------|
index | param1 | param2 | param3 | param4 | param5 | param6 | param7 |
|-------|--------|--------|--------|--------|--------|--------|--------|
| row1 | val1 | val2 | val3 | val4 | val5 | val6 | val7 |
| row2 | ...
| 1 | val1 | val2 | val3 | val4 | val5 | val6 | val7 |
| 2 | ...
```

- Each 'val' cell is its own `Option` with a unique modelica path
- Each `Option` can be grouped in an arbitrary number of groups. `val1` is in no groups, `val2` in one ('subgroup1') and `val4` in two ('group1', 'subgroup2'). The `ScheduleGroups` array for `val4` would look as follows: `['group1', 'subgroup2']`
- `param1` is a `LeafColumn` at the top level (`columns` array directly).
- `param2` and `param3` are `LeafColumn` entries nested inside a `GroupColumn` with `label: "subgroup1"`.
- `param4` and `param5` are `LeafColumn` entries nested inside a `GroupColumn` with `label: "subgroup2"`, which is itself nested inside a `GroupColumn` with `label: "group1"`.
- Each 'val' cell is a `Cell` whose `columnInstanceName` matches the corresponding `LeafColumn`'s `instanceName`.

## Write Data - User Configurations

Expand Down
2 changes: 2 additions & 0 deletions server/src/parser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import path from "path";
import * as loader from "./loader";
import * as parser from "./parser";
import * as templates from "./template";
import * as schedule from "./schedule";
export { SystemTypeN as SystemType, Template } from "./template";
export { buildParameterTable } from "./schedule";

/**
*
Expand Down
21 changes: 10 additions & 11 deletions server/src/parser/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,15 @@ 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.
* - Currently, templateNodes is flattened before being returned.
* - In the future, when the UI can handle nested packages, templateNodes should be
* used directly 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,
): { className: string; json: Object | undefined }[] {
const entryPoints: { className: string; json: Object | undefined }[] = [];
MODELICA_JSON_PATH.forEach((dir) => {
// 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.
Expand Down Expand Up @@ -208,13 +207,13 @@ function getPathFromClassName(
* @returns The loaded JSON object or undefined if not found
*/
export function loader(className: string): Object | undefined {
// TODO: allow modelica paths
if (!className.startsWith("Modelica")) {
for (const dir of MODELICA_JSON_PATH) {
const jsonFile = getPathFromClassName(className, dir);
if (jsonFile && fs.existsSync(jsonFile)) {
return require(jsonFile);
}
if (className.startsWith("Modelica") && !className.startsWith("Modelica.Units")) {
return undefined;
}
for (const dir of MODELICA_JSON_PATH) {
const jsonFile = getPathFromClassName(className, dir);
if (jsonFile && fs.existsSync(jsonFile)) {
return require(jsonFile);
}
}
}
26 changes: 21 additions & 5 deletions server/src/parser/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,14 @@ export class ShortClass extends Element {
this.description = definition.description?.description_string;
this.replaceable = definition.replaceable;

if (specifier.value?.class_modification) {
this.mods = getModificationList(
specifier.value,
this.modelicaPath,
this.type,
);
}

if (this.replaceable) {
initializeReplaceable(this, definition, basePath);
}
Expand Down Expand Up @@ -714,7 +722,7 @@ function setUIInfo(instance: Element): void {
instance.group = group ? evaluateExpression(group) : "";
instance.tab = tab ? evaluateExpression(tab) : "";
const _enable = isDisabledGroup ? false : true;
instance.enable = enable ? enable : _enable;
instance.enable = enable ? evaluateExpression(enable) : _enable;
} else {
instance.enable = isDisabledGroup ? instance.enable : true;
}
Expand Down Expand Up @@ -1068,12 +1076,20 @@ function _constructElement(
let element: Element | undefined;

switch (elementType) {
case "type":
case "type": {
// May only be predefined types, enumerations, array of type, or classes extending from type.
// Synctatically, these are short class definitions, but they need to be treated specifically
// as they define enumerations that are used in the parameter dialogs.
element = new Enumeration(definition, basePath, elementType);
// Route to Enumeration only if the short class specifier has an enum_list;
// otherwise it's a type alias (e.g. MassFlowRate = Real(...)) and goes to ShortClass.
const typeSpecifier =
(definition.class_definition ?? definition).class_specifier
.short_class_specifier;
if (typeSpecifier?.value?.enum_list) {
element = new Enumeration(definition, basePath, elementType);
} else {
element = new ShortClass(definition, basePath, elementType);
}
break;
}
case "class":
case "connector":
case "model":
Expand Down
Loading
Loading