Skip to content

Commit b95b336

Browse files
committed
feat(core): fmu synthetic connectors support integer, boolean, and string variables
1 parent b93d127 commit b95b336

1 file changed

Lines changed: 32 additions & 13 deletions

File tree

  • packages/core/src/compiler/modelica

packages/core/src/compiler/modelica/fmu.ts

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ interface FmuScalarVariable {
3232
causality: "input" | "output" | "local" | "parameter" | "calculatedParameter" | "independent";
3333
variability: "continuous" | "discrete" | "fixed" | "tunable" | "constant";
3434
description: string;
35-
start?: number;
35+
type: "Real" | "Integer" | "Boolean" | "String" | "Enumeration";
36+
start?: number | boolean | string;
3637
}
3738

3839
// ── XML parsing ──
@@ -73,7 +74,14 @@ function parseFmuModelDescription(xml: string): {
7374
"continuous") as FmuScalarVariable["variability"];
7475
const varDesc = headerAttrs.match(/\bdescription\s*=\s*"([^"]*)"/)?.[1] ?? "";
7576

76-
// Extract start value from nested <Real>, <Integer>, etc.
77+
// Extract type based on inner tags
78+
let type: FmuScalarVariable["type"] = "Real";
79+
if (attrs.includes("<Integer")) type = "Integer";
80+
else if (attrs.includes("<Boolean")) type = "Boolean";
81+
else if (attrs.includes("<String")) type = "String";
82+
else if (attrs.includes("<Enumeration")) type = "Enumeration";
83+
84+
// Extract start value from nested tags
7785
const startMatch = attrs.match(/\bstart\s*=\s*"([^"]*)"/);
7886
const start = startMatch ? parseFloat(startMatch[1] ?? "") : undefined;
7987

@@ -82,6 +90,7 @@ function parseFmuModelDescription(xml: string): {
8290
causality,
8391
variability,
8492
description: varDesc,
93+
type,
8594
...(Number.isFinite(start) ? { start: start as number } : {}),
8695
});
8796
}
@@ -159,23 +168,24 @@ function extractFromZip(zipData: Uint8Array, targetName: string): Uint8Array | n
159168
function createSyntheticConnector(
160169
parent: ModelicaClassInstance,
161170
isInput: boolean,
162-
realType: ModelicaClassInstance | null,
171+
baseType: ModelicaClassInstance | null,
172+
typeName = "Real",
163173
): ModelicaClassInstance {
164174
// If we have the predefined Real type, clone it and set classKind to CONNECTOR.
165175
// This makes the synthetic connector structurally identical to MSL's
166176
// `connector RealInput = input Real`, ensuring proper plug-compatibility.
167-
if (realType) {
168-
const connector = realType.clone();
177+
if (baseType) {
178+
const connector = baseType.clone();
169179
connector.classKind = ModelicaClassKind.CONNECTOR;
170180
// Keep the original Real name — isTypeCompatibleWith matches predefined types by name.
171181
// classKind=CONNECTOR is what makes diagram renderers recognize this as a port.
172182
connector.declaredElements = [];
173183
return connector;
174184
}
175-
// Fallback when Real is not available (no MSL loaded)
185+
// Fallback when base type is not available (no MSL loaded)
176186
const connector = new ModelicaClassInstance(parent);
177187
connector.classKind = ModelicaClassKind.CONNECTOR;
178-
connector.name = isInput ? "RealInput" : "RealOutput";
188+
connector.name = isInput ? `${typeName}Input` : `${typeName}Output`;
179189
connector.instantiated = true;
180190
connector.declaredElements = [];
181191

@@ -436,8 +446,13 @@ export class ModelicaFmuEntity extends ModelicaClassInstance {
436446
this.declaredElements = [];
437447
this.#syntheticComponents = [];
438448

439-
// Resolve the predefined Real type for non-port component class instances
440-
const realType = this.root?.resolveSimpleName("Real") as ModelicaClassInstance | null;
449+
// Resolve the predefined types for non-port and port component class instances
450+
const types = {
451+
Real: this.root?.resolveSimpleName("Real") as ModelicaClassInstance | null,
452+
Integer: this.root?.resolveSimpleName("Integer") as ModelicaClassInstance | null,
453+
Boolean: this.root?.resolveSimpleName("Boolean") as ModelicaClassInstance | null,
454+
String: this.root?.resolveSimpleName("String") as ModelicaClassInstance | null,
455+
};
441456

442457
// Segregate by causality to calculate port spacing
443458
const inputs = this.fmuVariables.filter((v) => v.causality === "input");
@@ -462,10 +477,14 @@ export class ModelicaFmuEntity extends ModelicaClassInstance {
462477
comp.causality = ModelicaCausality.OUTPUT;
463478
}
464479

480+
// Find the right base type for this FMU variable
481+
const typeName = v.type === "Enumeration" ? "Integer" : v.type;
482+
const baseType = types[typeName as keyof typeof types];
483+
465484
if (isInput || isOutput) {
466485
// Create a synthetic CONNECTOR class instance so diagram renderers
467486
// recognise this component as a port (they filter for classKind === CONNECTOR).
468-
comp.classInstance = createSyntheticConnector(this, isInput, realType);
487+
comp.classInstance = createSyntheticConnector(this, isInput, baseType, typeName);
469488

470489
let y = 0;
471490
if (isInput) {
@@ -508,9 +527,9 @@ export class ModelicaFmuEntity extends ModelicaClassInstance {
508527
return ModelicaComponentInstance.prototype.annotation.call(this, name, annotations) as T | null;
509528
};
510529
} else {
511-
// Non-port variables: use Real type
512-
if (realType) {
513-
comp.classInstance = realType.clone();
530+
// Non-port variables: use base type
531+
if (baseType) {
532+
comp.classInstance = baseType.clone();
514533
}
515534
}
516535

0 commit comments

Comments
 (0)